aboutsummaryrefslogtreecommitdiff
path: root/tests/drivers/ext_cache_test.js
diff options
context:
space:
mode:
Diffstat (limited to 'tests/drivers/ext_cache_test.js')
-rw-r--r--tests/drivers/ext_cache_test.js27
1 files changed, 26 insertions, 1 deletions
diff --git a/tests/drivers/ext_cache_test.js b/tests/drivers/ext_cache_test.js
index 11bf50b..f389fcc 100644
--- a/tests/drivers/ext_cache_test.js
+++ b/tests/drivers/ext_cache_test.js
@@ -29,8 +29,15 @@ const stub = {
appendMarkdown(v) { this.value += v; return this; }
appendCodeblock(v, language) { this.value += '\n```' + language + '\n' + v + '\n```\n'; return this; }
},
+ CompletionItem: class { constructor(label, kind) { Object.assign(this, { label, kind }); } },
+ CompletionList: class { constructor(items, isIncomplete) { Object.assign(this, { items, isIncomplete }); } },
+ CompletionItemKind: { Variable: 5 },
+ CompletionItemTag: { Deprecated: 1 },
+ CodeActionKind: { QuickFix: 'quickfix' },
+ CodeAction: class { constructor(title, kind) { Object.assign(this, { title, kind }); } },
+ WorkspaceEdit: class { constructor() { this.edits = []; } replace(uri, range, newText) { this.edits.push({ uri, range, newText }); } },
Hover: class { constructor(c) { this.contents = c; } },
- Uri: { parse: s => ({ toString: () => s, fsPath: decodeURIComponent(s.replace(/^file:\/\//, '')) }) },
+ Uri: { file: p => ({ toString: () => 'file://' + p, fsPath: p }), parse: s => ({ toString: () => s, fsPath: decodeURIComponent(s.replace(/^file:\/\//, '')) }) },
workspace: {
workspaceFolders: undefined,
textDocuments: world.textDocuments,
@@ -41,6 +48,7 @@ const stub = {
for (const k of ['onDidChange', 'onDidCreate', 'onDidDelete']) w[k] = f => { w.handlers[k] = f; return { dispose() {} }; };
return w;
},
+ registerTextDocumentContentProvider: () => ({ dispose() {} }),
...['onDidChangeConfiguration', 'onDidOpenTextDocument', 'onDidChangeTextDocument', 'onDidSaveTextDocument', 'onDidCloseTextDocument']
.reduce((o, k) => { o[k] = f => { world.listeners[k] = f; return { dispose() {} }; }; return o; }, {}),
},
@@ -54,7 +62,12 @@ const stub = {
delete() {}, dispose() {},
}),
registerDocumentSemanticTokensProvider: () => ({ dispose() {} }),
+ registerCompletionItemProvider: (sel, p) => { world.completion = p; return { dispose() {} }; },
+ registerCodeActionsProvider: (sel, p) => { world.actions = p; return { dispose() {} }; },
registerHoverProvider: (sel, p) => { world.hover = p; return { dispose() {} }; },
+ registerDocumentFormattingEditProvider: () => ({ dispose() {} }),
+ registerDefinitionProvider: (sel, p) => { world.definition = p; return { dispose() {} }; },
+ registerReferenceProvider: (sel, p) => { world.references = p; return { dispose() {} }; },
},
commands: { registerCommand: () => ({ dispose() {} }) },
};
@@ -116,6 +129,9 @@ const mainUri = 'file://' + main;
const hv = world.hover && await world.hover.provideHover(mainDoc, { line: 1, character: 4 });
const hc = hv && hv.contents[0];
check('the hover is markdown with a zc code block, parsed and not escaped', hc && !hc.escaped && /^```zc\n/.test(hc.value) && /\n```/.test(hc.value), JSON.stringify(hc));
+ const suggestions = await world.completion.provideCompletionItems(mainDoc, { line: 1, character: 4 }, { isCancellationRequested: false });
+ check('completion provider returns signatures and replacement ranges', suggestions instanceof stub.CompletionList && suggestions.items.some(c => c.detail && c.range && c.insertText), JSON.stringify(suggestions && suggestions.items.slice(0, 2)));
+ check('cancelled completion requests are ignored', await world.completion.provideCompletionItems(mainDoc, { line: 1, character: 4 }, { isCancellationRequested: true }) === null);
check('session one: the answer is saved under a key', savedKeys.length === 1 && /^[0-9a-f]{64}$/.test(savedKeys[0]), JSON.stringify(savedKeys));
ext.deactivate();
await sleep(700);
@@ -146,6 +162,15 @@ const mainUri = 'file://' + main;
await sleep(200);
check('the old tree is answered from the cache, without asking the server', world.lines.some(l => /from the cache/.test(l)) && !world.lines.some(l => /-> textDocument\/didChange/.test(l)), JSON.stringify(world.lines.slice(-4)));
+ const expiredDoc = doc(path.join(root, 'expired.zc'), 'include rt; fn main() {\n let used = rt.new(int, 3);\n rt.free(used);\n \n}');
+ const expiredList = await world.completion.provideCompletionItems(expiredDoc, { line: 3, character: 1 }, { isCancellationRequested: false });
+ const expiredItem = expiredList && expiredList.items.find(c => c.label === 'used');
+ check('completion maps consumed lifetimes to the editor strikethrough tag', expiredItem && expiredItem.tags?.[0] === stub.CompletionItemTag.Deprecated && /lifetime ended/.test(expiredItem.documentation?.value));
+ const actionDoc = doc(path.join(root, 'imports.zc'), 'fn main() { rt.put_n(7); }');
+ const actions = await world.actions.provideCodeActions(actionDoc, new stub.Range(0, 15, 0, 15), {}, { isCancellationRequested: false });
+ check('quick fixes reach the editor as workspace edits', actions.some(a => a.kind === 'quickfix' && a.edit.edits.some(e => e.newText === 'include rt;\n')));
+ const cancelled = await world.actions.provideCodeActions(actionDoc, new stub.Range(0, 15, 0, 15), {}, { isCancellationRequested: true });
+ check('cancelled quick-fix requests produce no editor edits', cancelled.length === 0);
ext.deactivate();
await sleep(700);
fs.rmSync(root, { recursive: true, force: true });