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

feat: signature help #724

Merged
merged 5 commits into from
Nov 5, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
test: signature help
  • Loading branch information
lars-reimann committed Nov 5, 2023
commit b50e7fed025535fc7d2a53a6a7f11eb2d86b5dad
Original file line number Diff line number Diff line change
@@ -72,9 +72,12 @@ export class SafeDsSignatureHelpProvider implements SignatureHelpProvider {
signatures: [
{
label: this.typeComputer.computeType(callable).toString(),
parameters: getParameters(callable).map((parameter) => ({
label: parameter.name,
})),
parameters: getParameters(callable).map((parameter) => {
const type = this.typeComputer.computeType(parameter);
return {
label: `${parameter.name}: ${type}`,
};
}),
documentation: createMarkupContent(this.documentationProvider.getDocumentation(callable)),
},
],
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { clearDocuments, parseHelper } from 'langium/test';
import { createSafeDsServices } from '../../../src/language/index.js';
import { SignatureHelp } from 'vscode-languageserver';
import { NodeFileSystem } from 'langium/node';
import { findTestRanges } from '../../helpers/testRanges.js';

const services = createSafeDsServices(NodeFileSystem).SafeDs;
const signatureHelpProvider = services.lsp.SignatureHelp!;
const workspaceManager = services.shared.workspace.WorkspaceManager;
const parse = parseHelper(services);

describe('SafeDsSignatureHelpProvider', async () => {
beforeEach(async () => {
// Load the builtin library
await workspaceManager.initializeWorkspace([]);
});

afterEach(async () => {
await clearDocuments(services);
});

it('should always select the first signature', async () => {
const code = `
fun f(p: Int)

pipeline myPipeline {
f(»«);
}
`;

const actualSignatureHelp = await getActualSignatureHelp(code);
expect(actualSignatureHelp?.activeSignature).toBe(0);
});

it.each([
{
testName: 'empty argument list',
code: `
fun f(p: Int)

pipeline myPipeline {
f(»«);
}
`,
expectedIndex: 0,
},
{
testName: 'before comma',
code: `
fun f(p: Int)

pipeline myPipeline {
f(»«, );
}
`,
expectedIndex: 0,
},
{
testName: 'after comma',
code: `
fun f(p: Int)

pipeline myPipeline {
f(1, »«);
}
`,
expectedIndex: 1,
},
])('should select the correct parameter ($testName)', async ({ code, expectedIndex }) => {
const actualSignatureHelp = await getActualSignatureHelp(code);
expect(actualSignatureHelp?.activeParameter).toBe(expectedIndex);
});

it.each([
{
testName: 'not in an abstract call',
code: '»«',
expectedSignature: undefined,
},
{
testName: 'unresolved callable',
code: `
pipeline myPipeline {
f(»«);
}
`,
expectedSignature: undefined,
},
{
testName: 'annotation call',
code: `
/**
* Lorem ipsum.
*/
annotation A(p: Int)

@A(»«)
pipeline myPipeline {}
`,
expectedSignature: [
{
label: '(p: Int) -> ()',
documentation: {
kind: 'markdown',
value: 'Lorem ipsum.',
},
parameters: [
{
label: 'p: Int',
},
],
},
],
},
{
testName: 'call',
code: `
/**
* Lorem ipsum.
*/
fun f(p: Int)

pipeline myPipeline {
f(»«);
}
`,
expectedSignature: [
{
label: '(p: Int) -> ()',
documentation: {
kind: 'markdown',
value: 'Lorem ipsum.',
},
parameters: [
{
label: 'p: Int',
},
],
},
],
},
])('should assign the correct signature ($testName)', async ({ code, expectedSignature }) => {
const actualSignatureHelp = await getActualSignatureHelp(code);
expect(actualSignatureHelp?.signatures).toStrictEqual(expectedSignature);
});
});

const getActualSignatureHelp = async (code: string): Promise<SignatureHelp | undefined> => {
const document = await parse(code);
const testRangesResult = findTestRanges(code, document.uri);
if (testRangesResult.isErr) {
throw new Error(testRangesResult.error.message);
} else if (testRangesResult.value.length === 0) {
throw new Error('No test ranges found.');
}

const position = testRangesResult.value[0]!.start;
return signatureHelpProvider.provideSignatureHelp(document, {
textDocument: document.textDocument,
position,
});
};