Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit eb54a98

Browse files
authored
Merge pull request #22 from xeger/friendlyStart
Add user-friendly error handling & auto-install
2 parents bb54464 + 22ecd52 commit eb54a98

File tree

1 file changed

+64
-25
lines changed

1 file changed

+64
-25
lines changed

src/extension.ts

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ export async function activate(context: ExtensionContext) {
4747
})
4848
);
4949

50-
// We're returning a Promise from this function that will start the Ruby
51-
// subprocess.
52-
await startLanguageServer();
50+
// If there's an open folder, use it as cwd when spawning commands
51+
// to promote correct package & language versioning.
52+
const getCWD = () => workspace.workspaceFolders?.[0]?.uri?.fsPath || process.cwd();
5353

5454
// This function is called when the extension is activated or when the
5555
// language server is restarted.
@@ -82,27 +82,27 @@ export async function activate(context: ExtensionContext) {
8282
args.push(`--plugins=${Array.from(plugins).join(",")}`);
8383
}
8484

85-
outputChannel.appendLine(`Starting language server with ${plugins.size} plugin(s)...`);
86-
let run: ServerOptions = { command: "stree", args };
87-
8885
// There's a bit of complexity here. Basically, if there's an open folder,
89-
// then w're going to check if the syntax_tree gem is inside the bundle. If
86+
// then we're going to check if the syntax_tree gem is inside the bundle. If
9087
// it is, then we'll run bundle exec stree. This is good, because it'll
9188
// ensure that we get the correct version of the gem. If it's not in the
9289
// bundle or there is no bundle, then we'll just run the global stree. This
9390
// might be correct in the end if the right environment variables are set,
9491
// but it's a bit of a prayer.
95-
if (workspace.workspaceFolders) {
96-
const cwd = workspace.workspaceFolders![0].uri.fsPath;
97-
98-
try {
99-
await promiseExec("bundle show syntax_tree", { cwd });
100-
run = { command: "bundle", args: ["exec", "stree", "lsp"], options: { cwd } };
101-
} catch {
102-
outputChannel.appendLine("No bundled syntax_tree, running global stree.");
103-
}
92+
const cwd = getCWD();
93+
let run: ServerOptions = { command: "stree", args };
94+
let where = 'global';
95+
96+
try {
97+
await promiseExec("bundle show syntax_tree", { cwd });
98+
run = { command: "bundle", args: ["exec", "stree", "lsp"], options: { cwd } };
99+
where = 'bundled';
100+
} catch {
101+
// No-op (just keep using the global stree)
104102
}
105103

104+
outputChannel.appendLine(`Starting language server with ${where} stree and ${plugins.size} plugin(s)...`);
105+
106106
// Here, we instantiate the language client. This is the object that is
107107
// responsible for the communication and management of the Ruby subprocess.
108108
languageClient = new LanguageClient("Syntax Tree", { run, debug: run }, {
@@ -112,16 +112,39 @@ export async function activate(context: ExtensionContext) {
112112
outputChannel
113113
});
114114

115-
// Here we're going to wait for the language server to start.
116-
await languageClient.start();
115+
try {
116+
// Here we're going to wait for the language server to start.
117+
await languageClient.start();
118+
// Finally, now that the language server has been properly started, we can
119+
// add the various features to the extension. Each of them in turn
120+
// implements Disposable so that they clean up their own resources.
121+
visualizer = new Visualize(languageClient, outputChannel);
122+
context.subscriptions.push(
123+
visualizer
124+
);
125+
} catch (e: any) {
126+
languageClient = null;
127+
const items = ['Restart']
128+
let msg = 'Something went wrong.';
129+
if (typeof e === 'string') {
130+
if (/ENOENT/.test(e)) {
131+
msg = 'Command not found. Is the syntax_tree RubyGem installed?';
132+
items.unshift('Install Gem');
133+
}
134+
}
117135

118-
// Finally, now that the language server has been properly started, we can
119-
// add the various features to the extension. Each of them in turn
120-
// implements Disposable so that they clean up their own resources.
121-
visualizer = new Visualize(languageClient, outputChannel);
122-
context.subscriptions.push(
123-
visualizer
124-
);
136+
const action = await window.showErrorMessage(msg, ...items);
137+
switch (action) {
138+
case 'Install Gem':
139+
installGem();
140+
break;
141+
case 'Restart':
142+
startLanguageServer();
143+
break;
144+
}
145+
if (action === 'Restart') {
146+
}
147+
}
125148
}
126149

127150
// This function is called as part of the shutdown or restart process. It's
@@ -142,6 +165,22 @@ export async function activate(context: ExtensionContext) {
142165
await stopLanguageServer();
143166
await startLanguageServer();
144167
}
168+
169+
// This function is called when the user wants to recover from ENOENT
170+
// on start. It starts the language server afterward.
171+
async function installGem() {
172+
const cwd = getCWD();
173+
try {
174+
await promiseExec("gem install syntax_tree", { cwd });
175+
startLanguageServer();
176+
} catch (e) {
177+
outputChannel.appendLine("Error installing gem: " + e);
178+
}
179+
}
180+
181+
// We're returning a Promise from this function that will start the Ruby
182+
// subprocess.
183+
await startLanguageServer();
145184
}
146185

147186
// This is the expected top-level export that is called by VSCode when the

0 commit comments

Comments
 (0)