Repository: sublimelsp/LSP

Issue:
Keep using trigger characters, or start using selectors?
For some reason the final `commit_completion` command re-triggers the completion panel "forever". I need to `esc` to hide it.

![Animation](https://user-images.githubusercontent.com/16542113/77469677-bea0de80-6e0f-11ea-995c-868676cf6f2b.png)

Settings are

```
	"auto_complete": true,
	"auto_complete_commit_on_tab": true,
	"auto_complete_with_fields": true,
```

_Originally posted by @deathaxe in https://github.com/sublimelsp/LSP/pull/866#issuecomment-603466761_


Discussion:
More related information about possible reasons can be found at https://github.com/sublimelsp/LSP/pull/866#issuecomment-605629210.

---

See the bug, too, using the LSP-json completions.

![Animation](https://user-images.githubusercontent.com/16542113/77848952-734c4e80-71c8-11ea-84cf-8270f791e4ee.png)

With regards to @jskinner's statements the triggers and selectors of the view are set to:

```
>>> view.settings().get("auto_complete_selector")
'- string - comment - constant | meta.main-key | meta.rule-key | meta.interpolation-key | meta.animation-key | meta.attributes-sequence - comment | meta.class-selector | meta.function-call.var meta.group'
>>> view.settings().get("auto_complete_character")
Unable to open /C/Apps/Sublime Text/Data/Packages/Default/Preferences.sublime-settings
>>> view.settings().get("auto_complete_triggers")
[{'characters': '<', 'selector': 'text.html'}, {'selector': 'punctuation.accessor'}, {'characters': '":', 'selector': 'source.json'}]
```

The `string` scope is explicitly excluded from completitions. The trigger `":` is defined.

It looks like ST only uses the first character of the trigger, thus retriggering AC after committing a value which ends with `"`. Same happens after completing the `"class"` or just removing and retyping the final `"`. Both the json-key and json-value are `string.quoted.double`.

Not sure whether the trigger is intended to be of one character only by design or whether it is a bug.
`'characters': '":'` means to trigger after any of `"` or `:`

If you want AC to trigger directly after a `":` in JSON (before the user has even typed a space), then you'd want an `auto_complete_trigger` entry of `{"selector": "source.json punctuation.separator.mapping.key-value.json"},`. This is putting aside that most people would enter a space at this point, so you wouldn't typically want to trigger AC here.

FWIW Sublime Text doesn't use any setting called `"auto_complete_character"`
In general, the `characters` portion of `auto_complete_trigger` should be ignored, and only the `selector` used where possible, most syntax definitions are fine grained enough to make this do exactly what you want.

That's why we have `{"selector": "punctuation.accessor"}` rather than `{"characters": "."}` in the defaults
> FWIW Sublime Text doesn't use any setting called "auto_complete_character"

Should have filtered it out. Didn't remember the correct setting when trying that out :-)

> If you want AC to trigger directly after a ": in JSON

Seems what the json language server expects as well with regards to its ouput (see below).

>  (before the user has even typed a space)

Find it weired as well.

> In general, the characters portion of auto_complete_trigger should be ignored,

I guess the issue with it is language-servers to return the `triggerCharacters` only instead of selectors. Sticking to selectors (only) would literly make automatic configuring a syntax specific trigger by the server impossible.

It would then need to be specified in the LSP.sublime-settings or provided by the language-specific plugin, such as json-lsp instead of the server.

---
```
:: <<< lsp-json 1: {'capabilities': {'textDocumentSync': 2, 'hoverProvider': True, 'completionProvider': {'resolveProvider': True, 'triggerCharacters': ['"', ':']}, 'documentRangeFormattingProvider': True, 'foldingRangeProvider': True, 'documentSymbolProvider': True, 'selectionRangeProvider': True, 'colorProvider': {}}}
```
Automatic configuration based on nothing but `triggerCharacters` from the LSP server is already impossible, if taken at face value it's entirely brain dead. Showing AC after every `"` or `:` character in JSON would give a terrible UX.

Just ignore it, it's a broken part of the LSP protocol.

If you don't want to ignore it, then use heuristics or settings to translate trigger characters into selectors.
> Just ignore it, it's a broken part of the LSP protocol.

Basically have the same feeling about it and agree about the scopes to be sufficient in general.

It might need some tweaks in the syntax specific settings of the Default Packages to support AC better in general - independend from LSP.

Somehow it just feels like AC triggers don't play well with `insert_snippet`. What do I mean?

Considder a json key `"class"` was entered. By typing double-colon a snippet `: |,` is entered _(the `|` is the caret)_. I'd find it cool to see the AC panel right after the snipped was entered, if one of `"auto_complete_triggers"` matches the final caret position (defined by `$0` in the snippet). Even have `"auto_complete_with_fields": true,` to explicitly enable completions in fields.

At least it should as soon as `"` is typed and `punctuation.definition.begin` being defined as trigger. With `settings.auto_match_enabled` the  `insert_snipped` command is called, which results in `: "|",`. AC is not triggered - I guess because the trigger is evaluated against the last inserted character of the snippet?

See:

![Animation](https://user-images.githubusercontent.com/16542113/78054882-0630e880-7383-11ea-8af1-4f7bb7e85cfb.png)

Notes: 

1. AC panel is explicitly triggerd by `ctrl+space`.
2. The screencast was made with showing the `""` issue. I am aware of other settings to be required to also support triggering `: |,` (just to say it clearly).








Removing the trigger chars for e.g. Python makes the completion not run when typing

    self.

Because the scope is

    self.
        ^ source.python

The dot receives a `punctuation.accessor` scope only after typing another character, say an `i`:

    self.i
        ^ source.python meta.qualified-name.python punctuation.accessor.dot.python

This prevents AC from showing up.

For Javascript, the situation seems to be OK:

    window.
          ^ source.js punctuation.accessor.js

This makes AC show up as intended.

For C and C++, the same is true: the `punctuation.accessor` scope is greedily scoped. So that's OK.

I generally like this idea, it seems more sublimey. Another big plus for me is that we can blame syntax authors to add greedy `punctuation.accessor` scopes. In the end the syntax becomes more precise and everyone wins.

For JSON, the situation is a bit special. The vscode-json-languageserver expects completions to be requested when encountering the start of a string, or when encountering a colon.

```js
{
    "private": true,
//  ^ source.json meta.mapping.key.json string.quoted.double.json punctuation.definition.string.begin.json
    "scripts": {
//           ^ source.json meta.mapping.json punctuation.separator.mapping.key-value.json
        "dev": "npm run development",
        "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "npm run development -- --watch",
        "watch-poll": "npm run watch -- --watch-poll",
        "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
        "prod": "npm run production",
        "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
    },
    "devDependencies": {
        "axios": "^0.19",
        "cross-env": "^5.1",
        "laravel-mix": "^4.0.7",
        "lodash": "^4.17.13",
        "resolve-url-loader": "^2.3.1",
        "sass": "^1.15.2",
        "sass-loader": "^7.1.0"
    }
}
```

In this case, it'd be best if a helper package (see #927) would provide the appropriate auto complete triggers.

Looking in the console, the default setting seems to be

```
>>> view.settings().get("auto_complete_triggers")
[{'characters': '<', 'selector': 'text.html'}, {'selector': 'punctuation.accessor'}]
```
So all LSP-json would have to do is provide
```
{'selector': 'source.json & (punctuation.separator.mapping.key-value | punctuation.definition.string.begin)'}
```

This would match the opening double-quote of a string and the colon:

```
>>> selector='source.json & (punctuation.separator.mapping.key-value | punctuation.definition.string.begin)'
>>> sublime.score_selector('source.json meta.mapping.key.json string.quoted.double.json punctuation.definition.string.begin.json', selector)
16384
>>> sublime.score_selector('source.json meta.mapping.json punctuation.separator.mapping.key-value.json', selector)
2048
```

But e.g. NOT the closing double-quote of a string:
```
>>> sublime.score_selector('source.json meta.mapping.value.json meta.mapping.value.json string.quoted.double.json punctuation.definition.string.end.json', selector)
0
```
I think the overall point is to try to leave the responsibility for proper AC settings (selectors, chars) to syntax packages in general. The idea is to add syntax-specific sublime-settings entries of each (default) syntax package, so AC could handle those situations with or without LSP. 

If changes are needed to make AC work better for a syntax, an issue should be filed at the Default Packages repo.
But vscode-json-languageserver is very specific here. It has a certain workflow expectation that is not necessarily needed in the default JSON.sublime-syntax. It would be weird to add support for that in Packages/Javascript/JSON.sublime-syntax. Perhaps I'm not seeing the argument clearly.
Another scope that might need special attention is the `$` from PHP and Bash. This has a scope of `punctuation.definition.variable`.

Perhaps we can maintain a dict, but ideally this would be handled by LSP-foo helper packages.

```python
completion_selector_from_base_scope = {
    "source.json": "punctuation.separator.mapping.key-value | punctuation.definition.string.begin",
    "source.php": "punctuation.definition.variable",
    "source.shell.bash": "punctuation.definition.variable"
}

```

EDIT: I guess another option could be that we'd ask users to put it into their client config. Then LSP-* helper packages can also auto-fill that in.
It was more or less a very general idea. ;-)  
Having it in LSP-* package is probably more correct as in theory two servers supporting same scope might need different triggers. But in practice, a general setting like that would probably work too. We could start with a generic setting and move to packages if that turns out not to be enough.
> The dot receives a `punctuation.accessor` scope only after typing another character, say an `i`:

That is definitely not ideal behavior. Would you mind opening an issue for that on the Packages repo so it isn't forgotten?

Resolving patch:
diff --git a/plugin/completion.py b/plugin/completion.py
--- a/plugin/completion.py
+++ b/plugin/completion.py
@@ -171,29 +171,15 @@ def initialize(self) -> None:
             # This is to make ST match with labels that have a weird prefix like a space character.
             self.view.settings().set("auto_complete_preserve_order", "none")
 
-    def _view_language(self, config_name: str) -> Optional[str]:
-        languages = self.view.settings().get('lsp_language')
-        return languages.get(config_name) if languages else None
-
     def register_trigger_chars(self, session: Session, trigger_chars: List[str]) -> None:
-        completion_triggers = self.view.settings().get('auto_complete_triggers', []) or []  # type: List[Dict[str, str]]
-        view_language = self._view_language(session.config.name)
-        if view_language:
-            for language in session.config.languages:
-                if language.id == view_language:
-                    for scope in language.scopes:
-                        # debug("registering", trigger_chars, "for", scope)
-                        scope_trigger = next(
-                            (trigger for trigger in completion_triggers if trigger.get('selector', None) == scope),
-                            None
-                        )
-                        if not scope_trigger:  # do not override user's trigger settings.
-                            completion_triggers.append({
-                                'characters': "".join(trigger_chars),
-                                'selector': scope
-                            })
-
-            self.view.settings().set('auto_complete_triggers', completion_triggers)
+        completion_triggers = self.view.settings().get('auto_complete_triggers') or []  # type: List[Dict[str, str]]
+
+        completion_triggers.append({
+            'characters': "".join(trigger_chars),
+            'selector': "- comment - punctuation.definition.string.end"
+        })
+
+        self.view.settings().set('auto_complete_triggers', completion_triggers)
 
     def on_query_completions(self, prefix: str, locations: List[int]) -> Optional[sublime.CompletionList]:
         if not self.initialized:


Test patch: