Skip to content

Feature/signature_help #230

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion extension/server/src/providers/completionItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default async function completionItemProvider(handler: CompletionParams):
const item = CompletionItem.create(`${procedure.name}`);
item.kind = CompletionItemKind.Function;
item.insertTextFormat = InsertTextFormat.Snippet;
item.insertText = `${procedure.name}(${procedure.subItems.map((parm, index) => `\${${index + 1}:${parm.name}}`).join(`:`)})`;
item.insertText = procedure.name;
item.detail = procedure.keywords.join(` `);
item.documentation = procedure.description;
items.push(item);
Expand Down
17 changes: 12 additions & 5 deletions extension/server/src/providers/linter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CodeAction, CodeActionKind, Diagnostic, DiagnosticSeverity, DidChangeWa
import { TextDocument } from 'vscode-languageserver-textdocument';
import { URI } from 'vscode-uri';
import { documents, parser } from '..';
import { IssueRange, Rules } from '../../../../../language/parserTypes';
import * as parserTypes from '../../../../../language/parserTypes';
import Linter from '../../../../../language/linter';
import Cache from '../../../../../language/models/cache';
import codeActionsProvider from './codeActions';
Expand All @@ -12,12 +12,17 @@ import documentFormattingProvider from './documentFormatting';
import * as Project from "../project";
import { connection, getFileRequest, getWorkingDirectory, validateUri, watchedFilesChangeEvent } from '../../connection';
import { parseMemberUri } from '../../data';
import Document from '../../../../../language/document';
import { signatureHelpProvider } from './signatureHelp';

export let jsonCache: { [uri: string]: string } = {};
export let documentParseCache: { [uri: string]: Document } = {};

let jsonCache: { [uri: string]: string } = {};

export function initialise(connection: _Connection) {
connection.onCodeAction(codeActionsProvider);
connection.onDocumentFormatting(documentFormattingProvider);
connection.onSignatureHelp(signatureHelpProvider);

// This only works for local workspace files
watchedFilesChangeEvent.push((params: DidChangeWatchedFilesParams) => {
Expand Down Expand Up @@ -75,7 +80,7 @@ export function initialise(connection: _Connection) {
})
}

export function calculateOffset(document: TextDocument, error: IssueRange) {
export function calculateOffset(document: TextDocument, error: parserTypes.IssueRange) {
const offset = error.offset;

return Range.create(
Expand Down Expand Up @@ -184,7 +189,7 @@ export async function refreshLinterDiagnostics(document: TextDocument, docs: Cac
const indentDiags: Diagnostic[] = [];
const generalDiags: Diagnostic[] = [];

const options: Rules = await getLintOptions(document.uri);
const options: parserTypes.Rules = await getLintOptions(document.uri);

let detail;

Expand All @@ -211,6 +216,8 @@ export async function refreshLinterDiagnostics(document: TextDocument, docs: Cac
const indentErrors = detail.indentErrors;
const errors = detail.errors;

documentParseCache[document.uri] = detail.doc;

if (indentErrors.length > 0) {
indentErrors.forEach(error => {
const range = Range.create(error.line, 0, error.line, error.currentIndent);
Expand Down Expand Up @@ -244,7 +251,7 @@ export async function refreshLinterDiagnostics(document: TextDocument, docs: Cac
}
}

export function getActions(document: TextDocument, errors: IssueRange[]) {
export function getActions(document: TextDocument, errors: parserTypes.IssueRange[]) {
let actions: CodeAction[] = [];

// We need to move subroutine to the end and reverse the contents
Expand Down
98 changes: 98 additions & 0 deletions extension/server/src/providers/linter/signatureHelp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { MarkedString, MarkupContent, MarkupKind, ParameterInformation, Range, SignatureHelp, SignatureHelpParams, SignatureHelpTriggerKind, SignatureInformation } from "vscode-languageserver";
import { documents, parser } from "..";
import { documentParseCache } from ".";
import Statement from "../../../../../language/statement";
import Declaration from "../../../../../language/models/declaration";

export async function signatureHelpProvider(params: SignatureHelpParams): Promise<SignatureHelp | undefined> {
const uri = params.textDocument.uri;
const document = documents.get(uri);

if (document) {
const isFree = (document.getText(Range.create(0, 0, 0, 6)).toUpperCase() === `**FREE`);

if (isFree) {
const offset = document?.offsetAt(params.position);
const parsedDocument = documentParseCache[uri];

if (parsedDocument) {
const statement = parsedDocument.getStatementByOffset(offset);
if (statement) {
const withBlocks = statement.asBlocks();
const currentTokenIx = withBlocks.findIndex(token => offset >= token.range.start && offset <= token.range.end);

// Check we're in a block type
if (currentTokenIx >= 0 && withBlocks[currentTokenIx].type === `block`) {
// Check the token before is a valid word
if (withBlocks[currentTokenIx - 1] && withBlocks[currentTokenIx - 1].type === `word`) {
const nameToken = withBlocks[currentTokenIx - 1];
const parmBlock = withBlocks[currentTokenIx];

const nameValue = nameToken.value!.toUpperCase();
const lineNumber = nameToken.range.line;

if (document) {
const doc = await parser.getDocs(uri);
if (doc) {

// If they're typing inside of a procedure, let's get the stuff from there too
const currentProcedure = doc.procedures.find((proc, index) =>
lineNumber >= proc.range.start &&
(lineNumber <= proc.range.end + 1 || index === doc.procedures.length - 1)
);

const possibleFunction: Declaration | undefined = [
currentProcedure && currentProcedure.scope ? currentProcedure.scope.procedures.find(proc => proc.name.toUpperCase() === nameValue) : undefined,
doc.procedures.find(proc => proc.name.toUpperCase() === nameValue)
].find(x => x); // find the first non-undefined item

if (possibleFunction) {
const parms = Statement.getParameters(parmBlock.block!);
const currentParm = parms.findIndex(token => offset >= token.range.start && offset <= token.range.end);
const activeParameter = currentParm >= 0 ? currentParm : 0;

let retrunValue = possibleFunction.keywords.filter(keyword => !keyword.startsWith(`EXTPROC`));
if (retrunValue.length === 0) retrunValue = [`void`];

const parmsString = possibleFunction.subItems.map(x => x.name).join(` : `);

let currentSig: SignatureInformation = {
label: `${possibleFunction.name}(${parmsString}): ${retrunValue.join(` `)}`,
activeParameter,
documentation: possibleFunction.description.trim().length > 0 ?
{
kind: MarkupKind.Markdown,
value: `---\n\n${possibleFunction.description}`
} : undefined,

parameters: possibleFunction.subItems.map((parm, i): ParameterInformation => {
const docLines = [
`\`${parm.name}: ${parm.keywords.join(` `)}\``,
parm.description.trim().length ? parm.description : undefined
].filter(x => x).join(`\n\n`);

return {
label: parm.name,
documentation: {
kind: MarkupKind.Markdown,
value: docLines
}
}
})
};

return {signatures: [currentSig], activeSignature: 0, activeParameter};
}
}
}
}
}

return { signatures: [] };
}
}
}
}

return;
}
5 changes: 4 additions & 1 deletion extension/server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ connection.onInitialize((params: InitializeParams) => {
result.capabilities.documentSymbolProvider = true;
result.capabilities.definitionProvider = true;
result.capabilities.completionProvider = {
triggerCharacters: [` `, `.`, `:`]
triggerCharacters: [` `, `.`, `:`, `(`]
};
result.capabilities.hoverProvider = true;
result.capabilities.referencesProvider = true;
Expand All @@ -81,6 +81,9 @@ connection.onInitialize((params: InitializeParams) => {
workDoneProgress: true
};
}
result.capabilities.signatureHelpProvider = {
triggerCharacters: [`(`, `:`],
};
}

if (hasWorkspaceFolderCapability) {
Expand Down