1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
'use strict';
const path = require('node:path');
function quote(value, shell) {
if (/[\r\n\0]/.test(value)) throw new Error('Run paths must not contain line breaks or NUL characters.');
if (shell === 'cmd') return `"${value}"`;
// PowerShell also ends single-quoted strings at the typographic quotes U+2018 to U+201B.
if (shell === 'powershell') return "'" + value.replace(/['\u2018-\u201B]/g, m => m + m) + "'";
return "'" + value.replace(/'/g, "'\\''") + "'";
}
function buildRunCommand(compiler, file, cwd, shell) {
const run = `${shell === 'powershell' ? '& ' : ''}${quote(compiler, shell)} ${quote(file, shell)} -r`;
if (shell === 'powershell') return `Set-Location -LiteralPath ${quote(cwd, shell)}; if ($?) { ${run} }`;
return `cd ${shell === 'cmd' ? '/d ' : ''}${quote(cwd, shell)} && ${run}`;
}
// Adapted from Code Runner's isPowershellOnWindows; also recognize pwsh and named profiles.
function terminalShell(vscode, platform) {
if (platform !== 'win32') return 'posix';
const config = vscode.workspace.getConfiguration('terminal');
const profileName = config.get('integrated.defaultProfile.windows');
const profile = config.get('integrated.profiles.windows', {})[profileName];
const profilePath = Array.isArray(profile?.path) ? profile.path[0] : profile?.path;
const shell = profilePath || profile?.source || profileName || vscode.env.shell;
if (/(?:powershell|pwsh)/i.test(shell)) return 'powershell';
if (/(?:^|[\\/])cmd(?:\.exe)?$|^Command Prompt$/i.test(shell)) return 'cmd';
throw new Error('Choose PowerShell or Command Prompt as the default Windows terminal profile.');
}
function registerRunner(vscode, context, platform = process.platform) {
let terminal = null;
let shell;
let preparing = false;
const command = vscode.commands.registerCommand('cie-pseudocode.runFile', async uri => {
if (preparing) return;
preparing = true;
try {
let document = uri ? await vscode.workspace.openTextDocument(uri) : vscode.window.activeTextEditor?.document;
if (!document || document.languageId !== 'cie-pseudocode') {
vscode.window.showErrorMessage('Open a CIE pseudocode file to run.');
return;
}
const wasUntitled = document.isUntitled;
if (wasUntitled) await vscode.window.showTextDocument(document);
if (!(await document.save())) return;
// Saving an untitled document replaces it with a new file-backed document.
if (wasUntitled) document = vscode.window.activeTextEditor?.document;
if (!document || document.isUntitled || document.uri.scheme !== 'file') {
vscode.window.showErrorMessage('Save the pseudocode to a local file before running.');
return;
}
const cwd = vscode.workspace.getWorkspaceFolder(document.uri)?.uri.fsPath || path.dirname(document.uri.fsPath);
const compiler = vscode.workspace.getConfiguration('cie-pseudocode', document.uri).get('compilerPath', 'cpsc');
if (!compiler.trim()) throw new Error('Set CIE Pseudocode: Compiler Path to cpsc or its full path.');
const runShell = terminal ? shell : terminalShell(vscode, platform);
const text = buildRunCommand(compiler, document.uri.fsPath, cwd, runShell);
// Adapted from Code Runner's executeCommandInTerminal (see ThirdPartyNotices.md).
if (terminal === null) {
terminal = vscode.window.createTerminal('CIE Pseudocode');
shell = runShell;
}
terminal.show(false);
terminal.sendText(text);
} catch (error) {
vscode.window.showErrorMessage(`Could not run pseudocode: ${error.message}`);
} finally {
preparing = false;
}
});
context.subscriptions.push(
command,
vscode.window.onDidCloseTerminal(closed => {
if (closed === terminal) terminal = null;
}),
{ dispose() { terminal?.dispose(); } }
);
}
module.exports = { buildRunCommand, registerRunner };
|