Skip to content

Commit

Permalink
Add triggerCharacters to completion source
Browse files Browse the repository at this point in the history
Completions are only triggered automatically, if the the trigger
character matches one of the given trigger characters.
  • Loading branch information
remcohaszing committed Aug 5, 2024
1 parent 54deb18 commit 1945f20
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 17 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ Create an LSP based completion source.
- `fromCompletionItemKind` (`Function`, optional) — Convert an LSP completion item kind to a
CodeMirror completion type.
- `section` (`string`, optional) — The section to use for completions.
- `triggerCharacters` (`string`) — Only trigger completions automatically when one of these
characters is typed.

#### Returns

Expand Down
27 changes: 21 additions & 6 deletions src/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ export declare namespace createCompletionSource {
* The section to use for completions.
*/
section?: string

/**
* Only trigger completions automatically when one of these characters is typed.
*/
triggerCharacters?: string
}
}

Expand All @@ -125,12 +130,22 @@ export function createCompletionSource(options: createCompletionSource.Options):
return async (context) => {
const textDocument = getTextDocument(context.state)

const completionContext: CompletionContext = context.explicit
? { triggerKind: 1 satisfies typeof CompletionTriggerKind.Invoked }
: {
triggerCharacter: context.state.sliceDoc(context.pos - 1, context.pos),
triggerKind: 2 satisfies typeof CompletionTriggerKind.TriggerCharacter
}
let completionContext: CompletionContext
if (context.explicit) {
completionContext = {
triggerKind: 1 satisfies typeof CompletionTriggerKind.Invoked
}
} else {
const triggerCharacter = context.state.sliceDoc(context.pos - 1, context.pos)
if (!options.triggerCharacters?.includes(triggerCharacter)) {
return null
}

completionContext = {
triggerCharacter,
triggerKind: 2 satisfies typeof CompletionTriggerKind.TriggerCharacter
}
}

const completions = await options.doComplete(
textDocument,
Expand Down
50 changes: 39 additions & 11 deletions test/completion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ test('completion args explicit', async () => {
expect(context).toStrictEqual({ triggerKind: CompletionTriggerKind.Invoked })
})

test('completion args implicit', async () => {
test('completion args implicit trigger character match', async () => {
let document: TextDocument | undefined
let position: Position | undefined
let context: LspCompletionContext | undefined
Expand All @@ -55,6 +55,7 @@ test('completion args implicit', async () => {

const completionSource = createCompletionSource({
markdownToDom,
triggerCharacters: 'x',
doComplete(doc, pos, ctx) {
document = doc
position = pos
Expand All @@ -72,6 +73,33 @@ test('completion args implicit', async () => {
})
})

test('completion args implicit trigger character no match', async () => {
let document: TextDocument | undefined
let position: Position | undefined
let context: LspCompletionContext | undefined

const view = new EditorView({
doc: 'Text\n',
extensions: [textDocument()]
})

const completionSource = createCompletionSource({
markdownToDom,
triggerCharacters: 'Tet',
doComplete(doc, pos, ctx) {
document = doc
position = pos
context = ctx
}
})

await completionSource(new CompletionContext(view.state, 3, false))

expect(document).toBeUndefined()
expect(position).toBeUndefined()
expect(context).toBeUndefined()
})

test('ignore null', async () => {
const view = new EditorView({
doc: 'Text\n',
Expand All @@ -85,7 +113,7 @@ test('ignore null', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 3, false))
const completions = await completionSource(new CompletionContext(view.state, 3, true))

expect(completions).toBeNull()
})
Expand All @@ -103,7 +131,7 @@ test('ignore undefined', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 3, false))
const completions = await completionSource(new CompletionContext(view.state, 3, true))

expect(completions).toBeNull()
})
Expand All @@ -122,7 +150,7 @@ test('ignore outdated', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 3, false, view))
const completions = await completionSource(new CompletionContext(view.state, 3, true, view))

expect(completions).toBeNull()
})
Expand All @@ -142,7 +170,7 @@ test('minimal meta', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 4, false))
const completions = await completionSource(new CompletionContext(view.state, 4, true))

expect(completions).toStrictEqual({
commitCharacters: undefined,
Expand Down Expand Up @@ -178,7 +206,7 @@ test('full meta', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 3, false))
const completions = await completionSource(new CompletionContext(view.state, 3, true))

expect(completions).toStrictEqual({
commitCharacters: undefined,
Expand Down Expand Up @@ -318,7 +346,7 @@ test('completion item kinds', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 3, false))
const completions = await completionSource(new CompletionContext(view.state, 3, true))

expect(completions).toStrictEqual({
commitCharacters: undefined,
Expand Down Expand Up @@ -544,7 +572,7 @@ test('textEditText', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 3, false))
const completions = await completionSource(new CompletionContext(view.state, 3, true))

expect(completions).toStrictEqual({
commitCharacters: undefined,
Expand Down Expand Up @@ -585,7 +613,7 @@ test('textEdit plain text', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 3, false))
const completions = await completionSource(new CompletionContext(view.state, 3, true))

const apply = completions!.options[0]!.apply as (v: EditorView) => unknown
apply(view)
Expand Down Expand Up @@ -620,7 +648,7 @@ test('textEdit snippet', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 3, false))
const completions = await completionSource(new CompletionContext(view.state, 3, true))

const apply = completions!.options[0]!.apply as (v: EditorView) => unknown
apply(view)
Expand Down Expand Up @@ -659,7 +687,7 @@ test('itemDefaults', async () => {
}
})

const completions = await completionSource(new CompletionContext(view.state, 3, false))
const completions = await completionSource(new CompletionContext(view.state, 3, true))

const apply = completions!.options[0]!.apply as (v: EditorView) => unknown
apply(view)
Expand Down

0 comments on commit 1945f20

Please sign in to comment.