Skip to content
This repository has been archived by the owner on Jul 28, 2021. It is now read-only.

Support notebook cells #59

Merged
merged 11 commits into from
Sep 2, 2020
Merged
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

## 0.1.11

- Support notebook cells ([#59](https://github.com/REditorSupport/vscode-r-lsp/pull/59))
- Closing all untitled documents will shutdown the shared language server.
- Fix a bug that prevents the extension from starting a new language server for untitled document.

## 0.1.10

- Fix multiple language server/clients created for a single workspace ([#53](https://github.com/REditorSupport/vscode-r-lsp/issues/53))
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "r-lsp",
"displayName": "R LSP Client",
"description": "R LSP Client for VS Code",
"version": "0.1.10",
"version": "0.1.11",
"license": "SEE LICENSE IN LICENSE",
"publisher": "REditorSupport",
"icon": "images/Rlogo.png",
Expand Down
139 changes: 100 additions & 39 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path = require('path');
import net = require('net');
import url = require('url');
import { spawn, ChildProcess } from 'child_process';
import { LanguageClient, LanguageClientOptions, StreamInfo, DocumentFilter } from 'vscode-languageclient';
import { LanguageClient, LanguageClientOptions, StreamInfo, DocumentFilter, ErrorAction, CloseAction } from 'vscode-languageclient';
import { ExtensionContext, workspace, Uri, TextDocument, WorkspaceConfiguration, OutputChannel, window, WorkspaceFolder } from 'vscode';
import { getRPath } from './util'

Expand Down Expand Up @@ -58,9 +58,7 @@ async function createClient(config: WorkspaceConfiguration, selector: DocumentFi
const childProcess = spawn(path, args, options);
client.outputChannel.appendLine(`R Language Server (${childProcess.pid}) started`);
childProcess.stderr.on('data', (chunk: Buffer) => {
const str = chunk.toString();
console.log(`R Language Server (${childProcess.pid}): ${str}`);
client.outputChannel.appendLine(str);
client.outputChannel.appendLine(chunk.toString());
});
childProcess.on('exit', (code, signal) => {
client.outputChannel.appendLine(`R Language Server (${childProcess.pid}) exited ` +
Expand Down Expand Up @@ -90,6 +88,10 @@ async function createClient(config: WorkspaceConfiguration, selector: DocumentFi
// Synchronize the setting section 'r' to the server
configurationSection: 'r.lsp',
},
errorHandler: {
error: () => ErrorAction.Shutdown,
closed: () => CloseAction.DoNotRestart,
},
};

// Create the language client and start the client.
Expand All @@ -114,13 +116,24 @@ function checkClient(name: string): boolean {
return client && client.needsStop();
}

function getKey(uri: Uri) {
switch (uri.scheme) {
case 'untitled':
return uri.scheme;
case 'vscode-notebook-cell':
return `vscode-notebook:${uri.fsPath}`;
default:
return uri.toString();
}
}

export function activate(context: ExtensionContext) {

const config = workspace.getConfiguration('r');
const outputChannel: OutputChannel = window.createOutputChannel('R Language Server');

async function didOpenTextDocument(document: TextDocument) {
if (document.uri.scheme !== 'file' && document.uri.scheme !== 'untitled') {
if (document.uri.scheme !== 'file' && document.uri.scheme !== 'untitled' && document.uri.scheme !== 'vscode-notebook-cell') {
return;
}

Expand All @@ -129,55 +142,101 @@ export function activate(context: ExtensionContext) {
}

const folder = workspace.getWorkspaceFolder(document.uri);
if (!folder) {

// All untitled documents share a server started from home folder
if (document.uri.scheme === 'untitled' && !checkClient('untitled')) {

// Each notebook uses a server started from parent folder
if (document.uri.scheme === 'vscode-notebook-cell') {
const key = getKey(document.uri);
if (!checkClient(key)) {
const documentSelector: DocumentFilter[] = [
{ scheme: 'untitled', language: 'r' },
{ scheme: 'untitled', language: 'rmd' },
{ scheme: 'vscode-notebook-cell', language: 'r', pattern: `${document.uri.fsPath}` },
];
let client = await createClient(config, documentSelector, os.homedir(), undefined, outputChannel);
let client = await createClient(config, documentSelector,
path.dirname(document.uri.fsPath), folder, outputChannel);
client.start();
clients.set('untitled', client);
initSet.delete('untitled');
return;
clients.set(key, client);
}
initSet.delete(key);
return;
}

// Each file outside workspace uses a server started from parent folder
if (document.uri.scheme === 'file' && !checkClient(document.uri.toString())) {
if (folder) {

// Each workspace uses a server started from the workspace folder
const key = getKey(folder.uri);
if (!checkClient(key)) {
const pattern = `${folder.uri.fsPath}/**/*`;
const documentSelector: DocumentFilter[] = [
{ scheme: 'file', pattern: document.uri.fsPath },
{ scheme: 'file', language: 'r', pattern: pattern },
{ scheme: 'file', language: 'rmd', pattern: pattern },
];
let client = await createClient(config, documentSelector,
path.dirname(document.uri.fsPath), undefined, outputChannel);
let client = await createClient(config, documentSelector, folder.uri.fsPath, folder, outputChannel);
client.start();
clients.set(document.uri.toString(), client);
initSet.delete(document.uri.toString());
clients.set(key, client);
}
initSet.delete(key);

} else {

// All untitled documents share a server started from home folder
if (document.uri.scheme === 'untitled') {
const key = getKey(document.uri);
if (!checkClient(key)) {
const documentSelector: DocumentFilter[] = [
{ scheme: 'untitled', language: 'r' },
{ scheme: 'untitled', language: 'rmd' },
];
let client = await createClient(config, documentSelector, os.homedir(), undefined, outputChannel);
client.start();
clients.set(key, client);
}
initSet.delete(key);

return;
}

return;
}
// Each file outside workspace uses a server started from parent folder
if (document.uri.scheme === 'file') {
const key = getKey(document.uri);
if (!checkClient(key)) {
const documentSelector: DocumentFilter[] = [
{ scheme: 'file', pattern: document.uri.fsPath },
];
let client = await createClient(config, documentSelector,
path.dirname(document.uri.fsPath), undefined, outputChannel);
client.start();
clients.set(key, client);
}
initSet.delete(key);

// Each workspace uses a server started from the workspace folder
if (!checkClient(folder.uri.toString())) {
const pattern = `${folder.uri.fsPath}/**/*`;
const documentSelector: DocumentFilter[] = [
{ scheme: 'file', language: 'r', pattern: pattern },
{ scheme: 'file', language: 'rmd', pattern: pattern },
];
let client = await createClient(config, documentSelector, folder.uri.fsPath, folder, outputChannel);
client.start();
clients.set(folder.uri.toString(), client);
initSet.delete(folder.uri.toString());
return;
}
}
}

async function didCloseTextDocument(document: TextDocument) {
let client = clients.get(document.uri.toString());
if (document.uri.scheme === 'untitled') {
const result = workspace.textDocuments.find((doc) => doc.uri.scheme === 'untitled');
if (result) {
// Stop the langauge server when all untitled documents are closed.
return;
}
}

if (document.uri.scheme === 'vscode-notebook-cell') {
const result = workspace.textDocuments.find((doc) =>
doc.uri.scheme === document.uri.scheme && doc.uri.fsPath === document.uri.fsPath);
if (result) {
// Stop the language server when all cell documents are closed (notebook closed).
return;
}
}

// Stop the language server when single file outside workspace is closed, or the above cases.
const key = getKey(document.uri);
let client = clients.get(key);
if (client) {
clients.delete(document.uri.toString());
clients.delete(key);
initSet.delete(key);
client.stop();
}
}
Expand All @@ -187,9 +246,11 @@ export function activate(context: ExtensionContext) {
workspace.textDocuments.forEach(didOpenTextDocument);
workspace.onDidChangeWorkspaceFolders((event) => {
for (let folder of event.removed) {
let client = clients.get(folder.uri.toString());
const key = getKey(folder.uri);
let client = clients.get(key);
if (client) {
clients.delete(folder.uri.toString());
clients.delete(key);
initSet.delete(key);
client.stop()
}
}
Expand Down