Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tab completion is still sorting the wrong keywords to the bottom #883

Closed
bcardarella opened this issue May 5, 2023 · 20 comments
Closed

Comments

@bcardarella
Copy link

I'm on the current version of Elixir LS and latest version of VSCode

Screenshot 2023-05-05 at 1 37 59 PM

Note how do is at the bottom. It should be at the top. This happens for def, defmodule, etc... where do should be priortized. This issue has been opened before and I have never seen it fixed properly.

@lukaszsamson
Copy link
Collaborator

@bcardarella Can you turn on tracing elixirLS.trace.server to verbose and post the protocol message?

@lukaszsamson
Copy link
Collaborator

Alternatively a piece of code that reproduces it reliably. I'm seeing it like this.
Screenshot 2023-05-06 at 12 26 26

@lukaszsamson
Copy link
Collaborator

Also post your editor settings.json. There are some settings controlling suggestions order. In my case I only have

  "editor.suggest.preview": true,
  "editor.suggest.matchOnWordStartOnly": false

but changing those does not make any difference on do ordering.

@lukaszsamson
Copy link
Collaborator

ping @bcardarella

@bcardarella
Copy link
Author

bcardarella commented May 18, 2023

@lukaszsamson apologies, catching up. Will post in a few

@bcardarella
Copy link
Author

I had editor.suggest.preview set to false and editor.suggest.matchOnWordStartOnly to set to true. I changed those values to match yours but still get:

Screenshot 2023-05-18 at 8 44 55 AM

@bcardarella
Copy link
Author

Where do I find the protocol message to post?

@lukaszsamson
Copy link
Collaborator

Enable tracing in extension settings "elixirLS.trace.server": "verbose". Messages will be printed to the Output panel

@bcardarella
Copy link
Author

I have it set to verbose but do not see anything in the Output panel

@lukaszsamson
Copy link
Collaborator

@lukaszsamson
Copy link
Collaborator

This is the message I'm interested in
Screenshot 2023-05-18 at 15 25 01

@bcardarella
Copy link
Author

bcardarella commented May 18, 2023

[Trace - 9:28:52 AM] Received response 'textDocument/completion - (371)' in 63ms.
Result: {
    "isIncomplete": true,
    "items": [
        {
            "deprecated": false,
            "detail": "keyword",
            "documentation": {
                "kind": "markdown",
                "value": ""
            },
            "insertText": "do\n  $0\nend",
            "insertTextFormat": 2,
            "kind": 14,
            "label": "do",
            "preselect": true,
            "sortText": "00000000",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec any()",
            "documentation": {
                "kind": "markdown",
                "value": "The top type, the set of all terms"
            },
            "insertText": "any()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "any",
            "labelDetails": {
                "description": "any/0",
                "detail": "()"
            },
            "sortText": "00000001",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec arity()",
            "documentation": {
                "kind": "markdown",
                "value": "The number of arguments that a function takes\n\n```\n@type arity() :: 0..255\n```\n"
            },
            "insertText": "arity()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "arity",
            "labelDetails": {
                "description": "arity/0",
                "detail": "()"
            },
            "sortText": "00000002",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec as_boolean(t)",
            "documentation": {
                "kind": "markdown",
                "value": "A type `t` whose value will be used as a _truthy_ value\n\n```\n@type as_boolean(t) :: t\n```\n"
            },
            "insertText": "as_boolean($0)",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "as_boolean",
            "labelDetails": {
                "description": "as_boolean/1",
                "detail": "(t)"
            },
            "sortText": "00000003",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec atom()",
            "documentation": {
                "kind": "markdown",
                "value": "An atom is a constant whose name is its own value. Some other languages call these symbols"
            },
            "insertText": "atom()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "atom",
            "labelDetails": {
                "description": "atom/0",
                "detail": "()"
            },
            "sortText": "00000004",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec binary()",
            "documentation": {
                "kind": "markdown",
                "value": "A blob of binary data\n\n```\n@type binary() ::\n  <<_::size(8)>>\n```\n"
            },
            "insertText": "binary()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "binary",
            "labelDetails": {
                "description": "binary/0",
                "detail": "()"
            },
            "sortText": "00000005",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec bitstring()",
            "documentation": {
                "kind": "markdown",
                "value": "A bunch of bits\n\n```\n@type bitstring() ::\n  <<_::size(1)>>\n```\n"
            },
            "insertText": "bitstring()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "bitstring",
            "labelDetails": {
                "description": "bitstring/0",
                "detail": "()"
            },
            "sortText": "00000006",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec boolean()",
            "documentation": {
                "kind": "markdown",
                "value": "`true` or `false`\n\n```\n@type boolean() ::\n  false | true\n```\n"
            },
            "insertText": "boolean()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "boolean",
            "labelDetails": {
                "description": "boolean/0",
                "detail": "()"
            },
            "sortText": "00000007",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec byte()",
            "documentation": {
                "kind": "markdown",
                "value": "A valid byte (0..255)\n\n```\n@type byte() :: 0..255\n```\n"
            },
            "insertText": "byte()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "byte",
            "labelDetails": {
                "description": "byte/0",
                "detail": "()"
            },
            "sortText": "00000008",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec char()",
            "documentation": {
                "kind": "markdown",
                "value": "A valid char (0..0x10ffff)\n\n```\n@type char() ::\n  0..1_114_111\n```\n"
            },
            "insertText": "char()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "char",
            "labelDetails": {
                "description": "char/0",
                "detail": "()"
            },
            "sortText": "00000009",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec charlist()",
            "documentation": {
                "kind": "markdown",
                "value": "A list of `char()`\n\n```\n@type charlist() :: [char()]\n```\n"
            },
            "insertText": "charlist()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "charlist",
            "labelDetails": {
                "description": "charlist/0",
                "detail": "()"
            },
            "sortText": "00000010",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec float()",
            "documentation": {
                "kind": "markdown",
                "value": "A floating-point number"
            },
            "insertText": "float()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "float",
            "labelDetails": {
                "description": "float/0",
                "detail": "()"
            },
            "sortText": "00000011",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec fun()",
            "documentation": {
                "kind": "markdown",
                "value": "A function\n\n```\n@type fun() ::\n  (... -> any)\n```\n"
            },
            "insertText": "fun()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "fun",
            "labelDetails": {
                "description": "fun/0",
                "detail": "()"
            },
            "sortText": "00000012",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec function()",
            "documentation": {
                "kind": "markdown",
                "value": "Same as `fun()`\n\n```\n@type function() :: fun()\n```\n"
            },
            "insertText": "function()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "function",
            "labelDetails": {
                "description": "function/0",
                "detail": "()"
            },
            "sortText": "00000013",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec identifier()",
            "documentation": {
                "kind": "markdown",
                "value": "A `pid()`, `port()` or `reference()`\n\n```\n@type identifier() ::\n  pid()\n  | port()\n  | reference()\n```\n"
            },
            "insertText": "identifier()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "identifier",
            "labelDetails": {
                "description": "identifier/0",
                "detail": "()"
            },
            "sortText": "00000014",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec integer()",
            "documentation": {
                "kind": "markdown",
                "value": "An integer number"
            },
            "insertText": "integer()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "integer",
            "labelDetails": {
                "description": "integer/0",
                "detail": "()"
            },
            "sortText": "00000015",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec iodata()",
            "documentation": {
                "kind": "markdown",
                "value": "An `iolist()` or a `binary()`\n\n```\n@type iodata() ::\n  iolist() | binary()\n```\n"
            },
            "insertText": "iodata()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "iodata",
            "labelDetails": {
                "description": "iodata/0",
                "detail": "()"
            },
            "sortText": "00000016",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec iolist()",
            "documentation": {
                "kind": "markdown",
                "value": "A list whose elements are either bytes, binaries or other iolists\n\n```\n@type iolist() ::\n  maybe_improper_list(\n    byte()\n    | binary()\n    | iolist(),\n    binary() | []\n  )\n```\n"
            },
            "insertText": "iolist()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "iolist",
            "labelDetails": {
                "description": "iolist/0",
                "detail": "()"
            },
            "sortText": "00000017",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec keyword(t)",
            "documentation": {
                "kind": "markdown",
                "value": "A keyword list with values of type `t`\n\n```\n@type keyword(t) :: [\n  {atom(), t}\n]\n```\n"
            },
            "insertText": "keyword($0)",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "keyword",
            "labelDetails": {
                "description": "keyword/1",
                "detail": "(t)"
            },
            "sortText": "00000018",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec keyword()",
            "documentation": {
                "kind": "markdown",
                "value": "A keyword list\n\n```\n@type keyword() :: [\n  {atom(), any()}\n]\n```\n"
            },
            "insertText": "keyword()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "keyword",
            "labelDetails": {
                "description": "keyword/0",
                "detail": "()"
            },
            "sortText": "00000019",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec list(t)",
            "documentation": {
                "kind": "markdown",
                "value": "Proper list ([]-terminated)"
            },
            "insertText": "list($0)",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "list",
            "labelDetails": {
                "description": "list/1",
                "detail": "(t)"
            },
            "sortText": "00000020",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec list()",
            "documentation": {
                "kind": "markdown",
                "value": "A list\n\n```\n@type list() :: [any()]\n```\n"
            },
            "insertText": "list()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "list",
            "labelDetails": {
                "description": "list/0",
                "detail": "()"
            },
            "sortText": "00000021",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec map()",
            "documentation": {
                "kind": "markdown",
                "value": "Any map"
            },
            "insertText": "map()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "map",
            "labelDetails": {
                "description": "map/0",
                "detail": "()"
            },
            "sortText": "00000022",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec maybe_improper_list(type1, type2)",
            "documentation": {
                "kind": "markdown",
                "value": "Proper or improper list (type1=contents, type2=termination)"
            },
            "insertText": "maybe_improper_list($0)",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "maybe_improper_list",
            "labelDetails": {
                "description": "maybe_improper_list/2",
                "detail": "(type1, type2)"
            },
            "sortText": "00000023",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec maybe_improper_list()",
            "documentation": {
                "kind": "markdown",
                "value": "An alias for `maybe_improper_list(any(), any())`\n\n```\n@type maybe_improper_list() ::\n  maybe_improper_list(\n    any(),\n    any()\n  )\n```\n"
            },
            "insertText": "maybe_improper_list()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "maybe_improper_list",
            "labelDetails": {
                "description": "maybe_improper_list/0",
                "detail": "()"
            },
            "sortText": "00000024",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec mfa()",
            "documentation": {
                "kind": "markdown",
                "value": "A tuple with {module, function, arity}\n\n```\n@type mfa() ::\n  {module(), atom(), arity()}\n```\n"
            },
            "insertText": "mfa()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "mfa",
            "labelDetails": {
                "description": "mfa/0",
                "detail": "()"
            },
            "sortText": "00000025",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec module()",
            "documentation": {
                "kind": "markdown",
                "value": "A module name. An alias for `atom()`\n\n```\n@type module() :: atom()\n```\n"
            },
            "insertText": "module()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "module",
            "labelDetails": {
                "description": "module/0",
                "detail": "()"
            },
            "sortText": "00000026",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec neg_integer()",
            "documentation": {
                "kind": "markdown",
                "value": "A negative integer"
            },
            "insertText": "neg_integer()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "neg_integer",
            "labelDetails": {
                "description": "neg_integer/0",
                "detail": "()"
            },
            "sortText": "00000027",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec no_return()",
            "documentation": {
                "kind": "markdown",
                "value": "A return type indicating that a function throws exceptions or loops forever and never terminates\n\n```\n@type no_return() :: none()\n```\n"
            },
            "insertText": "no_return()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "no_return",
            "labelDetails": {
                "description": "no_return/0",
                "detail": "()"
            },
            "sortText": "00000028",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec node()",
            "documentation": {
                "kind": "markdown",
                "value": "An atom representing a node name\n\n```\n@type node() :: atom()\n```\n"
            },
            "insertText": "node()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "node",
            "labelDetails": {
                "description": "node/0",
                "detail": "()"
            },
            "sortText": "00000029",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec non_neg_integer()",
            "documentation": {
                "kind": "markdown",
                "value": "A non-negative integer"
            },
            "insertText": "non_neg_integer()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "non_neg_integer",
            "labelDetails": {
                "description": "non_neg_integer/0",
                "detail": "()"
            },
            "sortText": "00000030",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec none()",
            "documentation": {
                "kind": "markdown",
                "value": "The bottom type, contains no terms"
            },
            "insertText": "none()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "none",
            "labelDetails": {
                "description": "none/0",
                "detail": "()"
            },
            "sortText": "00000031",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec nonempty_charlist()",
            "documentation": {
                "kind": "markdown",
                "value": "A non-empty list of `char()`\n\n```\n@type nonempty_charlist() :: [\n  char(),\n  ...\n]\n```\n"
            },
            "insertText": "nonempty_charlist()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "nonempty_charlist",
            "labelDetails": {
                "description": "nonempty_charlist/0",
                "detail": "()"
            },
            "sortText": "00000032",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec nonempty_improper_list(type1, type2)",
            "documentation": {
                "kind": "markdown",
                "value": "Improper list (type1=contents, type2=termination)"
            },
            "insertText": "nonempty_improper_list($0)",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "nonempty_improper_list",
            "labelDetails": {
                "description": "nonempty_improper_list/2",
                "detail": "(type1, type2)"
            },
            "sortText": "00000033",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec nonempty_list",
            "documentation": {
                "kind": "markdown",
                "value": "A non-empty list\n\n```\n@type nonempty_list ::\n  nonempty_list(any())\n```\n"
            },
            "insertText": "nonempty_list()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "nonempty_list",
            "labelDetails": {
                "description": "nonempty_list/0",
                "detail": "()"
            },
            "sortText": "00000034",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec nonempty_list(t)",
            "documentation": {
                "kind": "markdown",
                "value": "Non-empty proper list"
            },
            "insertText": "nonempty_list($0)",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "nonempty_list",
            "labelDetails": {
                "description": "nonempty_list/1",
                "detail": "(t)"
            },
            "sortText": "00000035",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec nonempty_maybe_improper_list()",
            "documentation": {
                "kind": "markdown",
                "value": "An alias for `nonempty_maybe_improper_list(any(), any())`\n\n```\n@type nonempty_maybe_improper_list() ::\n  nonempty_maybe_improper_list(\n    any(),\n    any()\n  )\n```\n"
            },
            "insertText": "nonempty_maybe_improper_list()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "nonempty_maybe_improper_list",
            "labelDetails": {
                "description": "nonempty_maybe_improper_list/0",
                "detail": "()"
            },
            "sortText": "00000036",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec nonempty_maybe_improper_list(type1, type2)",
            "documentation": {
                "kind": "markdown",
                "value": "Non-empty proper or improper list"
            },
            "insertText": "nonempty_maybe_improper_list($0)",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "nonempty_maybe_improper_list",
            "labelDetails": {
                "description": "nonempty_maybe_improper_list/2",
                "detail": "(type1, type2)"
            },
            "sortText": "00000037",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec number()",
            "documentation": {
                "kind": "markdown",
                "value": "An integer or a float\n\n```\n@type number() ::\n  integer() | float()\n```\n"
            },
            "insertText": "number()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "number",
            "labelDetails": {
                "description": "number/0",
                "detail": "()"
            },
            "sortText": "00000038",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec pid()",
            "documentation": {
                "kind": "markdown",
                "value": "A process identifier, pid, identifies a process"
            },
            "insertText": "pid()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "pid",
            "labelDetails": {
                "description": "pid/0",
                "detail": "()"
            },
            "sortText": "00000039",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec port()",
            "documentation": {
                "kind": "markdown",
                "value": "A port identifier identifies an Erlang port"
            },
            "insertText": "port()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "port",
            "labelDetails": {
                "description": "port/0",
                "detail": "()"
            },
            "sortText": "00000040",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec pos_integer()",
            "documentation": {
                "kind": "markdown",
                "value": "A positive integer"
            },
            "insertText": "pos_integer()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "pos_integer",
            "labelDetails": {
                "description": "pos_integer/0",
                "detail": "()"
            },
            "sortText": "00000041",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec reference()",
            "documentation": {
                "kind": "markdown",
                "value": "A reference is a term that is unique in an Erlang runtime system, created by calling `make_ref/0`"
            },
            "insertText": "reference()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "reference",
            "labelDetails": {
                "description": "reference/0",
                "detail": "()"
            },
            "sortText": "00000042",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec struct()",
            "documentation": {
                "kind": "markdown",
                "value": "A struct\n\n```\n@type struct() :: %{\n  :__struct__ => atom(),\n  optional(atom()) => any()\n}\n```\n"
            },
            "insertText": "struct()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "struct",
            "labelDetails": {
                "description": "struct/0",
                "detail": "()"
            },
            "sortText": "00000043",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec term()",
            "documentation": {
                "kind": "markdown",
                "value": "Same as `any()`\n\n```\n@type term() :: any()\n```\n"
            },
            "insertText": "term()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "term",
            "labelDetails": {
                "description": "term/0",
                "detail": "()"
            },
            "sortText": "00000044",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec timeout()",
            "documentation": {
                "kind": "markdown",
                "value": "A non-negative integer or `:infinity`\n\n```\n@type timeout() ::\n  :infinity\n  | non_neg_integer()\n```\n"
            },
            "insertText": "timeout()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "timeout",
            "labelDetails": {
                "description": "timeout/0",
                "detail": "()"
            },
            "sortText": "00000045",
            "tags": []
        },
        {
            "deprecated": false,
            "detail": "typespec tuple()",
            "documentation": {
                "kind": "markdown",
                "value": "Tuple of any size"
            },
            "insertText": "tuple()",
            "insertTextFormat": 2,
            "kind": 7,
            "label": "tuple",
            "labelDetails": {
                "description": "tuple/0",
                "detail": "()"
            },
            "sortText": "00000046",
            "tags": []
        },
        {
            "command": {
                "command": "editor.action.triggerParameterHints",
                "title": "Trigger Parameter Hint"
            },
            "deprecated": false,
            "detail": "(macro) Kernel.defexception(fields)",
            "documentation": {
                "kind": "markdown",
                "value": "Defines an exception."
            },
            "insertText": "defexception [${1::message}]",
            "insertTextFormat": 2,
            "kind": 15,
            "label": "defexception",
            "labelDetails": {
                "description": "Kernel.defexception/1",
                "detail": "(fields)"
            },
            "sortText": "00000047",
            "tags": []
        },
        {
            "command": {
                "command": "editor.action.triggerParameterHints",
                "title": "Trigger Parameter Hint"
            },
            "deprecated": false,
            "detail": "(macro) Kernel.defmacro(call, expr \\\\ nil)",
            "documentation": {
                "kind": "markdown",
                "value": "Defines a public macro with the given name and body."
            },
            "insertText": "defmacro $1 do\n\t$0\nend",
            "insertTextFormat": 2,
            "kind": 15,
            "label": "defmacro",
            "labelDetails": {
                "description": "Kernel.defmacro/2",
                "detail": "(call, expr \\\\ nil)"
            },
            "sortText": "00000048",
            "tags": []
        },
        {
            "command": {
                "command": "editor.action.triggerParameterHints",
                "title": "Trigger Parameter Hint"
            },
            "deprecated": false,
            "detail": "(macro) Kernel.defmacrop(call, expr \\\\ nil)",
            "documentation": {
                "kind": "markdown",
                "value": "Defines a private macro with the given name and body."
            },
            "insertText": "defmacrop $1 do\n\t$0\nend",
            "insertTextFormat": 2,
            "kind": 15,
            "label": "defmacrop",
            "labelDetails": {
                "description": "Kernel.defmacrop/2",
                "detail": "(call, expr \\\\ nil)"
            },
            "sortText": "00000049",
            "tags": []
        },
        {
            "command": {
                "command": "editor.action.triggerParameterHints",
                "title": "Trigger Parameter Hint"
            },
            "deprecated": false,
            "detail": "(macro) Kernel.defmodule(alias, do_block)",
            "documentation": {
                "kind": "markdown",
                "value": "Defines a module given by name with the given contents."
            },
            "insertText": "defmodule LivebookApp$1 do\n\t$0\nend",
            "insertTextFormat": 2,
            "kind": 15,
            "label": "defmodule",
            "labelDetails": {
                "description": "Kernel.defmodule/2",
                "detail": "(alias, do_block)"
            },
            "sortText": "00000050",
            "tags": []
        },
        {
            "command": {
                "command": "editor.action.triggerParameterHints",
                "title": "Trigger Parameter Hint"
            },
            "deprecated": false,
            "detail": "(macro) Kernel.defoverridable(keywords_or_behaviour)",
            "documentation": {
                "kind": "markdown",
                "value": "Makes the given definitions in the current module overridable."
            },
            "insertText": "defoverridable($1)$0",
            "insertTextFormat": 2,
            "kind": 14,
            "label": "defoverridable",
            "labelDetails": {
                "description": "Kernel.defoverridable/1",
                "detail": "(keywords_or_behaviour)"
            },
            "sortText": "00000051",
            "tags": []
        },
        {
            "command": {
                "command": "editor.action.triggerParameterHints",
                "title": "Trigger Parameter Hint"
            },
            "deprecated": false,
            "detail": "(macro) Kernel.defprotocol(name, do_block)",
            "documentation": {
                "kind": "markdown",
                "value": "Defines a protocol."
            },
            "insertText": "defprotocol LivebookApp$1 do\n\t$0\nend",
            "insertTextFormat": 2,
            "kind": 15,
            "label": "defprotocol",
            "labelDetails": {
                "description": "Kernel.defprotocol/2",
                "detail": "(name, do_block)"
            },
            "sortText": "00000052",
            "tags": []
        }
    ]
}

@lukaszsamson
Copy link
Collaborator

I'm out of ideas here. elixirLS returns a correct response here.
Notice that do CompletionItem is first in the list, has "sortText": "00000000" and "preselect": true. All other entries aren't preselected and have sortText placing them later.

From the spec https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion

to achieve consistency across languages and to honor different clients usually the client is responsible for filtering and sorting. This has also the advantage that client can experiment with different filter and sorting models. However servers can enforce different behavior by setting a filterText / sortText

and https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItem

	/**
	 * Select this item when showing.
	 *
	 * *Note* that only one completion item can be selected and that the
	 * tool / client decides which item that is. The rule is that the *first*
	 * item of those that match best is selected.
	 */
	preselect?: boolean;

	/**
	 * A string that should be used when comparing this item
	 * with other items. When omitted the label is used
	 * as the sort text for this item.
	 */
	sortText?: string;

@bcardarella
Copy link
Author

So strange, the mystery continues

@lukaszsamson
Copy link
Collaborator

I did some search on vscode github

It seems the behaviour is by design

microsoft/vscode#136898

Yes - the sort text looses its relevance when user have a prefix typed. We like to think about that as "user typing selects suggestion". This doc tries do clarify this behavior. We understand that language authors tend to disagree with this, because you spend lots of thoughts on a particular sorting, but users typing prefixes or other selecting characters are always given precedence.

microsoft/vscode#71660 (comment)

Yes, that's the design and honestly unlikely to change. The match-score with the current prefix (which the user typed) is stronger than the preselect property that the language brain computed. Only with equal scores, preselect comes to play. The reasoning is that user-input is worth more and I still think that's a good idea.

microsoft/vscode#79516 (comment)

An extension can define the initial order with sortText but as soon as the user starts typing a prefix that initial order is overruled by the scores computed by comparing completions against the prefix.

microsoft/vscode#109067 (comment) and referenced vscode docs https://github.com/microsoft/vscode/blob/1.78.2/src/vscode-dts/vscode.d.ts

		/**
		 * A string that should be used when comparing this item
		 * with other items. When `falsy` the {@link CompletionItem.label label}
		 * is used.
		 *
		 * Note that `sortText` is only used for the initial ordering of completion
		 * items. When having a leading word (prefix) ordering is based on how
		 * well completions match that prefix and the initial ordering is only used
		 * when completions match equally well. The prefix is defined by the
		 * {@linkcode CompletionItem.range range}-property and can therefore be different
		 * for each completion.
		 */
		sortText?: string;

		/**
		 * A string that should be used when filtering a set of
		 * completion items. When `falsy` the {@link CompletionItem.label label}
		 * is used.
		 *
		 * Note that the filter text is matched against the leading word (prefix) which is defined
		 * by the {@linkcode CompletionItem.range range}-property.
		 */
		filterText?: string;

		/**
		 * Select this item when showing. *Note* that only one completion item can be selected and
		 * that the editor decides which item that is. The rule is that the *first* item of those
		 * that match best is selected.
		 */
		preselect?: boolean;
		/**
		 * A string or snippet that should be inserted in a document when selecting
		 * this completion. When `falsy` the {@link CompletionItem.label label}
		 * is used.
		 */
		insertText?: string | SnippetString;

		/**
		 * A range or a insert and replace range selecting the text that should be replaced by this completion item.
		 *
		 * When omitted, the range of the {@link TextDocument.getWordRangeAtPosition current word} is used as replace-range
		 * and as insert-range the start of the {@link TextDocument.getWordRangeAtPosition current word} to the
		 * current position is used.
		 *
		 * *Note 1:* A range must be a {@link Range.isSingleLine single line} and it must
		 * {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}.
		 * *Note 2:* A insert range must be a prefix of a replace range, that means it must be contained and starting at the same position.
		 */
		range?: Range | { inserting: Range; replacing: Range };

Basically, what is says (tries to say) is that once a prefix exists (ht in your sample) the match quality of that prefix and a suggestion overrules all sortText.

microsoft/vscode#103682 (comment)

The sortText is only respected when no prefix is selected and since the latter item selects a prefix a score is computed and assigned.

microsoft/vscode#79516
microsoft/vscode#71660
microsoft/vscode#109067
microsoft/vscode#103682
microsoft/vscode#136898

some ideas about filterText and whitespaces in microsoft/vscode#66860

one big isse fixed lately microsoft/vscode#164716 - microsoft/vscode#179988

So it may be worth experimenting with the ranges. There are no ranges in LSP contrary to vscode. It may be worth migrating from insertText to textEdits or insertTextFormat and insertTextMode

from LSP spec 3.17

/**
	 * A string that should be inserted into a document when selecting
	 * this completion. When omitted the label is used as the insert text
	 * for this item.
	 *
	 * The `insertText` is subject to interpretation by the client side.
	 * Some tools might not take the string literally. For example
	 * VS Code when code complete is requested in this example
	 * `con<cursor position>` and a completion item with an `insertText` of
	 * `console` is provided it will only insert `sole`. Therefore it is
	 * recommended to use `textEdit` instead since it avoids additional client
	 * side interpretation.
	 */
	insertText?: string;

	/**
	 * The format of the insert text. The format applies to both the
	 * `insertText` property and the `newText` property of a provided
	 * `textEdit`. If omitted defaults to `InsertTextFormat.PlainText`.
	 *
	 * Please note that the insertTextFormat doesn't apply to
	 * `additionalTextEdits`.
	 */
	insertTextFormat?: [InsertTextFormat](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#insertTextFormat);

	/**
	 * How whitespace and indentation is handled during completion
	 * item insertion. If not provided the client's default value depends on
	 * the `textDocument.completion.insertTextMode` client capability.
	 *
	 * @since 3.16.0
	 * @since 3.17.0 - support for `textDocument.completion.insertTextMode`
	 */
	insertTextMode?: [InsertTextMode](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#insertTextMode);

	/**
	 * An edit which is applied to a document when selecting this completion.
	 * When an edit is provided the value of `insertText` is ignored.
	 *
	 * *Note:* The range of the edit must be a single line range and it must
	 * contain the position at which completion has been requested.
	 *
	 * Most editors support two different operations when accepting a completion
	 * item. One is to insert a completion text and the other is to replace an
	 * existing text with a completion text. Since this can usually not be
	 * predetermined by a server it can report both ranges. Clients need to
	 * signal support for `InsertReplaceEdit`s via the
	 * `textDocument.completion.completionItem.insertReplaceSupport` client
	 * capability property.
	 *
	 * *Note 1:* The text edit's range as well as both ranges from an insert
	 * replace edit must be a [single line] and they must contain the position
	 * at which completion has been requested.
	 * *Note 2:* If an `InsertReplaceEdit` is returned the edit's insert range
	 * must be a prefix of the edit's replace range, that means it must be
	 * contained and starting at the same position.
	 *
	 * @since 3.16.0 additional type `InsertReplaceEdit`
	 */
	textEdit?: [TextEdit](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit) | [InsertReplaceEdit](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#insertReplaceEdit);

	/**
	 * The edit text used if the completion item is part of a CompletionList and
	 * CompletionList defines an item default for the text edit range.
	 *
	 * Clients will only honor this property if they opt into completion list
	 * item defaults using the capability `completionList.itemDefaults`.
	 *
	 * If not provided and a list's default range is provided the label
	 * property is used as a text.
	 *
	 * @since 3.17.0
	 */
	textEditText?: string;

@bcardarella
Copy link
Author

bcardarella commented May 18, 2023

So why is it that it sorts OK for you but not for me?

@lukaszsamson
Copy link
Collaborator

Also notice without whitespaces after do
Screenshot 2023-05-18 at 17 05 14
with space after do
Screenshot 2023-05-18 at 17 05 30

You have some extension or setting that is displaying some text after your line. Maybe it's messing up the selection algo? Have you tried with all extensions disabled?

@lukaszsamson
Copy link
Collaborator

Oh I reproduced it with setting "editor.snippetSuggestions": "top". I have it set to inline and with that and bottom it works OK. do completion is a snippet. I'm not sure why we return other completions as snippets but this is not relevant here.

So definitively it's not a bug but editor setting.

@lukaszsamson
Copy link
Collaborator

After changes from e7c7a54 do will be suggested even if there are whitespaces after the cursor

@bcardarella
Copy link
Author

I went through some history of this and found this suggestion from @lukaszsamson #302 (comment) which solves the problem. It looks like inline is the default and I have no idea why mine was set to top but that appears to be the source of the problem. Finally fixed 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants