Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve go-to-definition for object pattern field aliases #242

Merged
merged 10 commits into from
Aug 26, 2023
48 changes: 28 additions & 20 deletions src/server/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface Definition {
uri: string;
cursor: Node;
body: Node;
name: string | undefined;
}

interface Search {
Expand All @@ -34,7 +35,6 @@ export function findMostSpecificNodeForPosition(
node.end &&
position.line >= node.start[0] - 1 &&
position.line <= node.end[0] - 1 &&
// position.line == node.start[0] - 1 &&
(position.line !== node.start[0] - 1 ||
position.character >= node.start[1]) &&
(position.line !== node.end[0] - 1 ||
Expand All @@ -46,9 +46,6 @@ export function findMostSpecificNodeForPosition(
let nodeLines: number;
let nodeChars: number;
nodes.forEach((n: Node) => {
// if (ignoredAstNodes.includes(n.name)) {
// return;
// }
const nLines = n.end![0] - n.start![0];
const nChars = n.end![1] - n.start![1];
if (
Expand Down Expand Up @@ -107,10 +104,12 @@ export function locationFromDefinition(definition: Definition) {
return location;
}

// TODO: refactor to use `findInPattern()`
function findNameInPattern(search: Search, pat: Node): Node | undefined {
function findNameInPattern(
search: Search,
pat: Node,
): [string, Node] | undefined {
return findInPattern(pat, (name, node) =>
name === search.name ? node : undefined,
name === search.name ? [name, node] : undefined,
);
}

Expand Down Expand Up @@ -147,7 +146,7 @@ export function findDefinition(
if (!node) {
return;
}
const reference = { uri, node };
const reference: Reference = { uri, node };
const importDefinition = followImport(context, reference);
if (importDefinition) {
return importDefinition;
Expand Down Expand Up @@ -246,8 +245,10 @@ function followImport(
return;
}
// Find the relevant field name
const field = matchNode(reference.node.parent?.parent, 'ObjP', () =>
matchNode(reference.node, 'VarP', (name: string) => name),
const field = matchNode(
reference.node.parent?.parent,
'ObjP',
() => reference.node.parent?.name,
);
// Follow the module import
return matchNode(importNode, 'ImportE', (path: string) => {
Expand Down Expand Up @@ -275,18 +276,18 @@ function followImport(
console.log('AST:', status.program.export.ast);
return;
}
const declaration = {
uri,
cursor: exportNode,
body: exportNode,
};
if (field) {
return searchObject(
{ uri: declaration.uri, node: declaration.body },
{ uri, node: exportNode },
{ type: 'variable', name: field },
); // || declaration
);
}
return declaration;
return {
uri,
cursor: exportNode,
body: exportNode,
name: undefined,
};
});
}

Expand Down Expand Up @@ -349,12 +350,13 @@ function searchDeclaration(
): Definition | undefined {
return (
matchNode(dec, 'LetD', (pat: Node, body: Node) => {
const varNode = findNameInPattern(search, pat);
const [name, varNode] = findNameInPattern(search, pat) || [];
return (
varNode && {
uri: reference.uri,
cursor: varNode,
body,
name,
}
);
}) ||
Expand All @@ -364,6 +366,7 @@ function searchDeclaration(
uri: reference.uri,
cursor: dec, // TODO: cursor on variable name
body,
name,
}
: undefined,
) ||
Expand All @@ -373,6 +376,7 @@ function searchDeclaration(
uri: reference.uri,
cursor: dec, // TODO: cursor on variable name
body: dec,
name,
}
: undefined,
)
Expand All @@ -391,6 +395,7 @@ function searchTypeBinding(
uri: reference.uri,
cursor: typ, // TODO: source location from `name`
body: typ,
name,
}
: undefined,
) ||
Expand All @@ -400,6 +405,7 @@ function searchTypeBinding(
uri: reference.uri,
cursor: dec, // TODO: cursor on variable name
body: dec,
name,
}
: undefined,
)
Expand Down Expand Up @@ -430,6 +436,7 @@ function searchObject(
uri: reference.uri,
cursor: arg,
body,
name,
},
) ||
matchNode(arg, 'DecField', (dec: Node) =>
Expand Down Expand Up @@ -458,12 +465,13 @@ function searchObject(
},
);
if (!definition) {
const pat = findNameInPattern(search, arg); // Function parameters
const [name, pat] = findNameInPattern(search, arg) || []; // Function parameters
if (pat) {
definition = {
uri: reference.uri,
cursor: pat,
body: pat,
name,
};
}
}
Expand Down
37 changes: 4 additions & 33 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,10 @@ connection.onHover((event) => {
}
if (docNode.name === 'Prog' && !docNode.doc) {
// Get doc comment at top of file
return asNode(docNode.args?.[0])?.doc;
const doc = asNode(docNode.args?.[0])?.doc;
if (doc) {
return doc;
}
}
return docNode.doc;
}
Expand Down Expand Up @@ -1352,38 +1355,6 @@ connection.onRequest(TEST_FILE_REQUEST, async (event): Promise<TestResult> => {
} else {
throw new Error(`Invalid test mode: '${mode}'`);
}
// else {
// const start = Date.now();
// const wasiResult = motoko.wasm(virtualPath, 'wasi');
// console.log('Compile time:', Date.now() - start);

// const WebAssembly = (global as any).WebAssembly;
// const module = await (
// WebAssembly.compileStreaming || WebAssembly.compile
// )(wasiResult.wasm);
// const WASI = require('wasi');
// const wasi = new WASI({});
// const inst = new WebAssembly.Instance(module, {
// wasi_unstable: wasi.exports,
// });
// wasi.setMemory(inst.exports.memory);
// inst.exports._start();

// // if (exitCode !== 0) {
// // console.log(stdout);
// // console.error(stderr);
// // console.log('Exit code:', exitCode);
// // }
// // return {
// // passed: exitCode === 0,
// // stdout,
// // stderr,
// // };

// console.log(Object.keys(inst.exports)); ///////

// return { passed: true, stdout: '', stderr: '' };
// }
} catch (err) {
console.error(err);
return {
Expand Down
7 changes: 7 additions & 0 deletions src/server/syntax.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,11 @@ describe('syntax', () => {
expectFields(prog.exportFields, [undefined]);
expectWithFields(prog.exportFields[0].exp, ['a', 'b']);
});
test('object pattern with alias', () => {
const prog = parse(
'module { let { a; b = c } = { a = "a"; b = "b" }; }',
);
expectFields(prog.exportFields, [undefined]);
expectWithFields(prog.exportFields[0].exp, ['a', 'c']);
});
});
10 changes: 2 additions & 8 deletions src/server/syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,8 @@ export function findInPattern<T>(
matchNode(pat, 'VarP', (name: string) => fn(name, pat)) ||
matchNode(pat, 'ObjP', (...args: Node[]) => {
for (const field of args) {
const aliasNode = field.args![0] as Node;
const alias = matchNode(
aliasNode,
'VarP',
(alias) => alias,
field.name,
);
const result = fn(alias, pat);
const fieldPat = field.args![0] as Node;
const result = findInPattern(fieldPat, fn);
if (result !== undefined) {
return result;
}
Expand Down
5 changes: 0 additions & 5 deletions src/server/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@ import { getAbsoluteUri, getRelativeUri } from './utils';

describe('utils', () => {
test('getRelativeUri', () => {
// expect(getRelativeUri('file:a/b/c', 'file:a')).toStrictEqual('..');
// expect(getRelativeUri('file://a/b/c', 'file://a')).toStrictEqual('..');
expect(getRelativeUri('file:///a/b', 'file:///a')).toStrictEqual('..');
expect(getRelativeUri('mo:a/b', 'mo:a/b/c')).toStrictEqual('c');
expect(getRelativeUri('mo:a/b', 'mo:a/c')).toStrictEqual('c');
});
test('getAbsoluteUri', () => {
expect(getAbsoluteUri('mo:a', 'b')).toStrictEqual('mo:a/b');
// expect(getAbsoluteUri('file:a/b', '..')).toStrictEqual('file:a');
// expect(getAbsoluteUri('file://a/b', '..')).toStrictEqual('file://a');
expect(getAbsoluteUri('file:///a/b', '..')).toStrictEqual('file:///a');
expect(getAbsoluteUri('mo:a/b', '../c')).toStrictEqual('mo:a/c');
// expect(getAbsoluteUri('file:///a', 'mo:b')).toStrictEqual('mo:b');
});
});