pax_global_header00006660000000000000000000000064145760436730014531gustar00rootroot0000000000000052 comment=1802a888d2e9a0d6c31d43f42a337b42f98fda7b efm-langserver-0.0.53/000077500000000000000000000000001457604367300145335ustar00rootroot00000000000000efm-langserver-0.0.53/.gitattributes000066400000000000000000000000621457604367300174240ustar00rootroot00000000000000# Workaround for gofmt/goimports *.go text eol=lf efm-langserver-0.0.53/.github/000077500000000000000000000000001457604367300160735ustar00rootroot00000000000000efm-langserver-0.0.53/.github/FUNDING.yml000066400000000000000000000013151457604367300177100ustar00rootroot00000000000000# These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: mattn # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] efm-langserver-0.0.53/.github/workflows/000077500000000000000000000000001457604367300201305ustar00rootroot00000000000000efm-langserver-0.0.53/.github/workflows/ci.yml000066400000000000000000000010121457604367300212400ustar00rootroot00000000000000name: CI on: [push, pull_request] jobs: test: name: Test runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] timeout-minutes: 5 steps: - name: Checkout code uses: actions/checkout@v3 - name: Setup Go uses: actions/setup-go@v4 with: go-version: 1.x - name: Lint uses: golangci/golangci-lint-action@v3 with: args: --verbose - name: Test run: make test efm-langserver-0.0.53/.github/workflows/release.yml000066400000000000000000000012411457604367300222710ustar00rootroot00000000000000name: Release on: push: tags: - 'v*' jobs: release: name: Release runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@master - name: Setup Go uses: actions/setup-go@v4 with: go-version: 1.x - name: Cross build run: make cross - name: Create Release id: create_release uses: actions/create-release@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} - name: Upload run: make upload env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} efm-langserver-0.0.53/.gitignore000066400000000000000000000000731457604367300165230ustar00rootroot00000000000000config .vim-lsp-settings efm-langserver efm-langserver.exe efm-langserver-0.0.53/.golangci.yaml000066400000000000000000000033721457604367300172650ustar00rootroot00000000000000--- run: timeout: 5m output: format: colored-line-number linters: # Disable all the default linters until issues found by these are dealt with. disable-all: true enable: - goimports - govet - revive - typecheck linters-settings: goimports: local-prefixes: github.com/mattn/efm-langserver revive: ignore-generated-header: true severity: warning confidence: 0.8 error-code: 0 warning-code: 0 rules: - name: add-constant arguments: - maxLitCount: "3" allowStrs: '""' allowInts: "0,1,2,3,4,5,0700,0660,0o700,0o660" allowFloats: "0.0,0.,1.0,1.,2.0,2." - name: blank-imports - name: context-as-argument - name: context-keys-type - name: dot-imports - name: empty-block - name: error-naming - name: error-return - name: error-strings - name: errorf - name: exported - name: if-return - name: increment-decrement - name: indent-error-flow - name: range - name: receiver-naming - name: redefines-builtin-id - name: superfluous-else - name: time-naming - name: unexported-return - name: unreachable-code - name: unused-parameter - name: var-declaration - name: var-naming issues: exclude-rules: - linters: - goimports path: langserver/handler_go112.go - linters: - goimports path: langserver/handler_go113.go - linters: - revive path: '(.+)_test\.go' text: "add-constant:" - linters: - revive path: '(.+)_test\.go' text: "exported:" - linters: - revive path: langserver/diff.go text: "var-naming: don't use leading k in Go names" efm-langserver-0.0.53/LICENSE000066400000000000000000000020751457604367300155440ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2022 Yasuhiro Matsumoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. efm-langserver-0.0.53/Makefile000066400000000000000000000031011457604367300161660ustar00rootroot00000000000000BIN := efm-langserver VERSION := $$(make -s show-version) CURRENT_REVISION := $(shell git rev-parse --short HEAD) BUILD_LDFLAGS := "-s -w -X main.revision=$(CURRENT_REVISION)" GOBIN ?= $(shell go env GOPATH)/bin export GO111MODULE=on .PHONY: all all: clean build .PHONY: build build: go build -ldflags=$(BUILD_LDFLAGS) -o $(BIN) . .PHONY: install install: go install -ldflags=$(BUILD_LDFLAGS) . .PHONY: show-version show-version: $(GOBIN)/gobump @gobump show -r . $(GOBIN)/gobump: @go install github.com/x-motemen/gobump/cmd/gobump@latest .PHONY: cross cross: $(GOBIN)/goxz goxz -n $(BIN) -pv=v$(VERSION) -build-ldflags=$(BUILD_LDFLAGS) . $(GOBIN)/goxz: go install github.com/Songmu/goxz/cmd/goxz@latest .PHONY: test test: build go test -v ./... .PHONY: schema_doc schema_doc: # https://github.com/coveooss/json-schema-for-humans generate-schema-doc --config template_name=md --config description_is_markdown=true --config show_breadcrumbs=false schema.json schema.md sed -i.bak 's/\\`/`/g' schema.md [ -f schema.md.bak ] && rm schema.md.bak .PHONY: clean clean: rm -rf $(BIN) goxz go clean .PHONY: bump bump: $(GOBIN)/gobump ifneq ($(shell git status --porcelain),) $(error git workspace is dirty) endif ifneq ($(shell git rev-parse --abbrev-ref HEAD),master) $(error current branch is not master) endif @gobump up -w . git commit -am "bump up version to $(VERSION)" git tag "v$(VERSION)" git push origin master git push origin "refs/tags/v$(VERSION)" .PHONY: upload upload: $(GOBIN)/ghr ghr "v$(VERSION)" goxz $(GOBIN)/ghr: go install github.com/tcnksm/ghr@latest efm-langserver-0.0.53/README.md000066400000000000000000000315641457604367300160230ustar00rootroot00000000000000# efm-langserver [![Actions Status](https://github.com/mattn/efm-langserver/workflows/CI/badge.svg)](https://github.com/mattn/efm-langserver/actions) General purpose Language Server that can use specified error message format generated from specified command. This is useful for editing code with linter. ![efm](https://raw.githubusercontent.com/mattn/efm-langserver/master/screenshot.png) * [Installation](#installation) * [Usage](#usage) + [Configuration](#configuration) - [InitializeParams](#initializeparams) + [Example for config.yaml](#example-for-configyaml) + [Example for DidChangeConfiguration notification](#example-for-didchangeconfiguration-notification) * [Client Setup](#client-setup) + [Configuration for vim-lsp](#configuration-for-vim-lsp) + [Configuration for coc.nvim](#configuration-for-cocnvim) + [Configuration for Eglot (Emacs)](#configuration-for-eglot) + [Configuration for neovim builtin LSP with nvim-lspconfig](#configuration-for-neovim-builtin-lsp-with-nvim-lspconfig) + [Configuration for Helix](#configuration-for-helix) + [Configuration for VSCode](#configuration-for-vscode) * [License](#license) * [Author](#author) ## Installation ```console go install github.com/mattn/efm-langserver@latest ``` or via [Homebrew](https://brew.sh/): ```console brew install efm-langserver ``` ## Usage ```text Usage of efm-langserver: -c string path to config.yaml -d dump configuration -logfile string logfile -loglevel int loglevel (default 1) -q Run quieter -v Print the version ``` ### Configuration Configuration can be done with either a `config.yaml` file, or through a [DidChangeConfiguration](https://microsoft.github.io/language-server-protocol/specification.html#workspace_didChangeConfiguration) notification from the client. `DidChangeConfiguration` can be called any time and will overwrite only provided properties. `DidChangeConfiguration` only supports V2 configuration and cannot set `LogFile`. `efm-langserver` does not include formatters/linters for any languages, you must install these manually, e.g. - lua: [LuaFormatter](https://github.com/Koihik/LuaFormatter) - python: [yapf](https://github.com/google/yapf) [isort](https://github.com/PyCQA/isort) - [vint](https://github.com/Kuniwak/vint) for Vim script - [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli) for Markdown - etc... #### InitializeParams Because the configuration can be updated on the fly, capabilities might change throughout the lifetime of the server. To enable support for capabilities that will be available later, set them in the [InitializeParams](https://microsoft.github.io/language-server-protocol/specification.html#initialize) Example ```json { "initializationOptions": { "documentFormatting": true, "documentRangeFormatting": true, "hover": true, "documentSymbol": true, "codeAction": true, "completion": true } } ``` ### Example for config.yaml Location of config.yaml is: * UNIX: `$XDG_CONFIG_HOME/efm-langserver/config.yaml` or `$HOME/.config/efm-langserver/config.yaml` * Windows: `%APPDATA%\efm-langserver\config.yaml` Below is example for `config.yaml` for Windows. Please see [schema.md](schema.md) for full documentation of the available options. ```yaml version: 2 root-markers: - .git/ lint-debounce: 1s commands: - command: notepad arguments: - ${INPUT} title: メモ帳 tools: any-excitetranslate: &any-excitetranslate hover-command: 'excitetranslate' hover-stdin: true blade-blade-formatter: &blade-blade-formatter format-command: 'blade-formatter --stdin' format-stdin: true css-prettier: &css-prettier format-command: './node_modules/.bin/prettier ${--tab-width:tabWidth} ${--single-quote:singleQuote} --parser css' csv-csvlint: &csv-csvlint lint-command: 'csvlint' dockerfile-hadolint: &dockerfile-hadolint lint-command: 'hadolint' lint-formats: - '%f:%l %m' eruby-erb: &eruby-erb lint-debounce: 2s lint-command: 'erb -x -T - | ruby -c' lint-stdin: true lint-offset: 1 format-stdin: true format-command: htmlbeautifier gitcommit-gitlint: &gitcommit-gitlint lint-command: 'gitlint' lint-stdin: true lint-formats: - '%l: %m: "%r"' - '%l: %m' html-prettier: &html-prettier format-command: './node_modules/.bin/prettier ${--tab-width:tabWidth} ${--single-quote:singleQuote} --parser html' javascript-eslint: &javascript-eslint lint-command: 'eslint -f visualstudio --stdin --stdin-filename ${INPUT}' lint-ignore-exit-code: true lint-stdin: true lint-formats: - "%f(%l,%c): %tarning %m" - "%f(%l,%c): %rror %m" json-fixjson: &json-fixjson format-command: 'fixjson' json-jq: &json-jq lint-command: 'jq .' json-prettier: &json-prettier format-command: './node_modules/.bin/prettier ${--tab-width:tabWidth} --parser json' lua-lua-format: &lua-lua-format format-command: 'lua-format -i' format-stdin: true make-checkmake: &make-checkmake lint-command: 'checkmake' lint-stdin: true markdown-markdownlint: &markdown-markdownlint lint-command: 'markdownlint -s -c %USERPROFILE%\.markdownlintrc' lint-stdin: true lint-formats: - '%f:%l %m' - '%f:%l:%c %m' - '%f: %l: %m' markdown-pandoc: &markdown-pandoc format-command: 'pandoc -f markdown -t gfm -sp --tab-stop=2' mix_credo: &mix_credo lint-command: "mix credo suggest --format=flycheck --read-from-stdin ${INPUT}" lint-stdin: true lint-formats: - '%f:%l:%c: %t: %m' - '%f:%l: %t: %m' root-markers: - mix.lock - mix.exs perl-perlcritic: &perl-perlcritic lint-command: 'perlcritic --nocolor -3 --verbose "%l:%c %m\n"' lint-ignore-exit-code: true lint-formats: - '%l:%c %m' perl-perltidy: &perl-perltidy format-command: "perltidy -b" format-stdin: true php-phpstan: &php-phpstan lint-command: './vendor/bin/phpstan analyze --error-format raw --no-progress' php-psalm: &php-psalm lint-command: './vendor/bin/psalm --output-format=emacs --no-progress' lint-formats: - '%f:%l:%c:%trror - %m' - '%f:%l:%c:%tarning - %m' prettierd: &prettierd format-command: > prettierd ${INPUT} ${--range-start=charStart} ${--range-end=charEnd} \ ${--tab-width=tabSize} format-stdin: true root-markers: - .prettierrc - .prettierrc.json - .prettierrc.js - .prettierrc.yml - .prettierrc.yaml - .prettierrc.json5 - .prettierrc.mjs - .prettierrc.cjs - .prettierrc.toml python-autopep8: &python-autopep8 format-command: 'autopep8 -' format-stdin: true python-black: &python-black format-command: 'black --quiet -' format-stdin: true python-flake8: &python-flake8 lint-command: 'flake8 --stdin-display-name ${INPUT} -' lint-stdin: true lint-formats: - '%f:%l:%c: %m' python-isort: &python-isort format-command: 'isort --quiet -' format-stdin: true python-mypy: &python-mypy lint-command: 'mypy --show-column-numbers' lint-formats: - '%f:%l:%c: %trror: %m' - '%f:%l:%c: %tarning: %m' - '%f:%l:%c: %tote: %m' python-pylint: &python-pylint lint-command: 'pylint --output-format text --score no --msg-template {path}:{line}:{column}:{C}:{msg} ${INPUT}' lint-stdin: false lint-formats: - '%f:%l:%c:%t:%m' lint-offset-columns: 1 lint-category-map: I: H R: I C: I W: W E: E F: E python-yapf: &python-yapf format-command: 'yapf --quiet' format-stdin: true rst-lint: &rst-lint lint-command: 'rst-lint' lint-formats: - '%tNFO %f:%l %m' - '%tARNING %f:%l %m' - '%tRROR %f:%l %m' - '%tEVERE %f:%l %m' rst-pandoc: &rst-pandoc format-command: 'pandoc -f rst -t rst -s --columns=79' sh-shellcheck: &sh-shellcheck lint-command: 'shellcheck -f gcc -x' lint-source: 'shellcheck' lint-formats: - '%f:%l:%c: %trror: %m' - '%f:%l:%c: %tarning: %m' - '%f:%l:%c: %tote: %m' sh-shfmt: &sh-shfmt format-command: 'shfmt -ci -s -bn' format-stdin: true vim-vint: &vim-vint lint-command: 'vint -' lint-stdin: true lint-formats: - '%f:%l:%c: %m' yaml-yamllint: &yaml-yamllint lint-command: 'yamllint -f parsable -' lint-stdin: true languages: blade: - <<: *blade-blade-formatter css: - <<: *css-prettier csv: - <<: *csv-csvlint dockerfile: - <<: *dockerfile-hadolint elixir: - <<: *mix_credo eruby: - <<: *eruby-erb gitcommit: - <<: *gitcommit-gitlint html: - <<: *html-prettier javascript: - <<: *javascript-eslint - <<: *prettierd json: - <<: *json-fixjson - <<: *json-jq # - <<: *json-prettier lua: - <<: *lua-lua-format make: - <<: *make-checkmake markdown: - <<: *markdown-markdownlint - <<: *markdown-pandoc perl: - <<: *perl-perltidy - <<: *perl-perlcritic php: - <<: *php-phpstan - <<: *php-psalm python: - <<: *python-black - <<: *python-flake8 - <<: *python-isort - <<: *python-mypy # - <<: *python-autopep8 # - <<: *python-yapf rst: - <<: *rst-lint - <<: *rst-pandoc sh: - <<: *sh-shellcheck - <<: *sh-shfmt vim: - <<: *vim-vint yaml: - <<: *yaml-yamllint =: - <<: *any-excitetranslate ``` If you want to debug output of commands: ```yaml version: 2 log-file: /path/to/output.log log-level: 1 ``` ### Example for DidChangeConfiguration notification ```json { "settings": { "rootMarkers": [".git/"], "languages": { "lua": { "formatCommand": "lua-format -i", "formatStdin": true } } } } ``` ## Client Setup ### Configuration for [vim-lsp](https://github.com/prabirshrestha/vim-lsp/) ```vim augroup LspEFM au! autocmd User lsp_setup call lsp#register_server({ \ 'name': 'efm-langserver', \ 'cmd': {server_info->['efm-langserver', '-c=/path/to/your/config.yaml']}, \ 'allowlist': ['vim', 'eruby', 'markdown', 'yaml'], \ }) augroup END ``` [vim-lsp-settings](https://github.com/mattn/vim-lsp-settings) provide installer for efm-langserver. ### Configuration for [coc.nvim](https://github.com/neoclide/coc.nvim) coc-settings.json ```jsonc // languageserver "languageserver": { "efm": { "command": "efm-langserver", "args": [], // custom config path // "args": ["-c", "/path/to/your/config.yaml"], "filetypes": ["vim", "eruby", "markdown", "yaml"] } }, ``` ### Configuration for [Eglot](https://github.com/joaotavora/eglot) (Emacs) Add to eglot-server-programs with major mode you want. ```lisp (with-eval-after-load 'eglot (add-to-list 'eglot-server-programs `(markdown-mode . ("efm-langserver")))) ``` ### Configuration for [neovim builtin LSP](https://neovim.io/doc/user/lsp.html) with [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) Neovim's built-in LSP client sends `DidChangeConfiguration`, so `config.yaml` is optional. `init.lua` example (`settings` follows [`schema.md`](schema.md)): ```lua require "lspconfig".efm.setup { init_options = {documentFormatting = true}, settings = { rootMarkers = {".git/"}, languages = { lua = { {formatCommand = "lua-format -i", formatStdin = true} } } } } ``` You can get premade tool definitions from [`creativenull/efmls-configs-nvim`](https://github.com/creativenull/efmls-configs-nvim): ```lua lua = { require('efmls-configs.linters.luacheck'), require('efmls-configs.formatters.stylua'), } ``` If you define your own, make sure to define as table: ```lua lua = { {formatCommand = "lua-format -i", formatStdin = true} } -- NOT lua = { formatCommand = "lua-format -i", formatStdin = true } -- and for multiple formatters, add to the table lua = { {formatCommand = "lua-format -i", formatStdin = true}, {formatCommand = "lua-pretty -i"} } ``` ### Configuration for [Helix](https://github.com/helix-editor/helix) `~/.config/helix/languages.toml` ```toml [language-server.efm] command = "efm-langserver" [[language]] name = "typescript" language-servers = [ { name = "efm", only-features = [ "diagnostics", "format" ] }, { name = "typescript-language-server", except-features = [ "format" ] } ] ``` ### Configuration for [VSCode](https://github.com/microsoft/vscode) [Generic LSP Client for VSCode](https://github.com/llllvvuu/vscode-glspc) Example `settings.json` (change to fit your local installs): ```json { "glspc.languageId": "lua", "glspc.serverCommand": "/Users/me/.local/share/nvim/mason/bin/efm-langserver", "glspc.pathPrepend": "/Users/me/.local/share/rtx/installs/python/3.11.4/bin:/Users/me/.local/share/rtx/installs/node/20.3.1/bin", } ``` ## License MIT ## Author Yasuhiro Matsumoto (a.k.a. mattn) efm-langserver-0.0.53/go.mod000066400000000000000000000005441457604367300156440ustar00rootroot00000000000000module github.com/mattn/efm-langserver go 1.20 require ( github.com/mattn/go-unicodeclass v0.0.2 github.com/reviewdog/errorformat v0.0.0-20240311054359-739e471a49b3 github.com/sourcegraph/jsonrpc2 v0.2.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/kr/pretty v0.1.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) efm-langserver-0.0.53/go.sum000066400000000000000000000055701457604367300156750ustar00rootroot00000000000000github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/haya14busa/go-checkstyle v0.0.0-20170303121022-5e9d09f51fa1/go.mod h1:RsN5RGgVYeXpcXNtWyztD5VIe7VNSEqpJvF2iEH7QvI= github.com/haya14busa/go-sarif v0.0.0-20210102043135-e2c5fed2fa3d/go.mod h1:1Hkn3JseGMB/hv1ywzkapVQDWV3bFgp6POZobZmR/5g= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-unicodeclass v0.0.1 h1:BKdh58FOa0n4QRd39jSeVEF7ncxxV3l4GL05LxZu+XA= github.com/mattn/go-unicodeclass v0.0.1/go.mod h1:dDCkCgOKUwD3sYX4N+tVQdFh/xlFQ1+cWakbQzy98T8= github.com/mattn/go-unicodeclass v0.0.2 h1:0pektcNiDCDNyEcNGTeRt6lf+EbVDS2dwaH4KnjYuT0= github.com/mattn/go-unicodeclass v0.0.2/go.mod h1:dDCkCgOKUwD3sYX4N+tVQdFh/xlFQ1+cWakbQzy98T8= github.com/reviewdog/errorformat v0.0.0-20220309155058-b075c45b6d9a h1:HIL+jTKsWmNT5WoTNwHQ0jUNJpFOmgeHLOsHMZInrF8= github.com/reviewdog/errorformat v0.0.0-20220309155058-b075c45b6d9a/go.mod h1:AqhrP0G7F9YRROF10JQwdd4cNO8bdm6bY6KzcOc3Cp8= github.com/reviewdog/errorformat v0.0.0-20230917110423-33358de5bbea h1:4nmQZsFy/s1J6faH8D7UQWcGz/BcC/uFMlsPkg/4hRg= github.com/reviewdog/errorformat v0.0.0-20230917110423-33358de5bbea/go.mod h1:AqhrP0G7F9YRROF10JQwdd4cNO8bdm6bY6KzcOc3Cp8= github.com/reviewdog/errorformat v0.0.0-20240311054359-739e471a49b3 h1:lV04V4ovHohXYTxIk6pttGRTu/AcxuYqJ/DGetAonqU= github.com/reviewdog/errorformat v0.0.0-20240311054359-739e471a49b3/go.mod h1:AqhrP0G7F9YRROF10JQwdd4cNO8bdm6bY6KzcOc3Cp8= github.com/sourcegraph/jsonrpc2 v0.1.0 h1:ohJHjZ+PcaLxDUjqk2NC3tIGsVa5bXThe1ZheSXOjuk= github.com/sourcegraph/jsonrpc2 v0.1.0/go.mod h1:ZafdZgk/axhT1cvZAPOhw+95nz2I/Ra5qMlU4gTRwIo= github.com/sourcegraph/jsonrpc2 v0.2.0 h1:KjN/dC4fP6aN9030MZCJs9WQbTOjWHhrtKVpzzSrr/U= github.com/sourcegraph/jsonrpc2 v0.2.0/go.mod h1:ZafdZgk/axhT1cvZAPOhw+95nz2I/Ra5qMlU4gTRwIo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= efm-langserver-0.0.53/langserver/000077500000000000000000000000001457604367300167035ustar00rootroot00000000000000efm-langserver-0.0.53/langserver/config.go000066400000000000000000000023711457604367300205020ustar00rootroot00000000000000package langserver import ( "fmt" "log" "os" "gopkg.in/yaml.v3" ) // LoadConfig load configuration from file func LoadConfig(yamlfile string) (*Config, error) { var config = Config{ ProvideDefinition: true, // Enabled by default. Commands: &[]Command{}, Languages: &map[string][]Language{}, RootMarkers: &[]string{}, } var config1 Config1 f, err := os.Open(yamlfile) if err != nil { log.Println("efm-langserver: no configuration file") return &config, nil } defer f.Close() err = yaml.NewDecoder(f).Decode(&config1) if err != nil || config1.Version == 2 { f, err = os.Open(yamlfile) if err != nil { return nil, err } defer f.Close() err = yaml.NewDecoder(f).Decode(&config) if err != nil { return nil, fmt.Errorf("can not read configuration: %v", err) } } else { config.Version = config1.Version config.Commands = &config1.Commands config.Logger = config1.Logger languages := make(map[string][]Language) for k, v := range config1.Languages { languages[k] = []Language{v} } config.Languages = &languages } config.Filename = yamlfile for _, v := range *config.Languages { for _, l := range v { if l.HoverChars == "" { l.HoverChars = "_" } } } return &config, nil } efm-langserver-0.0.53/langserver/diff.go000066400000000000000000000125541457604367300201510ustar00rootroot00000000000000// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file.package langserver // Source: // https://github.com/golang/tools/blob/78b158585360beccadc3faac6e35759f491831f3/internal/lsp/diff/myers/diff.go package langserver import ( "strings" ) // OpKind is used to denote the type of operation a line represents. type OpKind int const ( // Delete is the operation kind for a line that is present in the input // but not in the output. Delete OpKind = iota // Insert is the operation kind for a line that is new in the output. Insert // Equal is the operation kind for a line that is the same in the input and // output, often used to provide context around edited lines. Equal ) // Sources: // https://blog.jcoglan.com/2017/02/17/the-myers-diff-algorithm-part-3/ // https://www.codeproject.com/Articles/42279/%2FArticles%2F42279%2FInvestigating-Myers-diff-algorithm-Part-1-of-2 // ComputeEdits computes diff edits from 2 string inputs func ComputeEdits(_ DocumentURI, before, after string) []TextEdit { ops := operations(splitLines(before), splitLines(after)) edits := make([]TextEdit, 0, len(ops)) for _, op := range ops { switch op.Kind { case Delete: // Delete: unformatted[i1:i2] is deleted. edits = append(edits, TextEdit{Range: Range{ Start: Position{Line: op.I1, Character: 0}, End: Position{Line: op.I2, Character: 0}, }}) case Insert: // Insert: formatted[j1:j2] is inserted at unformatted[i1:i1]. if content := strings.Join(op.Content, ""); content != "" { edits = append(edits, TextEdit{ Range: Range{ Start: Position{Line: op.I1, Character: 0}, End: Position{Line: op.I2, Character: 0}, }, NewText: content, }) } } } return edits } type operation struct { Kind OpKind Content []string // content from b I1, I2 int // indices of the line in a J1 int // indices of the line in b, J2 implied by len(Content) } // operations returns the list of operations to convert a into b, consolidating // operations for multiple lines and not including equal lines. func operations(a, b []string) []*operation { if len(a) == 0 && len(b) == 0 { return nil } trace, offset := shortestEditSequence(a, b) snakes := backtrack(trace, len(a), len(b), offset) M, N := len(a), len(b) var i int solution := make([]*operation, len(a)+len(b)) add := func(op *operation, i2, j2 int) { if op == nil { return } op.I2 = i2 if op.Kind == Insert { op.Content = b[op.J1:j2] } solution[i] = op i++ } x, y := 0, 0 for _, snake := range snakes { if len(snake) < 2 { continue } var op *operation // delete (horizontal) for snake[0]-snake[1] > x-y { if op == nil { op = &operation{ Kind: Delete, I1: x, J1: y, } } x++ if x == M { break } } add(op, x, y) op = nil // insert (vertical) for snake[0]-snake[1] < x-y { if op == nil { op = &operation{ Kind: Insert, I1: x, J1: y, } } y++ } add(op, x, y) op = nil // equal (diagonal) for x < snake[0] { x++ y++ } if x >= M && y >= N { break } } return solution[:i] } // backtrack uses the trace for the edit sequence computation and returns the // "snakes" that make up the solution. A "snake" is a single deletion or // insertion followed by zero or diagonals. func backtrack(trace [][]int, x, y, offset int) [][]int { snakes := make([][]int, len(trace)) d := len(trace) - 1 for ; x > 0 && y > 0 && d > 0; d-- { V := trace[d] if len(V) == 0 { continue } snakes[d] = []int{x, y} k := x - y var kPrev int if k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) { kPrev = k + 1 } else { kPrev = k - 1 } x = V[kPrev+offset] y = x - kPrev } if x < 0 || y < 0 { return snakes } snakes[d] = []int{x, y} return snakes } // shortestEditSequence returns the shortest edit sequence that converts a into b. func shortestEditSequence(a, b []string) ([][]int, int) { M, N := len(a), len(b) V := make([]int, 2*(N+M)+1) offset := N + M trace := make([][]int, N+M+1) // Iterate through the maximum possible length of the SES (N+M). for d := 0; d <= N+M; d++ { copyV := make([]int, len(V)) // k lines are represented by the equation y = x - k. We move in // increments of 2 because end points for even d are on even k lines. for k := -d; k <= d; k += 2 { // At each point, we either go down or to the right. We go down if // k == -d, and we go to the right if k == d. We also prioritize // the maximum x value, because we prefer deletions to insertions. var x int if k == -d || (k != d && V[k-1+offset] < V[k+1+offset]) { x = V[k+1+offset] // down } else { x = V[k-1+offset] + 1 // right } y := x - k // Diagonal moves while we have equal contents. for x < M && y < N && a[x] == b[y] { x++ y++ } V[k+offset] = x // Return if we've exceeded the maximum values. if x == M && y == N { // Makes sure to save the state of the array before returning. copy(copyV, V) trace[d] = copyV return trace, offset } } // Save the state of the array. copy(copyV, V) trace[d] = copyV } return nil, 0 } func splitLines(text string) []string { lines := strings.SplitAfter(text, "\n") if lines[len(lines)-1] == "" { lines = lines[:len(lines)-1] } return lines } efm-langserver-0.0.53/langserver/duration.go000066400000000000000000000020231457604367300210540ustar00rootroot00000000000000package langserver import ( "encoding/json" "errors" "time" ) // Duration time.Duration wrapper type Duration time.Duration // MarshalJSON method mash to json func (d Duration) MarshalJSON() ([]byte, error) { return json.Marshal(time.Duration(d).String()) } // UnmarshalJSON method Unmash duration from string or decimal func (d *Duration) UnmarshalJSON(b []byte) error { var v any if err := json.Unmarshal(b, &v); err != nil { return err } switch value := v.(type) { case float64: *d = Duration(time.Duration(value)) return nil case string: tmp, err := time.ParseDuration(value) if err != nil { return err } *d = Duration(tmp) return nil default: return errors.New("invalid duration") } } // UnmarshalYAML method Unmash duration from string or decimal func (d *Duration) UnmarshalYAML(unmarshal func(any) error) error { var s string if err := unmarshal(&s); err != nil { return err } duration, err := time.ParseDuration(s) if err != nil { return err } *d = Duration(duration) return nil } efm-langserver-0.0.53/langserver/handle_initialize.go000066400000000000000000000055001457604367300227060ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "os/exec" "path/filepath" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleInitialize(_ context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } h.conn = conn var params InitializeParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } // https://microsoft.github.io/language-server-protocol/specification#initialize // The rootUri of the workspace. Is null if no folder is open. if params.RootURI != "" { rootPath, err := fromURI(params.RootURI) if err != nil { return nil, err } h.rootPath = filepath.Clean(rootPath) h.addFolder(rootPath) } var completion *CompletionProvider var hasCompletionCommand bool var hasHoverCommand bool var hasCodeActionCommand bool var hasSymbolCommand bool var hasFormatCommand bool var hasRangeFormatCommand bool var hasDefinitionCommand bool if params.InitializationOptions != nil { hasCompletionCommand = params.InitializationOptions.Completion hasHoverCommand = params.InitializationOptions.Hover hasCodeActionCommand = params.InitializationOptions.CodeAction hasSymbolCommand = params.InitializationOptions.DocumentSymbol hasFormatCommand = params.InitializationOptions.DocumentFormatting hasRangeFormatCommand = params.InitializationOptions.RangeFormatting } if len(h.commands) > 0 { hasCodeActionCommand = true } if h.provideDefinition { if _, err = exec.LookPath("ctags"); err == nil { hasDefinitionCommand = true } } for _, config := range h.configs { for _, v := range config { if v.CompletionCommand != "" { hasCompletionCommand = true } if v.HoverCommand != "" { hasHoverCommand = true } if v.SymbolCommand != "" { hasSymbolCommand = true } if v.FormatCommand != "" { hasFormatCommand = true if v.FormatCanRange { hasRangeFormatCommand = true } } } } if hasCompletionCommand { chars := []string{"."} if len(h.triggerChars) > 0 { chars = h.triggerChars } completion = &CompletionProvider{ TriggerCharacters: chars, } } return InitializeResult{ Capabilities: ServerCapabilities{ TextDocumentSync: TDSKFull, DocumentFormattingProvider: hasFormatCommand, RangeFormattingProvider: hasRangeFormatCommand, DocumentSymbolProvider: hasSymbolCommand, DefinitionProvider: hasDefinitionCommand, CompletionProvider: completion, HoverProvider: hasHoverCommand, CodeActionProvider: hasCodeActionCommand, Workspace: &ServerCapabilitiesWorkspace{ WorkspaceFolders: WorkspaceFoldersServerCapabilities{ Supported: true, ChangeNotifications: true, }, }, }, }, nil } efm-langserver-0.0.53/langserver/handle_shutdown.go000066400000000000000000000004531457604367300224220ustar00rootroot00000000000000package langserver import ( "context" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleShutdown(_ context.Context, conn *jsonrpc2.Conn, _ *jsonrpc2.Request) (result any, err error) { if h.lintTimer != nil { h.lintTimer.Stop() } close(h.request) return nil, conn.Close() } efm-langserver-0.0.53/langserver/handle_text_document_code_action.go000066400000000000000000000114241457604367300257600ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "runtime" "strings" "time" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentCodeAction(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params CodeActionParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } return h.codeAction(params.TextDocument.URI, ¶ms) } func (h *langHandler) executeCommand(params *ExecuteCommandParams) (any, error) { if len(params.Arguments) != 1 { return nil, fmt.Errorf("invalid command") } uri, ok := params.Arguments[0].(string) if !ok { return nil, fmt.Errorf("invalid argument") } fname, _ := fromURI(DocumentURI(uri)) if fname != "" { fname = filepath.ToSlash(fname) if runtime.GOOS == "windows" { fname = strings.ToLower(fname) } } tok := strings.Split(params.Command, "\t") if len(tok) != 3 || tok[0] != "efm-langserver" { return nil, fmt.Errorf("invalid command") } params.Command = tok[1] var command *Command f, ok := h.files[DocumentURI(tok[2])] if !ok { return nil, fmt.Errorf("document not found: %v", uri) } if cfgs, ok := h.configs[f.LanguageID]; ok { loop_lang: for _, cfg := range cfgs { for _, v := range cfg.Commands { if tok[1] == v.Command { command = &v break loop_lang } } } } if command == nil { if command == nil { if cfgs, ok := h.configs[wildcard]; ok { loop_wild: for _, cfg := range cfgs { for _, v := range cfg.Commands { if tok[1] == v.Command { command = &v break loop_wild } } } } } if command == nil { for _, v := range h.commands { if tok[1] == v.Command { command = &v break } } if command == nil { return nil, fmt.Errorf("command not found: %v", params.Command) } } } var cmd *exec.Cmd var args []string var output string if !strings.HasPrefix(command.Command, ":") { if runtime.GOOS == "windows" { args = []string{"/c", replaceCommandInputFilename(command.Command, fname, h.rootPath)} for _, v := range command.Arguments { arg := fmt.Sprint(v) tmp := replaceCommandInputFilename(arg, fname, h.rootPath) if tmp != arg && fname == "" { h.logger.Println("invalid uri") return nil, fmt.Errorf("invalid uri: %v", uri) } arg = tmp args = append(args, arg) } cmd = exec.Command("cmd", args...) } else { args = []string{"-c", replaceCommandInputFilename(command.Command, fname, h.rootPath)} for _, v := range command.Arguments { arg := fmt.Sprint(v) tmp := replaceCommandInputFilename(arg, fname, h.rootPath) if tmp != arg && fname == "" { h.logger.Println("invalid uri") return nil, fmt.Errorf("invalid uri: %v", uri) } arg = tmp args = append(args, arg) } cmd = exec.Command("sh", args...) } cmd.Dir = h.rootPath cmd.Env = os.Environ() b, err := cmd.CombinedOutput() if err != nil { return nil, err } if h.loglevel >= 3 { h.logger.Print(strings.Join(cmd.Args, " ")+":", string(b)) } output = string(b) } else { if command.Command == ":reload-config" { config, err := LoadConfig(h.filename) if err != nil { return nil, err } h.commands = *config.Commands h.configs = *config.Languages h.rootMarkers = *config.RootMarkers h.triggerChars = config.TriggerChars h.loglevel = config.LogLevel h.lintDebounce = time.Duration(config.LintDebounce) } h.logMessage(LogInfo, "Reloaded configuration file") output = "OK" } return output, nil } func filterCommands(uri DocumentURI, commands []Command) []Command { results := []Command{} for _, v := range commands { if v.OS != "" { found := false for _, os := range strings.FieldsFunc(v.OS, func(r rune) bool { return r == ',' }) { if strings.TrimSpace(os) == runtime.GOOS { found = true } } if !found { continue } } results = append(results, Command{ Title: v.Title, Command: fmt.Sprintf("efm-langserver\t%s\t%s", v.Command, string(uri)), Arguments: []any{string(uri)}, }) } return results } func (h *langHandler) codeAction(uri DocumentURI, _ *CodeActionParams) ([]Command, error) { f, ok := h.files[uri] if !ok { return nil, fmt.Errorf("document not found: %v", uri) } commands := []Command{} commands = append(commands, filterCommands(uri, h.commands)...) if cfgs, ok := h.configs[f.LanguageID]; ok { for _, cfg := range cfgs { commands = append(commands, filterCommands(uri, cfg.Commands)...) } } if cfgs, ok := h.configs[wildcard]; ok { for _, cfg := range cfgs { commands = append(commands, filterCommands(uri, cfg.Commands)...) } } return commands, nil } efm-langserver-0.0.53/langserver/handle_text_document_completion.go000066400000000000000000000056011457604367300256620ustar00rootroot00000000000000package langserver import ( "bufio" "bytes" "context" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "runtime" "strings" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentCompletion(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params CompletionParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } return h.completion(params.TextDocument.URI, ¶ms) } func (h *langHandler) completion(uri DocumentURI, params *CompletionParams) ([]CompletionItem, error) { f, ok := h.files[uri] if !ok { return nil, fmt.Errorf("document not found: %v", uri) } fname, err := fromURI(uri) if err != nil { h.logger.Println("invalid uri") return nil, fmt.Errorf("invalid uri: %v: %v", err, uri) } fname = filepath.ToSlash(fname) if runtime.GOOS == "windows" { fname = strings.ToLower(fname) } var configs []Language if cfgs, ok := h.configs[f.LanguageID]; ok { for _, cfg := range cfgs { if cfg.CompletionCommand != "" { configs = append(configs, cfg) } } } if cfgs, ok := h.configs[wildcard]; ok { for _, cfg := range cfgs { if cfg.CompletionCommand != "" { configs = append(configs, cfg) } } } if len(configs) == 0 { if h.loglevel >= 1 { h.logger.Printf("completion for LanguageID not supported: %v", f.LanguageID) } return nil, nil } for _, config := range configs { if config.CompletionCommand == "" { return nil, nil } command := config.CompletionCommand if strings.Contains(command, "${POSITION}") { command = strings.Replace(command, "${POSITION}", fmt.Sprintf("%d:%d", params.TextDocumentPositionParams.Position.Line, params.Position.Character), -1) } if !config.CompletionStdin && !strings.Contains(command, "${INPUT}") { command = command + " ${INPUT}" } command = replaceCommandInputFilename(command, fname, h.rootPath) var cmd *exec.Cmd if runtime.GOOS == "windows" { cmd = exec.Command("cmd", "/c", command) } else { cmd = exec.Command("sh", "-c", command) } cmd.Dir = h.findRootPath(fname, config) cmd.Env = append(os.Environ(), config.Env...) if config.CompletionStdin { cmd.Stdin = strings.NewReader(f.Text) } b, err := cmd.CombinedOutput() if err != nil { h.logger.Printf("completion command failed: %v", err) return nil, fmt.Errorf("completion command failed: %v: %v", err, string(b)) } if h.loglevel >= 3 { h.logger.Println(command+":", string(b)) } result := []CompletionItem{} scanner := bufio.NewScanner(bytes.NewReader(b)) for scanner.Scan() { result = append(result, CompletionItem{ Label: scanner.Text(), InsertText: scanner.Text(), }) } return result, nil } return nil, fmt.Errorf("completion for LanguageID not supported: %v", f.LanguageID) } efm-langserver-0.0.53/langserver/handle_text_document_definition.go000066400000000000000000000100211457604367300256310ustar00rootroot00000000000000package langserver import ( "bufio" "context" "encoding/json" "fmt" "os" "path/filepath" "runtime" "strconv" "strings" "unicode/utf16" "github.com/mattn/go-unicodeclass" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentDefinition(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DocumentDefinitionParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } return h.definition(params.TextDocument.URI, ¶ms) } func (h *langHandler) findTag(fname string, tag string) ([]Location, error) { f, err := os.Open(fname) if err != nil { return nil, err } defer f.Close() locations := []Location{} scanner := bufio.NewScanner(f) for scanner.Scan() { text := scanner.Text() if strings.HasPrefix(text, "!") { continue } token := strings.SplitN(text, "\t", 4) if len(token) < 4 { continue } if token[0] == tag { token[2] = strings.TrimRight(token[2], `;"`) fullpath := filepath.Clean(filepath.Join(h.rootPath, token[1])) b, err := os.ReadFile(fullpath) if err != nil { continue } lines := strings.Split(string(b), "\n") if strings.HasPrefix(token[2], "/") { pattern := token[2] hasPrefix := strings.HasPrefix(pattern, "/^") if hasPrefix { pattern = strings.TrimLeft(pattern, "/^") } hasSuffix := strings.HasSuffix(pattern, "$/") if hasSuffix { pattern = strings.TrimRight(pattern, "$/") } for i, line := range lines { match := false if hasPrefix && hasSuffix && line == pattern { match = true } else if hasPrefix && strings.HasPrefix(line, pattern) { match = true } else if hasSuffix && strings.HasSuffix(line, pattern) { match = true } if match { locations = append(locations, Location{ URI: toURI(fullpath), Range: Range{ Start: Position{Line: i, Character: 0}, End: Position{Line: i, Character: 0}, }, }) } } } else { i, err := strconv.Atoi(token[2]) if err != nil { continue } locations = append(locations, Location{ URI: toURI(fullpath), Range: Range{ Start: Position{Line: i - 1, Character: 0}, End: Position{Line: i - 1, Character: 0}, }, }) } } } return locations, nil } func (h *langHandler) findTagsFile(fname string) string { base := filepath.Clean(filepath.Dir(fname)) for { _, err := os.Stat(filepath.Join(base, "tags")) if err == nil { break } if base == h.rootPath { base = "" break } tmp := filepath.Dir(base) if tmp == "" || tmp == base { base = "" break } base = tmp } return base } func (h *langHandler) definition(uri DocumentURI, params *DocumentDefinitionParams) ([]Location, error) { f, ok := h.files[uri] if !ok { return nil, fmt.Errorf("document not found: %v", uri) } lines := strings.Split(f.Text, "\n") if params.Position.Line < 0 || params.Position.Line > len(lines) { return nil, fmt.Errorf("invalid position: %v", params.Position) } chars := utf16.Encode([]rune(lines[params.Position.Line])) if params.Position.Character < 0 || params.Position.Character > len(chars) { return nil, fmt.Errorf("invalid position: %v", params.Position) } prevPos := 0 currPos := -1 prevCls := unicodeclass.Invalid for i, char := range chars { currCls := unicodeclass.Is(rune(char)) if currCls != prevCls { if i <= params.Position.Character { prevPos = i } else { if char == '_' { continue } currPos = i break } } prevCls = currCls } if currPos == -1 { currPos = len(chars) } tag := string(utf16.Decode(chars[prevPos:currPos])) fname, err := fromURI(uri) if err != nil { return nil, nil } fname = filepath.ToSlash(fname) if runtime.GOOS == "windows" { fname = strings.ToLower(fname) } base := h.findTagsFile(fname) if base == "" { return nil, nil } return h.findTag(filepath.Join(base, "tags"), tag) } efm-langserver-0.0.53/langserver/handle_text_document_definition_test.go000066400000000000000000000015411457604367300266770ustar00rootroot00000000000000package langserver import ( "fmt" "log" "os" "path/filepath" "testing" ) func TestFindTagFile(t *testing.T) { cwd, _ := os.Getwd() cwd = filepath.Clean(filepath.Join(cwd, "..")) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: cwd, } base := h.findTagsFile(filepath.Join(cwd, "testdata/foo/bar")) if base == "" { t.Fatal("tags file must be found") } if base != filepath.Clean(filepath.Join(cwd, "testdata")) { t.Fatal("tags file must be location at testdata/tags") } } func TestFindTag(t *testing.T) { cwd, _ := os.Getwd() cwd = filepath.Clean(filepath.Join(cwd, "..")) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: cwd, } locations, err := h.findTag(filepath.Join(cwd, "testdata/tags"), "langHandler") if err != nil { t.Fatal(err) } fmt.Println(locations) } efm-langserver-0.0.53/langserver/handle_text_document_did_change.go000066400000000000000000000012411457604367300255520ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentDidChange(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DidChangeTextDocumentParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } if len(params.ContentChanges) == 1 { if err := h.updateFile(params.TextDocument.URI, params.ContentChanges[0].Text, ¶ms.TextDocument.Version, eventTypeChange); err != nil { return nil, err } } return nil, nil } efm-langserver-0.0.53/langserver/handle_text_document_did_close.go000066400000000000000000000010441457604367300254330ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentDidClose(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DidCloseTextDocumentParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } if err := h.closeFile(params.TextDocument.URI); err != nil { return nil, err } return nil, nil } efm-langserver-0.0.53/langserver/handle_text_document_did_open.go000066400000000000000000000013711457604367300252720ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentDidOpen(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DidOpenTextDocumentParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } if err := h.openFile(params.TextDocument.URI, params.TextDocument.LanguageID, params.TextDocument.Version); err != nil { return nil, err } if err := h.updateFile(params.TextDocument.URI, params.TextDocument.Text, ¶ms.TextDocument.Version, eventTypeOpen); err != nil { return nil, err } return nil, nil } efm-langserver-0.0.53/langserver/handle_text_document_did_save.go000066400000000000000000000012271457604367300252670ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentDidSave(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DidSaveTextDocumentParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } if params.Text != nil { err = h.updateFile(params.TextDocument.URI, *params.Text, nil, eventTypeSave) } else { err = h.saveFile(params.TextDocument.URI) } if err != nil { return nil, err } return nil, nil } efm-langserver-0.0.53/langserver/handle_text_document_formatting.go000066400000000000000000000136321457604367300256660ustar00rootroot00000000000000package langserver import ( "bytes" "context" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "time" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentFormatting(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DocumentFormattingParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } rng := Range{Position{-1, -1}, Position{-1, -1}} return h.rangeFormatRequest(params.TextDocument.URI, rng, params.Options) } func (h *langHandler) handleTextDocumentRangeFormatting(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result interface{}, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DocumentRangeFormattingParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } return h.rangeFormatRequest(params.TextDocument.URI, params.Range, params.Options) } func (h *langHandler) rangeFormatRequest(uri DocumentURI, rng Range, opt FormattingOptions) ([]TextEdit, error) { if h.formatTimer != nil { if h.loglevel >= 4 { h.logger.Printf("format debounced: %v", h.formatDebounce) } return []TextEdit{}, nil } h.mu.Lock() h.formatTimer = time.AfterFunc(h.formatDebounce, func() { h.mu.Lock() h.formatTimer = nil h.mu.Unlock() }) h.mu.Unlock() return h.rangeFormatting(uri, rng, opt) } func (h *langHandler) rangeFormatting(uri DocumentURI, rng Range, options FormattingOptions) ([]TextEdit, error) { f, ok := h.files[uri] if !ok { return nil, fmt.Errorf("document not found: %v", uri) } fname, err := fromURI(uri) if err != nil { return nil, fmt.Errorf("invalid uri: %v: %v", err, uri) } fname = filepath.ToSlash(fname) if runtime.GOOS == "windows" { fname = strings.ToLower(fname) } var configs []Language if cfgs, ok := h.configs[f.LanguageID]; ok { for _, cfg := range cfgs { if cfg.FormatCommand != "" { if dir := matchRootPath(fname, cfg.RootMarkers); dir == "" && cfg.RequireMarker { continue } configs = append(configs, cfg) } } } if cfgs, ok := h.configs[wildcard]; ok { for _, cfg := range cfgs { if cfg.FormatCommand != "" { configs = append(configs, cfg) } } } if len(configs) == 0 { if h.loglevel >= 1 { h.logger.Printf("format for LanguageID not supported: %v", f.LanguageID) } return nil, nil } originalText := f.Text text := originalText formatted := false Configs: for _, config := range configs { if config.FormatCommand == "" { continue } // File options command := config.FormatCommand if !config.FormatStdin && !strings.Contains(command, "${INPUT}") { command = command + " ${INPUT}" } command = replaceCommandInputFilename(command, fname, h.rootPath) // Formatting Options for placeholder, value := range options { // {--flag:placeholder} => --flag // {--flag=placeholder} => --flag= // {--flag:!placeholder} => --flag if value is false re, err := regexp.Compile(fmt.Sprintf(`\${([^:|^}]+):%s}`, placeholder)) re2, err2 := regexp.Compile(fmt.Sprintf(`\${([^=|^}]+)=%s}`, placeholder)) nre, nerr := regexp.Compile(fmt.Sprintf(`\${([^:|^}]+):!%s}`, placeholder)) nre2, nerr2 := regexp.Compile(fmt.Sprintf(`\${([^=|^}]+)=!%s}`, placeholder)) if err != nil || err2 != nil || nerr != nil || nerr2 != nil { h.logger.Println(command+":", err) continue Configs } switch v := value.(type) { default: command = re.ReplaceAllString(command, fmt.Sprintf("$1 %v", v)) command = re2.ReplaceAllString(command, fmt.Sprintf("$1=%v", v)) case bool: const FLAG = "$1" if v { command = re.ReplaceAllString(command, FLAG) command = re2.ReplaceAllString(command, FLAG) } else { command = nre.ReplaceAllString(command, FLAG) command = nre2.ReplaceAllString(command, FLAG) } } } // Range Options if rng.Start.Line != -1 { charStart := convertRowColToIndex(text, rng.Start.Line, rng.Start.Character) charEnd := convertRowColToIndex(text, rng.End.Line, rng.End.Character) rangeOptions := map[string]int{ "charStart": charStart, "charEnd": charEnd, "rowStart": rng.Start.Line, "colStart": rng.Start.Character, "rowEnd": rng.End.Line, "colEnd": rng.End.Character, } for placeholder, value := range rangeOptions { // {--flag:placeholder} => --flag // {--flag=placeholder} => --flag= re, err := regexp.Compile(fmt.Sprintf(`\${([^:|^}]+):%s}`, placeholder)) re2, err2 := regexp.Compile(fmt.Sprintf(`\${([^=|^}]+)=%s}`, placeholder)) if err != nil || err2 != nil { h.logger.Println(command+":", err) continue Configs } command = re.ReplaceAllString(command, fmt.Sprintf("$1 %d", value)) command = re2.ReplaceAllString(command, fmt.Sprintf("$1=%d", value)) } } // remove unfilled placeholders re := regexp.MustCompile(`\${[^}]*}`) command = re.ReplaceAllString(command, "") // Execute the command var cmd *exec.Cmd if runtime.GOOS == "windows" { cmd = exec.Command("cmd", "/c", command) } else { cmd = exec.Command("sh", "-c", command) } cmd.Dir = h.findRootPath(fname, config) cmd.Env = append(os.Environ(), config.Env...) if config.FormatStdin { cmd.Stdin = strings.NewReader(text) } var buf bytes.Buffer cmd.Stderr = &buf b, err := cmd.Output() if err != nil { h.logger.Println(command+":", buf.String()) continue } formatted = true if h.loglevel >= 3 { h.logger.Println(command+":", string(b)) } text = strings.Replace(string(b), "\r", "", -1) } if formatted { if h.loglevel >= 3 { h.logger.Println("format succeeded") } return ComputeEdits(uri, originalText, text), nil } return nil, fmt.Errorf("format for LanguageID not supported: %v", f.LanguageID) } efm-langserver-0.0.53/langserver/handle_text_document_formatting_test.go000066400000000000000000000017641457604367300267300ustar00rootroot00000000000000package langserver import ( "log" "os" "path/filepath" "testing" ) func TestFormattingRequireRootMatcher(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ "vim": { { LintCommand: `echo ` + file + `:2:No it is normal!`, LintIgnoreExitCode: true, LintAfterOpen: true, LintStdin: true, RequireMarker: true, RootMarkers: []string{".vimlintrc"}, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } rng := Range{Position{-1, -1}, Position{-1, -1}} d, err := h.rangeFormatRequest(uri, rng, FormattingOptions{}) if err != nil { t.Fatal(err) } if len(d) != 0 { t.Fatal("text edits should be zero as we have no root marker for the language but require one", d) } } efm-langserver-0.0.53/langserver/handle_text_document_hover.go000066400000000000000000000067501457604367300246420ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "runtime" "strings" "unicode/utf16" "github.com/mattn/go-unicodeclass" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentHover(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params HoverParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } return h.hover(params.TextDocument.URI, ¶ms) } func (h *langHandler) hover(uri DocumentURI, params *HoverParams) (*Hover, error) { f, ok := h.files[uri] if !ok { return nil, fmt.Errorf("document not found: %v", uri) } fname, err := fromURI(uri) if err != nil { h.logger.Println("invalid uri") return nil, fmt.Errorf("invalid uri: %v: %v", err, uri) } fname = filepath.ToSlash(fname) if runtime.GOOS == "windows" { fname = strings.ToLower(fname) } lines := strings.Split(f.Text, "\n") if params.Position.Line < 0 || params.Position.Line > len(lines) { return nil, fmt.Errorf("invalid position: %v", params.Position) } chars := utf16.Encode([]rune(lines[params.Position.Line])) if params.Position.Character < 0 || params.Position.Character > len(chars) { return nil, fmt.Errorf("invalid position: %v", params.Position) } var configs []Language if cfgs, ok := h.configs[f.LanguageID]; ok { for _, cfg := range cfgs { if cfg.HoverCommand != "" { configs = append(configs, cfg) } } } if cfgs, ok := h.configs[wildcard]; ok { for _, cfg := range cfgs { if cfg.HoverCommand != "" { configs = append(configs, cfg) } } } if len(configs) == 0 { if h.loglevel >= 1 { h.logger.Printf("hover for LanguageID not supported: %v", f.LanguageID) } return nil, nil } for _, config := range configs { prevPos := 0 currPos := -1 prevCls := unicodeclass.Invalid for i, char := range chars { currCls := unicodeclass.Is(rune(char)) if currCls != prevCls { if strings.ContainsRune(config.HoverChars, rune(char)) { continue } if i <= params.Position.Character { prevPos = i } else { currPos = i break } } prevCls = currCls } if currPos == -1 { currPos = len(chars) } word := string(utf16.Decode(chars[prevPos:currPos])) if config.HoverCommand == "" { continue } command := config.HoverCommand if !config.HoverStdin && !strings.Contains(command, "${INPUT}") { command = command + " ${INPUT}" } command = strings.Replace(command, "${INPUT}", word, -1) var cmd *exec.Cmd if runtime.GOOS == "windows" { cmd = exec.Command("cmd", "/c", command) } else { cmd = exec.Command("sh", "-c", command) } cmd.Dir = h.findRootPath(fname, config) cmd.Env = append(os.Environ(), config.Env...) if config.HoverStdin { cmd.Stdin = strings.NewReader(word) } b, err := cmd.CombinedOutput() if err != nil { return nil, err } if h.loglevel >= 3 { h.logger.Println(command+":", string(b)) } var content MarkupContent if config.HoverType == "markdown" { content.Kind = Markdown } else { content.Kind = PlainText } content.Value = strings.TrimSpace(string(b)) return &Hover{ Contents: content, Range: &Range{ Start: Position{ Line: params.Position.Line, Character: prevPos, }, End: Position{ Line: params.Position.Line, Character: currPos, }, }, }, nil } return nil, nil } efm-langserver-0.0.53/langserver/handle_text_document_symbol.go000066400000000000000000000105161457604367300250170ustar00rootroot00000000000000package langserver import ( "bufio" "bytes" "context" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "runtime" "strings" "github.com/reviewdog/errorformat" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleTextDocumentSymbol(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DocumentSymbolParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } return h.symbol(params.TextDocument.URI) } var symbolKindMap = map[string]int{ "file": 1, "module": 2, "namespace": 3, "package": 4, "class": 5, "method": 6, "property": 7, "field": 8, "constructor": 9, "enum": 10, "interface": 11, "function": 12, "variable": 13, "constant": 14, "string": 15, "number": 16, "boolean": 17, "array": 18, "object": 19, "key": 20, "null": 21, "enummember": 22, "struct": 23, "event": 24, "operator": 25, "typeparameter": 26, } func (h *langHandler) symbol(uri DocumentURI) ([]SymbolInformation, error) { f, ok := h.files[uri] if !ok { return nil, fmt.Errorf("document not found: %v", uri) } fname, err := fromURI(uri) if err != nil { h.logger.Println("invalid uri") return nil, fmt.Errorf("invalid uri: %v: %v", err, uri) } fname = filepath.ToSlash(fname) if runtime.GOOS == "windows" { fname = strings.ToLower(fname) } var configs []Language if cfgs, ok := h.configs[f.LanguageID]; ok { for _, cfg := range cfgs { if cfg.SymbolCommand != "" { configs = append(configs, cfg) } } } if cfgs, ok := h.configs[wildcard]; ok { for _, cfg := range cfgs { if cfg.SymbolCommand != "" { configs = append(configs, cfg) } } } if len(configs) == 0 { configs = []Language{ { SymbolCommand: "ctags -x --_xformat=%{input}:%n:1:%K!%N", SymbolFormats: []string{"%f:%l:%c:%m"}, }, } } symbols := []SymbolInformation{} for _, config := range configs { command := config.SymbolCommand if !config.SymbolStdin && !strings.Contains(command, "${INPUT}") { command = command + " ${INPUT}" } command = replaceCommandInputFilename(command, fname, h.rootPath) formats := config.LintFormats if len(formats) == 0 { formats = []string{"%f:%l:%m", "%f:%l:%c:%m"} } efms, err := errorformat.NewErrorformat(formats) if err != nil { h.logger.Println("invalid error-format") return nil, fmt.Errorf("invalid error-format: %v", config.SymbolFormats) } var cmd *exec.Cmd if runtime.GOOS == "windows" { cmd = exec.Command("cmd", "/c", command) } else { cmd = exec.Command("sh", "-c", command) } cmd.Dir = h.findRootPath(fname, config) cmd.Env = append(os.Environ(), config.Env...) if config.SymbolStdin { cmd.Stdin = strings.NewReader(f.Text) } b, err := cmd.CombinedOutput() if err != nil { continue } if h.loglevel >= 3 { h.logger.Println(command+":", string(b)) } scanner := bufio.NewScanner(bytes.NewReader(b)) for scanner.Scan() { for _, ef := range efms.Efms { m := ef.Match(string(scanner.Text())) if m == nil { continue } if config.SymbolStdin && (m.F == "stdin" || m.F == "-" || m.F == "") { m.F = fname } else { m.F = filepath.ToSlash(m.F) } if m.C == 0 { m.C = 1 } path, err := filepath.Abs(m.F) if err != nil { h.logger.Println(err) continue } path = filepath.ToSlash(path) if runtime.GOOS == "windows" { path = strings.ToLower(path) } if path != fname { h.logger.Println(path, fname) continue } token := strings.SplitN(m.M, "!", 2) kind := symbolKindMap["key"] if len(token) == 2 { if tmp, ok := symbolKindMap[strings.ToLower(token[0])]; ok { kind = tmp } } else { token = []string{"", m.M} } symbols = append(symbols, SymbolInformation{ Location: Location{ URI: uri, Range: Range{ Start: Position{Line: m.L - 1 - config.LintOffset, Character: m.C - 1}, End: Position{Line: m.L - 1 - config.LintOffset, Character: m.C - 1}, }, }, Kind: int64(kind), Name: token[1], }) } } } return symbols, nil } efm-langserver-0.0.53/langserver/handle_workspace_did_change_configuration.go000066400000000000000000000021561457604367300276230ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "time" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleWorkspaceDidChangeConfiguration(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DidChangeConfigurationParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } return h.didChangeConfiguration(¶ms.Settings) } func (h *langHandler) didChangeConfiguration(config *Config) (any, error) { if config.Languages != nil { h.configs = *config.Languages } if config.RootMarkers != nil { h.rootMarkers = *config.RootMarkers } if config.TriggerChars != nil { h.triggerChars = config.TriggerChars } if config.Commands != nil { h.commands = *config.Commands } if config.LogLevel > 0 { h.loglevel = config.LogLevel } if config.LintDebounce > 0 { h.lintDebounce = time.Duration(config.LintDebounce) } if config.FormatDebounce > 0 { h.formatDebounce = time.Duration(config.FormatDebounce) } return nil, nil } efm-langserver-0.0.53/langserver/handle_workspace_did_change_workspace_folders.go000066400000000000000000000021751457604367300304710ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleDidChangeWorkspaceWorkspaceFolders(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params DidChangeWorkspaceFoldersParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } return h.didChangeWorkspaceFolders(¶ms) } func (h *langHandler) didChangeWorkspaceFolders(params *DidChangeWorkspaceFoldersParams) (result any, err error) { var folders []string for _, removed := range params.Event.Removed { for _, folder := range h.folders { if toURI(folder) != removed.URI { folders = append(folders, folder) } } } for _, added := range params.Event.Added { found := false for _, folder := range h.folders { if toURI(folder) == added.URI { found = true break } } if !found { if folder, err := fromURI(added.URI); err == nil { folders = append(folders, folder) } } } h.folders = folders return nil, nil } efm-langserver-0.0.53/langserver/handle_workspace_execute_command.go000066400000000000000000000007371457604367300257720ustar00rootroot00000000000000package langserver import ( "context" "encoding/json" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleWorkspaceExecuteCommand(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } var params ExecuteCommandParams if err := json.Unmarshal(*req.Params, ¶ms); err != nil { return nil, err } return h.executeCommand(¶ms) } efm-langserver-0.0.53/langserver/handle_workspace_workspace_folders.go000066400000000000000000000012101457604367300263310ustar00rootroot00000000000000package langserver import ( "context" "path/filepath" "github.com/sourcegraph/jsonrpc2" ) func (h *langHandler) handleWorkspaceWorkspaceFolders(_ context.Context, _ *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { if req.Params == nil { return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeInvalidParams} } return h.workspaceFolders() } func (h *langHandler) workspaceFolders() (result any, err error) { workspaces := []WorkspaceFolder{} for _, workspace := range h.folders { workspaces = append(workspaces, WorkspaceFolder{ URI: toURI(workspace), Name: filepath.Base(workspace), }) } return workspaces, nil } efm-langserver-0.0.53/langserver/handler.go000066400000000000000000000474551457604367300206660ustar00rootroot00000000000000package langserver import ( "bytes" "context" "fmt" "log" "net/url" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "sync" "time" "unicode" "unicode/utf16" "github.com/reviewdog/errorformat" "github.com/sourcegraph/jsonrpc2" "github.com/mattn/go-unicodeclass" ) type eventType int const ( eventTypeChange eventType = iota eventTypeSave eventTypeOpen ) type lintRequest struct { URI DocumentURI EventType eventType } // Config is type Config struct { Version int `yaml:"version"` LogFile string `yaml:"log-file"` LogLevel int `yaml:"log-level" json:"logLevel"` Commands *[]Command `yaml:"commands" json:"commands"` Languages *map[string][]Language `yaml:"languages" json:"languages"` RootMarkers *[]string `yaml:"root-markers" json:"rootMarkers"` TriggerChars []string `yaml:"trigger-chars" json:"triggerChars"` LintDebounce Duration `yaml:"lint-debounce" json:"lintDebounce"` FormatDebounce Duration `yaml:"format-debounce" json:"formatDebounce"` // Toggle support for "go to definition" requests. ProvideDefinition bool `yaml:"provide-definition"` Filename string `yaml:"-"` Logger *log.Logger `yaml:"-"` } // Config1 is type Config1 struct { Version int `yaml:"version"` Logger *log.Logger `yaml:"-"` Commands []Command `yaml:"commands"` Languages map[string]Language `yaml:"languages"` } // Language is type Language struct { Prefix string `yaml:"prefix" json:"prefix"` LintFormats []string `yaml:"lint-formats" json:"lintFormats"` LintStdin bool `yaml:"lint-stdin" json:"lintStdin"` LintOffset int `yaml:"lint-offset" json:"lintOffset"` LintOffsetColumns int `yaml:"lint-offset-columns" json:"lintOffsetColumns"` LintCommand string `yaml:"lint-command" json:"lintCommand"` LintIgnoreExitCode bool `yaml:"lint-ignore-exit-code" json:"lintIgnoreExitCode"` LintCategoryMap map[string]string `yaml:"lint-category-map" json:"lintCategoryMap"` LintSource string `yaml:"lint-source" json:"lintSource"` LintSeverity int `yaml:"lint-severity" json:"lintSeverity"` LintWorkspace bool `yaml:"lint-workspace" json:"lintWorkspace"` LintAfterOpen bool `yaml:"lint-after-open" json:"lintAfterOpen"` LintOnSave bool `yaml:"lint-on-save" json:"lintOnSave"` FormatCommand string `yaml:"format-command" json:"formatCommand"` FormatCanRange bool `yaml:"format-can-range" json:"formatCanRange"` FormatStdin bool `yaml:"format-stdin" json:"formatStdin"` SymbolCommand string `yaml:"symbol-command" json:"symbolCommand"` SymbolStdin bool `yaml:"symbol-stdin" json:"symbolStdin"` SymbolFormats []string `yaml:"symbol-formats" json:"symbolFormats"` CompletionCommand string `yaml:"completion-command" json:"completionCommand"` CompletionStdin bool `yaml:"completion-stdin" json:"completionStdin"` HoverCommand string `yaml:"hover-command" json:"hoverCommand"` HoverStdin bool `yaml:"hover-stdin" json:"hoverStdin"` HoverType string `yaml:"hover-type" json:"hoverType"` HoverChars string `yaml:"hover-chars" json:"hoverChars"` Env []string `yaml:"env" json:"env"` RootMarkers []string `yaml:"root-markers" json:"rootMarkers"` RequireMarker bool `yaml:"require-marker" json:"requireMarker"` Commands []Command `yaml:"commands" json:"commands"` } // NewHandler create JSON-RPC handler for this language server. func NewHandler(config *Config) jsonrpc2.Handler { if config.Logger == nil { config.Logger = log.New(os.Stderr, "", log.LstdFlags) } handler := &langHandler{ loglevel: config.LogLevel, logger: config.Logger, commands: *config.Commands, configs: *config.Languages, provideDefinition: config.ProvideDefinition, files: make(map[DocumentURI]*File), request: make(chan lintRequest), lintDebounce: time.Duration(config.LintDebounce), lintTimer: nil, formatDebounce: time.Duration(config.FormatDebounce), formatTimer: nil, conn: nil, filename: config.Filename, rootMarkers: *config.RootMarkers, triggerChars: config.TriggerChars, lastPublishedURIs: make(map[string]map[DocumentURI]struct{}), } go handler.linter() return jsonrpc2.HandlerWithError(handler.handle) } type langHandler struct { mu sync.Mutex loglevel int logger *log.Logger commands []Command configs map[string][]Language provideDefinition bool files map[DocumentURI]*File request chan lintRequest lintDebounce time.Duration lintTimer *time.Timer formatDebounce time.Duration formatTimer *time.Timer conn *jsonrpc2.Conn rootPath string filename string folders []string rootMarkers []string triggerChars []string // lastPublishedURIs is mapping from LanguageID string to mapping of // whether diagnostics are published in a DocumentURI or not. lastPublishedURIs map[string]map[DocumentURI]struct{} } // File is type File struct { LanguageID string Text string Version int } // WordAt is func (f *File) WordAt(pos Position) string { lines := strings.Split(f.Text, "\n") if pos.Line < 0 || pos.Line >= len(lines) { return "" } chars := utf16.Encode([]rune(lines[pos.Line])) if pos.Character < 0 || pos.Character > len(chars) { return "" } prevPos := 0 currPos := -1 prevCls := unicodeclass.Invalid for i, char := range chars { currCls := unicodeclass.Is(rune(char)) if currCls != prevCls { if i <= pos.Character { prevPos = i } else { if char == '_' { continue } currPos = i break } } prevCls = currCls } if currPos == -1 { currPos = len(chars) } return string(utf16.Decode(chars[prevPos:currPos])) } func isWindowsDrivePath(path string) bool { if len(path) < 4 { return false } return unicode.IsLetter(rune(path[0])) && path[1] == ':' } func isWindowsDriveURI(uri string) bool { if len(uri) < 4 { return false } return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':' } func fromURI(uri DocumentURI) (string, error) { u, err := url.ParseRequestURI(string(uri)) if err != nil { return "", err } if u.Scheme != "file" { return "", fmt.Errorf("only file URIs are supported, got %v", u.Scheme) } if isWindowsDriveURI(u.Path) { u.Path = u.Path[1:] } return u.Path, nil } func toURI(path string) DocumentURI { if isWindowsDrivePath(path) { path = "/" + path } return DocumentURI((&url.URL{ Scheme: "file", Path: filepath.ToSlash(path), }).String()) } func (h *langHandler) lintRequest(uri DocumentURI, eventType eventType) { if h.lintTimer != nil { h.lintTimer.Reset(h.lintDebounce) return } h.lintTimer = time.AfterFunc(h.lintDebounce, func() { h.lintTimer = nil h.request <- lintRequest{URI: uri, EventType: eventType} }) } func (h *langHandler) logMessage(typ MessageType, message string) { h.conn.Notify( context.Background(), "window/logMessage", &LogMessageParams{ Type: typ, Message: message, }) } func (h *langHandler) linter() { running := make(map[DocumentURI]context.CancelFunc) for { lintReq, ok := <-h.request if !ok { break } cancel, ok := running[lintReq.URI] if ok { cancel() } ctx, cancel := context.WithCancel(context.Background()) running[lintReq.URI] = cancel go func() { uriToDiagnostics, err := h.lint(ctx, lintReq.URI, lintReq.EventType) if err != nil { h.logger.Println(err) return } for diagURI, diagnostics := range uriToDiagnostics { if diagURI == "file:" { diagURI = lintReq.URI } version := 0 if _, ok := h.files[lintReq.URI]; ok { version = h.files[lintReq.URI].Version } h.conn.Notify( ctx, "textDocument/publishDiagnostics", &PublishDiagnosticsParams{ URI: diagURI, Diagnostics: diagnostics, Version: version, }) } }() } } func matchRootPath(fname string, markers []string) string { dir := filepath.Dir(filepath.Clean(fname)) var prev string for dir != prev { files, _ := os.ReadDir(dir) for _, file := range files { name := file.Name() isDir := file.IsDir() for _, marker := range markers { if strings.HasSuffix(marker, "/") { if !isDir { continue } marker = strings.TrimRight(marker, "/") if ok, _ := filepath.Match(marker, name); ok { return dir } } else { if isDir { continue } if ok, _ := filepath.Match(marker, name); ok { return dir } } } } prev = dir dir = filepath.Dir(dir) } return "" } func (h *langHandler) findRootPath(fname string, lang Language) string { if dir := matchRootPath(fname, lang.RootMarkers); dir != "" { return dir } if dir := matchRootPath(fname, h.rootMarkers); dir != "" { return dir } for _, folder := range h.folders { if len(fname) > len(folder) && strings.EqualFold(fname[:len(folder)], folder) { return folder } } return h.rootPath } func isFilename(s string) bool { switch s { case "stdin", "-", "", "": return true default: return false } } func (h *langHandler) lint(ctx context.Context, uri DocumentURI, eventType eventType) (map[DocumentURI][]Diagnostic, error) { f, ok := h.files[uri] if !ok { return nil, fmt.Errorf("document not found: %v", uri) } fname, err := fromURI(uri) if err != nil { return nil, fmt.Errorf("invalid uri: %v: %v", err, uri) } fname = filepath.ToSlash(fname) var configs []Language if cfgs, ok := h.configs[f.LanguageID]; ok { for _, cfg := range cfgs { // if we require markers and find that they dont exist we do not add the configuration if dir := matchRootPath(fname, cfg.RootMarkers); dir == "" && cfg.RequireMarker == true { continue } switch eventType { case eventTypeOpen: // if LintAfterOpen is not true, ignore didOpen if !cfg.LintAfterOpen { continue } case eventTypeChange: // if LintOnSave is true, ignore didChange if cfg.LintOnSave { continue } default: } if cfg.LintCommand != "" { configs = append(configs, cfg) } } } if cfgs, ok := h.configs[wildcard]; ok { for _, cfg := range cfgs { if cfg.LintCommand != "" { configs = append(configs, cfg) } } } if len(configs) == 0 { if h.loglevel >= 1 { h.logger.Printf("lint for LanguageID not supported: %v", f.LanguageID) } return map[DocumentURI][]Diagnostic{}, nil } uriToDiagnostics := map[DocumentURI][]Diagnostic{ uri: {}, } publishedURIs := make(map[DocumentURI]struct{}) for i, config := range configs { // To publish empty diagnostics when errors are fixed if config.LintWorkspace { for lastPublishedURI := range h.lastPublishedURIs[f.LanguageID] { if _, ok := uriToDiagnostics[lastPublishedURI]; !ok { uriToDiagnostics[lastPublishedURI] = []Diagnostic{} } } } if config.LintCommand == "" { continue } command := config.LintCommand if !config.LintStdin && !config.LintWorkspace && !strings.Contains(command, "${INPUT}") { command = command + " ${INPUT}" } rootPath := h.findRootPath(fname, config) command = replaceCommandInputFilename(command, fname, rootPath) formats := config.LintFormats if len(formats) == 0 { formats = []string{"%f:%l:%m", "%f:%l:%c:%m"} } efms, err := errorformat.NewErrorformat(formats) if err != nil { return nil, fmt.Errorf("invalid error-format: %v", config.LintFormats) } var cmd *exec.Cmd if runtime.GOOS == "windows" { cmd = exec.CommandContext(ctx, "cmd", "/c", command) } else { cmd = exec.CommandContext(ctx, "sh", "-c", command) } cmd.Dir = rootPath cmd.Env = append(os.Environ(), config.Env...) if config.LintStdin { cmd.Stdin = strings.NewReader(f.Text) } b, err := cmd.CombinedOutput() if err != nil { if succeeded(err) { return nil, nil } } // Most of lint tools exit with non-zero value. But some commands // return with zero value. We can not handle the output is real result // or output of usage. So efm-langserver ignore that command exiting // with zero-value. So if you want to handle the command which exit // with zero value, please specify lint-ignore-exit-code. if err == nil && !config.LintIgnoreExitCode { h.logMessage(LogError, "command `"+command+"` exit with zero. probably you forgot to specify `lint-ignore-exit-code: true`.") continue } if h.loglevel >= 3 { h.logger.Println(command+":", string(b)) } var source *string if config.LintSource != "" { source = &configs[i].LintSource } var prefix string if config.Prefix != "" { prefix = fmt.Sprintf("[%s] ", config.Prefix) } scanner := efms.NewScanner(bytes.NewReader(b)) for scanner.Scan() { entry := scanner.Entry() if !entry.Valid { continue } if config.LintStdin && isFilename(entry.Filename) { entry.Filename = fname path, err := filepath.Abs(entry.Filename) if err != nil { continue } path = filepath.ToSlash(path) if runtime.GOOS == "windows" && strings.ToLower(path) != strings.ToLower(fname) { continue } else if path != fname { continue } } else { entry.Filename = filepath.ToSlash(entry.Filename) } word := "" // entry.Col is expected to be one based, if the linter returns zero based we // have the ability to add an offset here. // We only add the offset if the linter reports entry.Col > 0 because 0 means the whole line if config.LintOffsetColumns > 0 && entry.Col > 0 { entry.Col = entry.Col + config.LintOffsetColumns } if entry.Lnum == 0 { entry.Lnum = 1 // entry.Lnum == 0 indicates the top line, set to 1 because it is subtracted later } if entry.Col == 0 { entry.Col = 1 // entry.Col == 0 indicates the whole line without column, set to 1 because it is subtracted later } else { word = f.WordAt(Position{Line: entry.Lnum - 1 - config.LintOffset, Character: entry.Col - 1}) } // we allow the config to provide a mapping between LSP types E,W,I,N and whatever categories the linter has if len(config.LintCategoryMap) > 0 { entry.Type = []rune(config.LintCategoryMap[string(entry.Type)])[0] } severity := 1 if config.LintSeverity != 0 { severity = config.LintSeverity } switch entry.Type { case 'E', 'e': severity = 1 case 'W', 'w': severity = 2 case 'I', 'i': severity = 3 case 'N', 'n': severity = 4 } diagURI := uri if entry.Filename != "" { if filepath.IsAbs(entry.Filename) { diagURI = toURI(entry.Filename) } else { diagURI = toURI(filepath.Join(rootPath, entry.Filename)) } } if runtime.GOOS == "windows" { if strings.ToLower(string(diagURI)) != strings.ToLower(string(uri)) && !config.LintWorkspace { continue } } else { if diagURI != uri && !config.LintWorkspace { continue } } if config.LintWorkspace { publishedURIs[diagURI] = struct{}{} } uriToDiagnostics[diagURI] = append(uriToDiagnostics[diagURI], Diagnostic{ Range: Range{ Start: Position{Line: entry.Lnum - 1 - config.LintOffset, Character: entry.Col - 1}, End: Position{Line: entry.Lnum - 1 - config.LintOffset, Character: entry.Col - 1 + len([]rune(word))}, }, Code: itoaPtrIfNotZero(entry.Nr), Message: prefix + entry.Text, Severity: severity, Source: source, }) } } // Update state here as no possibility of cancelation for _, config := range configs { if config.LintWorkspace { h.lastPublishedURIs[f.LanguageID] = publishedURIs break } } return uriToDiagnostics, nil } func itoaPtrIfNotZero(n int) *string { if n == 0 { return nil } s := strconv.Itoa(n) return &s } func (h *langHandler) closeFile(uri DocumentURI) error { delete(h.files, uri) return nil } func (h *langHandler) saveFile(uri DocumentURI) error { h.lintRequest(uri, eventTypeSave) return nil } func (h *langHandler) openFile(uri DocumentURI, languageID string, version int) error { f := &File{ Text: "", LanguageID: languageID, Version: version, } h.files[uri] = f return nil } func (h *langHandler) updateFile(uri DocumentURI, text string, version *int, eventType eventType) error { f, ok := h.files[uri] if !ok { return fmt.Errorf("document not found: %v", uri) } f.Text = text if version != nil { f.Version = *version } h.lintRequest(uri, eventType) return nil } func (h *langHandler) configFor(uri DocumentURI) []Language { f, ok := h.files[uri] if !ok { return []Language{} } c, ok := h.configs[f.LanguageID] if !ok { return []Language{} } return c } func (h *langHandler) addFolder(folder string) { folder = filepath.Clean(folder) found := false for _, cur := range h.folders { if cur == folder { found = true break } } if !found { h.folders = append(h.folders, folder) } } func (h *langHandler) handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (result any, err error) { switch req.Method { case "initialize": return h.handleInitialize(ctx, conn, req) case "initialized": return case "shutdown": return h.handleShutdown(ctx, conn, req) case "textDocument/didOpen": return h.handleTextDocumentDidOpen(ctx, conn, req) case "textDocument/didChange": return h.handleTextDocumentDidChange(ctx, conn, req) case "textDocument/didSave": return h.handleTextDocumentDidSave(ctx, conn, req) case "textDocument/didClose": return h.handleTextDocumentDidClose(ctx, conn, req) case "textDocument/formatting": return h.handleTextDocumentFormatting(ctx, conn, req) case "textDocument/rangeFormatting": return h.handleTextDocumentRangeFormatting(ctx, conn, req) case "textDocument/documentSymbol": return h.handleTextDocumentSymbol(ctx, conn, req) case "textDocument/completion": return h.handleTextDocumentCompletion(ctx, conn, req) case "textDocument/definition": return h.handleTextDocumentDefinition(ctx, conn, req) case "textDocument/hover": return h.handleTextDocumentHover(ctx, conn, req) case "textDocument/codeAction": return h.handleTextDocumentCodeAction(ctx, conn, req) case "workspace/executeCommand": return h.handleWorkspaceExecuteCommand(ctx, conn, req) case "workspace/didChangeConfiguration": return h.handleWorkspaceDidChangeConfiguration(ctx, conn, req) case "workspace/didChangeWorkspaceFolders": return h.handleDidChangeWorkspaceWorkspaceFolders(ctx, conn, req) case "workspace/workspaceFolders": return h.handleWorkspaceWorkspaceFolders(ctx, conn, req) } return nil, &jsonrpc2.Error{Code: jsonrpc2.CodeMethodNotFound, Message: fmt.Sprintf("method not supported: %s", req.Method)} } func replaceCommandInputFilename(command, fname, rootPath string) string { ext := filepath.Ext(fname) ext = strings.TrimPrefix(ext, ".") command = strings.Replace(command, "${INPUT}", escapeBrackets(fname), -1) command = strings.Replace(command, "${FILEEXT}", ext, -1) command = strings.Replace(command, "${FILENAME}", escapeBrackets(filepath.FromSlash(fname)), -1) command = strings.Replace(command, "${ROOT}", escapeBrackets(rootPath), -1) return command } func escapeBrackets(path string) string { path = strings.Replace(path, "(", `\(`, -1) path = strings.Replace(path, ")", `\)`, -1) return path } func succeeded(err error) bool { exitErr, ok := err.(*exec.ExitError) // When the context is canceled, the process is killed, // and the exit code is -1 return ok && exitErr.ExitCode() < 0 } efm-langserver-0.0.53/langserver/handler_test.go000066400000000000000000000426421457604367300217160ustar00rootroot00000000000000package langserver import ( "context" "errors" "log" "os" "path/filepath" "strings" "testing" "time" ) func TestLintNoLinter(t *testing.T) { h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), configs: map[string][]Language{}, files: map[DocumentURI]*File{ DocumentURI("file:///foo"): {}, }, } _, err := h.lint(context.Background(), "file:///foo", eventTypeChange) if err != nil { t.Fatal("Should not be an error if no linters") } } func TestLintNoFileMatched(t *testing.T) { h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), configs: map[string][]Language{}, files: map[DocumentURI]*File{ DocumentURI("file:///foo"): {}, }, } _, err := h.lint(context.Background(), "file:///bar", eventTypeChange) if err == nil { t.Fatal("Should be an error if no linters") } } func TestLintFileMatched(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ "vim": { { LintCommand: `echo ` + file + `:2:No it is normal!`, LintIgnoreExitCode: true, LintStdin: true, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } uriToDiag, err := h.lint(context.Background(), uri, eventTypeChange) d := uriToDiag[uri] if err != nil { t.Fatal(err) } if len(d) != 1 { t.Fatal("diagnostics should be only one", d) } if d[0].Range.Start.Line != 1 { t.Fatalf("range.start.line should be %v but got: %v", 1, d[0].Range.Start.Line) } if d[0].Range.Start.Character != 0 { t.Fatalf("range.start.character should be %v but got: %v", 0, d[0].Range.Start.Character) } if d[0].Severity != 1 { t.Fatalf("severity should be %v but got: %v", 0, d[0].Severity) } if strings.TrimSpace(d[0].Message) != "No it is normal!" { t.Fatalf("message should be %q but got: %q", "No it is normal!", strings.TrimSpace(d[0].Message)) } } func TestLintFileMatchedForce(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ wildcard: { { LintCommand: `echo ` + file + `:2:No it is normal!`, LintIgnoreExitCode: true, LintStdin: true, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } uriToDiag, err := h.lint(context.Background(), uri, eventTypeChange) d := uriToDiag[uri] if err != nil { t.Fatal(err) } if len(d) != 1 { t.Fatal("diagnostics should be only one") } if d[0].Range.Start.Line != 1 { t.Fatalf("range.start.line should be %v but got: %v", 1, d[0].Range.Start.Line) } if d[0].Range.Start.Character != 0 { t.Fatalf("range.start.character should be %v but got: %v", 0, d[0].Range.Start.Character) } if d[0].Severity != 1 { t.Fatalf("severity should be %v but got: %v", 0, d[0].Severity) } if strings.TrimSpace(d[0].Message) != "No it is normal!" { t.Fatalf("message should be %q but got: %q", "No it is normal!", strings.TrimSpace(d[0].Message)) } } // column 0 remains unchanged, regardles of the configured offset // column 0 indicates a whole line (although for 0-based column linters we can not distinguish between word starting at 0 and the whole line) func TestLintOffsetColumnsZero(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ wildcard: { { LintCommand: `echo ` + file + `:2:0:msg`, LintFormats: []string{"%f:%l:%c:%m"}, LintIgnoreExitCode: true, LintStdin: true, LintOffsetColumns: 1, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } uriToDiag, err := h.lint(context.Background(), uri, eventTypeChange) d := uriToDiag[uri] if err != nil { t.Fatal(err) } if len(d) != 1 { t.Fatal("diagnostics should be only one") } if d[0].Range.Start.Character != 0 { t.Fatalf("range.start.character should be %v but got: %v", 0, d[0].Range.Start.Character) } } // without column offset, 1-based columns are assumed, which means that we should get 0 for column 1 // as LSP assumes 0-based columns func TestLintOffsetColumnsNoOffset(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ wildcard: { { LintCommand: `echo ` + file + `:2:1:msg`, LintFormats: []string{"%f:%l:%c:%m"}, LintIgnoreExitCode: true, LintStdin: true, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } uriToDiag, err := h.lint(context.Background(), uri, eventTypeChange) d := uriToDiag[uri] if err != nil { t.Fatal(err) } if len(d) != 1 { t.Fatal("diagnostics should be only one") } if d[0].Range.Start.Character != 0 { t.Fatalf("range.start.character should be %v but got: %v", 0, d[0].Range.Start.Character) } } // for column 1 with offset we should get column 1 back // without the offset efm would subtract 1 as it expects 1 based columns func TestLintOffsetColumnsNonZero(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ wildcard: { { LintCommand: `echo ` + file + `:2:1:msg`, LintFormats: []string{"%f:%l:%c:%m"}, LintIgnoreExitCode: true, LintStdin: true, LintOffsetColumns: 1, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } uriToDiag, err := h.lint(context.Background(), uri, eventTypeChange) d := uriToDiag[uri] if err != nil { t.Fatal(err) } if len(d) != 1 { t.Fatal("diagnostics should be only one") } if d[0].Range.Start.Character != 1 { t.Fatalf("range.start.character should be %v but got: %v", 1, d[0].Range.Start.Character) } } func TestLintCategoryMap(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) mapping := make(map[string]string) mapping["R"] = "I" // pylint refactoring to info formats := []string{"%f:%l:%c:%t:%m"} h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ wildcard: { { LintCommand: `echo ` + file + `:2:1:R:No it is normal!`, LintIgnoreExitCode: true, LintStdin: true, LintFormats: formats, LintCategoryMap: mapping, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } uriToDiag, err := h.lint(context.Background(), uri, eventTypeChange) d := uriToDiag[uri] if err != nil { t.Fatal(err) } if len(d) != 1 { t.Fatal("diagnostics should be only one") } if d[0].Severity != 3 { t.Fatalf("Severity should be %v but is: %v", 3, d[0].Severity) } } // Test if lint is executed if required root markers for the language are missing func TestLintRequireRootMarker(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ "vim": { { LintCommand: `echo ` + file + `:2:No it is normal!`, LintIgnoreExitCode: true, LintStdin: true, RequireMarker: true, RootMarkers: []string{".vimlintrc"}, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } d, err := h.lint(context.Background(), uri, eventTypeChange) if err != nil { t.Fatal(err) } if len(d) != 0 { t.Fatal("diagnostics should be zero as we have no root marker for the language but require one", d) } } // Test if lint can return diagnostics for multiple files func TestLintMultipleFiles(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") file2 := filepath.Join(base, "bar") uri := toURI(file) uri2 := toURI(file2) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ "vim": { { LintCommand: `echo ` + file + `:2:1:First file! && echo ` + file2 + `:1:2:Second file!`, LintFormats: []string{"%f:%l:%c:%m"}, LintIgnoreExitCode: true, LintWorkspace: true, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, uri2: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, lastPublishedURIs: make(map[string]map[DocumentURI]struct{}), } d, err := h.lint(context.Background(), uri, eventTypeChange) if err != nil { t.Fatal(err) } if len(d) != 2 { t.Fatalf("diagnostics should be two, but got %#v", d) } if d[uri][0].Range.Start.Character != 0 { t.Fatalf("first range.start.character should be %v but got: %v", 0, d[uri][0].Range.Start.Character) } if d[uri][0].Range.Start.Line != 1 { t.Fatalf("first range.start.line should be %v but got: %v", 1, d[uri][0].Range.Start.Line) } if d[uri2][0].Range.Start.Character != 1 { t.Fatalf("second range.start.character should be %v but got: %v", 1, d[uri2][0].Range.Start.Character) } if d[uri2][0].Range.Start.Line != 0 { t.Fatalf("second range.start.line should be %v but got: %v", 0, d[uri2][0].Range.Start.Line) } h.configs["vim"][0].LintCommand = `echo ` + file + `:2:1:First file only!` d, err = h.lint(context.Background(), uri, eventTypeChange) if err != nil { t.Fatal(err) } if len(d) != 2 { t.Fatalf("diagnostics should be two, but got %#v", d) } if d[uri][0].Range.Start.Character != 0 { t.Fatalf("first range.start.character should be %v but got: %v", 0, d[uri][0].Range.Start.Character) } if d[uri][0].Range.Start.Line != 1 { t.Fatalf("first range.start.line should be %v but got: %v", 1, d[uri][0].Range.Start.Line) } if len(d[uri2]) != 0 { t.Fatalf("second diagnostics should be empty but got: %v", d[uri2]) } } // Test if lint can return diagnostics for multiple files even when cancelled func TestLintMultipleFilesWithCancel(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") file2 := filepath.Join(base, "bar") uri := toURI(file) uri2 := toURI(file2) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ "vim": { { LintCommand: `echo ` + file + `:2:1:First file! && echo ` + file2 + `:1:2:Second file! && echo ` + file2 + `:Empty l and c!`, LintFormats: []string{"%f:%l:%c:%m", "%f:%m"}, LintIgnoreExitCode: true, LintWorkspace: true, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, uri2: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, lastPublishedURIs: make(map[string]map[DocumentURI]struct{}), } d, err := h.lint(context.Background(), uri, eventTypeChange) if err != nil { t.Fatal(err) } if len(d) != 2 { t.Fatalf("diagnostics should be two, but got %#v", d) } if d[uri][0].Range.Start.Character != 0 { t.Fatalf("first range.start.character should be %v but got: %v", 0, d[uri][0].Range.Start.Character) } if d[uri][0].Range.Start.Line != 1 { t.Fatalf("first range.start.line should be %v but got: %v", 1, d[uri][0].Range.Start.Line) } if d[uri2][0].Range.Start.Character != 1 { t.Fatalf("second range.start.character should be %v but got: %v", 1, d[uri2][0].Range.Start.Character) } if d[uri2][0].Range.Start.Line != 0 { t.Fatalf("second range.start.line should be %v but got: %v", 0, d[uri2][0].Range.Start.Line) } if d[uri2][1].Range.Start.Character != 0 { t.Fatalf("second range.start.character should be %v but got: %v", 0, d[uri2][1].Range.Start.Character) } if d[uri2][1].Range.Start.Line != 0 { t.Fatalf("second range.start.line should be %v but got: %v", 0, d[uri2][1].Range.Start.Line) } startedFlagPath := "already-started" defer os.Remove(startedFlagPath) // Emulate heavy job h.configs["vim"][0].LintCommand = `touch ` + startedFlagPath + ` && sleep 1000000 && echo ` + file + `:2:1:First file only!` ctx, cancel := context.WithCancel(context.Background()) go func() { h.lint(ctx, uri, eventTypeChange) }() for true { if _, err := os.Stat(startedFlagPath); errors.Is(err, os.ErrNotExist) { time.Sleep(50 * time.Microsecond) continue } break } cancel() h.configs["vim"][0].LintCommand = `echo ` + file + `:2:1:First file only!` d, err = h.lint(context.Background(), uri, eventTypeChange) if err != nil { t.Fatal(err) } if len(d) != 2 { t.Fatalf("diagnostics should be two, but got %#v", d) } if d[uri][0].Range.Start.Character != 0 { t.Fatalf("first range.start.character should be %v but got: %v", 0, d[uri][0].Range.Start.Character) } if d[uri][0].Range.Start.Line != 1 { t.Fatalf("first range.start.line should be %v but got: %v", 1, d[uri][0].Range.Start.Line) } if len(d[uri2]) != 0 { t.Fatalf("second diagnostics should be empty but got: %v", d[uri2]) } } func TestLintNoDiagnostics(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ "vim": { { LintCommand: "echo ", LintIgnoreExitCode: true, LintStdin: true, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } uriToDiag, err := h.lint(context.Background(), uri, eventTypeChange) if err != nil { t.Fatal(err) } d, ok := uriToDiag[uri] if !ok { t.Fatal("didn't get any diagnostics") } if len(d) != 0 { t.Fatal("diagnostics should be an empty list", d) } } func TestLintOnSave(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ "vim": { { LintCommand: `echo ` + file + `:2:No it is normal!`, LintIgnoreExitCode: true, LintStdin: true, LintOnSave: true, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: "scriptencoding utf-8\nabnormal!\n", }, }, } uriToDiag, err := h.lint(context.Background(), uri, eventTypeChange) if err != nil { t.Fatal(err) } d := uriToDiag[uri] if len(d) != 0 { t.Fatal("diagnostics should be empty", d) } uriToDiag, err = h.lint(context.Background(), uri, eventTypeSave) if err != nil { t.Fatal(err) } d = uriToDiag[uri] if len(d) != 1 { t.Fatal("diagnostics should be only one", d) } if d[0].Range.Start.Line != 1 { t.Fatalf("range.start.line should be %v but got: %v", 1, d[0].Range.Start.Line) } if d[0].Range.Start.Character != 0 { t.Fatalf("range.start.character should be %v but got: %v", 0, d[0].Range.Start.Character) } if d[0].Severity != 1 { t.Fatalf("severity should be %v but got: %v", 0, d[0].Severity) } if strings.TrimSpace(d[0].Message) != "No it is normal!" { t.Fatalf("message should be %q but got: %q", "No it is normal!", strings.TrimSpace(d[0].Message)) } } func TestHover(t *testing.T) { base, _ := os.Getwd() file := filepath.Join(base, "foo") uri := toURI(file) text := "test_test-test.test" for scenario, config := range map[string]struct { text string position Position hoverChars string expected string }{ "_ is default": { text, Position{ Line: 0, Character: 0, }, "_", "test_test", }, "with -": { text, Position{ Line: 0, Character: 0, }, "_-", "test_test-test", }, "with - and .": { text, Position{ Line: 0, Character: 0, }, "_-.", "test_test-test.test", }, "inner position": { text, Position{ Line: 0, Character: 4, }, "_-.", "test_test-test.test", }, } { h := &langHandler{ logger: log.New(log.Writer(), "", log.LstdFlags), rootPath: base, configs: map[string][]Language{ "vim": { { HoverCommand: "echo ${INPUT}", HoverChars: config.hoverChars, }, }, }, files: map[DocumentURI]*File{ uri: { LanguageID: "vim", Text: config.text, }, }, } t.Run(scenario, func(t *testing.T) { hover, err := h.hover(uri, &HoverParams{ TextDocumentPositionParams{ TextDocument: TextDocumentIdentifier{uri}, Position: config.position, }, }) if err != nil { t.Fatal(err) } content := hover.Contents.(MarkupContent).Value if content != config.expected { t.Fatal("invalid hover contents:", content+",", "exptected:", config.expected) } }) } } efm-langserver-0.0.53/langserver/lsp.go000066400000000000000000000316731457604367300200420ustar00rootroot00000000000000package langserver const wildcard = "=" // DocumentURI is type DocumentURI string // InitializeParams is type InitializeParams struct { ProcessID int `json:"processId,omitempty"` RootURI DocumentURI `json:"rootUri,omitempty"` InitializationOptions *InitializeOptions `json:"initializationOptions,omitempty"` Capabilities ClientCapabilities `json:"capabilities,omitempty"` Trace string `json:"trace,omitempty"` } // InitializeOptions is type InitializeOptions struct { DocumentFormatting bool `json:"documentFormatting"` RangeFormatting bool `json:"documentRangeFormatting"` Hover bool `json:"hover"` DocumentSymbol bool `json:"documentSymbol"` CodeAction bool `json:"codeAction"` Completion bool `json:"completion"` } // ClientCapabilities is type ClientCapabilities struct{} // InitializeResult is type InitializeResult struct { Capabilities ServerCapabilities `json:"capabilities,omitempty"` } // MessageType is type MessageType int // LogError is const ( _ MessageType = iota LogError LogWarning LogInfo LogLog ) // TextDocumentSyncKind is type TextDocumentSyncKind int // TDSKNone is const ( TDSKNone TextDocumentSyncKind = iota TDSKFull TDSKIncremental ) // CompletionProvider is type CompletionProvider struct { ResolveProvider bool `json:"resolveProvider,omitempty"` TriggerCharacters []string `json:"triggerCharacters"` } // WorkspaceFoldersServerCapabilities is type WorkspaceFoldersServerCapabilities struct { Supported bool `json:"supported"` ChangeNotifications bool `json:"changeNotifications"` } // ServerCapabilitiesWorkspace is type ServerCapabilitiesWorkspace struct { WorkspaceFolders WorkspaceFoldersServerCapabilities `json:"workspaceFolders"` } // ServerCapabilities is type ServerCapabilities struct { TextDocumentSync TextDocumentSyncKind `json:"textDocumentSync,omitempty"` DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` CompletionProvider *CompletionProvider `json:"completionProvider,omitempty"` DefinitionProvider bool `json:"definitionProvider,omitempty"` DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` RangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` HoverProvider bool `json:"hoverProvider,omitempty"` CodeActionProvider bool `json:"codeActionProvider,omitempty"` Workspace *ServerCapabilitiesWorkspace `json:"workspace,omitempty"` } // TextDocumentItem is type TextDocumentItem struct { URI DocumentURI `json:"uri"` LanguageID string `json:"languageId"` Version int `json:"version"` Text string `json:"text"` } // VersionedTextDocumentIdentifier is type VersionedTextDocumentIdentifier struct { TextDocumentIdentifier Version int `json:"version"` } // TextDocumentIdentifier is type TextDocumentIdentifier struct { URI DocumentURI `json:"uri"` } // DidOpenTextDocumentParams is type DidOpenTextDocumentParams struct { TextDocument TextDocumentItem `json:"textDocument"` } // DidCloseTextDocumentParams is type DidCloseTextDocumentParams struct { TextDocument TextDocumentIdentifier `json:"textDocument"` } // TextDocumentContentChangeEvent is type TextDocumentContentChangeEvent struct { Range Range `json:"range"` RangeLength int `json:"rangeLength"` Text string `json:"text"` } // DidChangeTextDocumentParams is type DidChangeTextDocumentParams struct { TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"` } // DidSaveTextDocumentParams is type DidSaveTextDocumentParams struct { Text *string `json:"text"` TextDocument TextDocumentIdentifier `json:"textDocument"` } // TextDocumentPositionParams is type TextDocumentPositionParams struct { TextDocument TextDocumentIdentifier `json:"textDocument"` Position Position `json:"position"` } // CompletionParams is type CompletionParams struct { TextDocumentPositionParams CompletionContext CompletionContext `json:"contentChanges"` } // CompletionContext is type CompletionContext struct { TriggerKind int `json:"triggerKind"` TriggerCharacter *string `json:"triggerCharacter"` } // HoverParams is type HoverParams struct { TextDocumentPositionParams } // Location is type Location struct { URI DocumentURI `json:"uri"` Range Range `json:"range"` } // Range is type Range struct { Start Position `json:"start"` End Position `json:"end"` } // Position is type Position struct { Line int `json:"line"` Character int `json:"character"` } // DiagnosticRelatedInformation is type DiagnosticRelatedInformation struct { Location Location `json:"location"` Message string `json:"message"` } // Diagnostic is type Diagnostic struct { Range Range `json:"range"` Severity int `json:"severity,omitempty"` Code *string `json:"code,omitempty"` Source *string `json:"source,omitempty"` Message string `json:"message"` RelatedInformation []DiagnosticRelatedInformation `json:"relatedInformation,omitempty"` } // PublishDiagnosticsParams is type PublishDiagnosticsParams struct { URI DocumentURI `json:"uri"` Diagnostics []Diagnostic `json:"diagnostics"` Version int `json:"version"` } // FormattingOptions is type FormattingOptions map[string]any // DocumentFormattingParams is type DocumentFormattingParams struct { TextDocument TextDocumentIdentifier `json:"textDocument"` Options FormattingOptions `json:"options"` } // DocumentRangeFormattingParams is type DocumentRangeFormattingParams struct { TextDocument TextDocumentIdentifier `json:"textDocument"` Range Range `json:"range"` Options FormattingOptions `json:"options"` } // TextEdit is type TextEdit struct { Range Range `json:"range"` NewText string `json:"newText"` } // DocumentSymbolParams is type DocumentSymbolParams struct { TextDocument TextDocumentIdentifier `json:"textDocument"` } // SymbolInformation is type SymbolInformation struct { Name string `json:"name"` Kind int64 `json:"kind"` Deprecated bool `json:"deprecated"` Location Location `json:"location"` ContainerName *string `json:"containerName"` } // CompletionItemKind is type CompletionItemKind int // TextCompletion is const ( TextCompletion CompletionItemKind = 1 MethodCompletion CompletionItemKind = 2 FunctionCompletion CompletionItemKind = 3 ConstructorCompletion CompletionItemKind = 4 FieldCompletion CompletionItemKind = 5 VariableCompletion CompletionItemKind = 6 ClassCompletion CompletionItemKind = 7 InterfaceCompletion CompletionItemKind = 8 ModuleCompletion CompletionItemKind = 9 PropertyCompletion CompletionItemKind = 10 UnitCompletion CompletionItemKind = 11 ValueCompletion CompletionItemKind = 12 EnumCompletion CompletionItemKind = 13 KeywordCompletion CompletionItemKind = 14 SnippetCompletion CompletionItemKind = 15 ColorCompletion CompletionItemKind = 16 FileCompletion CompletionItemKind = 17 ReferenceCompletion CompletionItemKind = 18 FolderCompletion CompletionItemKind = 19 EnumMemberCompletion CompletionItemKind = 20 ConstantCompletion CompletionItemKind = 21 StructCompletion CompletionItemKind = 22 EventCompletion CompletionItemKind = 23 OperatorCompletion CompletionItemKind = 24 TypeParameterCompletion CompletionItemKind = 25 ) // CompletionItemTag is type CompletionItemTag int // InsertTextFormat is type InsertTextFormat int // PlainTextTextFormat is const ( PlainTextTextFormat InsertTextFormat = 1 SnippetTextFormat InsertTextFormat = 2 ) // Command is type Command struct { Title string `json:"title" yaml:"title"` Command string `json:"command" yaml:"command"` Arguments []any `json:"arguments,omitempty" yaml:"arguments,omitempty"` OS string `json:"-" yaml:"os,omitempty"` } // WorkspaceEdit is type WorkspaceEdit struct { Changes any `json:"changes"` // { [uri: DocumentUri]: TextEdit[]; }; DocumentChanges any `json:"documentChanges"` // (TextDocumentEdit[] | (TextDocumentEdit | CreateFile | RenameFile | DeleteFile)[]); } // CodeAction is type CodeAction struct { Title string `json:"title"` Diagnostics []Diagnostic `json:"diagnostics"` IsPreferred bool `json:"isPreferred"` // TODO Edit *WorkspaceEdit `json:"edit"` Command *Command `json:"command"` } // CompletionItem is type CompletionItem struct { Label string `json:"label"` Kind CompletionItemKind `json:"kind,omitempty"` Tags []CompletionItemTag `json:"tags,omitempty"` Detail string `json:"detail,omitempty"` Documentation string `json:"documentation,omitempty"` // string | MarkupContent Deprecated bool `json:"deprecated,omitempty"` Preselect bool `json:"preselect,omitempty"` SortText string `json:"sortText,omitempty"` FilterText string `json:"filterText,omitempty"` InsertText string `json:"insertText,omitempty"` InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"` TextEdit *TextEdit `json:"textEdit,omitempty"` AdditionalTextEdits []TextEdit `json:"additionalTextEdits,omitempty"` CommitCharacters []string `json:"commitCharacters,omitempty"` Command *Command `json:"command,omitempty"` Data any `json:"data,omitempty"` } // Hover is type Hover struct { Contents any `json:"contents"` Range *Range `json:"range"` } // MarkedString is type MarkedString struct { Language string `json:"language"` Value string `json:"value"` } // MarkupKind is type MarkupKind string // PlainText is const ( PlainText MarkupKind = "plaintext" Markdown MarkupKind = "markdown" ) // MarkupContent is type MarkupContent struct { Kind MarkupKind `json:"kind"` Value string `json:"value"` } // WorkDoneProgressParams is type WorkDoneProgressParams struct { WorkDoneToken any `json:"workDoneToken"` } // ExecuteCommandParams is type ExecuteCommandParams struct { WorkDoneProgressParams Command string `json:"command"` Arguments []any `json:"arguments,omitempty"` } // CodeActionKind is type CodeActionKind string // Empty is const ( Empty CodeActionKind = "" QuickFix CodeActionKind = "quickfix" Refactor CodeActionKind = "refactor" RefactorExtract CodeActionKind = "refactor.extract" RefactorInline CodeActionKind = "refactor.inline" RefactorRewrite CodeActionKind = "refactor.rewrite" Source CodeActionKind = "source" SourceOrganizeImports CodeActionKind = "source.organizeImports" ) // CodeActionContext is type CodeActionContext struct { Diagnostics []Diagnostic `json:"diagnostics"` Only []CodeActionKind `json:"only,omitempty"` } // PartialResultParams is type PartialResultParams struct { PartialResultToken any `json:"partialResultToken"` } // CodeActionParams is type CodeActionParams struct { WorkDoneProgressParams PartialResultParams TextDocument TextDocumentIdentifier `json:"textDocument"` Range Range `json:"range"` Context CodeActionContext `json:"context"` } // DidChangeConfigurationParams is type DidChangeConfigurationParams struct { Settings Config `json:"settings"` } // NotificationMessage is type NotificationMessage struct { Method string `json:"message"` Params any `json:"params"` } // DocumentDefinitionParams is type DocumentDefinitionParams struct { TextDocumentPositionParams WorkDoneProgressParams PartialResultParams } // ShowMessageParams is type ShowMessageParams struct { Type MessageType `json:"type"` Message string `json:"message"` } // LogMessageParams is type LogMessageParams struct { Type MessageType `json:"type"` Message string `json:"message"` } // DidChangeWorkspaceFoldersParams is type DidChangeWorkspaceFoldersParams struct { Event WorkspaceFoldersChangeEvent `json:"event"` } // WorkspaceFoldersChangeEvent is type WorkspaceFoldersChangeEvent struct { Added []WorkspaceFolder `json:"added,omitempty"` Removed []WorkspaceFolder `json:"removed,omitempty"` } // WorkspaceFolder is type WorkspaceFolder struct { URI DocumentURI `json:"uri"` Name string `json:"name"` } efm-langserver-0.0.53/langserver/util.go000066400000000000000000000007271457604367300202150ustar00rootroot00000000000000package langserver import ( "strings" ) func convertRowColToIndex(s string, row, col int) int { lines := strings.Split(s, "\n") if row < 0 { row = 0 } else if row >= len(lines) { row = len(lines) - 1 } if col < 0 { col = 0 } else if col > len(lines[row]) { col = len(lines[row]) } index := 0 for i := 0; i < row; i++ { // Add the length of each line plus 1 for the newline character index += len(lines[i]) + 1 } index += col return index } efm-langserver-0.0.53/main.go000066400000000000000000000054701457604367300160140ustar00rootroot00000000000000package main import ( "context" "flag" "fmt" "io" "log" "os" "path/filepath" "runtime" "github.com/sourcegraph/jsonrpc2" "gopkg.in/yaml.v3" "github.com/mattn/efm-langserver/langserver" ) const ( name = "efm-langserver" version = "0.0.53" ) var revision = "HEAD" func main() { var yamlfile string var logfile string var loglevel int var dump bool var showVersion bool var quiet bool flag.StringVar(&yamlfile, "c", "", "path to config.yaml") flag.StringVar(&logfile, "logfile", "", "logfile") flag.IntVar(&loglevel, "loglevel", 1, "loglevel") flag.BoolVar(&dump, "d", false, "dump configuration") flag.BoolVar(&showVersion, "v", false, "Print the version") flag.BoolVar(&quiet, "q", false, "Run quieter") flag.Parse() if showVersion { fmt.Printf("%s %s (rev: %s/%s)\n", name, version, revision, runtime.Version()) return } if yamlfile == "" { var configHome string if runtime.GOOS == "windows" { configHome = os.Getenv("APPDATA") } else { configHome = os.Getenv("XDG_CONFIG_HOME") if configHome == "" { configHome = filepath.Join(os.Getenv("HOME"), ".config") } } dir := filepath.Join(configHome, "efm-langserver") if err := os.MkdirAll(dir, 0o700); err != nil { log.Fatal(err) } yamlfile = filepath.Join(dir, "config.yaml") } else { _, err := os.Stat(yamlfile) if err != nil { log.Fatal(err) } } config, err := langserver.LoadConfig(yamlfile) if err != nil { log.Fatal(err) } if dump { err = yaml.NewEncoder(os.Stdout).Encode(&config) if err != nil { log.Fatal(err) } os.Exit(0) } if flag.NArg() != 0 { flag.Usage() os.Exit(1) } if quiet { log.SetOutput(io.Discard) } log.Println("efm-langserver: reading on stdin, writing on stdout") if logfile == "" { logfile = config.LogFile } if config.LogLevel > 0 { loglevel = config.LogLevel } var connOpt []jsonrpc2.ConnOpt if logfile != "" { f, err := os.OpenFile(logfile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0o660) if err != nil { log.Fatal(err) } defer f.Close() config.Logger = log.New(f, "", log.LstdFlags) if loglevel >= 5 { connOpt = append(connOpt, jsonrpc2.LogMessages(config.Logger)) } } if quiet && (logfile == "" || loglevel < 5) { connOpt = append(connOpt, jsonrpc2.LogMessages(log.New(io.Discard, "", 0))) } handler := langserver.NewHandler(config) <-jsonrpc2.NewConn( context.Background(), jsonrpc2.NewBufferedStream(stdrwc{}, jsonrpc2.VSCodeObjectCodec{}), handler, connOpt...).DisconnectNotify() log.Println("efm-langserver: connections closed") } type stdrwc struct{} func (stdrwc) Read(p []byte) (int, error) { return os.Stdin.Read(p) } func (c stdrwc) Write(p []byte) (int, error) { return os.Stdout.Write(p) } func (c stdrwc) Close() error { if err := os.Stdin.Close(); err != nil { return err } return os.Stdout.Close() } efm-langserver-0.0.53/renovate.json000066400000000000000000000000511457604367300172450ustar00rootroot00000000000000{ "extends": [ "config:base" ] } efm-langserver-0.0.53/schema.json000066400000000000000000000175051457604367300166760ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-04/schema", "additionalProperties": false, "description": "If configuring via `DidChangeConfiguration` (e.g. an editor API such as `nvim-lspconfig`), all properties should be in camelCase instead of kebab-case.", "definitions": { "command-definition": { "description": "list of commands", "items": { "additionalProperties": false, "properties": { "arguments": { "description": "arguments for the command", "items": { "type": "string" }, "type": "array" }, "command": { "description": "command to execute", "type": "string" }, "os": { "description": "command executable OS environment", "type": "string" }, "title": { "description": "title for clients", "type": "string" } }, "type": "object" }, "type": "array" }, "tool-definition": { "additionalProperties": false, "description": "definition of the tool", "properties": { "prefix": { "description": "If `lint-source` doesn't work, you can set a prefix here instead, which will render the messages as \"[prefix] message\".", "type": "string" }, "format-can-range": { "description": "Whether the formatting command handles range start and range end", "type": "boolean" }, "format-command": { "description": "Formatting command. Input filename can be injected using `${INPUT}`, and flags can be injected using `${--flag:key}` (adds `--flag ` if value exists for key), `${--flag=key}` (adds `--flag=` if value exists for key), or `${--flag:!key}` (adds `--flag` if value for key is falsy).\n\n`efm-langserver` may provide values for keys `charStart`, `charEnd`, `rowStart`, `rowEnd`, `colStart`, `colEnd`, or any key in [`interface FormattingOptions`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions).\n\nExample: `prettier --stdin --stdin-filepath ${INPUT} ${--tab-width:tabWidth} ${--use-tabs:insertSpaces} ${--range-start=charStart} ${--range-start=charEnd}`", "type": "string" }, "format-stdin": { "description": "use stdin for the format", "type": "boolean" }, "hover-command": { "description": "hover command", "type": "string" }, "hover-stdin": { "description": "use stdin for the hover", "type": "boolean" }, "hover-type": { "description": "hover result type", "type": "string", "enum": [ "markdown", "plaintext" ] }, "hover-chars": { "type": "string" }, "env": { "description": "command environment variables and values", "items": { "type": "string", "pattern": "^.+=.+$" }, "type": "array" }, "lint-command": { "description": "Lint command. Input filename can be injected using `${INPUT}`.", "type": "string" }, "lint-offset-columns": { "description": "offset value to skip columns", "type": "number" }, "lint-category-map": { "description": "Map linter categories to LSP categories", "type": "object" }, "lint-formats": { "description": "List of Vim errorformats to capture. See: https://vimhelp.org/quickfix.txt.html#errorformats. If this is not expressive enough, you can edit the `lint-command` to do some preprocessing, e.g. using `sed` or `jq`.\n\n`efm-langserver` uses a Go implementation to parse the errors, which comes with a CLI for quick testing: https://github.com/reviewdog/errorformat", "items": { "type": "string" }, "type": "array" }, "lint-ignore-exit-code": { "default": true, "description": "ignore exit code of lint", "type": "boolean" }, "lint-offset": { "description": "offset value to skip lines", "type": "number" }, "lint-after-open": { "default": true, "description": "lint after open", "type": "boolean" }, "lint-on-save": { "description": "only lint on save, i.e. don't lint on text changed", "type": "boolean" }, "lint-severity": { "description": "default severity to show if violation doesn't provide severity. 1 = error, 2 = warning, 3 = info, 4 = hint", "type": "number" }, "lint-source": { "description": "show where the lint came from, e.g. 'eslint'", "type": "string" }, "lint-stdin": { "default": true, "description": "use stdin for the lint", "type": "boolean" }, "lint-workspace": { "description": "indicates that the command lints the whole workspace and thus doesn't need a filename argument nor stdin", "type": "boolean" }, "completion-command": { "description": "completion command", "type": "string" }, "completion-stdin": { "default": true, "description": "use stdin for the completion", "type": "boolean" }, "symbol-command": { "type": "string" }, "symbol-stdin": { "type": "boolean" }, "symbol-formats": { "items": { "type": "string" }, "type": "array" }, "root-markers": { "description": "markers to find root directory", "items": { "type": "string" }, "type": "array" }, "require-marker": { "description": "require a marker to run linter", "type": "boolean" }, "commands": { "$ref": "#/definitions/command-definition" } }, "type": "object" } }, "properties": { "commands": { "$ref": "#/definitions/command-definition" }, "languages": { "description": "list of language", "patternProperties": { "^([a-z0-9_-]+)+$": { "items": { "$ref": "#/definitions/tool-definition" }, "type": "array" } } }, "tools": { "description": "definition of tools", "patternProperties": { "^([a-z0-9_-]+)+$": { "$ref": "#/definitions/tool-definition" } }, "type": "object" }, "version": { "description": "version of this yaml format", "type": "number" }, "root-markers": { "description": "markers to find root directory", "items": { "type": "string" }, "type": "array" }, "log-file": { "description": "(YAML only) path to log file", "type": "string" }, "log-level": { "description": "log level", "minimum": 1, "type": "number" }, "format-debounce": { "description": "duration to debounce calls to the formatter executable. e.g: 1s", "type": "string" }, "lint-debounce": { "description": "duration to debounce calls to the linter executable. e.g.: 1s", "type": "string" }, "provide-definition": { "description": "(YAML only) Whether this language server should be used for go-to-definition requests", "type": "boolean" }, "trigger-chars": { "description": "trigger characters for completion", "items": { "type": "string" }, "type": "array" } }, "title": "efm-langserver", "type": "object" } efm-langserver-0.0.53/schema.md000066400000000000000000001643201457604367300163230ustar00rootroot00000000000000# efm-langserver - [1. Property `commands`](#commands) - [1.1. commands items](#autogenerated_heading_2) - [1.1.1. Property `arguments`](#commands_items_arguments) - [1.1.1.1. arguments items](#autogenerated_heading_3) - [1.1.2. Property `command`](#commands_items_command) - [1.1.3. Property `os`](#commands_items_os) - [1.1.4. Property `title`](#commands_items_title) - [2. Property `languages`](#languages) - [2.1. Pattern Property `^([a-z0-9_-]+)+$`](#languages_pattern1) - [2.1.1. tool-definition](#autogenerated_heading_4) - [2.1.1.1. Property `prefix`](#languages_pattern1_items_prefix) - [2.1.1.2. Property `format-can-range`](#languages_pattern1_items_format-can-range) - [2.1.1.3. Property `format-command`](#languages_pattern1_items_format-command) - [2.1.1.4. Property `format-stdin`](#languages_pattern1_items_format-stdin) - [2.1.1.5. Property `hover-command`](#languages_pattern1_items_hover-command) - [2.1.1.6. Property `hover-stdin`](#languages_pattern1_items_hover-stdin) - [2.1.1.7. Property `hover-type`](#languages_pattern1_items_hover-type) - [2.1.1.8. Property `hover-chars`](#languages_pattern1_items_hover-chars) - [2.1.1.9. Property `env`](#languages_pattern1_items_env) - [2.1.1.9.1. env items](#autogenerated_heading_5) - [2.1.1.10. Property `lint-command`](#languages_pattern1_items_lint-command) - [2.1.1.11. Property `lint-offset-columns`](#languages_pattern1_items_lint-offset-columns) - [2.1.1.12. Property `lint-category-map`](#languages_pattern1_items_lint-category-map) - [2.1.1.13. Property `lint-formats`](#languages_pattern1_items_lint-formats) - [2.1.1.13.1. lint-formats items](#autogenerated_heading_6) - [2.1.1.14. Property `lint-ignore-exit-code`](#languages_pattern1_items_lint-ignore-exit-code) - [2.1.1.15. Property `lint-offset`](#languages_pattern1_items_lint-offset) - [2.1.1.16. Property `lint-on-save`](#languages_pattern1_items_lint-on-save) - [2.1.1.17. Property `lint-severity`](#languages_pattern1_items_lint-severity) - [2.1.1.18. Property `lint-source`](#languages_pattern1_items_lint-source) - [2.1.1.19. Property `lint-stdin`](#languages_pattern1_items_lint-stdin) - [2.1.1.20. Property `lint-workspace`](#languages_pattern1_items_lint-workspace) - [2.1.1.21. Property `completion-command`](#languages_pattern1_items_completion-command) - [2.1.1.22. Property `completion-stdin`](#languages_pattern1_items_completion-stdin) - [2.1.1.23. Property `symbol-command`](#languages_pattern1_items_symbol-command) - [2.1.1.24. Property `symbol-stdin`](#languages_pattern1_items_symbol-stdin) - [2.1.1.25. Property `symbol-formats`](#languages_pattern1_items_symbol-formats) - [2.1.1.25.1. symbol-formats items](#autogenerated_heading_7) - [2.1.1.26. Property `root-markers`](#languages_pattern1_items_root-markers) - [2.1.1.26.1. root-markers items](#autogenerated_heading_8) - [2.1.1.27. Property `require-marker`](#languages_pattern1_items_require-marker) - [2.1.1.28. Property `commands`](#languages_pattern1_items_commands) - [3. Property `tools`](#tools) - [3.1. Pattern Property `tool-definition`](#tools_pattern1) - [4. Property `version`](#version) - [5. Property `root-markers`](#root-markers) - [5.1. root-markers items](#autogenerated_heading_9) - [6. Property `log-file`](#log-file) - [7. Property `log-level`](#log-level) - [8. Property `format-debounce`](#format-debounce) - [9. Property `lint-debounce`](#lint-debounce) - [10. Property `provide-definition`](#provide-definition) - [11. Property `trigger-chars`](#trigger-chars) - [11.1. trigger-chars items](#autogenerated_heading_10) **Title:** efm-langserver | | | | ------------------------- | ------------------------------------------------------- | | **Type** | `object` | | **Required** | No | | **Additional properties** | [[Not allowed]](# "Additional Properties not allowed.") | **Description:** If configuring via `DidChangeConfiguration` (e.g. an editor API such as `nvim-lspconfig`), all properties should be in camelCase instead of kebab-case. | Property | Pattern | Type | Deprecated | Definition | Title/Description | | -------------------------------------------- | ------- | --------------- | ---------- | ----------------------------------- | ------------------------------------------------------------------------------------- | | - [commands](#commands ) | No | array of object | No | In #/definitions/command-definition | list of commands | | - [languages](#languages ) | No | object | No | - | list of language | | - [tools](#tools ) | No | object | No | - | definition of tools | | - [version](#version ) | No | number | No | - | version of this yaml format | | - [root-markers](#root-markers ) | No | array of string | No | - | markers to find root directory | | - [log-file](#log-file ) | No | string | No | - | (YAML only) path to log file | | - [log-level](#log-level ) | No | number | No | - | log level | | - [format-debounce](#format-debounce ) | No | string | No | - | duration to debounce calls to the formatter executable. e.g: 1s | | - [lint-debounce](#lint-debounce ) | No | string | No | - | duration to debounce calls to the linter executable. e.g.: 1s | | - [provide-definition](#provide-definition ) | No | boolean | No | - | (YAML only) Whether this language server should be used for go-to-definition requests | | - [trigger-chars](#trigger-chars ) | No | array of string | No | - | trigger characters for completion | ## 1. Property `commands` | | | | -------------- | -------------------------------- | | **Type** | `array of object` | | **Required** | No | | **Defined in** | #/definitions/command-definition | **Description:** list of commands | | Array restrictions | | -------------------- | ------------------ | | **Min items** | N/A | | **Max items** | N/A | | **Items unicity** | False | | **Additional items** | False | | **Tuple validation** | See below | | Each item of this array must be | Description | | --------------------------------- | ----------- | | [commands items](#commands_items) | - | ### 1.1. commands items | | | | ------------------------- | ------------------------------------------------------- | | **Type** | `object` | | **Required** | No | | **Additional properties** | [[Not allowed]](# "Additional Properties not allowed.") | | Property | Pattern | Type | Deprecated | Definition | Title/Description | | ----------------------------------------- | ------- | --------------- | ---------- | ---------- | --------------------------------- | | - [arguments](#commands_items_arguments ) | No | array of string | No | - | arguments for the command | | - [command](#commands_items_command ) | No | string | No | - | command to execute | | - [os](#commands_items_os ) | No | string | No | - | command executable OS environment | | - [title](#commands_items_title ) | No | string | No | - | title for clients | #### 1.1.1. Property `arguments` | | | | ------------ | ----------------- | | **Type** | `array of string` | | **Required** | No | **Description:** arguments for the command | | Array restrictions | | -------------------- | ------------------ | | **Min items** | N/A | | **Max items** | N/A | | **Items unicity** | False | | **Additional items** | False | | **Tuple validation** | See below | | Each item of this array must be | Description | | -------------------------------------------------- | ----------- | | [arguments items](#commands_items_arguments_items) | - | ##### 1.1.1.1. arguments items | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | #### 1.1.2. Property `command` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** command to execute #### 1.1.3. Property `os` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** command executable OS environment #### 1.1.4. Property `title` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** title for clients ## 2. Property `languages` | | | | ------------------------- | ------------------------------------------------------------------------- | | **Type** | `object` | | **Required** | No | | **Additional properties** | [[Any type: allowed]](# "Additional Properties of any type are allowed.") | **Description:** list of language | Property | Pattern | Type | Deprecated | Definition | Title/Description | | ------------------------------------------ | ------- | ----- | ---------- | ---------- | ----------------- | | - [^([a-z0-9_-]+)+$](#languages_pattern1 ) | Yes | array | No | - | - | ### 2.1. Pattern Property `^([a-z0-9_-]+)+$` > All properties whose name matches the regular expression ```^([a-z0-9_-]+)+$``` ([Test](https://regex101.com/?regex=%5E%28%5Ba-z0-9_-%5D%2B%29%2B%24)) must respect the following conditions | | | | ------------ | ------- | | **Type** | `array` | | **Required** | No | | | Array restrictions | | -------------------- | ------------------ | | **Min items** | N/A | | **Max items** | N/A | | **Items unicity** | False | | **Additional items** | False | | **Tuple validation** | See below | | Each item of this array must be | Description | | -------------------------------------------- | ---------------------- | | [tool-definition](#languages_pattern1_items) | definition of the tool | #### 2.1.1. tool-definition | | | | ------------------------- | ------------------------------------------------------- | | **Type** | `object` | | **Required** | No | | **Additional properties** | [[Not allowed]](# "Additional Properties not allowed.") | | **Defined in** | #/definitions/tool-definition | **Description:** definition of the tool | Property | Pattern | Type | Deprecated | Definition | Title/Description | | --------------------------------------------------------------------------- | ------- | ---------------- | ---------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | - [prefix](#languages_pattern1_items_prefix ) | No | string | No | - | If `lint-source` doesn't work, you can set a prefix here instead, which will render the messages as "[prefix] message". | | - [format-can-range](#languages_pattern1_items_format-can-range ) | No | boolean | No | - | Whether the formatting command handles range start and range end | | - [format-command](#languages_pattern1_items_format-command ) | No | string | No | - | Formatting command. Input filename can be injected using `${INPUT}`, and flags can be injected using `${--flag:key}` (adds `--flag ` if value exists for key), `${--flag=key}` (adds `--flag=` if value exists for key), or `${--flag:!key}` (adds `--flag` if value for key is falsy).

`efm-langserver` may provide values for keys `charStart`, `charEnd`, `rowStart`, `rowEnd`, `colStart`, `colEnd`, or any key in [`interface FormattingOptions`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions).

Example: `prettier --stdin --stdin-filepath ${INPUT} ${--tab-width:tabWidth} ${--use-tabs:insertSpaces} ${--range-start=charStart} ${--range-start=charEnd}` | | - [format-stdin](#languages_pattern1_items_format-stdin ) | No | boolean | No | - | use stdin for the format | | - [hover-command](#languages_pattern1_items_hover-command ) | No | string | No | - | hover command | | - [hover-stdin](#languages_pattern1_items_hover-stdin ) | No | boolean | No | - | use stdin for the hover | | - [hover-type](#languages_pattern1_items_hover-type ) | No | enum (of string) | No | - | hover result type | | - [hover-chars](#languages_pattern1_items_hover-chars ) | No | string | No | - | - | | - [env](#languages_pattern1_items_env ) | No | array of string | No | - | command environment variables and values | | - [lint-command](#languages_pattern1_items_lint-command ) | No | string | No | - | Lint command. Input filename can be injected using `${INPUT}`. | | - [lint-offset-columns](#languages_pattern1_items_lint-offset-columns ) | No | number | No | - | offset value to skip columns | | - [lint-category-map](#languages_pattern1_items_lint-category-map ) | No | object | No | - | Map linter categories to LSP categories | | - [lint-formats](#languages_pattern1_items_lint-formats ) | No | array of string | No | - | List of Vim errorformats to capture. See: https://vimhelp.org/quickfix.txt.html#errorformats. If this is not expressive enough, you can edit the `lint-command` to do some preprocessing, e.g. using `sed` or `jq`.

`efm-langserver` uses a Go implementation to parse the errors, which comes with a CLI for quick testing: https://github.com/reviewdog/errorformat | | - [lint-ignore-exit-code](#languages_pattern1_items_lint-ignore-exit-code ) | No | boolean | No | - | ignore exit code of lint | | - [lint-offset](#languages_pattern1_items_lint-offset ) | No | number | No | - | offset value to skip lines | | - [lint-on-save](#languages_pattern1_items_lint-on-save ) | No | boolean | No | - | only lint on save, i.e. don't lint on text changed | | - [lint-severity](#languages_pattern1_items_lint-severity ) | No | number | No | - | default severity to show if violation doesn't provide severity. 1 = error, 2 = warning, 3 = info, 4 = hint | | - [lint-source](#languages_pattern1_items_lint-source ) | No | string | No | - | show where the lint came from, e.g. 'eslint' | | - [lint-stdin](#languages_pattern1_items_lint-stdin ) | No | boolean | No | - | use stdin for the lint | | - [lint-workspace](#languages_pattern1_items_lint-workspace ) | No | boolean | No | - | indicates that the command lints the whole workspace and thus doesn't need a filename argument nor stdin | | - [completion-command](#languages_pattern1_items_completion-command ) | No | string | No | - | completion command | | - [completion-stdin](#languages_pattern1_items_completion-stdin ) | No | boolean | No | - | use stdin for the completion | | - [symbol-command](#languages_pattern1_items_symbol-command ) | No | string | No | - | - | | - [symbol-stdin](#languages_pattern1_items_symbol-stdin ) | No | boolean | No | - | - | | - [symbol-formats](#languages_pattern1_items_symbol-formats ) | No | array of string | No | - | - | | - [root-markers](#languages_pattern1_items_root-markers ) | No | array of string | No | - | markers to find root directory | | - [require-marker](#languages_pattern1_items_require-marker ) | No | boolean | No | - | require a marker to run linter | | - [commands](#languages_pattern1_items_commands ) | No | array of object | No | Same as [commands](#commands ) | list of commands | ##### 2.1.1.1. Property `prefix` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** If `lint-source` doesn't work, you can set a prefix here instead, which will render the messages as "[prefix] message". ##### 2.1.1.2. Property `format-can-range` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | **Description:** Whether the formatting command handles range start and range end ##### 2.1.1.3. Property `format-command` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** Formatting command. Input filename can be injected using `${INPUT}`, and flags can be injected using `${--flag:key}` (adds `--flag ` if value exists for key), `${--flag=key}` (adds `--flag=` if value exists for key), or `${--flag:!key}` (adds `--flag` if value for key is falsy). `efm-langserver` may provide values for keys `charStart`, `charEnd`, `rowStart`, `rowEnd`, `colStart`, `colEnd`, or any key in [`interface FormattingOptions`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions). Example: `prettier --stdin --stdin-filepath ${INPUT} ${--tab-width:tabWidth} ${--use-tabs:insertSpaces} ${--range-start=charStart} ${--range-start=charEnd}` ##### 2.1.1.4. Property `format-stdin` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | **Description:** use stdin for the format ##### 2.1.1.5. Property `hover-command` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** hover command ##### 2.1.1.6. Property `hover-stdin` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | **Description:** use stdin for the hover ##### 2.1.1.7. Property `hover-type` | | | | ------------ | ------------------ | | **Type** | `enum (of string)` | | **Required** | No | **Description:** hover result type Must be one of: * "markdown" * "plaintext" ##### 2.1.1.8. Property `hover-chars` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | ##### 2.1.1.9. Property `env` | | | | ------------ | ----------------- | | **Type** | `array of string` | | **Required** | No | **Description:** command environment variables and values | | Array restrictions | | -------------------- | ------------------ | | **Min items** | N/A | | **Max items** | N/A | | **Items unicity** | False | | **Additional items** | False | | **Tuple validation** | See below | | Each item of this array must be | Description | | ------------------------------------------------ | ----------- | | [env items](#languages_pattern1_items_env_items) | - | ##### 2.1.1.9.1. env items | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | | Restrictions | | | --------------------------------- | ------------------------------------------------------------------- | | **Must match regular expression** | ```^.+=.+$``` [Test](https://regex101.com/?regex=%5E.%2B%3D.%2B%24) | ##### 2.1.1.10. Property `lint-command` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** Lint command. Input filename can be injected using `${INPUT}`. ##### 2.1.1.11. Property `lint-offset-columns` | | | | ------------ | -------- | | **Type** | `number` | | **Required** | No | **Description:** offset value to skip columns ##### 2.1.1.12. Property `lint-category-map` | | | | ------------------------- | ------------------------------------------------------------------------- | | **Type** | `object` | | **Required** | No | | **Additional properties** | [[Any type: allowed]](# "Additional Properties of any type are allowed.") | **Description:** Map linter categories to LSP categories ##### 2.1.1.13. Property `lint-formats` | | | | ------------ | ----------------- | | **Type** | `array of string` | | **Required** | No | **Description:** List of Vim errorformats to capture. See: https://vimhelp.org/quickfix.txt.html#errorformats. If this is not expressive enough, you can edit the `lint-command` to do some preprocessing, e.g. using `sed` or `jq`. `efm-langserver` uses a Go implementation to parse the errors, which comes with a CLI for quick testing: https://github.com/reviewdog/errorformat | | Array restrictions | | -------------------- | ------------------ | | **Min items** | N/A | | **Max items** | N/A | | **Items unicity** | False | | **Additional items** | False | | **Tuple validation** | See below | | Each item of this array must be | Description | | ------------------------------------------------------------------ | ----------- | | [lint-formats items](#languages_pattern1_items_lint-formats_items) | - | ##### 2.1.1.13.1. lint-formats items | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | ##### 2.1.1.14. Property `lint-ignore-exit-code` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | | **Default** | `true` | **Description:** ignore exit code of lint ##### 2.1.1.15. Property `lint-offset` | | | | ------------ | -------- | | **Type** | `number` | | **Required** | No | **Description:** offset value to skip lines ##### 2.1.1.16. Property `lint-on-save` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | **Description:** only lint on save, i.e. don't lint on text changed ##### 2.1.1.17. Property `lint-severity` | | | | ------------ | -------- | | **Type** | `number` | | **Required** | No | **Description:** default severity to show if violation doesn't provide severity. 1 = error, 2 = warning, 3 = info, 4 = hint ##### 2.1.1.18. Property `lint-source` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** show where the lint came from, e.g. 'eslint' ##### 2.1.1.19. Property `lint-stdin` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | | **Default** | `true` | **Description:** use stdin for the lint ##### 2.1.1.20. Property `lint-workspace` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | **Description:** indicates that the command lints the whole workspace and thus doesn't need a filename argument nor stdin ##### 2.1.1.21. Property `completion-command` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** completion command ##### 2.1.1.22. Property `completion-stdin` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | | **Default** | `true` | **Description:** use stdin for the completion ##### 2.1.1.23. Property `symbol-command` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | ##### 2.1.1.24. Property `symbol-stdin` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | ##### 2.1.1.25. Property `symbol-formats` | | | | ------------ | ----------------- | | **Type** | `array of string` | | **Required** | No | | | Array restrictions | | -------------------- | ------------------ | | **Min items** | N/A | | **Max items** | N/A | | **Items unicity** | False | | **Additional items** | False | | **Tuple validation** | See below | | Each item of this array must be | Description | | ---------------------------------------------------------------------- | ----------- | | [symbol-formats items](#languages_pattern1_items_symbol-formats_items) | - | ##### 2.1.1.25.1. symbol-formats items | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | ##### 2.1.1.26. Property `root-markers` | | | | ------------ | ----------------- | | **Type** | `array of string` | | **Required** | No | **Description:** markers to find root directory | | Array restrictions | | -------------------- | ------------------ | | **Min items** | N/A | | **Max items** | N/A | | **Items unicity** | False | | **Additional items** | False | | **Tuple validation** | See below | | Each item of this array must be | Description | | ------------------------------------------------------------------ | ----------- | | [root-markers items](#languages_pattern1_items_root-markers_items) | - | ##### 2.1.1.26.1. root-markers items | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | ##### 2.1.1.27. Property `require-marker` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | **Description:** require a marker to run linter ##### 2.1.1.28. Property `commands` | | | | ---------------------- | --------------------- | | **Type** | `array of object` | | **Required** | No | | **Same definition as** | [commands](#commands) | **Description:** list of commands ## 3. Property `tools` | | | | ------------------------- | ------------------------------------------------------------------------- | | **Type** | `object` | | **Required** | No | | **Additional properties** | [[Any type: allowed]](# "Additional Properties of any type are allowed.") | **Description:** definition of tools | Property | Pattern | Type | Deprecated | Definition | Title/Description | | -------------------------------------- | ------- | ------ | ---------- | -------------------------------------------------------------- | ---------------------- | | - [^([a-z0-9_-]+)+$](#tools_pattern1 ) | Yes | object | No | Same as [languages_pattern1_items](#languages_pattern1_items ) | definition of the tool | ### 3.1. Pattern Property `tool-definition` > All properties whose name matches the regular expression ```^([a-z0-9_-]+)+$``` ([Test](https://regex101.com/?regex=%5E%28%5Ba-z0-9_-%5D%2B%29%2B%24)) must respect the following conditions | | | | ------------------------- | ------------------------------------------------------- | | **Type** | `object` | | **Required** | No | | **Additional properties** | [[Not allowed]](# "Additional Properties not allowed.") | | **Same definition as** | [languages_pattern1_items](#languages_pattern1_items) | **Description:** definition of the tool ## 4. Property `version` | | | | ------------ | -------- | | **Type** | `number` | | **Required** | No | **Description:** version of this yaml format ## 5. Property `root-markers` | | | | ------------ | ----------------- | | **Type** | `array of string` | | **Required** | No | **Description:** markers to find root directory | | Array restrictions | | -------------------- | ------------------ | | **Min items** | N/A | | **Max items** | N/A | | **Items unicity** | False | | **Additional items** | False | | **Tuple validation** | See below | | Each item of this array must be | Description | | ----------------------------------------- | ----------- | | [root-markers items](#root-markers_items) | - | ### 5.1. root-markers items | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | ## 6. Property `log-file` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** (YAML only) path to log file ## 7. Property `log-level` | | | | ------------ | -------- | | **Type** | `number` | | **Required** | No | **Description:** log level | Restrictions | | | ------------ | ------ | | **Minimum** | ≥ 1 | ## 8. Property `format-debounce` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** duration to debounce calls to the formatter executable. e.g: 1s ## 9. Property `lint-debounce` | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | **Description:** duration to debounce calls to the linter executable. e.g.: 1s ## 10. Property `provide-definition` | | | | ------------ | --------- | | **Type** | `boolean` | | **Required** | No | **Description:** (YAML only) Whether this language server should be used for go-to-definition requests ## 11. Property `trigger-chars` | | | | ------------ | ----------------- | | **Type** | `array of string` | | **Required** | No | **Description:** trigger characters for completion | | Array restrictions | | -------------------- | ------------------ | | **Min items** | N/A | | **Max items** | N/A | | **Items unicity** | False | | **Additional items** | False | | **Tuple validation** | See below | | Each item of this array must be | Description | | ------------------------------------------- | ----------- | | [trigger-chars items](#trigger-chars_items) | - | ### 11.1. trigger-chars items | | | | ------------ | -------- | | **Type** | `string` | | **Required** | No | ---------------------------------------------------------------------------------------------------------------------------- Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2023-09-06 at 04:43:26 -0700 efm-langserver-0.0.53/screenshot.png000066400000000000000000000160701457604367300174220ustar00rootroot00000000000000PNG  IHDRsRGBgAMA a pHYsodIDATx^ WyqЦBP+/jJ4PCLBMR'8*Pb+&mD">%U` rVAmUP@Lwww읳H̙3gfggγ3]riU"""]SH )B#""E(`DD"0""RFDDPH )B#""E(`DD"0""RFDDPH )bss/՗kkN|_rmoZDD&gC՗ۃ5_'bulPo{:mnoZDD&gG8P?-gW1~;(`DDFYjU.=_Ce cJN]Z|y8ODDW,`֭[Wm۶ڽ{w=NYC9W/&㪅~ۀٱcGk׮jƍʕ+:""^CaÆæfw0qWey3N͛7auz:'"": nA,:h nvn0\X9c! }8QYcӦM-Y 6c  Ut Xlc> AH U˚5kz""N{\m7[?$_D{E&e//0KM4`DDdPH )B#""E(`d(ZNDQȢ n 乵hgΝվ}zV^=P58zًΩ7{~˗ԙor9OFd2Fz3zw/^}ԛo~y1KW\W5ί6pZ2,ڨNН]ݾeÇ^Ww~~ԁPgMmJ?iJlˆ֯®iѣ?pM`޽{+V0]c"Ѐٹ}:=t@n~;{!M>m]0︺Kq^o6z@[.,YGe~_oC7eh+eY,K]GThԸ2,{`ٶNNty,VCy#`8$_rPJp_>(й8 ~9R^||YN{++ƣ=ӀgoW2) .|6 BI:91&Xs>9{&m 5tV?Wn*&]. P_0;^}ͯ0LI0ViS a-#G3nD-49i `ptǜzM NrƭyO0W܀*I+j6&'` Գ8CDָcW$03Xu_"@sϾ ys敾Df<=y$IP^80]Ť<+ &0J"< =߆ !8mp+ ɸ{B2^% Ǘ B騞b0sux0,[׾V}н /.cgPW ƉJ䜨-W:EPp1/,`4`+fwef~YF9>aЖDcڇ׸xT'7`L1Ɨ d8~ǽYHzs޲3?rmᮻ_?kj VFmT/w }a38äAc-ٷ_ Ǥ|L20.LZo _6uL1f¼/G" ]/`[Ձ7]GT W?ugA÷aO9] &moZt9n2Ket0-/n 6`WB [& mL1fcW-Dd:W >o޿5n.c{_EKo8`rz'`[d8 Ҁd֦g]c:|V*s`2.C E?Snk{?d+9ur& PGDg*%KTrJOԁeuK?oЕ[!" tڂ$ &u0\mٲާۗ:91&X-YzW]< Duʹ5>Itp{1 ( 1_0]ȽIM1&2_,yUfU)O~R/X~S:8qdߤ\2n_ƀv[dnuah->^f6,1n2N7͗C~oW/x3K/^Y.^zίU+Y~گ&Hh9i' RHs0+W6lP-_PrfڵzF$"Rq+a6m+2`(d\6o0<#"UWTGDDu00:c:eƇuv{_n'`x%`Xg0 ͛7ۼuց~ | 7= nvn0\X9c!8Q0`xF.sNoW_N k'-MXbDD AíQ-| ’д7{똹YG̚{ǘS4`*` ^}yɀ^*]3cMhDDdM<`FcxcN§"0""RFDDPH )B#""E(`DDi{EUK.1ͼhY: _~yK.$\FDDsΩCd4W-0^zi}%.#"" H )B#""E(`DD"0""RFDDPH )B#""E(`DD"0""RDh)""Ӏ_Y/Zg0""RFDDPH )B#""E(`DD"0""RFDDPH )B#""E(`DDi/Z4`-EDt0"""0""RFDDPH )B#""E(`DD"0""RFDDPH )B#""Et0""b: EK1xz#""E(`DD"0""RFDDPHKαfձȜ94s6Q !,0"29Qsw@t LDlȑ{۷W7V߽уcwQyW޳}wՎL(PRѹ; d:{ɻN{l"0A|SG?.gW߹⊰8dIG`XwN%>`L֏>6ɪWm'g9_INj} lxee-.:?\0M4 zm(`(PRѹ; 4lHhyt|sUݿ,gm_Ӈ0/Ꮞ\P`?XC)``hyJ#%EQБW&`e@~C<я,Ƹa^{@]nY]nEr MߺΚfm{C'ϭ>DWh,0lCa0 _(}fh06` 6 (>>Mh\FYG7P>"{grb7~c]o;x] 3ף̮[j9urnkNaڇl{mfrP?  o~Go6BdUˏT?m?8>>0_{32c0m^t'MMl>=no&`޹`Wn (>$ VFS,t7̸7?qPrӱ~U{W߸m< ۏxvӦNXtBʦ)S0/`&5f Yߨqfw'\lQ0K" Bf:}]4*<즞Fi{'lS ' M}hXFP'-bW1K:>rbtQuiI}[&N:vdҫQkhzMe{_Qg~/d|ǪO=D}z `{h b}Չk6obh臉I;-fnAm15vaGNN0Fm5NZf cl:}!Ӆ[bĿ2?N3}6 <ɼ1&&9{«{pBŞ:4u\Qt t{NC=EAB K;܀pcdi=⟻D`,Xq=w) >a؏2yt灶=ԦS!.uSʘӾw>b}71 &iȆ9 vw&0O  ȩc3l>`X[ose|ԏnkQ0m1)`6 Qsw@̂u<<< T#XD6̋wFp`Da":'aԭ-S'N=zh#2ӆf%>0ƒ}%ay }wAx<J P,|Qsw@t LDqvd:DQ !,0"29Qsw@t̥l)`DDdٲeKqp^իWU;2u[ҙ+VT{W)"ѣkr,kvd2ꀹȑ#Ç<@=LDBFV8M0C×qEp,BU|0fΝGmu#:,Jgؼ.=l7╠)eJ{ܴ6cp=x`]?},㷇2qr E{ۋu6Svz>dݬ9)Fϋm]v5}^=ОC9s֕֡ͦ~bڧha?h>rڑ#;`χ?*29l^WVN n?ۑ,1v I;-pҲ._):rЎmi.|;ul]̷ΏϿM;]Ig4}7C4}^,hÎI:9A[g>DE~qűÜuqJG ;e pP4qr2:v3,V L: :iguXܗtHg6o;\oe=o2?i3l_Luu>1ݾN>DEߞ,w]ԉإDڬrlT&2J1v\quoѩuf;xMPO3J}>m`ܦvfylö#D?/4/}Fۘn_ZǦ!>/YnXY5/g]e2Q\yR8FїIgor_F^Q;>۞qY.S|V}EйF"{Y>h^6}^9ur!aeO嬋,e%uqE+}%˗S^p0q`z/WpԳ6]Qs ('fm6 <4GXcC{&O#\t,;t{iۿHNLiر[?N۾}uraE|^|䮋x}kmzMaκGv~o[godz6:4< Nw-g tF&]ʉoY;t~ڰ WɩCyzFN;b܎&k`ߞa}yr G/iCAghL:`rqr'8h+:JfG'8xhoX[6k+EӖm6f0َ6r|:^ټ_j0 ''}+5'CggA'E0<9ʎ/ sE0|ó[%%oIoDW\D2uH9 )B#""E(`DD"0""Ri( .ދ{IENDB`efm-langserver-0.0.53/testdata/000077500000000000000000000000001457604367300163445ustar00rootroot00000000000000efm-langserver-0.0.53/testdata/foo/000077500000000000000000000000001457604367300171275ustar00rootroot00000000000000efm-langserver-0.0.53/testdata/foo/bar/000077500000000000000000000000001457604367300176735ustar00rootroot00000000000000efm-langserver-0.0.53/testdata/foo/bar/.gitignore000066400000000000000000000000001457604367300216510ustar00rootroot00000000000000efm-langserver-0.0.53/testdata/tags000066400000000000000000001131561457604367300172340ustar00rootroot00000000000000!_TAG_FILE_FORMAT 2 !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted/ !_TAG_PROGRAM_AUTHOR Joel Stemmer /stemmertech@gmail.com/ !_TAG_PROGRAM_NAME gotags !_TAG_PROGRAM_URL https://github.com/jstemmer/gotags !_TAG_PROGRAM_VERSION 1.4.1 /devel +62fb079e2b Thu Sep 19 04:46:17 2019 +0000/ AdditionalTextEdits langserver\lsp.go 277;" w access:public ctype:CompletionItem line:277 type:[]TextEdit Arguments langserver\lsp.go 245;" w access:public ctype:Command line:245 type:[]interface{} Arguments langserver\lsp.go 320;" w access:public ctype:ExecuteCommandParams line:320 type:[]interface{} Capabilities langserver\lsp.go 10;" w access:public ctype:InitializeParams line:10 type:ClientCapabilities Capabilities langserver\lsp.go 24;" w access:public ctype:InitializeResult line:24 type:ServerCapabilities Changes langserver\lsp.go 250;" w access:public ctype:WorkspaceEdit line:250 type:interface{} Character langserver\lsp.go 140;" w access:public ctype:Position line:140 type:int ClassCompletion langserver\lsp.go 208;" c access:public line:208 type:CompletionItemKind ClientCapabilities langserver\lsp.go 19;" t access:public line:19 type:struct Close main.go 120;" m access:public ctype:stdrwc line:120 signature:() type:error Code langserver\lsp.go 153;" w access:public ctype:Diagnostic line:153 type:*string CodeAction langserver\lsp.go 255;" t access:public line:255 type:struct CodeActionContext langserver\lsp.go 339;" t access:public line:339 type:struct CodeActionKind langserver\lsp.go 324;" t access:public line:324 type:string CodeActionParams langserver\lsp.go 350;" t access:public line:350 type:struct CodeActionProvider langserver\lsp.go 51;" w access:public ctype:ServerCapabilities line:51 type:bool ColorCompletion langserver\lsp.go 217;" c access:public line:217 type:CompletionItemKind Command langserver\lsp.go 242;" t access:public line:242 type:struct Command langserver\lsp.go 244;" w access:public ctype:Command line:244 type:string Command langserver\lsp.go 260;" w access:public ctype:CodeAction line:260 type:*Command Command langserver\lsp.go 279;" w access:public ctype:CompletionItem line:279 type:*Command Command langserver\lsp.go 319;" w access:public ctype:ExecuteCommandParams line:319 type:string Commands langserver\handler.go 23;" w access:public ctype:Config line:23 type:[]Command Commands langserver\handler.go 33;" w access:public ctype:Config1 line:33 type:[]Command CommitCharacters langserver\lsp.go 278;" w access:public ctype:CompletionItem line:278 type:[]string CompletionCommand langserver\handler.go 47;" w access:public ctype:Language line:47 type:string CompletionContext langserver\lsp.go 111;" w access:public ctype:CompletionParams line:111 type:CompletionContext CompletionContext langserver\lsp.go 115;" t access:public line:115 type:struct CompletionItem langserver\lsp.go 264;" t access:public line:264 type:struct CompletionItemKind langserver\lsp.go 198;" t access:public line:198 type:int CompletionItemTag langserver\lsp.go 230;" t access:public line:230 type:int CompletionParams langserver\lsp.go 109;" t access:public line:109 type:struct CompletionProvider langserver\lsp.go 38;" t access:public line:38 type:struct CompletionProvider langserver\lsp.go 47;" w access:public ctype:ServerCapabilities line:47 type:*CompletionProvider Config langserver\handler.go 21;" t access:public line:21 type:struct Config1 langserver\handler.go 30;" t access:public line:30 type:struct ConstantCompletion langserver\lsp.go 222;" c access:public line:222 type:CompletionItemKind ConstructorCompletion langserver\lsp.go 205;" c access:public line:205 type:CompletionItemKind ContainerName langserver\lsp.go 194;" w access:public ctype:SymbolInformation line:194 type:*string ContentChanges langserver\lsp.go 93;" w access:public ctype:DidChangeTextDocumentParams line:93 type:[]TextDocumentContentChangeEvent Contents langserver\lsp.go 285;" w access:public ctype:Hover line:285 type:interface{} Context langserver\lsp.go 356;" w access:public ctype:CodeActionParams line:356 type:CodeActionContext Data langserver\lsp.go 280;" w access:public ctype:CompletionItem line:280 type:interface{} DefinitionProvider langserver\lsp.go 48;" w access:public ctype:ServerCapabilities line:48 type:bool Deprecated langserver\lsp.go 192;" w access:public ctype:SymbolInformation line:192 type:bool Deprecated langserver\lsp.go 270;" w access:public ctype:CompletionItem line:270 type:bool Detail langserver\lsp.go 268;" w access:public ctype:CompletionItem line:268 type:string Diagnostic langserver\lsp.go 150;" t access:public line:150 type:struct DiagnosticRelatedInformation langserver\lsp.go 144;" t access:public line:144 type:struct Diagnostics langserver\lsp.go 162;" w access:public ctype:PublishDiagnosticsParams line:162 type:[]Diagnostic Diagnostics langserver\lsp.go 257;" w access:public ctype:CodeAction line:257 type:[]Diagnostic Diagnostics langserver\lsp.go 340;" w access:public ctype:CodeActionContext line:340 type:[]Diagnostic DidChangeConfigurationParams langserver\lsp.go 360;" t access:public line:360 type:struct DidChangeTextDocumentParams langserver\lsp.go 91;" t access:public line:91 type:struct DidCloseTextDocumentParams langserver\lsp.go 79;" t access:public line:79 type:struct DidOpenTextDocumentParams langserver\lsp.go 74;" t access:public line:74 type:struct DidSaveTextDocumentParams langserver\lsp.go 97;" t access:public line:97 type:struct DocumentChanges langserver\lsp.go 251;" w access:public ctype:WorkspaceEdit line:251 type:interface{} DocumentDefinitionParams langserver\lsp.go 371;" t access:public line:371 type:struct DocumentFormattingParams langserver\lsp.go 172;" t access:public line:172 type:struct DocumentFormattingProvider langserver\lsp.go 49;" w access:public ctype:ServerCapabilities line:49 type:bool DocumentSymbolParams langserver\lsp.go 184;" t access:public line:184 type:struct DocumentSymbolProvider langserver\lsp.go 46;" w access:public ctype:ServerCapabilities line:46 type:bool Documentation langserver\lsp.go 269;" w access:public ctype:CompletionItem line:269 type:string Edit langserver\lsp.go 259;" w access:public ctype:CodeAction line:259 type:*WorkspaceEdit Empty langserver\lsp.go 328;" c access:public line:328 type:CodeActionKind End langserver\lsp.go 134;" w access:public ctype:Range line:134 type:Position EnumCompletion langserver\lsp.go 214;" c access:public line:214 type:CompletionItemKind EnumMemberCompletion langserver\lsp.go 221;" c access:public line:221 type:CompletionItemKind Env langserver\handler.go 51;" w access:public ctype:Language line:51 type:[]string EventCompletion langserver\lsp.go 224;" c access:public line:224 type:CompletionItemKind ExecuteCommandParams langserver\lsp.go 316;" t access:public line:316 type:struct FieldCompletion langserver\lsp.go 206;" c access:public line:206 type:CompletionItemKind File langserver\handler.go 82;" t access:public line:82 type:struct FileCompletion langserver\lsp.go 218;" c access:public line:218 type:CompletionItemKind FilterText langserver\lsp.go 273;" w access:public ctype:CompletionItem line:273 type:string FolderCompletion langserver\lsp.go 220;" c access:public line:220 type:CompletionItemKind FormatCommand langserver\handler.go 44;" w access:public ctype:Language line:44 type:string FormatStdin langserver\handler.go 45;" w access:public ctype:Language line:45 type:bool FormattingOptions langserver\lsp.go 166;" t access:public line:166 type:struct FunctionCompletion langserver\lsp.go 204;" c access:public line:204 type:CompletionItemKind Hover langserver\lsp.go 284;" t access:public line:284 type:struct HoverCommand langserver\handler.go 48;" w access:public ctype:Language line:48 type:string HoverParams langserver\lsp.go 121;" t access:public line:121 type:struct HoverProvider langserver\lsp.go 50;" w access:public ctype:ServerCapabilities line:50 type:bool HoverStdin langserver\handler.go 49;" w access:public ctype:Language line:49 type:bool HoverType langserver\handler.go 50;" w access:public ctype:Language line:50 type:string InitializationOptions langserver\lsp.go 9;" w access:public ctype:InitializeParams line:9 type:InitializeOptions InitializeOptions langserver\lsp.go 15;" t access:public line:15 type:struct InitializeParams langserver\lsp.go 6;" t access:public line:6 type:struct InitializeResult langserver\lsp.go 23;" t access:public line:23 type:struct InsertSpaces langserver\lsp.go 168;" w access:public ctype:FormattingOptions line:168 type:bool InsertText langserver\lsp.go 274;" w access:public ctype:CompletionItem line:274 type:string InsertTextFormat langserver\lsp.go 233;" t access:public line:233 type:int InsertTextFormat langserver\lsp.go 275;" w access:public ctype:CompletionItem line:275 type:InsertTextFormat InterfaceCompletion langserver\lsp.go 209;" c access:public line:209 type:CompletionItemKind IsPreferred langserver\lsp.go 258;" w access:public ctype:CodeAction line:258 type:bool KeywordCompletion langserver\lsp.go 215;" c access:public line:215 type:CompletionItemKind Kind langserver\lsp.go 191;" w access:public ctype:SymbolInformation line:191 type:int64 Kind langserver\lsp.go 266;" w access:public ctype:CompletionItem line:266 type:CompletionItemKind Kind langserver\lsp.go 306;" w access:public ctype:MarkupContent line:306 type:MarkupKind Label langserver\lsp.go 265;" w access:public ctype:CompletionItem line:265 type:string Language langserver\handler.go 38;" t access:public line:38 type:struct Language langserver\lsp.go 291;" w access:public ctype:MarkedString line:291 type:string LanguageID langserver\handler.go 83;" w access:public ctype:File line:83 type:string LanguageID langserver\lsp.go 57;" w access:public ctype:TextDocumentItem line:57 type:string Languages langserver\handler.go 24;" w access:public ctype:Config line:24 type:map[string][]Language Languages langserver\handler.go 34;" w access:public ctype:Config1 line:34 type:map[string]Language Line langserver\lsp.go 139;" w access:public ctype:Position line:139 type:int LintCommand langserver\handler.go 42;" w access:public ctype:Language line:42 type:string LintFormats langserver\handler.go 39;" w access:public ctype:Language line:39 type:[]string LintIgnoreExitCode langserver\handler.go 43;" w access:public ctype:Language line:43 type:bool LintOffset langserver\handler.go 41;" w access:public ctype:Language line:41 type:int LintStdin langserver\handler.go 40;" w access:public ctype:Language line:40 type:bool Location langserver\lsp.go 126;" t access:public line:126 type:struct Location langserver\lsp.go 145;" w access:public ctype:DiagnosticRelatedInformation line:145 type:Location Location langserver\lsp.go 193;" w access:public ctype:SymbolInformation line:193 type:Location LogWriter langserver\handler.go 26;" w access:public ctype:Config line:26 type:io.Writer LogWriter langserver\handler.go 32;" w access:public ctype:Config1 line:32 type:io.Writer Markdown langserver\lsp.go 301;" c access:public line:301 MarkedString langserver\lsp.go 290;" t access:public line:290 type:struct MarkupContent langserver\lsp.go 305;" t access:public line:305 type:struct MarkupKind langserver\lsp.go 296;" t access:public line:296 type:string Message langserver\lsp.go 146;" w access:public ctype:DiagnosticRelatedInformation line:146 type:string Message langserver\lsp.go 155;" w access:public ctype:Diagnostic line:155 type:string Method langserver\lsp.go 366;" w access:public ctype:NotificationMessage line:366 type:string MethodCompletion langserver\lsp.go 203;" c access:public line:203 type:CompletionItemKind ModuleCompletion langserver\lsp.go 210;" c access:public line:210 type:CompletionItemKind Name langserver\lsp.go 190;" w access:public ctype:SymbolInformation line:190 type:string NewHandler langserver\handler.go 55;" f access:public line:55 signature:(config *Config) type:jsonrpc2.Handler NewText langserver\lsp.go 180;" w access:public ctype:TextEdit line:180 type:string NotificationMessage langserver\lsp.go 365;" t access:public line:365 type:struct Only langserver\lsp.go 341;" w access:public ctype:CodeActionContext line:341 type:[]CodeActionKind OperatorCompletion langserver\lsp.go 225;" c access:public line:225 type:CompletionItemKind Options langserver\lsp.go 174;" w access:public ctype:DocumentFormattingParams line:174 type:FormattingOptions Params langserver\lsp.go 367;" w access:public ctype:NotificationMessage line:367 type:interface{} PartialResultParams langserver\lsp.go 345;" t access:public line:345 type:struct PartialResultParams langserver\lsp.go 352;" e access:public ctype:CodeActionParams line:352 type:PartialResultParams PartialResultParams langserver\lsp.go 374;" e access:public ctype:DocumentDefinitionParams line:374 type:PartialResultParams PartialResultToken langserver\lsp.go 346;" w access:public ctype:PartialResultParams line:346 type:interface{} PlainText langserver\lsp.go 300;" c access:public line:300 type:MarkupKind PlainTextTextFormat langserver\lsp.go 237;" c access:public line:237 type:InsertTextFormat Position langserver\lsp.go 105;" w access:public ctype:TextDocumentPositionParams line:105 type:Position Position langserver\lsp.go 138;" t access:public line:138 type:struct Preselect langserver\lsp.go 271;" w access:public ctype:CompletionItem line:271 type:bool ProcessID langserver\lsp.go 7;" w access:public ctype:InitializeParams line:7 type:int PropertyCompletion langserver\lsp.go 211;" c access:public line:211 type:CompletionItemKind PublishDiagnosticsParams langserver\lsp.go 160;" t access:public line:160 type:struct QuickFix langserver\lsp.go 329;" c access:public line:329 Range langserver\lsp.go 128;" w access:public ctype:Location line:128 type:Range Range langserver\lsp.go 132;" t access:public line:132 type:struct Range langserver\lsp.go 151;" w access:public ctype:Diagnostic line:151 type:Range Range langserver\lsp.go 179;" w access:public ctype:TextEdit line:179 type:Range Range langserver\lsp.go 286;" w access:public ctype:Hover line:286 type:*Range Range langserver\lsp.go 355;" w access:public ctype:CodeActionParams line:355 type:Range Range langserver\lsp.go 85;" w access:public ctype:TextDocumentContentChangeEvent line:85 type:Range RangeLength langserver\lsp.go 86;" w access:public ctype:TextDocumentContentChangeEvent line:86 type:int Read main.go 112;" m access:public ctype:stdrwc line:112 signature:(p []byte) type:int, error Refactor langserver\lsp.go 330;" c access:public line:330 RefactorExtract langserver\lsp.go 331;" c access:public line:331 RefactorInline langserver\lsp.go 332;" c access:public line:332 RefactorRewrite langserver\lsp.go 333;" c access:public line:333 ReferenceCompletion langserver\lsp.go 219;" c access:public line:219 type:CompletionItemKind RelatedInformation langserver\lsp.go 156;" w access:public ctype:Diagnostic line:156 type:[]DiagnosticRelatedInformation ResolveProvider langserver\lsp.go 39;" w access:public ctype:CompletionProvider line:39 type:bool RootURI langserver\lsp.go 8;" w access:public ctype:InitializeParams line:8 type:string ServerCapabilities langserver\lsp.go 44;" t access:public line:44 type:struct Settings langserver\lsp.go 361;" w access:public ctype:DidChangeConfigurationParams line:361 type:interface{} Severity langserver\lsp.go 152;" w access:public ctype:Diagnostic line:152 type:int SnippetCompletion langserver\lsp.go 216;" c access:public line:216 type:CompletionItemKind SnippetTextFormat langserver\lsp.go 238;" c access:public line:238 type:InsertTextFormat SortText langserver\lsp.go 272;" w access:public ctype:CompletionItem line:272 type:string Source langserver\lsp.go 154;" w access:public ctype:Diagnostic line:154 type:*string Source langserver\lsp.go 334;" c access:public line:334 SourceOrganizeImports langserver\lsp.go 335;" c access:public line:335 Start langserver\lsp.go 133;" w access:public ctype:Range line:133 type:Position StructCompletion langserver\lsp.go 223;" c access:public line:223 type:CompletionItemKind SymbolCommand langserver\handler.go 46;" w access:public ctype:Language line:46 type:string SymbolInformation langserver\lsp.go 189;" t access:public line:189 type:struct TDSKFull langserver\lsp.go 33;" c access:public line:33 TDSKIncremental langserver\lsp.go 34;" c access:public line:34 TDSKNone langserver\lsp.go 32;" c access:public line:32 type:TextDocumentSyncKind TabSize langserver\lsp.go 167;" w access:public ctype:FormattingOptions line:167 type:int64 Tags langserver\lsp.go 267;" w access:public ctype:CompletionItem line:267 type:[]CompletionItemTag TestCtags langserver\handle_text_document_definition_test.go 21;" f access:public line:21 signature:(t *testing.T) TestFindTags langserver\handle_text_document_definition_test.go 9;" f access:public line:9 signature:(t *testing.T) TestLintFileMatched langserver\handler_test.go 40;" f access:public line:40 signature:(t *testing.T) TestLintFileMatchedForce langserver\handler_test.go 91;" f access:public line:91 signature:(t *testing.T) TestLintNoFileMatched langserver\handler_test.go 25;" f access:public line:25 signature:(t *testing.T) TestLintNoLinter langserver\handler_test.go 10;" f access:public line:10 signature:(t *testing.T) Text langserver\handler.go 84;" w access:public ctype:File line:84 type:string Text langserver\lsp.go 59;" w access:public ctype:TextDocumentItem line:59 type:string Text langserver\lsp.go 87;" w access:public ctype:TextDocumentContentChangeEvent line:87 type:string Text langserver\lsp.go 98;" w access:public ctype:DidSaveTextDocumentParams line:98 type:*string TextCompletion langserver\lsp.go 202;" c access:public line:202 type:CompletionItemKind TextDocument langserver\lsp.go 104;" w access:public ctype:TextDocumentPositionParams line:104 type:TextDocumentIdentifier TextDocument langserver\lsp.go 173;" w access:public ctype:DocumentFormattingParams line:173 type:TextDocumentIdentifier TextDocument langserver\lsp.go 185;" w access:public ctype:DocumentSymbolParams line:185 type:TextDocumentIdentifier TextDocument langserver\lsp.go 354;" w access:public ctype:CodeActionParams line:354 type:TextDocumentIdentifier TextDocument langserver\lsp.go 75;" w access:public ctype:DidOpenTextDocumentParams line:75 type:TextDocumentItem TextDocument langserver\lsp.go 80;" w access:public ctype:DidCloseTextDocumentParams line:80 type:TextDocumentIdentifier TextDocument langserver\lsp.go 92;" w access:public ctype:DidChangeTextDocumentParams line:92 type:VersionedTextDocumentIdentifier TextDocument langserver\lsp.go 99;" w access:public ctype:DidSaveTextDocumentParams line:99 type:TextDocumentIdentifier TextDocumentContentChangeEvent langserver\lsp.go 84;" t access:public line:84 type:struct TextDocumentIdentifier langserver\lsp.go 64;" e access:public ctype:VersionedTextDocumentIdentifier line:64 type:TextDocumentIdentifier TextDocumentIdentifier langserver\lsp.go 69;" t access:public line:69 type:struct TextDocumentItem langserver\lsp.go 55;" t access:public line:55 type:struct TextDocumentPositionParams langserver\lsp.go 103;" t access:public line:103 type:struct TextDocumentPositionParams langserver\lsp.go 110;" e access:public ctype:CompletionParams line:110 type:TextDocumentPositionParams TextDocumentPositionParams langserver\lsp.go 122;" e access:public ctype:HoverParams line:122 type:TextDocumentPositionParams TextDocumentPositionParams langserver\lsp.go 372;" e access:public ctype:DocumentDefinitionParams line:372 type:TextDocumentPositionParams TextDocumentSync langserver\lsp.go 45;" w access:public ctype:ServerCapabilities line:45 type:TextDocumentSyncKind TextDocumentSyncKind langserver\lsp.go 28;" t access:public line:28 type:int TextEdit langserver\lsp.go 178;" t access:public line:178 type:struct TextEdit langserver\lsp.go 276;" w access:public ctype:CompletionItem line:276 type:*TextEdit Title langserver\lsp.go 243;" w access:public ctype:Command line:243 type:string Title langserver\lsp.go 256;" w access:public ctype:CodeAction line:256 type:string Trace langserver\lsp.go 11;" w access:public ctype:InitializeParams line:11 type:string TriggerCharacter langserver\lsp.go 117;" w access:public ctype:CompletionContext line:117 type:*string TriggerCharacters langserver\lsp.go 40;" w access:public ctype:CompletionProvider line:40 type:[]string TriggerKind langserver\lsp.go 116;" w access:public ctype:CompletionContext line:116 type:int TypeParameterCompletion langserver\lsp.go 226;" c access:public line:226 type:CompletionItemKind URI langserver\lsp.go 127;" w access:public ctype:Location line:127 type:string URI langserver\lsp.go 161;" w access:public ctype:PublishDiagnosticsParams line:161 type:string URI langserver\lsp.go 56;" w access:public ctype:TextDocumentItem line:56 type:string URI langserver\lsp.go 70;" w access:public ctype:TextDocumentIdentifier line:70 type:string UnitCompletion langserver\lsp.go 212;" c access:public line:212 type:CompletionItemKind Value langserver\lsp.go 292;" w access:public ctype:MarkedString line:292 type:string Value langserver\lsp.go 307;" w access:public ctype:MarkupContent line:307 type:string ValueCompletion langserver\lsp.go 213;" c access:public line:213 type:CompletionItemKind VariableCompletion langserver\lsp.go 207;" c access:public line:207 type:CompletionItemKind Version langserver\handler.go 22;" w access:public ctype:Config line:22 type:int Version langserver\handler.go 31;" w access:public ctype:Config1 line:31 type:int Version langserver\lsp.go 58;" w access:public ctype:TextDocumentItem line:58 type:int Version langserver\lsp.go 65;" w access:public ctype:VersionedTextDocumentIdentifier line:65 type:int VersionedTextDocumentIdentifier langserver\lsp.go 63;" t access:public line:63 type:struct WorkDoneProgressParams langserver\lsp.go 311;" t access:public line:311 type:struct WorkDoneProgressParams langserver\lsp.go 317;" e access:public ctype:ExecuteCommandParams line:317 type:WorkDoneProgressParams WorkDoneProgressParams langserver\lsp.go 351;" e access:public ctype:CodeActionParams line:351 type:WorkDoneProgressParams WorkDoneProgressParams langserver\lsp.go 373;" e access:public ctype:DocumentDefinitionParams line:373 type:WorkDoneProgressParams WorkDoneToken langserver\lsp.go 312;" w access:public ctype:WorkDoneProgressParams line:312 type:interface{} WorkspaceEdit langserver\lsp.go 249;" t access:public line:249 type:struct Write main.go 116;" m access:public ctype:stdrwc line:116 signature:(p []byte) type:int, error bufio langserver\handle_text_document_completion.go 4;" i line:4 bufio langserver\handle_text_document_definition.go 4;" i line:4 bufio langserver\handle_text_document_symbol.go 4;" i line:4 bytes langserver\handle_text_document_completion.go 5;" i line:5 bytes langserver\handle_text_document_symbol.go 5;" i line:5 closeFile langserver\handler.go 267;" m access:private ctype:langHandler line:267 signature:(uri string) type:error codeAction langserver\handle_text_document_code_action.go 85;" m access:private ctype:langHandler line:85 signature:(uri string, params *CodeActionParams) type:[]Command, error commands langserver\handler.go 73;" w access:private ctype:langHandler line:73 type:[]Command completion langserver\handle_text_document_completion.go 31;" m access:private ctype:langHandler line:31 signature:(uri string, params *CompletionParams) type:[]CompletionItem, error configFor langserver\handler.go 297;" m access:private ctype:langHandler line:297 signature:(uri string) type:[]Language configs langserver\handler.go 74;" w access:private ctype:langHandler line:74 type:map[string][]Language conn langserver\handler.go 77;" w access:private ctype:langHandler line:77 type:*jsonrpc2.Conn context langserver\handle_initialize.go 4;" i line:4 context langserver\handle_shutdown.go 4;" i line:4 context langserver\handle_text_document_code_action.go 4;" i line:4 context langserver\handle_text_document_completion.go 6;" i line:6 context langserver\handle_text_document_definition.go 5;" i line:5 context langserver\handle_text_document_did_change.go 4;" i line:4 context langserver\handle_text_document_did_close.go 4;" i line:4 context langserver\handle_text_document_did_open.go 4;" i line:4 context langserver\handle_text_document_did_save.go 4;" i line:4 context langserver\handle_text_document_formatting.go 4;" i line:4 context langserver\handle_text_document_hover.go 4;" i line:4 context langserver\handle_text_document_symbol.go 6;" i line:6 context langserver\handle_workspace_did_change_configuration.go 4;" i line:4 context langserver\handle_workspace_execute_command.go 4;" i line:4 context langserver\handler.go 4;" i line:4 context main.go 4;" i line:4 ctags langserver\handle_text_document_definition.go 34;" m access:private ctype:langHandler line:34 signature:(fname string, word string) type:[]Location, error definition langserver\handle_text_document_definition.go 112;" m access:private ctype:langHandler line:112 signature:(uri string, params *DocumentDefinitionParams) type:[]Location, error didChangeConfiguration langserver\handler.go 309;" m access:private ctype:langHandler line:309 signature:(params *DidChangeConfigurationParams) type:interface{}, error encoding/json langserver\handle_initialize.go 5;" i line:5 encoding/json langserver\handle_text_document_code_action.go 5;" i line:5 encoding/json langserver\handle_text_document_completion.go 7;" i line:7 encoding/json langserver\handle_text_document_definition.go 6;" i line:6 encoding/json langserver\handle_text_document_did_change.go 5;" i line:5 encoding/json langserver\handle_text_document_did_close.go 5;" i line:5 encoding/json langserver\handle_text_document_did_open.go 5;" i line:5 encoding/json langserver\handle_text_document_did_save.go 5;" i line:5 encoding/json langserver\handle_text_document_formatting.go 5;" i line:5 encoding/json langserver\handle_text_document_hover.go 5;" i line:5 encoding/json langserver\handle_text_document_symbol.go 7;" i line:7 encoding/json langserver\handle_workspace_did_change_configuration.go 5;" i line:5 encoding/json langserver\handle_workspace_execute_command.go 5;" i line:5 executeCommand langserver\handle_text_document_code_action.go 28;" m access:private ctype:langHandler line:28 signature:(params *ExecuteCommandParams) type:interface{}, error files langserver\handler.go 75;" w access:private ctype:langHandler line:75 type:map[string]*File findTags langserver\handle_text_document_definition.go 94;" m access:private ctype:langHandler line:94 signature:(fname string) type:string flag main.go 5;" i line:5 fmt langserver\handle_text_document_code_action.go 6;" i line:6 fmt langserver\handle_text_document_completion.go 8;" i line:8 fmt langserver\handle_text_document_definition.go 7;" i line:7 fmt langserver\handle_text_document_definition_test.go 4;" i line:4 fmt langserver\handle_text_document_formatting.go 6;" i line:6 fmt langserver\handle_text_document_hover.go 6;" i line:6 fmt langserver\handle_text_document_symbol.go 8;" i line:8 fmt langserver\handler.go 5;" i line:5 fmt main.go 6;" i line:6 formatting langserver\handle_text_document_formatting.go 29;" m access:private ctype:langHandler line:29 signature:(uri string) type:[]TextEdit, error fromURI langserver\handler.go 101;" f access:private line:101 signature:(uri string) type:string, error github.com/mattn/efm-langserver/langserver main.go 14;" i line:14 github.com/mattn/go-unicodeclass langserver\handle_text_document_definition.go 17;" i line:17 github.com/mattn/go-unicodeclass langserver\handle_text_document_hover.go 13;" i line:13 github.com/reviewdog/errorformat langserver\handle_text_document_symbol.go 15;" i line:15 github.com/reviewdog/errorformat langserver\handler.go 16;" i line:16 github.com/sourcegraph/jsonrpc2 langserver\handle_initialize.go 8;" i line:8 github.com/sourcegraph/jsonrpc2 langserver\handle_shutdown.go 6;" i line:6 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_code_action.go 12;" i line:12 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_completion.go 15;" i line:15 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_definition.go 18;" i line:18 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_did_change.go 7;" i line:7 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_did_close.go 7;" i line:7 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_did_open.go 7;" i line:7 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_did_save.go 7;" i line:7 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_formatting.go 13;" i line:13 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_hover.go 14;" i line:14 github.com/sourcegraph/jsonrpc2 langserver\handle_text_document_symbol.go 16;" i line:16 github.com/sourcegraph/jsonrpc2 langserver\handle_workspace_did_change_configuration.go 7;" i line:7 github.com/sourcegraph/jsonrpc2 langserver\handle_workspace_execute_command.go 7;" i line:7 github.com/sourcegraph/jsonrpc2 langserver\handler.go 17;" i line:17 github.com/sourcegraph/jsonrpc2 main.go 15;" i line:15 gopkg.in/yaml.v3 main.go 12;" i line:12 handle langserver\handler.go 313;" m access:private ctype:langHandler line:313 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleInitialize langserver\handle_initialize.go 11;" m access:private ctype:langHandler line:11 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleShutdown langserver\handle_shutdown.go 9;" m access:private ctype:langHandler line:9 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentCodeAction langserver\handle_text_document_code_action.go 15;" m access:private ctype:langHandler line:15 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentCompletion langserver\handle_text_document_completion.go 18;" m access:private ctype:langHandler line:18 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentDefinition langserver\handle_text_document_definition.go 21;" m access:private ctype:langHandler line:21 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentDidChange langserver\handle_text_document_did_change.go 10;" m access:private ctype:langHandler line:10 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentDidClose langserver\handle_text_document_did_close.go 10;" m access:private ctype:langHandler line:10 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentDidOpen langserver\handle_text_document_did_open.go 10;" m access:private ctype:langHandler line:10 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentDidSave langserver\handle_text_document_did_save.go 10;" m access:private ctype:langHandler line:10 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentFormatting langserver\handle_text_document_formatting.go 16;" m access:private ctype:langHandler line:16 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentHover langserver\handle_text_document_hover.go 17;" m access:private ctype:langHandler line:17 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleTextDocumentSymbol langserver\handle_text_document_symbol.go 19;" m access:private ctype:langHandler line:19 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleWorkspaceDidChangeConfiguration langserver\handle_workspace_did_change_configuration.go 10;" m access:private ctype:langHandler line:10 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error handleWorkspaceExecuteCommand langserver\handle_workspace_execute_command.go 10;" m access:private ctype:langHandler line:10 signature:(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) type:interface{}, error hover langserver\handle_text_document_hover.go 30;" m access:private ctype:langHandler line:30 signature:(uri string, params *HoverParams) type:*Hover, error io langserver\handler.go 6;" i line:6 io/ioutil langserver\handle_text_document_definition.go 8;" i line:8 isWindowsDrivePath langserver\handler.go 87;" f access:private line:87 signature:(path string) type:bool isWindowsDriveURI langserver\handler.go 94;" f access:private line:94 signature:(uri string) type:bool langHandler langserver\handler.go 71;" t access:private line:71 type:struct langserver langserver\handle_initialize.go 1;" p line:1 langserver langserver\handle_shutdown.go 1;" p line:1 langserver langserver\handle_text_document_code_action.go 1;" p line:1 langserver langserver\handle_text_document_completion.go 1;" p line:1 langserver langserver\handle_text_document_definition.go 1;" p line:1 langserver langserver\handle_text_document_definition_test.go 1;" p line:1 langserver langserver\handle_text_document_did_change.go 1;" p line:1 langserver langserver\handle_text_document_did_close.go 1;" p line:1 langserver langserver\handle_text_document_did_open.go 1;" p line:1 langserver langserver\handle_text_document_did_save.go 1;" p line:1 langserver langserver\handle_text_document_formatting.go 1;" p line:1 langserver langserver\handle_text_document_hover.go 1;" p line:1 langserver langserver\handle_text_document_symbol.go 1;" p line:1 langserver langserver\handle_workspace_did_change_configuration.go 1;" p line:1 langserver langserver\handle_workspace_execute_command.go 1;" p line:1 langserver langserver\handler.go 1;" p line:1 langserver langserver\handler_test.go 1;" p line:1 langserver langserver\lsp.go 1;" p line:1 lint langserver\handler.go 146;" m access:private ctype:langHandler line:146 signature:(uri string) type:[]Diagnostic, error linter langserver\handler.go 125;" m access:private ctype:langHandler line:125 signature:() loadConfig main.go 18;" f access:private line:18 signature:(yamlfile string) type:*langserver.Config, error log langserver\handle_text_document_definition_test.go 5;" i line:5 log langserver\handler.go 7;" i line:7 log langserver\handler_test.go 4;" i line:4 log main.go 7;" i line:7 logger langserver\handler.go 72;" w access:private ctype:langHandler line:72 type:*log.Logger main main.go 1;" p line:1 main main.go 51;" f access:private line:51 signature:() net/url langserver\handler.go 8;" i line:8 openFile langserver\handler.go 277;" m access:private ctype:langHandler line:277 signature:(uri string, languageID string) type:error os langserver\handle_text_document_completion.go 9;" i line:9 os langserver\handle_text_document_definition.go 9;" i line:9 os langserver\handle_text_document_formatting.go 7;" i line:7 os langserver\handle_text_document_hover.go 7;" i line:7 os langserver\handle_text_document_symbol.go 9;" i line:9 os langserver\handler.go 9;" i line:9 os main.go 8;" i line:8 os/exec langserver\handle_text_document_code_action.go 7;" i line:7 os/exec langserver\handle_text_document_completion.go 10;" i line:10 os/exec langserver\handle_text_document_formatting.go 8;" i line:8 os/exec langserver\handle_text_document_hover.go 8;" i line:8 os/exec langserver\handle_text_document_symbol.go 10;" i line:10 os/exec langserver\handler.go 10;" i line:10 path/filepath langserver\handle_initialize.go 6;" i line:6 path/filepath langserver\handle_text_document_code_action.go 8;" i line:8 path/filepath langserver\handle_text_document_completion.go 11;" i line:11 path/filepath langserver\handle_text_document_definition.go 10;" i line:10 path/filepath langserver\handle_text_document_formatting.go 9;" i line:9 path/filepath langserver\handle_text_document_symbol.go 11;" i line:11 path/filepath langserver\handler.go 11;" i line:11 path/filepath main.go 9;" i line:9 regexp langserver\handle_text_document_definition.go 11;" i line:11 request langserver\handler.go 76;" w access:private ctype:langHandler line:76 type:chan string rootPath langserver\handler.go 78;" w access:private ctype:langHandler line:78 type:string runtime langserver\handle_text_document_code_action.go 9;" i line:9 runtime langserver\handle_text_document_completion.go 12;" i line:12 runtime langserver\handle_text_document_definition.go 12;" i line:12 runtime langserver\handle_text_document_formatting.go 10;" i line:10 runtime langserver\handle_text_document_hover.go 9;" i line:9 runtime langserver\handle_text_document_symbol.go 12;" i line:12 runtime langserver\handler.go 12;" i line:12 runtime langserver\handler_test.go 5;" i line:5 runtime main.go 10;" i line:10 saveFile langserver\handler.go 272;" m access:private ctype:langHandler line:272 signature:(uri string) type:error stdrwc main.go 110;" t access:private line:110 type:struct strconv langserver\handle_text_document_definition.go 13;" i line:13 strings langserver\handle_text_document_code_action.go 10;" i line:10 strings langserver\handle_text_document_completion.go 13;" i line:13 strings langserver\handle_text_document_definition.go 14;" i line:14 strings langserver\handle_text_document_formatting.go 11;" i line:11 strings langserver\handle_text_document_hover.go 10;" i line:10 strings langserver\handle_text_document_symbol.go 13;" i line:13 strings langserver\handler.go 13;" i line:13 strings langserver\handler_test.go 6;" i line:6 symbol langserver\handle_text_document_symbol.go 61;" m access:private ctype:langHandler line:61 signature:(uri string) type:[]SymbolInformation, error symbolKindMap langserver\handle_text_document_symbol.go 32;" v access:private line:32 testing langserver\handle_text_document_definition_test.go 6;" i line:6 testing langserver\handler_test.go 7;" i line:7 toURI langserver\handler.go 115;" f access:private line:115 signature:(path string) type:*url.URL unicode langserver\handler.go 14;" i line:14 unicode/utf16 langserver\handle_text_document_definition.go 15;" i line:15 unicode/utf16 langserver\handle_text_document_hover.go 11;" i line:11 updateFile langserver\handler.go 286;" m access:private ctype:langHandler line:286 signature:(uri string, text string) type:error wildcard langserver\lsp.go 3;" c access:private line:3