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)); this.ws.send(JSON.stringify(msg));
} catch (err) { } catch (err) {
this.log(`Failed to send websocket message: ${err}`); this.log(`Failed to send websocket message: ${err}`);
console.log(err);
await this.teardown(); await this.teardown();
} }
}; };

View File

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

View File

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

View File

@ -8,6 +8,13 @@ import { LangConfig, langs } from "./langs";
const TIMEOUT_MS = 3000; 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 { class Test {
lang: string; lang: string;
type: string; type: string;
@ -24,6 +31,8 @@ class Test {
send = (msg: any) => { send = (msg: any) => {
this.ws.onMessage(JSON.stringify(msg)); this.ws.onMessage(JSON.stringify(msg));
this.messages.push(msg);
this.handledMessages += 1;
}; };
constructor(lang: string, type: string) { 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) => { return await new Promise((resolve, reject) => {
this.handleUpdate = () => { this.handleUpdate = () => {
if (this.timedOut) { if (this.timedOut) {
@ -203,7 +212,320 @@ class Test {
throw new Error("formatted code did not match"); 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) { function lint(lang: string) {

View File

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