Skip to content

Commit 301245d

Browse files
committed
Update README and improve version handling in setupSelectedVersion; refactor setupSidebar for better command execution
1 parent 1b306c5 commit 301245d

File tree

5 files changed

+155
-122
lines changed

5 files changed

+155
-122
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ If you encounter any issues with the Processing VSCode extension, try the follow
3737
- Restart VS Code.
3838
- Make sure no other Processing extensions are installed in VSCode.
3939
- Quit any running instances of Processing.
40-
- Double-check that you're using the correct version of Processing (4.4.76).
40+
- Double-check that you're using the correct version of Processing (4.4.6+).
4141
- Try running the Processing app once before opening VS Code.
4242

4343
## Compatibility

client/src/extension.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const state = {
2626
export async function activate(context: ExtensionContext) {
2727
// TODO: Detect other Processing extensions and warn the user
2828

29+
// TODO: Cache the selected version between sessions
2930
await setupSelectedVersion(context);
3031
setupCommands(context);
3132
setupLanguageServer();

client/src/setupCommands.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { ExtensionContext, commands, Uri, window, workspace } from 'vscode';
33
import { state } from './extension';
44

55
export function setupCommands(context: ExtensionContext) {
6-
const config = workspace.getConfiguration('processing');
7-
86
const runSketch = commands.registerCommand('processing.sketch.run', (resource: Uri) => {
9-
const autosave = config.get<boolean>('autosave');
7+
const autosave = workspace
8+
.getConfiguration('processing')
9+
.get<boolean>('autosave');
1010
if (autosave === true) {
1111
// Save all files before running the sketch
1212
commands.executeCommand('workbench.action.files.saveAll');
@@ -64,9 +64,12 @@ export function setupCommands(context: ExtensionContext) {
6464
window.showErrorMessage("No sketch folder provided.");
6565
return;
6666
}
67+
// TODO: Copy examples/readonly sketches to a temp location and open them there
6768

68-
69-
const newWindow = config.get<boolean>('newWindow');
69+
const newWindow = workspace
70+
.getConfiguration('processing')
71+
.get<boolean>('newWindow');
72+
7073
if (newWindow === true) {
7174
await commands.executeCommand('vscode.openFolder', Uri.file(folder), true);
7275
} else {

client/src/setupSelectedVersion.ts

Lines changed: 73 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -9,71 +9,7 @@ export async function setupSelectedVersion(context: ExtensionContext) {
99

1010
const config = workspace.getConfiguration('processing');
1111

12-
let binaryPath = context.asAbsolutePath(join(`install-locator-${process.platform}`, 'bin', 'install-locator'));
13-
const javaPath = context.asAbsolutePath(join(`install-locator-${process.platform}`, 'bin', 'java'));
14-
15-
await new Promise<void>((resolve, reject) => {
16-
// add executable permissions to the binary
17-
if (process.platform !== "win32") {
18-
exec(`chmod +x ${binaryPath}`, (error, stdout, stderr) => {
19-
if (error) {
20-
reject(error);
21-
}
22-
if (stderr) {
23-
reject(stderr);
24-
}
25-
resolve();
26-
});
27-
28-
// add executable permissions to the java binary
29-
exec(`chmod +x ${javaPath}`, (error, stdout, stderr) => {
30-
if (error) {
31-
reject(error);
32-
}
33-
if (stderr) {
34-
reject(stderr);
35-
}
36-
resolve();
37-
});
38-
} else {
39-
// on windows we need to add the .bat to the binary path
40-
binaryPath = `${binaryPath}.bat`;
41-
resolve();
42-
}
43-
}).catch((e) => {
44-
console.error(`Error setting permissions for ${binaryPath}: ${e}`);
45-
window.showErrorMessage(`Error setting permissions for ${binaryPath}: ${e}`);
46-
});
47-
48-
const versions = await new Promise<ProcessingVersion[]>((resolve, reject) => {
49-
exec(binaryPath, (error, stdout, stderr) => {
50-
if (error) {
51-
reject(error);
52-
}
53-
if (stderr) {
54-
reject(stderr);
55-
}
56-
const jsArray = stdout
57-
// remove the square brackets
58-
.replace("[", "")
59-
.replace("]", "")
60-
// split into array items
61-
.split(',')
62-
.map(s => s.trim().split("^"))
63-
.map(v => ({ path: v[0], version: v[1] }))
64-
// order by semver
65-
.sort((a, b) => compareVersions(a.version, b.version))
66-
.reverse();
67-
68-
;
69-
resolve(jsArray);
70-
});
71-
}).catch((e) => {
72-
console.error(`Error getting Processing versions: ${e}`);
73-
window.showErrorMessage(`Error getting Processing versions: ${e}`);
74-
});
75-
76-
// TODO: For snap grab processing from the path
12+
const versions = await fetchProcessingVersions(context);
7713

7814
if (!versions || versions.length === 0) {
7915
await window.showErrorMessage(
@@ -120,3 +56,75 @@ export async function setupSelectedVersion(context: ExtensionContext) {
12056
}
12157
state.selectedVersion = selectedVersion;
12258
}
59+
async function fetchProcessingVersions(context: ExtensionContext) {
60+
const binaryPath = await getBinaryPathWithPermissions(context);
61+
62+
const versions = await new Promise<ProcessingVersion[]>((resolve, reject) => {
63+
exec(binaryPath, (error, stdout, stderr) => {
64+
if (error) {
65+
reject(error);
66+
}
67+
if (stderr) {
68+
reject(stderr);
69+
}
70+
const jsArray = stdout
71+
// remove the square brackets
72+
.replace("[", "")
73+
.replace("]", "")
74+
// split into array items
75+
.split(',')
76+
.map(s => s.trim().split("^"))
77+
.map(v => ({ path: v[0], version: v[1] }))
78+
// order by semver
79+
.sort((a, b) => compareVersions(a.version, b.version))
80+
.reverse();
81+
82+
;
83+
resolve(jsArray);
84+
});
85+
}).catch((e) => {
86+
console.error(`Error getting Processing versions: ${e}`);
87+
window.showErrorMessage(`Error getting Processing versions: ${e}`);
88+
});
89+
return versions;
90+
}
91+
92+
async function getBinaryPathWithPermissions(context: ExtensionContext) {
93+
let binaryPath = context.asAbsolutePath(join(`install-locator-${process.platform}`, 'bin', 'install-locator'));
94+
const javaPath = context.asAbsolutePath(join(`install-locator-${process.platform}`, 'bin', 'java'));
95+
96+
await new Promise<void>((resolve, reject) => {
97+
// add executable permissions to the binary
98+
if (process.platform !== "win32") {
99+
exec(`chmod +x ${binaryPath}`, (error, stdout, stderr) => {
100+
if (error) {
101+
reject(error);
102+
}
103+
if (stderr) {
104+
reject(stderr);
105+
}
106+
resolve();
107+
});
108+
109+
// add executable permissions to the java binary
110+
exec(`chmod +x ${javaPath}`, (error, stdout, stderr) => {
111+
if (error) {
112+
reject(error);
113+
}
114+
if (stderr) {
115+
reject(stderr);
116+
}
117+
resolve();
118+
});
119+
} else {
120+
// on windows we need to add the .bat to the binary path
121+
binaryPath = `${binaryPath}.bat`;
122+
resolve();
123+
}
124+
}).catch((e) => {
125+
console.error(`Error setting permissions for ${binaryPath}: ${e}`);
126+
window.showErrorMessage(`Error setting permissions for ${binaryPath}: ${e}`);
127+
});
128+
return binaryPath;
129+
}
130+

client/src/setupSidebar.ts

Lines changed: 72 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { exec } from 'child_process';
1+
import { exec, spawn } from 'child_process';
22
import { join } from 'path';
3-
import { ProviderResult, TreeDataProvider, TreeItem, TreeItemCollapsibleState, window } from 'vscode';
3+
import { Event, EventEmitter, ProviderResult, TreeDataProvider, TreeItem, TreeItemCollapsibleState, window } from 'vscode';
44
import { state } from './extension';
55
import { existsSync } from 'fs';
66

@@ -24,40 +24,89 @@ export interface Folder {
2424
export async function setupSidebar() {
2525
// TODO: Show welcome screens whilst we are starting Processing
2626
// TODO: Open examples as read-only or in a temporary location
27+
// TODO: Reload examples and sketchbook when Processing version changes
28+
// TODO: Add cache to results to speed up loading
2729

2830
setupExamples();
2931
setupSketchbook();
3032
}
3133

3234
async function setupExamples() {
33-
const examples = await new Promise<Folder[]>((resolve) => {
34-
exec(`${state.selectedVersion.path} contributions examples list`, (error, stdout, stderr) => {
35-
if (error) {
36-
console.error(`exec error: ${error}`);
37-
return;
38-
}
39-
resolve(JSON.parse(stdout));
40-
});
41-
});
42-
43-
const examplesProvider = new ProcessingWindowDataProvider(examples);
35+
const examplesProvider = new ProcessingWindowDataProvider('contributions examples list');
4436
window.createTreeView('processingSidebarExamplesView', { treeDataProvider: examplesProvider });
4537
}
4638

4739
async function setupSketchbook() {
48-
const sketchbook = await new Promise<Folder[]>((resolve) => {
49-
exec(`${state.selectedVersion.path} sketchbook list`, (error, stdout, stderr) => {
50-
if (error) {
51-
console.error(`exec error: ${error}`);
52-
return;
53-
}
54-
resolve(JSON.parse(stdout));
55-
});
56-
});
57-
const sketchbookProvider = new ProcessingWindowDataProvider(sketchbook);
40+
const sketchbookProvider = new ProcessingWindowDataProvider('sketchbook list');
5841
window.createTreeView('processingSidebarSketchbookView', { treeDataProvider: sketchbookProvider });
5942
}
6043

44+
45+
class ProcessingWindowDataProvider implements TreeDataProvider<FolderTreeItem | SketchTreeItem> {
46+
constructor(
47+
public readonly command: string
48+
) {
49+
this._folders = [];
50+
this.populate();
51+
}
52+
private _folders: Folder[];
53+
54+
private _onDidChangeTreeData: EventEmitter<any> = new EventEmitter<any>();
55+
readonly onDidChangeTreeData: Event<any> = this._onDidChangeTreeData.event;
56+
57+
async populate() {
58+
this._folders = await this.grabSketchesWithCommand(this.command);
59+
this._onDidChangeTreeData.fire(null);
60+
}
61+
62+
63+
getTreeItem(element: FolderTreeItem): TreeItem | Thenable<TreeItem> {
64+
return element;
65+
}
66+
getChildren(element?: FolderTreeItem): ProviderResult<(FolderTreeItem | SketchTreeItem)[]> {
67+
if (element === undefined) {
68+
return this._folders.map((folder) => new FolderTreeItem(folder)) ?? [];
69+
} else {
70+
const sketches = element.folder.sketches?.map((sketch) => {
71+
return new SketchTreeItem(sketch);
72+
}) ?? [];
73+
const folders = element.folder.children?.map((folder) => {
74+
return new FolderTreeItem(folder);
75+
}) ?? [];
76+
77+
// Sort sketches and folders
78+
sketches.sort((a, b) => a.sketch.name.localeCompare(b.sketch.name));
79+
folders.sort((a, b) => a.folder.name.localeCompare(b.folder.name));
80+
81+
return [...sketches, ...folders];
82+
}
83+
}
84+
85+
grabSketchesWithCommand(command: string): Promise<Folder[]> {
86+
return new Promise<Folder[]>((resolve) => {
87+
const process = spawn(state.selectedVersion.path, command.split(' '));
88+
let data = '';
89+
process.stdout.on('data', (chunk) => {
90+
data += chunk;
91+
});
92+
process.on('close', (code) => {
93+
if (code !== 0) {
94+
console.error(`Process exited with code ${code}`);
95+
resolve([]);
96+
return;
97+
}
98+
try {
99+
const folders = JSON.parse(data) as Folder[];
100+
resolve(folders);
101+
} catch (e) {
102+
console.error(`Error parsing JSON: ${e}`);
103+
resolve([]);
104+
}
105+
});
106+
});
107+
}
108+
}
109+
61110
class FolderTreeItem extends TreeItem {
62111
constructor(
63112
public readonly folder: Folder
@@ -90,32 +139,4 @@ class SketchTreeItem extends TreeItem {
90139
}
91140
}
92141

93-
class ProcessingWindowDataProvider implements TreeDataProvider<FolderTreeItem | SketchTreeItem> {
94-
constructor(
95-
public readonly folders: Folder[],
96-
) {
97-
}
98-
99-
getTreeItem(element: FolderTreeItem): TreeItem | Thenable<TreeItem> {
100-
return element;
101-
}
102-
getChildren(element?: FolderTreeItem): ProviderResult<(FolderTreeItem | SketchTreeItem)[]> {
103-
if (element === undefined) {
104-
return this.folders.map((folder) => new FolderTreeItem(folder)) ?? [];
105-
} else {
106-
const sketches = element.folder.sketches?.map((sketch) => {
107-
return new SketchTreeItem(sketch);
108-
}) ?? [];
109-
const folders = element.folder.children?.map((folder) => {
110-
return new FolderTreeItem(folder);
111-
}) ?? [];
112-
113-
// Sort sketches and folders
114-
sketches.sort((a, b) => a.sketch.name.localeCompare(b.sketch.name));
115-
folders.sort((a, b) => a.folder.name.localeCompare(b.folder.name));
116-
117-
return [...sketches, ...folders];
118-
}
119-
}
120-
}
121142

0 commit comments

Comments
 (0)