Skip to content

Commit 403a16d

Browse files
committed
Initial version of Process controlled launching of sketches
1 parent 7df6e2e commit 403a16d

File tree

6 files changed

+257
-75
lines changed

6 files changed

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

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"}

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)