Detect when autocomplete is open in view update (to prevent autocomplete blinking)

I’m writing an extension as part of an autocomplete extension that opens the autocomplete window whenever a user clicks on whitespace. What I have is currently working; here’s the code:

export function showAutocompleteOnWhitespaceClick(viewUpdate: ViewUpdate) {
  if (viewUpdate.selectionSet) {
    const {view, state} = viewUpdate;

    const selection = state.selection.asSingle().ranges[0];
    const currentWord = state.wordAt(selection.from);
    const isWhitespace = currentWord == null;

    if (isWhitespace) {
      setTimeout(() => {
        startCompletion(view);
      }, 0);
    }
  }
}

const extension [
  autocomplete,
  ViewPlugin.define((_) => ({update: showAutocompleteOnWhitespaceClick})),
];

This works, but the autocomplete blinks when I click between lines. I have been trying to figure out why that is happening–I thought maybe that I needed to check the current status of the completion in my function, but completionStatus and currentCompletions both are empty.

Is the autocomplete always closed whenever there is a new selection? I have looked through the docs but I don’t see a configuration option that seems to address the behavior I’m seeing.

Yes, unless it’s typing or backspacing, any cursor motion will close the completion.

I’ve been digging through the code in an attempt to understand it and propose a solution–I have found the following:

  • in the definition for the completionPlugin, there is this doesReset line that seems to trigger some sort of reset if there is a change to the document or if the selection changes and the given user event is not either typing or deleting (source)
  • in the definition for ActiveSource there is this line that seems to set the source as inactive if there are any selection changes

I am still reading through the code that exists and trying to wrap my head around it. In the meantime, if I were to put up a PR, I would consider adding something to the autocomplete config properties. My two ideas:

  • An option that is really similar to closeOnBlur but instead it would be something like closeOnSelectionChange that defaults to true
  • A function that you could pass in to configure when the autocomplete close–something like handleCloseCompletion or something that is called when the autocomplete popover would close and it returns a boolean. This option is super half-baked, I’m not sure it really fits into the architectural model of CM6

I am not adverse to putting up a PR to make this change, but I’m wondering if you have any interest in adding a feature like this? My use case is that the (custom) language that I’m writing an editor for always has a sense of “next keyword”, and so the autocomplete is valid in various places. It would be a better UX to have the popover reposition instead of disappearing and then immediately re-appearing (e.g. “blinking”).

The current completions are related to the cursor position. I can’t really imagine how keeping the completions open when the selection moves makes sense.

The current completions are related to the cursor position. I can’t really imagine how keeping the completions open when the selection moves makes sense.

In the case of the app I’m working on, the language has a concept of “next keyword” that is a valid choice for any place there is whitespace after a given token. For example, if the code editor had the following content:

FOO x



and the next valid token was BAR, I would want the autocomplete menu to be available on any of the 3 new lines.

I think that the idea of “opening” and “closing” the completions is not really how I’m thinking about this–I’m thinking about this more in terms of destroying and creating DOM nodes. For example, if you have a completion for one position and the same completion is valid for another position, I would want to keep that DOM node from being destroyed, and instead just reposition it.

It’s similar to what validFor does, in terms of keeping the same completion set open for typing a word, but instead of it being for typing, I am trying to also keep it open for other interactions like clicking.

After reading that, do you see a way to address this use case?

This may be different enough from how the autocompletion package works to be easier to handle with a separate implementation.

This may be different enough from how the autocompletion package works to be easier to handle with a separate implementation.

Got it. I was starting to suspect that would be the case, but I appreciate your confirmation!