Mid-word completion that replaces the rest of the word


Having fun with CM6, and so far so good with completeFromList to perform completions.

It works great for completions that should append text, but there’s another case where replacing some text after the completion is the optimal behavior.

I’ll use <completion> to indicate where the completion happens.

Use case: Mid-word completion

If I pick a Completion with the label my-completion I’d expect the new word text to be:
word-with-my-completion, but instead the result is word-with-my-completionmultiple-parts.

What’s the recommended way to add the completion logic that replaces the rest of the word? I tried modifying the to property of the completion result, but that looks like it also affects the filtering of matching items.


Leave it to me to find a solution shortly after I posted this :laughing:

Not sure if this is the best way, but it works for this use case, and could be useful for others looking to do the same.

const apply: Completion["apply"] = (view, completion, from, to) => {
    const completionNode = syntaxTree(view.state).resolveInner(to, 0);
    const nodeTextAfterCompletionTo = view.state.sliceDoc(to, completionNode.to);
    const wordRemainder = nodeTextAfterCompletionTo.match(/^[\w-_]+/)?.[0];
    if (wordRemainder?.length) {
        // we've completed mid-word, so we should also overwrite the remainder as part of doing the completion
        to += wordRemainder.length;
    view.dispatch(insertCompletionText(view.state, completion.label, from, to));

// assign the custom `apply` to each of the options from `completeFromList`
for (let option of result.options) {
    option.apply = apply;

I don’t think I am able to help you implement it. But a personal opinion: I hate it when an editor does this. When an editor does it, I always invest a lot of time to figuer out how to disable it. I really don’t like the idea to automatically delete code that was previously hand written. It is also easy to just hit Ctrl-delete to delete a word. So maybe I can convince you to simply not do it?

This logic is for a very specific subset of domain-specific symbols where what the user wants is to replace the previous value with what they completed to. For any other completions I’m sticking with the default CM behavior :slight_smile: