Skip to content

Commit

Permalink
Add a channel for nREPL output to the Output Window
Browse files Browse the repository at this point in the history
Add a channel to show nREPL output to the user. This channel will be
used for the following:

stdout:

Any stdout messages are sent to the channel.

stderr:

Any stderr messages are sent to the channel, and the channel is brought
to the front to make the user aware.

exit:

If the nREPL closes before be we can load the port, then the exit code
is printed to the nREPL output channel, and the channel is brough to the
front.

Fixes avli#52
Fixes avli#53
Fixed avli#69

Also fixes a bug where the 'Starting nREPL' status message would never
clear if the repl failed to start.
  • Loading branch information
marcomorain committed Feb 7, 2018
1 parent fe873cb commit ffd21a9
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 19 deletions.
4 changes: 1 addition & 3 deletions src/cljConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,10 @@ const startNRepl = (): void => {
.then(connectionInfo => nreplConnection = connectionInfo)
.then(() => nreplClient.test(nreplConnection))
.then(stopLoadingAnimation)
.then(() => saveConnection(nreplConnection))
.catch(({ nreplError }) => {
.then(() => saveConnection(nreplConnection), ({ nreplError }) => {
stopLoadingAnimation();
if (!nreplError)
nreplError = "Can't start nREPL.";

disconnect(false);
vscode.window.showErrorMessage(nreplError);
});
Expand Down
53 changes: 37 additions & 16 deletions src/nreplController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ let nreplProcess: ChildProcess;

const isStarted = () => !!nreplProcess;

// Create a channel in the Output window so that the user
// can view output from the nREPL session.
const nreplChannel = vscode.window.createOutputChannel('nREPL');

const start = (): Promise<CljConnectionInformation> => {
if (isStarted())
return Promise.reject({ nreplError: 'nREPL already started.' });
Expand All @@ -46,30 +50,37 @@ const start = (): Promise<CljConnectionInformation> => {
detached: !(os.platform() === 'win32')
});

// Clear any output from previous nREPL sessions to help users focus
// on the current session only.
nreplChannel.clear();

return new Promise((resolve, reject) => {
nreplProcess.stdout.addListener('data', data => {
const nreplConnectionMatch = data.toString().match(R_NREPL_CONNECTION_INFO);

// Send any stdout messages to the channel, but do not bring the
// channel to the front, since these messages are likely just
// informative.
nreplChannel.append(data.toString());

if (nreplConnectionMatch && nreplConnectionMatch[1]) {
const [host, port] = nreplConnectionMatch[1].split(':');
return resolve({ host, port: Number.parseInt(port) });
}
});

nreplProcess.stderr.on('data', data => {
console.info('nrepl stderr =>', data.toString());
});

nreplProcess.on('exit', (code, signal) => {
console.info(`nREPL exit => ${code} / Signal: ${signal}`);
stop();
return reject();
// Log errors and bring panel to the front.
nreplChannel.append(data.toString());
nreplChannel.show();
});

nreplProcess.on('close', (code, signal) => {
console.info(`nREPL close => ${code} / Signal: ${signal}`);
stop();
return reject();
nreplProcess.on('exit', (code) => {
// nREPL process has exited before we were able to read a host / port.
const message = `nREPL exited with code ${code}`
nreplChannel.appendLine(message);
nreplChannel.show();
return reject({ nreplError: message});
});
});
};
Expand All @@ -78,12 +89,22 @@ const stop = () => {
if (nreplProcess) {
// Workaround http://azimi.me/2014/12/31/kill-child_process-node-js.html
nreplProcess.removeAllListeners();
if(os.platform() === 'win32'){
exec('taskkill /pid ' + nreplProcess.pid + ' /T /F')
}
else {
process.kill(-nreplProcess.pid);

try {
// Killing the process will throw an error `kill ESRCH` this method
// is invoked after the nREPL process has exited. This happens when
// we try to gracefully clean up after spawning the nREPL fails.
// We wrap the killing code in `try/catch` to handle this.
if(os.platform() === 'win32'){
exec('taskkill /pid ' + nreplProcess.pid + ' /T /F')
}
else {
process.kill(-nreplProcess.pid);
}
} catch (exception) {
console.error("Error cleaning up nREPL process", exception);
}

nreplProcess = null;
}
};
Expand Down

0 comments on commit ffd21a9

Please sign in to comment.