add some arg help
This commit is contained in:
parent
028ccf2bf9
commit
09d2420508
|
|
@ -2,6 +2,7 @@ import { TextDocument } from 'vscode-languageserver-textdocument'
|
|||
import { buildDiagnostics } from './diagnostics'
|
||||
import { buildSemanticTokens, TOKEN_MODIFIERS, TOKEN_TYPES } from './semanticTokens'
|
||||
import { provideCompletions } from './completion/completionProvider'
|
||||
import { provideSignatureHelp } from './signatureHelp'
|
||||
import { PRELUDE_NAMES } from './metadata/prelude-names'
|
||||
import { parser } from '../../../src/parser/shrimp'
|
||||
import { setGlobals } from '../../../src/parser/tokenizer'
|
||||
|
|
@ -29,6 +30,7 @@ connection.onInitialize(handleInitialize)
|
|||
connection.languages.semanticTokens.on(handleSemanticTokens)
|
||||
documents.onDidChangeContent(handleDocumentChange)
|
||||
connection.onCompletion(handleCompletion)
|
||||
connection.onSignatureHelp(handleSignatureHelp)
|
||||
|
||||
// Debug commands
|
||||
connection.onRequest('shrimp/parseTree', handleParseTree)
|
||||
|
|
@ -49,6 +51,9 @@ function handleInitialize(): InitializeResult {
|
|||
completionProvider: {
|
||||
triggerCharacters: ['.'],
|
||||
},
|
||||
signatureHelpProvider: {
|
||||
triggerCharacters: [' '],
|
||||
},
|
||||
semanticTokensProvider: {
|
||||
legend: {
|
||||
tokenTypes: TOKEN_TYPES,
|
||||
|
|
@ -113,6 +118,12 @@ function handleCompletion(params: any) {
|
|||
return [...keywordCompletions, ...preludeCompletions]
|
||||
}
|
||||
|
||||
function handleSignatureHelp(params: any) {
|
||||
const document = documents.get(params.textDocument.uri)
|
||||
if (!document) return
|
||||
return provideSignatureHelp(document, params.position)
|
||||
}
|
||||
|
||||
function handleParseTree(params: { uri: string }) {
|
||||
connection.console.log(`🦐 Parse tree requested for: ${params.uri}`)
|
||||
const document = documents.get(params.uri)
|
||||
|
|
|
|||
105
vscode-extension/server/src/signatureHelp.ts
Normal file
105
vscode-extension/server/src/signatureHelp.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { SignatureHelp, SignatureInformation, ParameterInformation } from 'vscode-languageserver/node'
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument'
|
||||
import { Tree, SyntaxNode } from '@lezer/common'
|
||||
import { parser } from '../../../src/parser/shrimp'
|
||||
import { completions } from './metadata/prelude-completions'
|
||||
|
||||
export const provideSignatureHelp = (
|
||||
document: TextDocument,
|
||||
position: { line: number; character: number }
|
||||
): SignatureHelp | undefined => {
|
||||
const text = document.getText()
|
||||
const tree = parser.parse(text)
|
||||
const cursorPos = document.offsetAt(position)
|
||||
|
||||
const context = findCallContext(tree, cursorPos, text)
|
||||
if (!context) return
|
||||
|
||||
const params = lookupFunctionParams(context.funcName)
|
||||
if (!params) return
|
||||
|
||||
return {
|
||||
signatures: [buildSignature(context.funcName, params)],
|
||||
activeParameter: Math.min(context.argCount, params.length - 1),
|
||||
}
|
||||
}
|
||||
|
||||
const findCallContext = (tree: Tree, cursorPos: number, text: string) => {
|
||||
const findBestCall = (node: SyntaxNode): SyntaxNode | undefined => {
|
||||
let result: SyntaxNode | undefined
|
||||
|
||||
const isCall = node.name === 'FunctionCall' || node.name === 'FunctionCallOrIdentifier'
|
||||
|
||||
// Call ends just before cursor (within 5 chars)
|
||||
if (isCall && node.to <= cursorPos && cursorPos <= node.to + 5) {
|
||||
result = node
|
||||
}
|
||||
|
||||
// Cursor is inside the call's span
|
||||
if (isCall && node.from < cursorPos && cursorPos < node.to) {
|
||||
result = node
|
||||
}
|
||||
|
||||
// Recurse - prefer smaller spans (more specific)
|
||||
let child = node.firstChild
|
||||
while (child) {
|
||||
const found = findBestCall(child)
|
||||
if (found) {
|
||||
const foundSpan = found.to - found.from
|
||||
const resultSpan = result ? result.to - result.from : Infinity
|
||||
if (foundSpan < resultSpan) {
|
||||
result = found
|
||||
}
|
||||
}
|
||||
child = child.nextSibling
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const call = findBestCall(tree.topNode)
|
||||
if (!call) return
|
||||
|
||||
// Count args before cursor
|
||||
let argCount = 0
|
||||
let child = call.firstChild
|
||||
while (child) {
|
||||
if ((child.name === 'PositionalArg' || child.name === 'NamedArg') && child.to <= cursorPos) {
|
||||
argCount++
|
||||
}
|
||||
child = child.nextSibling
|
||||
}
|
||||
|
||||
// Extract function name
|
||||
const firstChild = call.firstChild
|
||||
if (!firstChild) return
|
||||
|
||||
let funcName: string | undefined
|
||||
if (firstChild.name === 'DotGet') {
|
||||
funcName = text.slice(firstChild.from, firstChild.to)
|
||||
} else if (firstChild.name === 'Identifier') {
|
||||
funcName = text.slice(firstChild.from, firstChild.to)
|
||||
}
|
||||
|
||||
if (!funcName) return
|
||||
|
||||
return { funcName, argCount }
|
||||
}
|
||||
|
||||
const lookupFunctionParams = (funcName: string): string[] | undefined => {
|
||||
// Handle module functions: "list.map" → modules.list.map
|
||||
if (funcName.includes('.')) {
|
||||
const [moduleName, methodName] = funcName.split('.')
|
||||
const module = completions.modules[moduleName as keyof typeof completions.modules]
|
||||
const method = module?.[methodName as keyof typeof module]
|
||||
return method?.params as string[] | undefined
|
||||
}
|
||||
|
||||
// TODO: Handle top-level prelude functions (print, range, etc.)
|
||||
}
|
||||
|
||||
const buildSignature = (funcName: string, params: string[]): SignatureInformation => {
|
||||
const label = `${funcName}(${params.join(', ')})`
|
||||
const parameters: ParameterInformation[] = params.map(p => ({ label: p }))
|
||||
return { label, parameters }
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user