wip
This commit is contained in:
parent
9b57304b87
commit
d195c5321c
|
|
@ -90,7 +90,7 @@ describe('autocomplete function names', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('autocomplete function arguments', () => {
|
describe('autocomplete positional arguments', () => {
|
||||||
test('shows args for shrimp function', () => {
|
test('shows args for shrimp function', () => {
|
||||||
const args = getArgsInScope(`
|
const args = getArgsInScope(`
|
||||||
add = do x y: x + y end
|
add = do x y: x + y end
|
||||||
|
|
@ -123,6 +123,44 @@ describe('autocomplete function arguments', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('autocomplete named arguments', () => {
|
||||||
|
test('shows remaining args after positional arg used', () => {
|
||||||
|
const args = getArgsInScope(`
|
||||||
|
add = do alpha bravo charlie: alpha + bravo + charlie end
|
||||||
|
add 5 <cursor>
|
||||||
|
`)
|
||||||
|
// alpha is used positionally
|
||||||
|
expect(args).toEqual(['bravo', 'charlie'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('filters args by prefix when typing', () => {
|
||||||
|
const args = getArgsInScope(`
|
||||||
|
add = do alpha bravo charlie: alpha + bravo + charlie end
|
||||||
|
add 5 b<cursor>
|
||||||
|
`)
|
||||||
|
// alpha is used, typing 'b' filters to bravo
|
||||||
|
expect(args).toEqual(['bravo'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('excludes named arg already used', () => {
|
||||||
|
const args = getArgsInScope(`
|
||||||
|
add = do alpha bravo charlie: alpha + bravo + charlie end
|
||||||
|
add bravo=10 <cursor>
|
||||||
|
`)
|
||||||
|
// bravo is used as named arg
|
||||||
|
expect(args).toEqual(['alpha', 'charlie'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('positional fills first slot, skips named args', () => {
|
||||||
|
const args = getArgsInScope(`
|
||||||
|
add = do alpha bravo charlie: alpha + bravo + charlie end
|
||||||
|
add bravo=5 <cursor>
|
||||||
|
`)
|
||||||
|
// bravo is named, 10 fills alpha (first positional slot)
|
||||||
|
expect(args).toEqual(['alpha', 'charlie'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|
||||||
const getVarsInScope = (codeWithCursor: string): CompletionItem[] => {
|
const getVarsInScope = (codeWithCursor: string): CompletionItem[] => {
|
||||||
|
|
@ -163,7 +201,3 @@ const getArgsInScope = (codeWithCursor: string): string[] => {
|
||||||
|
|
||||||
return items.map((v) => v.name)
|
return items.map((v) => v.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getArgItems = (codeWithCursor: string): CompletionItem[] => {
|
|
||||||
return getVarsInScope(codeWithCursor).filter((v) => v.kind === 'arg')
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -42,16 +42,19 @@ const autocompleteArg = (view: EditorView, context: FunctionCallContext): Comple
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count how many positional args have already been used
|
// Collect used args
|
||||||
const usedPositionalArgs = countPositionalArgs(context.fnCallNode, cursor)
|
const usedPositionalArgs = countPositionalArgs(context.fnCallNode, cursor)
|
||||||
|
const usedNamedArgs = collectUsedNamedArgs(context.fnCallNode, cursor, view)
|
||||||
|
|
||||||
if (fn.kind === 'shrimp-fn') {
|
if (fn.kind === 'shrimp-fn') {
|
||||||
// Skip params that have already been filled positionally
|
// Filter out named args, then skip positional slots
|
||||||
const remainingParams = fn.params.slice(usedPositionalArgs)
|
const availableParams = fn.params.filter((p) => !usedNamedArgs.has(p))
|
||||||
|
const remainingParams = availableParams.slice(usedPositionalArgs)
|
||||||
return remainingParams.map((paramName) => ({ kind: 'arg', name: paramName }))
|
return remainingParams.map((paramName) => ({ kind: 'arg', name: paramName }))
|
||||||
} else if (fn.kind === 'nose-command') {
|
} else if (fn.kind === 'nose-command') {
|
||||||
// Skip params that have already been filled positionally
|
// Filter out named args, then skip positional slots
|
||||||
const remainingParams = fn.def.signature.params.slice(usedPositionalArgs)
|
const availableParams = fn.def.signature.params.filter((p) => !usedNamedArgs.has(p.name))
|
||||||
|
const remainingParams = availableParams.slice(usedPositionalArgs)
|
||||||
return remainingParams.map((param) => ({
|
return remainingParams.map((param) => ({
|
||||||
kind: 'arg',
|
kind: 'arg',
|
||||||
name: param.name,
|
name: param.name,
|
||||||
|
|
@ -266,6 +269,32 @@ const countPositionalArgs = (fnCallNode: SyntaxNode, cursor: number): number =>
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const collectUsedNamedArgs = (
|
||||||
|
fnCallNode: SyntaxNode,
|
||||||
|
cursor: number,
|
||||||
|
view: EditorView
|
||||||
|
): Set<string> => {
|
||||||
|
const usedNames = new Set<string>()
|
||||||
|
let child = fnCallNode.firstChild
|
||||||
|
|
||||||
|
while (child) {
|
||||||
|
// Only collect NamedArg nodes that end before cursor
|
||||||
|
if (child.type.id === Terms.NamedArg && child.to <= cursor) {
|
||||||
|
// Find the NamedArgPrefix child which contains "paramname="
|
||||||
|
const prefixNode = child.firstChild
|
||||||
|
if (prefixNode?.type.id === Terms.NamedArgPrefix) {
|
||||||
|
const prefixText = view.state.doc.sliceString(prefixNode.from, prefixNode.to)
|
||||||
|
// Remove the trailing '=' to get the param name
|
||||||
|
const paramName = prefixText.slice(0, -1)
|
||||||
|
usedNames.add(paramName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
child = child.nextSibling
|
||||||
|
}
|
||||||
|
|
||||||
|
return usedNames
|
||||||
|
}
|
||||||
|
|
||||||
type ShrimpFn = {
|
type ShrimpFn = {
|
||||||
kind: 'shrimp-fn'
|
kind: 'shrimp-fn'
|
||||||
name: string
|
name: string
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user