in src/extension/src/lspclient.ts [236:319]
private async initServer(): Promise<connectionInfo | undefined> {
let releaseLock: any
try {
// Use a lockfile to coordianate between multiple VS Code windows.
// Only the first process to acquire the lock needs to start the server.
releaseLock = await lockfile.lock(LAUNCH_LOCKFILE, {
realpath: false,
stale: LAUNCH_LOCKFILE_TIMEOUT,
})
} catch (err) {
if ((err as {code: string}).code != 'ELOCKED') {
throw err
}
// If the lockfile is already held, wait for the other process to finish starting the server.
this.outputChannelClient?.appendLine(
'INFO: Launch detected via another VS Code window. Pausing to allow it to complete.'
)
await new Promise(resolve => setTimeout(resolve, LAUNCH_LOCKFILE_TIMEOUT))
return await this.getExistingServerAddress()
}
if (this.outputChannelServerInit === undefined)
this.outputChannelServerInit = vscode.window.createOutputChannel(
'Uber LSP Server Initialization'
)
else this.outputChannelServerInit.appendLine('\n\n==== NEW SERVER ====')
this.outputChannelClient?.appendLine(
'INFO: Initializing a new server process. See output in "Uber LSP Server Initialization" channel.'
)
// Bazel depends on the parent process environment, as well as additional ULSP_CONFIG_DIR needed to launch service.
const currentEnv = {...process.env}
currentEnv.ULSP_CONFIG_DIR = this.serverLaunchConfig?.configDir
// Process will be detached, so write stdout and stderr to a temporary file.
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ulsp-'))
const tmpFileName = path.join(tmpDir, 'log.txt')
this.outputChannelServerInit.appendLine(
`Server output file: ${tmpFileName}`
)
await execAsync(`touch ${tmpFileName}`)
// Support both MacOS and Linux
const fullCmd =
process.platform === 'darwin'
? `nohup ${this.serverLaunchConfig?.cmd} >${tmpFileName} 2>&1 &`
: `setsid ${this.serverLaunchConfig?.cmd} >${tmpFileName} 2>&1 &`
// Wait for the server to indicate readiness.
// Reject if server does not indicate readiness within timeout.
await new Promise<void>((resolve, reject) => {
setTimeout(async () => {
await releaseLock()
reject(
Error(
'Language server start exceeded timeout, see "Uber LSP Server Initialization" output panel for details'
)
)
}, 30000)
const tail = new Tail(tmpFileName)
tail.on('line', async (line: string) => {
this.outputChannelServerInit?.appendLine(line)
if (this.serverLaunchConfig && line.includes(this.serverLaunchConfig.serverReadyLine)) {
await releaseLock()
resolve()
}
})
const serverProcess = cp.spawn(fullCmd, {
env: currentEnv,
detached: true,
shell: true,
stdio: 'ignore',
})
serverProcess.unref()
})
// After server has been started, check its output file for connection info.
return await this.getExistingServerAddress()
}