Skip to content

Commit afc2f59

Browse files
authored
Merge pull request #20 from processing/process-control
Process controlled launching of sketches
2 parents 7df6e2e + 3e29e93 commit afc2f59

File tree

7 files changed

+278
-76
lines changed

7 files changed

+278
-76
lines changed

client/src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { setupSidebar } from './setupSidebar';
99
import { setupDecorators } from './setupDecorators';
1010
import { setupPDEFiles } from './setupPDEFiles';
1111
import { EventEmitter } from 'stream';
12+
import setupConsole from './setupConsole';
1213

1314

1415
export interface ProcessingVersion {
@@ -38,6 +39,7 @@ export async function activate(context: ExtensionContext) {
3839
setupCommands(context);
3940
setupLanguageServer();
4041
setupSidebar(context);
42+
setupConsole(context);
4143
setupDecorators(context);
4244
setupPDEFiles();
4345
}

client/src/setupCommands.ts

Lines changed: 70 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,77 @@
11
import { basename, dirname, join } from 'path';
22
import { ExtensionContext, commands, Uri, window, workspace } from 'vscode';
3-
import { state } from './extension';
3+
// import { state } from './extension';
44

5-
const sketchNumber = 0;
5+
// const sketchNumber = 0;
66

77
export function setupCommands(context: ExtensionContext) {
8-
const runSketch = commands.registerCommand('processing.sketch.run', (resource: Uri) => {
9-
// TODO: Use VScode contexts to highlight run button when sketch is running, blocked until we do not run the sketch in a terminal
10-
// https://code.visualstudio.com/api/references/when-clause-contexts
11-
12-
const autosave = workspace
13-
.getConfiguration('processing')
14-
.get<boolean>('autosave');
15-
if (autosave === true) {
16-
// Save all files before running the sketch
17-
commands.executeCommand('workbench.action.files.saveAll');
18-
}
19-
if (resource == undefined) {
20-
const editor = window.activeTextEditor;
21-
if (editor) {
22-
resource = editor.document.uri;
23-
}
24-
}
25-
26-
if (!resource) {
27-
return;
28-
}
29-
30-
// TODO: Give feedback if the sketch is starting
31-
32-
let terminal = state.terminal;
33-
// Create a new terminal
34-
if (terminal === undefined || terminal.exitStatus) {
35-
window.terminals.forEach(t => {
36-
if (t.name === "Sketch") {
37-
t.dispose();
38-
}
39-
});
40-
state.terminal = window.createTerminal("Sketch");
41-
terminal = state.terminal;
42-
// Show the terminal panel the first time
43-
terminal.show(true);
44-
} else {
45-
// Send the command to the terminal
46-
terminal.sendText('\x03', false);
47-
}
48-
49-
// clear the terminal
50-
terminal.sendText("clear", true);
51-
52-
let path = state.selectedVersion.path;
53-
if (process.platform === "win32") {
54-
// on windows we need to escape spaces
55-
path = `& "${path}"`;
56-
}
57-
58-
let cmd = `${path} cli --sketch="${dirname(resource.fsPath)}" --run`;
59-
if (process.platform === "win32") {
60-
// on windows we need to pipe stderr to stdout and convert to string
61-
cmd += ` 2>&1`;
62-
}
63-
64-
terminal.sendText(cmd, true);
65-
});
66-
67-
const stopSketch = commands.registerCommand('processing.sketch.stop', () => {
68-
if (state.terminal === undefined) {
69-
return;
70-
}
71-
72-
// Send the command to the terminal
73-
state.terminal.sendText('\x03', false);
74-
});
8+
// const runSketch = commands.registerCommand('processing.sketch.run', (resource: Uri) => {
9+
// // TODO: Use VScode contexts to highlight run button when sketch is running, blocked until we do not run the sketch in a terminal
10+
// // https://code.visualstudio.com/api/references/when-clause-contexts
11+
12+
// const autosave = workspace
13+
// .getConfiguration('processing')
14+
// .get<boolean>('autosave');
15+
// if (autosave === true) {
16+
// // Save all files before running the sketch
17+
// commands.executeCommand('workbench.action.files.saveAll');
18+
// }
19+
// if (resource == undefined) {
20+
// const editor = window.activeTextEditor;
21+
// if (editor) {
22+
// resource = editor.document.uri;
23+
// }
24+
// }
25+
26+
// if (!resource) {
27+
// return;
28+
// }
29+
30+
// return;
31+
32+
// let terminal = state.terminal;
33+
// // Create a new terminal
34+
// if (terminal === undefined || terminal.exitStatus) {
35+
// window.terminals.forEach(t => {
36+
// if (t.name === "Sketch") {
37+
// t.dispose();
38+
// }
39+
// });
40+
// state.terminal = window.createTerminal("Sketch");
41+
// terminal = state.terminal;
42+
// // Show the terminal panel the first time
43+
// terminal.show(true);
44+
// } else {
45+
// // Send the command to the terminal
46+
// terminal.sendText('\x03', false);
47+
// }
48+
49+
// // clear the terminal
50+
// terminal.sendText("clear", true);
51+
52+
// let path = state.selectedVersion.path;
53+
// if (process.platform === "win32") {
54+
// // on windows we need to escape spaces
55+
// path = `& "${path}"`;
56+
// }
57+
58+
// let cmd = `${path} cli --sketch="${dirname(resource.fsPath)}" --run`;
59+
// if (process.platform === "win32") {
60+
// // on windows we need to pipe stderr to stdout and convert to string
61+
// cmd += ` 2>&1`;
62+
// }
63+
64+
// terminal.sendText(cmd, true);
65+
// });
66+
67+
// const stopSketch = commands.registerCommand('processing.sketch.stop', () => {
68+
// if (state.terminal === undefined) {
69+
// return;
70+
// }
71+
72+
// // Send the command to the terminal
73+
// state.terminal.sendText('\x03', false);
74+
// });
7575

7676
const openSketch = commands.registerCommand('processing.sketch.open', async (folder: string, isReadOnly: boolean) => {
7777
if (!folder) {
@@ -161,7 +161,7 @@ export function setupCommands(context: ExtensionContext) {
161161

162162
// TODO: Add command to select Processing version and set the setting
163163

164-
context.subscriptions.push(runSketch, stopSketch, openSketch, newSketch);
164+
context.subscriptions.push(openSketch, newSketch);
165165
}
166166

167167
// Helper function to convert a number to alphabetical (e.g., 0 = a, 1 = b, ..., 25 = z, 26 = aa, etc.)

client/src/setupConsole.ts

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { ChildProcess, spawn } from 'child_process';
2+
import { commands, ExtensionContext, Uri, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace } from 'vscode';
3+
import { state } from './extension';
4+
import { dirname } from 'path';
5+
import * as treeKill from 'tree-kill';
6+
7+
export default function setupConsole(context: ExtensionContext) {
8+
const sketchProcesses: ChildProcess[] = [];
9+
10+
const provider = new ProcessingConsoleViewProvider();
11+
12+
const register = window.registerWebviewViewProvider('processingConsoleView', provider);
13+
14+
const startSketch = commands.registerCommand('processing.sketch.run', (resource: Uri) => {
15+
const autosave = workspace
16+
.getConfiguration('processing')
17+
.get<boolean>('autosave');
18+
if (autosave === true) {
19+
// Save all files before running the sketch
20+
commands.executeCommand('workbench.action.files.saveAll');
21+
}
22+
if (resource == undefined) {
23+
const editor = window.activeTextEditor;
24+
if (editor) {
25+
resource = editor.document.uri;
26+
}
27+
}
28+
29+
if (!resource) {
30+
return;
31+
}
32+
commands.executeCommand('processingConsoleView.focus');
33+
commands.executeCommand('processing.sketch.stop');
34+
35+
const proc = spawn(
36+
state.selectedVersion.path,
37+
['cli', `--sketch=${dirname(resource.fsPath)}`, '--run'],
38+
{
39+
shell: false,
40+
}
41+
);
42+
proc.stdout.on("data", (data) => {
43+
provider.webview?.webview.postMessage({ type: 'stdout', value: data?.toString() });
44+
});
45+
proc.stderr.on("data", (data) => {
46+
provider.webview?.webview.postMessage({ type: 'stderr', value: data?.toString() });
47+
// TODO: Handle and highlight errors in the editor
48+
});
49+
proc.on('close', (code) => {
50+
provider.webview?.webview.postMessage({ type: 'close', value: code?.toString() });
51+
sketchProcesses.splice(sketchProcesses.indexOf(proc), 1);
52+
commands.executeCommand('setContext', 'processing.sketch.running', false);
53+
});
54+
provider.webview?.show?.(true);
55+
provider.webview?.webview.postMessage({ type: 'clear'});
56+
sketchProcesses.push(proc);
57+
commands.executeCommand('setContext', 'processing.sketch.running', true);
58+
});
59+
60+
const restartSketch = commands.registerCommand('processing.sketch.restart', (resource: Uri) => {
61+
commands.executeCommand('processing.sketch.run', resource);
62+
});
63+
64+
const stopSketch = commands.registerCommand('processing.sketch.stop', () => {
65+
for (const proc of sketchProcesses) {
66+
treeKill(proc.pid as number);
67+
}
68+
});
69+
70+
context.subscriptions.push(
71+
register,
72+
startSketch,
73+
restartSketch,
74+
stopSketch
75+
);
76+
}
77+
78+
// TODO: Add setting for timestamps
79+
// TODO: Add setting for collapsing similar messages
80+
// TODO: Add option to enable/disable stdout and stderr
81+
class ProcessingConsoleViewProvider implements WebviewViewProvider {
82+
public webview?: WebviewView;
83+
84+
public resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext): Thenable<void> | void {
85+
webviewView.webview.options = { enableScripts: true };
86+
webviewView.webview.html = `
87+
<!DOCTYPE html>
88+
<html>
89+
<body>
90+
<script>
91+
window.addEventListener('message', event => {
92+
93+
const message = event.data; // The JSON data our extension sent
94+
95+
const isScrolledToBottom = (window.innerHeight + window.scrollY) >= document.body.offsetHeight;
96+
97+
const ts = document.createElement("span");
98+
ts.style.color = "gray";
99+
const now = new Date();
100+
const hours = now.getHours().toString().padStart(2, '0');
101+
const minutes = now.getMinutes().toString().padStart(2, '0');
102+
const seconds = now.getSeconds().toString().padStart(2, '0');
103+
ts.textContent = "[" + hours + ":" + minutes + ":" + seconds + "] ";
104+
105+
106+
switch (message.type) {
107+
case 'clear':
108+
document.body.innerHTML = '';
109+
break;
110+
case 'stdout':
111+
var pre = document.createElement("pre");
112+
pre.style.color = "white";
113+
pre.textContent = message.value;
114+
if (pre.textContent.endsWith("\\n")) {
115+
pre.textContent = pre.textContent.slice(0, -1);
116+
}
117+
pre.prepend(ts);
118+
document.body.appendChild(pre);
119+
break;
120+
case 'stderr':
121+
var pre = document.createElement("pre");
122+
pre.style.color = "red";
123+
pre.textContent = message.value;
124+
if (pre.textContent.endsWith("\\n")) {
125+
pre.textContent = pre.textContent.slice(0, -1);
126+
}
127+
pre.prepend(ts);
128+
document.body.appendChild(pre);
129+
break;
130+
case 'close':
131+
var pre = document.createElement("pre");
132+
pre.style.color = "gray";
133+
pre.textContent = "Process exited with code " + message.value;
134+
pre.prepend(ts);
135+
document.body.appendChild(pre);
136+
break;
137+
}
138+
139+
if (isScrolledToBottom) {
140+
window.scrollTo(0, document.body.scrollHeight);
141+
}
142+
});
143+
</script>
144+
</body>
145+
</html>
146+
`;
147+
webviewView.onDidDispose(() => {
148+
commands.executeCommand("processing.sketch.stop");
149+
});
150+
this.webview = webviewView;
151+
}
152+
153+
}

client/tsconfig.tsbuildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"root":["./src/compareversions.ts","./src/extension.ts","./src/setupcommands.ts","./src/setupdecorators.ts","./src/setuplanguageserver.ts","./src/setuppdefiles.ts","./src/setupselectedversion.ts","./src/setupsidebar.ts"],"version":"5.8.3"}
1+
{"root":["./src/compareversions.ts","./src/extension.ts","./src/setupcommands.ts","./src/setupconsole.ts","./src/setupdecorators.ts","./src/setuplanguageserver.ts","./src/setuppdefiles.ts","./src/setupselectedversion.ts","./src/setupsidebar.ts"],"version":"5.8.3"}

media/restart.svg

Lines changed: 5 additions & 0 deletions
Loading

package-lock.json

Lines changed: 16 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)