Get LSP test working for Python

This commit is contained in:
Radon Rosborough 2020-07-30 10:40:13 -06:00
parent e19d6a2c06
commit 6e92e506fc
5 changed files with 342 additions and 19 deletions

View File

@ -187,6 +187,7 @@ export class Session {
this.ws.send(JSON.stringify(msg));
} catch (err) {
this.log(`Failed to send websocket message: ${err}`);
console.log(err);
await this.teardown();
}
};

View File

@ -39,7 +39,7 @@ export interface LangConfig {
init?: any;
config?: any;
lang?: string;
code?: string;
code?: string; // FIXME
after?: string;
item?: string; // FIXME
};
@ -1404,6 +1404,8 @@ main = do
},
},
},
code: "import func",
item: "functools",
},
template: `print("Hello, world!")
`,

View File

@ -26,11 +26,7 @@ if (["-h", "-help", "--help", "help"].includes(args[0])) {
}
let cmdline;
if (
args.length === 1 &&
langs[args[0]] &&
typeof langs[args[0]].lsp === "string"
) {
if (args.length === 1 && langs[args[0]] && langs[args[0]].lsp) {
cmdline = ["bash", "-c", langs[args[0]].lsp!.start];
} else {
cmdline = args;

View File

@ -8,6 +8,13 @@ import { LangConfig, langs } from "./langs";
const TIMEOUT_MS = 3000;
function findPosition(str: string, idx: number) {
const lines = str.substring(0, idx).split("\n");
const line = lines.length - 1;
const character = lines[lines.length - 1].length;
return { line, character };
}
class Test {
lang: string;
type: string;
@ -24,6 +31,8 @@ class Test {
send = (msg: any) => {
this.ws.onMessage(JSON.stringify(msg));
this.messages.push(msg);
this.handledMessages += 1;
};
constructor(lang: string, type: string) {
@ -112,7 +121,7 @@ class Test {
}
};
wait = async (handler: (msg: any) => boolean) => {
wait = async <T>(handler: (msg: any) => T) => {
return await new Promise((resolve, reject) => {
this.handleUpdate = () => {
if (this.timedOut) {
@ -203,7 +212,320 @@ class Test {
throw new Error("formatted code did not match");
}
};
testLsp = async () => {};
testLsp = async () => {
const code = this.config.lsp!.code!; // FIXME
const after = this.config.lsp!.after;
const item = this.config.lsp!.item!; // FIXME
const root = await this.wait((msg: any) => {
if (msg.event === "lspStarted") {
return msg.root;
}
});
const idx = after
? this.config.template.indexOf(after)
: this.config.template.length;
const pos = findPosition(this.config.template, idx);
const newCode =
this.config.template.slice(0, idx) +
code +
this.config.template.slice(idx);
const newIdx = idx + code.length;
const newPos = findPosition(newCode, newIdx);
this.send({
event: "lspInput",
input: {
jsonrpc: "2.0",
id: "0d75333a-47d8-4da8-8030-c81d7bd9eed7",
method: "initialize",
params: {
processId: null,
clientInfo: { name: "vscode" },
rootPath: root,
rootUri: `file://${root}`,
capabilities: {
workspace: {
applyEdit: true,
workspaceEdit: {
documentChanges: true,
resourceOperations: ["create", "rename", "delete"],
failureHandling: "textOnlyTransactional",
},
didChangeConfiguration: { dynamicRegistration: true },
didChangeWatchedFiles: { dynamicRegistration: true },
symbol: {
dynamicRegistration: true,
symbolKind: {
valueSet: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
],
},
},
executeCommand: { dynamicRegistration: true },
configuration: true,
workspaceFolders: true,
},
textDocument: {
publishDiagnostics: {
relatedInformation: true,
versionSupport: false,
tagSupport: { valueSet: [1, 2] },
},
synchronization: {
dynamicRegistration: true,
willSave: true,
willSaveWaitUntil: true,
didSave: true,
},
completion: {
dynamicRegistration: true,
contextSupport: true,
completionItem: {
snippetSupport: true,
commitCharactersSupport: true,
documentationFormat: ["markdown", "plaintext"],
deprecatedSupport: true,
preselectSupport: true,
tagSupport: { valueSet: [1] },
},
completionItemKind: {
valueSet: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
],
},
},
hover: {
dynamicRegistration: true,
contentFormat: ["markdown", "plaintext"],
},
signatureHelp: {
dynamicRegistration: true,
signatureInformation: {
documentationFormat: ["markdown", "plaintext"],
parameterInformation: { labelOffsetSupport: true },
},
contextSupport: true,
},
definition: { dynamicRegistration: true, linkSupport: true },
references: { dynamicRegistration: true },
documentHighlight: { dynamicRegistration: true },
documentSymbol: {
dynamicRegistration: true,
symbolKind: {
valueSet: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
],
},
hierarchicalDocumentSymbolSupport: true,
},
codeAction: {
dynamicRegistration: true,
isPreferredSupport: true,
codeActionLiteralSupport: {
codeActionKind: {
valueSet: [
"",
"quickfix",
"refactor",
"refactor.extract",
"refactor.inline",
"refactor.rewrite",
"source",
"source.organizeImports",
],
},
},
},
codeLens: { dynamicRegistration: true },
formatting: { dynamicRegistration: true },
rangeFormatting: { dynamicRegistration: true },
onTypeFormatting: { dynamicRegistration: true },
rename: { dynamicRegistration: true, prepareSupport: true },
documentLink: { dynamicRegistration: true, tooltipSupport: true },
typeDefinition: { dynamicRegistration: true, linkSupport: true },
implementation: { dynamicRegistration: true, linkSupport: true },
colorProvider: { dynamicRegistration: true },
foldingRange: {
dynamicRegistration: true,
rangeLimit: 5000,
lineFoldingOnly: true,
},
declaration: { dynamicRegistration: true, linkSupport: true },
},
},
initializationOptions: this.config.lsp!.init || {},
trace: "off",
workspaceFolders: [
{
uri: `file://${root}`,
name: `file://${root}`,
},
],
},
},
});
await this.wait((msg: any) => {
return (
msg.event === "lspOutput" &&
msg.output.id === "0d75333a-47d8-4da8-8030-c81d7bd9eed7"
);
});
this.send({
event: "lspInput",
input: { jsonrpc: "2.0", method: "initialized", params: {} },
});
this.send({
event: "lspInput",
input: {
jsonrpc: "2.0",
method: "textDocument/didOpen",
params: {
textDocument: {
uri: `file://${root}/${this.config.main}`,
languageId: "python", // FIXME
version: 1,
text: `${this.config.template}`,
},
},
},
});
this.send({
event: "lspInput",
input: {
jsonrpc: "2.0",
method: "textDocument/didChange",
params: {
textDocument: {
uri: `file://${root}/${this.config.main}`,
version: 3,
},
contentChanges: [
{
range: {
start: pos,
end: pos,
},
rangeLength: 0,
text: code,
},
],
},
},
});
this.send({
event: "lspInput",
input: {
jsonrpc: "2.0",
id: "ecdb8a55-f755-4553-ae8e-91d6ebbc2045",
method: "textDocument/completion",
params: {
textDocument: {
uri: `file://${root}/${this.config.main}`,
},
position: newPos,
context: { triggerKind: 1 },
},
},
});
const items: any = await this.wait((msg: any) => {
if (msg.event === "lspOutput") {
if (msg.output.method === "workspace/configuration") {
this.send({
event: "lspInput",
input: {
jsonrpc: "2.0",
id: msg.output.id,
result: Array(msg.output.params.items.length).fill(
this.config.lsp!.config !== undefined
? this.config.lsp!.config
: {}
),
},
});
} else if (msg.output.id === "ecdb8a55-f755-4553-ae8e-91d6ebbc2045") {
return msg.output.result.items;
}
}
});
if (
!(items && items.filter(({ label }: any) => label === item).length > 0)
) {
throw new Error("completion item did not appear");
}
};
}
function lint(lang: string) {

View File

@ -25,11 +25,13 @@ interface RijuConfig {
id: string;
monacoLang?: string;
main: string;
format?: string;
lspDisableDynamicRegistration?: boolean;
lspInit?: any;
lspConfig?: any;
lspLang?: string;
format?: any;
lsp?: {
disableDynamicRegistration?: boolean;
init?: any;
config?: any;
lang?: string;
};
template: string;
}
@ -91,13 +93,13 @@ class RijuMessageWriter extends AbstractMessageWriter {
switch ((msg as any).method) {
case "initialize":
(msg as any).params.processId = null;
if (config.lspDisableDynamicRegistration) {
if (config.lsp!.disableDynamicRegistration) {
this.disableDynamicRegistration(msg);
}
break;
case "textDocument/didOpen":
if (config.lspLang) {
(msg as any).params.textDocument.languageId = config.lspLang;
if (config.lsp!.lang) {
(msg as any).params.textDocument.languageId = config.lsp!.lang;
}
}
if (DEBUG) {
@ -201,7 +203,7 @@ async function main() {
console.error("Unexpected message from server:", message);
return;
}
const services = MonacoServices.create(editor, {
const services = MonacoServices.create(editor as any, {
rootUri: `file://${message.root}`,
});
servicesDisposable = Services.install(services);
@ -230,12 +232,12 @@ async function main() {
return Array(
(configuration(params, token) as {}[]).length
).fill(
config.lspConfig !== undefined ? config.lspConfig : {}
config.lsp!.config !== undefined ? config.lsp!.config : {}
);
},
},
},
initializationOptions: config.lspInit || {},
initializationOptions: config.lsp!.init || {},
},
connectionProvider: {
get: (errorHandler: any, closeHandler: any) =>