Skip to content

Commit

Permalink
Allow custom command to be excecuted for LSP, and run it by shell. FIxes
Browse files Browse the repository at this point in the history
  • Loading branch information
Tal Hadad committed Jun 27, 2023
1 parent 3779678 commit 3c24715
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 211 deletions.
20 changes: 20 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1511,6 +1511,26 @@
"default": true,
"markdownDescription": "Use multiple language servers for [multi-root workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces). If disabled, only one language server will be used to handle all requests from all workspaces and files."
},
"r.lsp.bootstrapFile": {
"type": "string",
"default": "",
"description": "The bootstrap R script file for the LSP server. If empty, loads the default one that is packaged in this extension."
},
"r.lsp.invokeCommand.windows": {
"type": "string",
"default": "'${rpath}' ${r.lsp.args} --silent --slave --no-save --no-restore -e 'base::source(base::commandArgs(TRUE))' --args '${r.lsp.bootstrapFile}'",
"description": "The shell command to run LSP (Windows)"
},
"r.lsp.invokeCommand.mac": {
"type": "string",
"default": "'${rpath}' ${r.lsp.args} --silent --slave --no-save --no-restore -e 'base::source(base::commandArgs(TRUE))' --args '${r.lsp.bootstrapFile}'",
"description": "The shell command to run LSP (Mac)"
},
"r.lsp.invokeCommand.linux": {
"type": "string",
"default": "'${rpath}' ${r.lsp.args} --silent --slave --no-save --no-restore -e 'base::source(base::commandArgs(TRUE))' --args '${r.lsp.bootstrapFile}'",
"description": "The shell command to run LSP (Linux)"
},
"r.rmarkdown.codeLensCommands": {
"type": "array",
"items": {
Expand Down
4 changes: 2 additions & 2 deletions src/helpViewer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
getRpath,
doWithProgress,
DummyMemento,
getRPathConfigEntry,
getOSConfigEntry,
escapeHtml,
makeWebviewCommandUriString,
uniqueEntries,
Expand Down Expand Up @@ -533,7 +533,7 @@ export class RHelp implements api.HelpPanel, vscode.WebviewPanelSerializer<strin
);
if (!aliases) {
void vscode.window.showErrorMessage(
`Failed to get list of R functions. Make sure that \`jsonlite\` is installed and r.${getRPathConfigEntry()} points to a valid R executable.`,
`Failed to get list of R functions. Make sure that \`jsonlite\` is installed and r.${getOSConfigEntry('rpath')} points to a valid R executable.`,
);
return undefined;
}
Expand Down
30 changes: 13 additions & 17 deletions src/languageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as net from 'net';
import { URL } from 'url';
import { LanguageClient, LanguageClientOptions, StreamInfo, DocumentFilter, ErrorAction, CloseAction, RevealOutputChannelOn } from 'vscode-languageclient/node';
import { Disposable, workspace, Uri, TextDocument, WorkspaceConfiguration, OutputChannel, window, WorkspaceFolder } from 'vscode';
import { DisposableProcess, getRLibPaths, getRpath, promptToInstallRPackage, spawn } from './util';
import { DisposableProcess, getRLibPaths, getRpath, getInvokeCommand, promptToInstallRPackage, spawn } from './util';
import { extensionContext } from './extension';
import { CommonOptions } from 'child_process';

Expand All @@ -26,8 +26,8 @@ export class LanguageService implements Disposable {
return this.stopLanguageService();
}

private spawnServer(client: LanguageClient, rPath: string, args: readonly string[], options: CommonOptions & { cwd: string }): DisposableProcess {
const childProcess = spawn(rPath, args, options);
private spawnServer(client: LanguageClient, command: string, options: CommonOptions & { cwd: string }): DisposableProcess {
const childProcess = spawn(command, undefined, options);
const pid = childProcess.pid || -1;
client.outputChannel.appendLine(`R Language Server (${pid}) started`);
childProcess.stderr.on('data', (chunk: Buffer) => {
Expand Down Expand Up @@ -80,18 +80,14 @@ export class LanguageService implements Disposable {
console.log(`LANG: ${env.LANG}`);
}

const rScriptPath = extensionContext.asAbsolutePath('R/languageServer.R');
const options = { cwd: cwd, env: env };
const args = (config.get<string[]>('lsp.args') ?? []).concat(
'--silent',
'--slave',
'--no-save',
'--no-restore',
'-e',
'base::source(base::commandArgs(TRUE))',
'--args',
rScriptPath
);
const rScriptPath = config.get<string>('lsp.bootstrapFile') || extensionContext.asAbsolutePath('R/languageServer.R');
const rLspArgs = config.get<string>('${lsp.args}') ?? '';
const options = { cwd: cwd, env: env, shell: true };

const commandExp = getInvokeCommand() ?? ''; // TODO: Abort gracefully
const command = commandExp.replaceAll('${rpath}', rPath).replaceAll('${r.lsp.args}', rLspArgs).replaceAll('${r.lsp.bootstrapFile}', rScriptPath);

console.log("Language Server Command: " + command)

const tcpServerOptions = () => new Promise<DisposableProcess | StreamInfo>((resolve, reject) => {
// Use a TCP socket because of problems with blocking STDIO
Expand All @@ -112,7 +108,7 @@ export class LanguageService implements Disposable {
server.listen(0, '127.0.0.1', () => {
const port = (server.address() as net.AddressInfo).port;
env.VSCR_LSP_PORT = String(port);
return this.spawnServer(client, rPath, args, options);
return this.spawnServer(client, command, options);
});
});

Expand Down Expand Up @@ -150,7 +146,7 @@ export class LanguageService implements Disposable {

// Create the language client and start the client.
if (use_stdio && process.platform !== 'win32') {
client = new LanguageClient('r', 'R Language Server', { command: rPath, args: args, options: options }, clientOptions);
client = new LanguageClient('r', 'R Language Server', { command: command, options: options }, clientOptions);
} else {
client = new LanguageClient('r', 'R Language Server', tcpServerOptions, clientOptions);
}
Expand Down
19 changes: 15 additions & 4 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ export async function getRpathFromSystem(): Promise<string> {
return rpath;
}

export function getRPathConfigEntry(term: boolean = false): string {
const trunc = (term ? 'rterm' : 'rpath');
export function getOSConfigEntry(trunc: 'rpath' | 'rterm' | 'lsp.invokeCommand'): string {
const platform = (
process.platform === 'win32' ? 'windows' :
process.platform === 'darwin' ? 'mac' :
Expand All @@ -77,7 +76,7 @@ export async function getRpath(quote = false, overwriteConfig?: string): Promise
}

// try the os-specific config entry for the rpath:
const configEntry = getRPathConfigEntry();
const configEntry = getOSConfigEntry('rpath');
rpath ||= config().get<string>(configEntry);

// read from path/registry:
Expand All @@ -104,7 +103,7 @@ export async function getRpath(quote = false, overwriteConfig?: string): Promise
}

export async function getRterm(): Promise<string | undefined> {
const configEntry = getRPathConfigEntry(true);
const configEntry = getOSConfigEntry('rterm');
let rpath = config().get<string>(configEntry);

rpath ||= await getRpathFromSystem();
Expand All @@ -117,6 +116,18 @@ export async function getRterm(): Promise<string | undefined> {
return undefined;
}

export function getInvokeCommand(): string | undefined {
const configEntry = getOSConfigEntry('lsp.invokeCommand');
let invokeCommand = config().get<string>(configEntry);

if (invokeCommand !== '') {
return invokeCommand;
}

void vscode.window.showErrorMessage(`Cannot launch R language server. Change setting r.${configEntry} to R language server command.`);
return undefined;
}

export function ToRStringLiteral(s: string, quote: string): string {
if (s === undefined) {
return 'NULL';
Expand Down
Loading

0 comments on commit 3c24715

Please sign in to comment.