UNPKG

codemirror-languageservice

Version:

Integrate a Language Server Protocol compatible language service into CodeMirror

137 lines 4.57 kB
import { insertCompletionText, snippet } from '@codemirror/autocomplete'; import { fromMarkupContent } from './markup-content.js'; import { getTextDocument } from './text-document.js'; let alphabet = 'abcdefghijklmnopqrstuvwxyz'; alphabet += alphabet.toUpperCase(); const defaultFromCompletionItemKind = (kind) => { switch (kind) { case 1: case 15: return 'text'; case 2: return 'method'; case 3: return 'function'; case 4: case 7: return 'class'; case 5: case 10: return 'property'; case 6: case 12: case 18: case 23: return 'variable'; case 8: case 22: case 25: return 'type'; case 9: return 'namespace'; case 13: return 'enum'; case 11: case 14: case 24: return 'keyword'; case 16: case 21: return 'constant'; case 20: return 'enum'; default: } }; /** * Create an LSP based completion source. * * @param options * Options to configure the completion. * @returns * A CodeMirror completion source that uses LSP based completions. */ export function createCompletionSource(options) { const fromCompletionItemKind = options.fromCompletionItemKind ?? defaultFromCompletionItemKind; let triggerCharacters = alphabet; if (options.triggerCharacters) { triggerCharacters += options.triggerCharacters; } return async (context) => { const textDocument = getTextDocument(context.state); let completionContext; if (context.explicit) { completionContext = { triggerKind: 1 }; } else { const triggerCharacter = context.state.sliceDoc(context.pos - 1, context.pos); if (!triggerCharacters.includes(triggerCharacter)) { return null; } completionContext = { triggerCharacter, triggerKind: 2 }; } const completions = await options.doComplete(textDocument, textDocument.positionAt(context.pos), completionContext); if (!completions) { return null; } if (textDocument.version !== getTextDocument(context.view?.state ?? context.state).version) { return null; } let items; let itemDefaults; if (Symbol.iterator in completions) { items = completions; } else { items = completions.items; itemDefaults = completions.itemDefaults; } const completionOptions = []; let minFrom = context.pos; let maxTo = context.pos; for (const item of items) { const { commitCharacters, detail, documentation, kind, label, textEdit, textEditText } = item; const completion = { commitCharacters, detail, info: documentation && (() => fromMarkupContent(documentation, document.createDocumentFragment(), options)), label, section: options.section, type: fromCompletionItemKind(kind) }; if (textEdit) { const range = 'range' in textEdit ? textEdit.range : textEdit.replace; const from = textDocument.offsetAt(range.start); const to = textDocument.offsetAt(range.end); if (from < minFrom) { minFrom = from; } if (to > maxTo) { maxTo = to; } const insert = textEdit.newText; const insertTextFormat = item.insertTextFormat ?? itemDefaults?.insertTextFormat; completion.apply = (view) => insertTextFormat === 2 ? snippet(insert.replaceAll(/\$(\d+)/g, '$${$1}'))(view, completion, from, to) : view.dispatch(insertCompletionText(view.state, insert, from, to)); } else if (textEditText) { completion.apply = textEditText; } completionOptions.push(completion); } return { from: minFrom, to: maxTo, commitCharacters: itemDefaults?.commitCharacters, options: completionOptions }; }; } //# sourceMappingURL=completion.js.map