elm-tooling / elm-language-server

Language server implementation for Elm
https://www.npmjs.com/package/@elm-tooling/elm-language-server
MIT License
419 stars 65 forks source link

Request textDocument/completion failed with message: Invalid regular expression #293

Closed predragnikolic closed 4 years ago

predragnikolic commented 4 years ago

While update the server from version 1.6.3 to 1.8.0 I noticed the the completion request is not working correctly.

I can reproduce the problem with elm-language-server 1.8.0 and 1.8.3

Steps to repro.

  1. clone https://github.com/rtfeldman/elm-spa-example
  2. open /elm-spa-example/src/Api.elm
  3. When triggering completions on certain places in the document(see the gif). This elm server will throw an error:
    :: <<< lsp-elm 73: {'message': 'Request textDocument/completion failed with message: Invalid regular expression: /port module api exposing (cred, addservererror, application, decodeerrors, delete, get, login, logout, post, put, register, settings, storecredwith, username, viewerchanges)\n\n{-| this module is responsible for communicating to the conduit api.\n\nit exposes an opaque endpoint type which is guaranteed to point to the correct url.\n\n-}\n\nimport api.endpoint as endpoint exposing (endpoint)\nimport avatar exposing (avatar)\nimport browser\nimport browser.navigation as nav\nimport http exposing (body, expect)\nimport json.decode as decode exposing (decoder, value, decodestring, field, string)\nimport json.decode.pipeline as pipeline exposing (optional, required)\nimport json.encode as encode\nimport url exposing (url)\nimport username exposing (username)\n\n\n\n-- cred\n\n\n{-| the authentication credentials for the viewer (that is, the currently logged-in user.)\n\nthis includes:\n\n  - the cred\'s username\n  - the cred\'s authentication token\n\nby design, there is no way to access the token directly as a string.\nit can be encoded for persistence, and it can be added to a header\nto a httpbuilder for a request, but that\'s it.\n\nthis token should never be rendered to the end user, and with this api, it\ncan\'t be!\n\n-}\ntype cred\n    = cred username string\n\n\nusername : cred -> username\nusername (cred val _) =\n    val\n\n\ncredheader : cred -> http.header\ncredheader (cred _ str) =\n    http.header "authorization" ("token " ++ str)\n\n\n{-| it\'s important that this is never exposed!\n\nwe expose `login` and `application` instead, so we can be certain that if anyone\never has access to a `cred` value, it came from either the login api endpoint\nor was passed in via flags.\n\n-}\ncreddecoder : decoder cred\ncreddecoder =\n    decode.succeed cred\n        |> required "username" username.decoder\n        |> required "token" decode.string\n\n\n\n-- persistence\n\n\ndecode : decoder (cred -> viewer) -> value -> result decode.error viewer\ndecode decoder value =\n    -- it\'s stored in localstorage as a json string;\n    -- first decode the value as a string, then\n    -- decode that string as json.\n    decode.decodevalue decode.string value\n        |> result.andthen (\\str -> decode.decodestring (decode.field "user" (decoderfromcred decoder)) str)\n\n\nport onstorechange : (value -> msg) -> sub msg\n\n\nviewerchanges : (maybe viewer -> msg) -> decoder (cred -> viewer) -> sub msg\nviewerchanges tomsg decoder =\n    onstorechange (\\value -> tomsg (decodefromchange decoder value))\n\n\ndecodefromchange : decoder (cred -> viewer) -> value -> maybe viewer\ndecodefromchange viewerdecoder val =\n    -- it\'s stored in localstorage as a json string;\n    -- first decode the value as a string, then\n    -- decode that string as json.\n    decode.decodevalue (storagedecoder viewerdecoder) val\n        |> result.tomaybe\n\n\nstorecredwith : cred -> avatar -> cmd msg\nstorecredwith (cred uname token) avatar =\n    let\n        json =\n            encode.object\n                [ ( "user"\n                  , encode.object\n                        [ ( "username", username.encode uname )\n                        , ( "token", encode.string token )\n                        , ( "image", avatar.encode avatar )\n                        ]\n                  )\n                ]\n    in\n    storecache (just json)\n\n\nlogout : cmd msg\nlogout =\n    storecache nothing\n\n\nport storecache : maybe value -> cmd msg\n\n\n\n-- serialization\n-- application\n\n\napplication :\n    decoder (cred -> viewer)\n    ->\n        { init : maybe viewer -> url -> nav.key -> ( model, cmd msg )\n        , onurlchange : url -> msg\n        , onurlrequest : browser.urlrequest -> msg\n        , subscriptions : model -> sub msg\n        , update : msg -> model -> ( model, cmd msg )\n        , view : model -> browser.document msg\n        }\n    -> program value model msg\napplication viewerdecoder config =\n    let\n        init flags url navkey =\n            let\n                maybeviewer =\n                    decode.decodevalue decode.string flags\n                        |> result.andthen (decode.decodestring (storagedecoder viewerdecoder))\n                        |> result.tomaybe\n            in\n            config.init maybeviewer url navkey\n    in\n    browser.application\n        { init = init\n        , onurlchange = config.onurlchange\n        , onurlrequest = config.onurlrequest\n        , subscriptions = config.subscriptions\n        , update = config.update\n        , view = config.view\n        }\n\n\nstoragedecoder : decoder (cred -> viewer) -> decoder viewer\nstoragedecoder viewerdecoder =\n    decode.field "user" (decoderfromcred viewerdecoder)\n\n\n\n-- http\n\n\nget : endpoint -> maybe cred -> decoder a -> http.request a\nget url maybecred decoder =\n    endpoint.request\n        { method = "get"\n        , url = url\n        , expect = http.expectjson decoder\n        , headers =\n            case maybecred of\n                just cred ->\n                    [ credheader cred ]\n\n                nothing ->\n                    []\n        , body = http.emptybody\n        , timeout = nothing\n        , withcredentials = false\n        }\n\n\nput : endpoint -> cred -> body -> decoder a -> http.request a\nput url cred body decoder =\n    endpoint.request\n        { method = "put"\n        , url = url\n        , expect = http.expectjson decoder\n        , headers = [ credheader cred ]\n        , body = body\n        , timeout = nothing\n        , withcredentials = false\n        }\n\n\npost : endpoint -> maybe cred -> body -> decoder a -> http.request a\npost url maybecred body decoder =\n    endpoint.request\n        { method = "post"\n        , url = url\n        , expect = http.expectjson decoder\n        , headers =\n            case maybecred of\n                just cred ->\n                    [ credheader cred ]\n\n                nothing ->\n                    []\n        , body = body\n        , timeout = nothing\n        , withcredentials = false\n        }\n\n\ndelete : endpoint -> cred -> body -> decoder a -> http.request a\ndelete url cred body decoder =\n    endpoint.request\n        { method = "delete"\n        , url = url\n        , expect = http.expectjson decoder\n        , headers = [ credheader cred ]\n        , body = body\n        , timeout = nothing\n        , withcredentials = false\n        }\n\n\nlogin : http.body -> decoder (cred -> a) -> http.request a\nlogin body decoder =\n    post endpoint.login nothing body (decode.field "user" (decoderfromcred decoder))\n\n\nregister : http.body -> decoder (cred -> a) -> http.request a\nregister body decoder =\n    post endpoint.users nothing body (decode.field "user" (decoderfromcred decoder))\n\n\nsettings : cred -> http.body -> decoder (cred -> a) -> http.request a\nsettings cred body decoder =\n    put endpoint.user cred body (decode.field "user" (decoderfromcred decoder))\n\n\ndecoderfromcred : decoder (cred -> a) -> decoder a\ndecoderfromcred decoder =\n    decode.map2 (\\fromcred cred -> fromcred cred)\n        decoder\n        creddecoder\n\n\n\n-- errors\n\n\naddservererror : list string -> list string\naddservererror list =\n    "server error" :: list\n\n\n{-| many api endpoints include an "errors" field in their badstatus responses.\n-}\ndecodeerrors : http.error -> list string\ndecodeerrors error =\n    case error of\n        http.badstatus response ->\n            response.body\n                |> decodestring (field "errors" errorsdecoder)\n                |> result.withdefault [ "server error" ]\n\n        err ->\n            [ "server error" ]\n\n\nerrorsdecoder : decoder (list string)\nerrorsdecoder =\n    decode.keyvaluepairs (decode.list decode.string)\n        |> decode.map (list.concatmap frompair)\n\n\nfrompair : ( string, list string ) -> list string\nfrompair ( field, errors ) =\n    list.map (\\error -> field ++ " " ++ error) errors\n\n\n\n-- localstorage keys\n\n\ncachestoragekey : string\ncachestoragekey =\n    "cache"\n\n\ncredstoragekey : string\ncredstoragekey =\n    "cred"\n/: Nothing to repeat', 'code': -32603}

output

Notice that the completion request only fail on some places (when triggered outside of the comment)... on some places the completion works (example, when I trigger the completion request inside the comment section).

Originally posted by @predragnikolic in https://github.com/sublimelsp/LSP-elm/pull/18#issuecomment-639932077

If you need more details, ping me :)

razzeee commented 4 years ago

Thanks for reporting this, very appreciated.