From 136dcb16d96d537c81290d48994c3c4bcaee248a Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 30 Apr 2018 22:50:23 -0400 Subject: [PATCH] Fix VS Code extension startup The change contributed in https://github.com/sbt/sbt/pull/4130 to start sbt within VS Code looked ok at first, but when I published the extension and started using it, I realized that it's a bit broken. Basically there's no cleanup logic (that I could find), so simply closing VS Code would leave `project/target/active.json` behind, which lets VS Code extension to attempt connection, and it fails because there's no server running. To workaround this, I'll attempt to connect to the socket to confirm sbt server is up. --- vscode-sbt-scala/client/package.json | 2 +- vscode-sbt-scala/client/src/extension.ts | 73 ++++++++++++++++++++---- vscode-sbt-scala/server/src/server.ts | 36 ++++++------ 3 files changed, 83 insertions(+), 28 deletions(-) diff --git a/vscode-sbt-scala/client/package.json b/vscode-sbt-scala/client/package.json index 1e729fc32..f4316539e 100644 --- a/vscode-sbt-scala/client/package.json +++ b/vscode-sbt-scala/client/package.json @@ -1,7 +1,7 @@ { "name": "vscode-sbt-scala", "displayName": "Scala (sbt)", - "version": "0.1.0", + "version": "0.2.0", "author": "Lightbend, Inc.", "license": "BSD-3-Clause", "publisher": "lightbend", diff --git a/vscode-sbt-scala/client/src/extension.ts b/vscode-sbt-scala/client/src/extension.ts index c5d3a1282..ff8206a37 100644 --- a/vscode-sbt-scala/client/src/extension.ts +++ b/vscode-sbt-scala/client/src/extension.ts @@ -1,28 +1,39 @@ 'use strict'; import * as path from 'path'; - -let fs = require('fs'); +import * as url from 'url'; +import * as net from 'net'; +let fs = require('fs'), + os = require('os'); import * as vscode from 'vscode'; import { ExtensionContext, workspace } from 'vscode'; // workspace, import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient'; +let terminal: vscode.Terminal = null; + +function delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export async function deactivate() { + if (terminal != null) { + terminal.sendText("exit"); + await delay(1000); + terminal.dispose(); + } +} + export async function activate(context: ExtensionContext) { // Start sbt - const terminal = vscode.window.createTerminal(`sbt`); + terminal = vscode.window.createTerminal(`sbt`); terminal.show(); terminal.sendText("sbt"); - - function delay(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); - } - // Wait for SBT server to start - let retries = 30; + let retries = 60; while (retries > 0) { retries--; await delay(1000); - if (fs.existsSync(path.join(workspace.rootPath, 'project', 'target', 'active.json'))) { + if (isServerUp()) { break; } } @@ -46,7 +57,47 @@ export async function activate(context: ExtensionContext) { return discoverToken(); } } - + + // Don't start VS Code connection until sbt server is confirmed to be up and running. + function isServerUp(): boolean { + let isFileThere = fs.existsSync(path.join(workspace.rootPath, 'project', 'target', 'active.json')); + if (!isFileThere) { + return false; + } else { + let skt = new net.Socket(); + try { + connectSocket(skt); + } catch(e) { + return false; + } + skt.end(); + return true; + } + } + + function connectSocket(socket: net.Socket):  net.Socket { + let u = discoverUrl(); + // let socket = net.Socket(); + if (u.protocol == 'tcp:') { + socket.connect(+u.port, '127.0.0.1'); + } else if (u.protocol == 'local:' && os.platform() == 'win32') { + let pipePath = '\\\\.\\pipe\\' + u.hostname; + socket.connect(pipePath); + } else if (u.protocol == 'local:') { + socket.connect(u.path); + } else { + throw 'Unknown protocol ' + u.protocol; + } + return socket; + } + + // the port file is hardcoded to a particular location relative to the build. + function discoverUrl(): url.Url { + let pf = path.join(process.cwd(), 'project', 'target', 'active.json'); + let portfile = JSON.parse(fs.readFileSync(pf)); + return url.parse(portfile.uri); + } + // the port file is hardcoded to a particular location relative to the build. function discoverToken(): any { let pf = path.join(workspace.rootPath, 'project', 'target', 'active.json'); diff --git a/vscode-sbt-scala/server/src/server.ts b/vscode-sbt-scala/server/src/server.ts index 05932d451..8404c7b74 100644 --- a/vscode-sbt-scala/server/src/server.ts +++ b/vscode-sbt-scala/server/src/server.ts @@ -2,32 +2,20 @@ import * as path from 'path'; import * as url from 'url'; -let net = require('net'), - fs = require('fs'), +import * as net from 'net'; +let fs = require('fs'), os = require('os'), stdin = process.stdin, stdout = process.stdout; -let u = discoverUrl(); - -let socket = net.Socket(); +let socket = new net.Socket(); socket.on('data', (chunk: any) => { // send it back to stdout stdout.write(chunk); }).on('end', () => { stdin.pause(); }); - -if (u.protocol == 'tcp:') { - socket.connect(u.port, '127.0.0.1'); -} else if (u.protocol == 'local:' && os.platform() == 'win32') { - let pipePath = '\\\\.\\pipe\\' + u.hostname; - socket.connect(pipePath); -} else if (u.protocol == 'local:') { - socket.connect(u.path); -} else { - throw 'Unknown protocol ' + u.protocol; -} +connectSocket(socket); stdin.resume(); stdin.on('data', (chunk: any) => { @@ -36,6 +24,22 @@ stdin.on('data', (chunk: any) => { socket.end(); }); +function connectSocket(socket: net.Socket): net.Socket { + let u = discoverUrl(); + // let socket = net.Socket(); + if (u.protocol == 'tcp:') { + socket.connect(+u.port, '127.0.0.1'); + } else if (u.protocol == 'local:' && os.platform() == 'win32') { + let pipePath = '\\\\.\\pipe\\' + u.hostname; + socket.connect(pipePath); + } else if (u.protocol == 'local:') { + socket.connect(u.path); + } else { + throw 'Unknown protocol ' + u.protocol; + } + return socket; +} + // the port file is hardcoded to a particular location relative to the build. function discoverUrl(): url.Url { let pf = path.join(process.cwd(), 'project', 'target', 'active.json');