and readded mlsp
parent
a8c20f979a
commit
b252e63580
|
|
@ -0,0 +1,223 @@
|
|||
-- globals from micro: https://github.com/zyedidia/micro/blob/master/runtime/help/plugins.md
|
||||
|
||||
read_globals = {
|
||||
"import",
|
||||
}
|
||||
|
||||
globals = {
|
||||
"VERSION",
|
||||
"init",
|
||||
"preinit",
|
||||
"postinit",
|
||||
"onSetActive",
|
||||
"onBufferOpen",
|
||||
"onBufPaneOpen",
|
||||
"onRune",
|
||||
"preRune",
|
||||
|
||||
"onCursorUp",
|
||||
"onCursorDown",
|
||||
"onCursorPageUp",
|
||||
"onCursorPageDown",
|
||||
"onCursorLeft",
|
||||
"onCursorRight",
|
||||
"onCursorStart",
|
||||
"onCursorEnd",
|
||||
"onSelectToStart",
|
||||
"onSelectToEnd",
|
||||
"onSelectUp",
|
||||
"onSelectDown",
|
||||
"onSelectLeft",
|
||||
"onSelectRight",
|
||||
"onSelectToStartOfText",
|
||||
"onSelectToStartOfTextToggle",
|
||||
"onWordRight",
|
||||
"onWordLeft",
|
||||
"onSelectWordRight",
|
||||
"onSelectWordLeft",
|
||||
"onMoveLinesUp",
|
||||
"onMoveLinesDown",
|
||||
"onDeleteWordRight",
|
||||
"onDeleteWordLeft",
|
||||
"onSelectLine",
|
||||
"onSelectToStartOfLine",
|
||||
"onSelectToEndOfLine",
|
||||
"onInsertNewline",
|
||||
"onInsertSpace",
|
||||
"onBackspace",
|
||||
"onDelete",
|
||||
"onCenter",
|
||||
"onInsertTab",
|
||||
"onSave",
|
||||
"onSaveAll",
|
||||
"onSaveAs",
|
||||
"onFind",
|
||||
"onFindLiteral",
|
||||
"onFindNext",
|
||||
"onFindPrevious",
|
||||
"onDiffPrevious",
|
||||
"onDiffNext",
|
||||
"onUndo",
|
||||
"onRedo",
|
||||
"onCopy",
|
||||
"onCopyLine",
|
||||
"onCut",
|
||||
"onCutLine",
|
||||
"onDuplicateLine",
|
||||
"onDeleteLine",
|
||||
"onIndentSelection",
|
||||
"onOutdentSelection",
|
||||
"onOutdentLine",
|
||||
"onIndentLine",
|
||||
"onPaste",
|
||||
"onSelectAll",
|
||||
"onOpenFile",
|
||||
"onStart",
|
||||
"onEnd",
|
||||
"onPageUp",
|
||||
"onPageDown",
|
||||
"onSelectPageUp",
|
||||
"onSelectPageDown",
|
||||
"onHalfPageUp",
|
||||
"onHalfPageDown",
|
||||
"onStartOfLine",
|
||||
"onEndOfLine",
|
||||
"onStartOfText",
|
||||
"onStartOfTextToggle",
|
||||
"onParagraphPrevious",
|
||||
"onParagraphNext",
|
||||
"onToggleHelp",
|
||||
"onToggleDiffGutter",
|
||||
"onToggleRuler",
|
||||
"onJumpLine",
|
||||
"onClearStatus",
|
||||
"onShellMode",
|
||||
"onCommandMode",
|
||||
"onQuit",
|
||||
"onQuitAll",
|
||||
"onAddTab",
|
||||
"onPreviousTab",
|
||||
"onNextTab",
|
||||
"onNextSplit",
|
||||
"onUnsplit",
|
||||
"onVSplit",
|
||||
"onHSplit",
|
||||
"onPreviousSplit",
|
||||
"onToggleMacro",
|
||||
"onPlayMacro",
|
||||
"onSuspend",
|
||||
"onScrollUp",
|
||||
"onScrollDown",
|
||||
"onSpawnMultiCursor",
|
||||
"onSpawnMultiCursorUp",
|
||||
"onSpawnMultiCursorDown",
|
||||
"onSpawnMultiCursorSelect",
|
||||
"onRemoveMultiCursor",
|
||||
"onRemoveAllMultiCursors",
|
||||
"onSkipMultiCursor",
|
||||
"onJumpToMatchingBrace",
|
||||
"onAutocomplete",
|
||||
|
||||
"preCursorUp",
|
||||
"preCursorDown",
|
||||
"preCursorPageUp",
|
||||
"preCursorPageDown",
|
||||
"preCursorLeft",
|
||||
"preCursorRight",
|
||||
"preCursorStart",
|
||||
"preCursorEnd",
|
||||
"preSelectToStart",
|
||||
"preSelectToEnd",
|
||||
"preSelectUp",
|
||||
"preSelectDown",
|
||||
"preSelectLeft",
|
||||
"preSelectRight",
|
||||
"preSelectToStartOfText",
|
||||
"preSelectToStartOfTextToggle",
|
||||
"preWordRight",
|
||||
"preWordLeft",
|
||||
"preSelectWordRight",
|
||||
"preSelectWordLeft",
|
||||
"preMoveLinesUp",
|
||||
"preMoveLinesDown",
|
||||
"preDeleteWordRight",
|
||||
"preDeleteWordLeft",
|
||||
"preSelectLine",
|
||||
"preSelectToStartOfLine",
|
||||
"preSelectToEndOfLine",
|
||||
"preInsertNewline",
|
||||
"preInsertSpace",
|
||||
"preBackspace",
|
||||
"preDelete",
|
||||
"preCenter",
|
||||
"preInsertTab",
|
||||
"preSave",
|
||||
"preSaveAll",
|
||||
"preSaveAs",
|
||||
"preFind",
|
||||
"preFindLiteral",
|
||||
"preFindNext",
|
||||
"preFindPrevious",
|
||||
"preDiffPrevious",
|
||||
"preDiffNext",
|
||||
"preUndo",
|
||||
"preRedo",
|
||||
"preCopy",
|
||||
"preCopyLine",
|
||||
"preCut",
|
||||
"preCutLine",
|
||||
"preDuplicateLine",
|
||||
"preDeleteLine",
|
||||
"preIndentSelection",
|
||||
"preOutdentSelection",
|
||||
"preOutdentLine",
|
||||
"preIndentLine",
|
||||
"prePaste",
|
||||
"preSelectAll",
|
||||
"preOpenFile",
|
||||
"preStart",
|
||||
"preEnd",
|
||||
"prePageUp",
|
||||
"prePageDown",
|
||||
"preSelectPageUp",
|
||||
"preSelectPageDown",
|
||||
"preHalfPageUp",
|
||||
"preHalfPageDown",
|
||||
"preStartOfLine",
|
||||
"preEndOfLine",
|
||||
"preStartOfText",
|
||||
"preStartOfTextToggle",
|
||||
"preParagraphPrevious",
|
||||
"preParagraphNext",
|
||||
"preToggleHelp",
|
||||
"preToggleDiffGutter",
|
||||
"preToggleRuler",
|
||||
"preJumpLine",
|
||||
"preClearStatus",
|
||||
"preShellMode",
|
||||
"preCommandMode",
|
||||
"preQuit",
|
||||
"preQuitAll",
|
||||
"preAddTab",
|
||||
"prePreviousTab",
|
||||
"preNextTab",
|
||||
"preNextSplit",
|
||||
"preUnsplit",
|
||||
"preVSplit",
|
||||
"preHSplit",
|
||||
"prePreviousSplit",
|
||||
"preToggleMacro",
|
||||
"prePlayMacro",
|
||||
"preSuspend",
|
||||
"preScrollUp",
|
||||
"preScrollDown",
|
||||
"preSpawnMultiCursor",
|
||||
"preSpawnMultiCursorUp",
|
||||
"preSpawnMultiCursorDown",
|
||||
"preSpawnMultiCursorSelect",
|
||||
"preRemoveMultiCursor",
|
||||
"preRemoveAllMultiCursors",
|
||||
"preSkipMultiCursor",
|
||||
"preJumpToMatchingBrace",
|
||||
"preAutocomplete",
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Andriamanitra
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
# List of language servers & how to get them
|
||||
|
||||
* [C/C++](#cc)
|
||||
* [Clojure](#clojure)
|
||||
* [Crystal](#crystal)
|
||||
* [Go](#go)
|
||||
* [Haskell](#haskell)
|
||||
* [JavaScript/TypeScript](#javascripttypescript)
|
||||
* [JSON](#json)
|
||||
* [Lua](#lua)
|
||||
* [Markdown](#markdown)
|
||||
* [Python](#python)
|
||||
* [Ruby](#ruby)
|
||||
* [Rust](#rust)
|
||||
* [Zig](#zig)
|
||||
|
||||
## C/C++
|
||||
|
||||
- [Clangd](https://clangd.llvm.org/)
|
||||
- Installation: [instructions](https://clangd.llvm.org/installation.html)
|
||||
- Command: `clangd`
|
||||
|
||||
## Clojure
|
||||
|
||||
- [clojure-lsp](https://github.com/clojure-lsp/clojure-lsp)
|
||||
- Installation: [instructions](https://clojure-lsp.io/installation/)
|
||||
- Command: `clojure-lsp`
|
||||
|
||||
## Crystal
|
||||
|
||||
- [Crystalline](https://github.com/elbywan/crystalline)
|
||||
- Installation:
|
||||
[instructions](https://github.com/elbywan/crystalline#global-install)
|
||||
- Command: `crystalline`
|
||||
|
||||
## Go
|
||||
|
||||
- [gopls](https://pkg.go.dev/golang.org/x/tools/gopls)
|
||||
- Installation:
|
||||
[instructions](https://pkg.go.dev/golang.org/x/tools/gopls#readme-installation)
|
||||
- Command: `gopls`
|
||||
|
||||
## Haskell
|
||||
|
||||
- [HLS](https://github.com/haskell/haskell-language-server)
|
||||
- Installation: use [ghcup](https://www.haskell.org/ghcup/)
|
||||
- Command: `haskell-language-server-wrapper --lsp`
|
||||
|
||||
## JavaScript/TypeScript
|
||||
|
||||
- [deno](https://github.com/denoland/deno)
|
||||
- Installation:
|
||||
[instructions](https://github.com/denoland/deno_install/blob/master/README.md#deno_install)
|
||||
- Command: `deno lsp`
|
||||
|
||||
- [quick-lint-js](https://github.com/quick-lint/quick-lint-js)
|
||||
- Only diagnostics (no formatting, hover information or code navigation)
|
||||
- Installation: `npm install -g quick-lint-js`
|
||||
- Command: `quick-lint-js --lsp`
|
||||
|
||||
## JSON
|
||||
|
||||
- [deno](https://github.com/denoland/deno)
|
||||
- Installation:
|
||||
[instructions](https://github.com/denoland/deno_install/blob/master/README.md#deno_install)
|
||||
- Command: `deno lsp`
|
||||
|
||||
## Lua
|
||||
|
||||
- [luals](https://github.com/luals/lua-language-server)
|
||||
- Installation: [instructions](https://luals.github.io/#other-install)
|
||||
- Command: `lua-language-server` (make sure the executable was installed in your $PATH)
|
||||
- [lua-lsp](https://github.com/Alloyed/lua-lsp)
|
||||
- Unmaintained. You are in for trouble if you want to get it to work with Lua
|
||||
5.4.
|
||||
- Installation: `luarocks install lua-lsp`
|
||||
([luarocks](https://github.com/luarocks/luarocks))
|
||||
- Command: `lua-lsp`
|
||||
|
||||
## Markdown
|
||||
|
||||
- [deno](https://github.com/denoland/deno)
|
||||
- Installation:
|
||||
[instructions](https://github.com/denoland/deno_install/blob/master/README.md#deno_install)
|
||||
- Command: `deno lsp`
|
||||
|
||||
## Python
|
||||
|
||||
- [pylsp](https://github.com/python-lsp/python-lsp-server)
|
||||
- Installation: `pip install python-lsp-server[all]`
|
||||
- Command: `pylsp`
|
||||
|
||||
- [Pyright](https://github.com/microsoft/pyright)
|
||||
- Installation: `npm install -g pyright`
|
||||
- Command: `pyright-langserver --stdio`
|
||||
|
||||
- [ruff](https://github.com/astral-sh/ruff)
|
||||
- Only diagnostics, formatting and code actions (no hover information or code navigation)
|
||||
- Installation: `pip install ruff`
|
||||
- Command: `ruff server`
|
||||
|
||||
## Ruby
|
||||
|
||||
- [ruby-lsp](https://github.com/Shopify/ruby-lsp)
|
||||
- Installation: `gem install ruby-lsp`
|
||||
- Command: `ruby-lsp`
|
||||
|
||||
- [solargraph](https://github.com/castwide/solargraph)
|
||||
- Installation: `gem install solargraph`
|
||||
- Command: `solargraph stdio`
|
||||
|
||||
## Rust
|
||||
|
||||
- [rust-analyzer](https://github.com/rust-lang/rust-analyzer)
|
||||
- Installation: `rustup component add rust-analyzer`
|
||||
- Command: `rust-analyzer`
|
||||
|
||||
## Zig
|
||||
|
||||
- [zls](https://github.com/zigtools/zls)
|
||||
- Installation: [instructions](https://github.com/zigtools/zls#installation)
|
||||
- Command: `zls`
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# µlsp
|
||||
|
||||
LSP client for [micro-editor](https://github.com/zyedidia/micro).
|
||||
Note that this is a work in progress and has not yet been tested extensively – expect there to be some bugs.
|
||||
Please [open an issue](https://github.com/Andriamanitra/mlsp/issues/new) if you run into any!
|
||||
|
||||
|
||||
## Demo
|
||||
|
||||
[https://asciinema.org/a/610761](https://asciinema.org/a/610761)
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Simply clone the repository to your micro plugins directory:
|
||||
|
||||
```
|
||||
git clone https://github.com/Andriamanitra/mlsp ~/.config/micro/plug/mlsp
|
||||
```
|
||||
|
||||
You will also need to install [language servers](LanguageServers.md) for the
|
||||
programming languages you want to use.
|
||||
|
||||
The plugin currently provides following commands:
|
||||
|
||||
- `lsp start deno lsp` starts a language server by executing command `deno lsp`.
|
||||
Without arguments the `lsp start` command will try to guess the right server by
|
||||
looking at the currently open filetype.
|
||||
- `lsp stop deno` stops the language server with name `deno`. Without arguments
|
||||
the `lsp stop` command will stop _all_ currently running language servers.
|
||||
- `lsp hover` shows hover information for the code under cursor.
|
||||
- `lsp format` formats the buffer that is currently open.
|
||||
- `lsp autocomplete` for code completion suggestions. PROTIP: If you wish to use the
|
||||
same key as micro's autocompletion (tab by default), enable `tabAutocomplete`
|
||||
in `config.lua` instead of binding `command:lsp autocomplete` to a key!
|
||||
- `lsp goto-definition` – open the definition for the symbol under cursor
|
||||
- `lsp goto-declaration` – open the declaration for the symbol under cursor
|
||||
- `lsp goto-typedefinition` – open the type definition for the symbol under cursor
|
||||
- `lsp goto-implementation` – open the implementation for the symbol under cursor
|
||||
- `lsp find-references` - find all references to the symbol under cursor (shows the results in a new pane)
|
||||
- `lsp document-symbols` - list all symbols in the current document
|
||||
- `lsp diagnostic-info` - show more information about a diagnostic on the current line (useful for multiline diagnostic messages)
|
||||
|
||||
You can type the commands on micro command prompt or bind them to keys by adding
|
||||
something like this to your `bindings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"F7": "command:lsp start",
|
||||
"F8": "command:lsp format",
|
||||
"Alt-h": "command:lsp hover",
|
||||
"Alt-d": "command:lsp goto-definition",
|
||||
"Alt-r": "command:lsp find-references"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Supported features
|
||||
|
||||
- [x] get hover information
|
||||
- [x] show diagnostics (disabled by default, edit `config.lua` to enable)
|
||||
- [x] autocomplete using tab (disabled by default, edit `config.lua` to enable)
|
||||
- [x] format document
|
||||
- [x] format selection
|
||||
- [x] go to definition
|
||||
- [x] go to declaration
|
||||
- [x] go to implementation
|
||||
- [x] go to type definition
|
||||
- [x] find references
|
||||
- [x] list document symbols
|
||||
- [ ] rename symbol
|
||||
- [ ] code actions
|
||||
- [x] incremental document synchronization (better performance when editing large files)
|
||||
- [ ] [suggest a feature](https://github.com/Andriamanitra/mlsp/issues/new)
|
||||
|
||||
|
||||
## Showing LSP information on statusline
|
||||
|
||||
The plugin provides a function `mlsp.status` that can be used in the status line format.
|
||||
Here is an example configuration (`~/.config/micro/settings.json`) that uses it:
|
||||
|
||||
```json
|
||||
{
|
||||
"statusformatl": "$(filename) $(modified)($(line),$(col)) | ft:$(opt:filetype) | µlsp:$(mlsp.status)"
|
||||
}
|
||||
```
|
||||
|
||||
See [micro documentation](https://github.com/zyedidia/micro/blob/master/runtime/help/options.md)
|
||||
and the built-in [status plugin](https://github.com/zyedidia/micro/blob/master/runtime/plugins/status/help/status.md)
|
||||
for more information on customizing the statusline.
|
||||
|
||||
|
||||
## Known issues
|
||||
|
||||
- The very first autocompletion with `rust-analyzer` after initialization is very slow (it can take multiple seconds).
|
||||
|
||||
## Other similar projects
|
||||
|
||||
* [AndCake/micro-plugin-lsp](https://github.com/AndCake/micro-plugin-lsp) is another LSP plugin for micro-editor.
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
-- defaults for omitted server options (you probably don't want to change these)
|
||||
local defaultLanguageServerOptions = {
|
||||
-- Unique name for the server to be shown in statusbar and logs
|
||||
-- Defaults to the same as cmd if omitted
|
||||
shortName = nil,
|
||||
|
||||
-- (REQUIRED) command to execute the language server
|
||||
cmd = "",
|
||||
|
||||
-- Arguments for the above command
|
||||
args = {},
|
||||
|
||||
-- Language server specific options that are sent to the server during
|
||||
-- initialization – you can usually omit this field
|
||||
initializationOptions = nil,
|
||||
|
||||
-- callback function that is called when language server is initialized
|
||||
-- (useful for debugging and disabling server capabilities)
|
||||
-- For example to disable getting hover information from a server:
|
||||
-- onInitialized = function(client)
|
||||
-- client.serverCapabilities.hoverProvider = false
|
||||
-- end
|
||||
onInitialized = nil,
|
||||
}
|
||||
|
||||
-- Pre-made configurations for commonly used language servers – you can also
|
||||
-- define your own servers to be used in settings at the bottom of this file.
|
||||
-- See defaultLanguageServerOptions above for the available options.
|
||||
languageServer = {
|
||||
clangd = {
|
||||
cmd = "clangd"
|
||||
},
|
||||
clojurelsp = {
|
||||
cmd = "clojure-lsp"
|
||||
},
|
||||
crystalline = {
|
||||
cmd = "crystalline"
|
||||
},
|
||||
deno = {
|
||||
cmd = "deno",
|
||||
args = {"lsp"}
|
||||
},
|
||||
gopls = {
|
||||
cmd = "gopls"
|
||||
},
|
||||
hls = {
|
||||
shortName = "hls",
|
||||
cmd = "haskell-language-server-wrapper",
|
||||
args = {"--lsp"}
|
||||
},
|
||||
julials = {
|
||||
shortName = "julials",
|
||||
cmd = "julia",
|
||||
args = {"--startup-file=no", "--history-file=no", "-e", "using LanguageServer; runserver()"}
|
||||
},
|
||||
lualsp = {
|
||||
cmd = "lua-lsp"
|
||||
},
|
||||
luals = {
|
||||
cmd = "lua-language-server"
|
||||
},
|
||||
pylsp = {
|
||||
cmd = "pylsp"
|
||||
},
|
||||
pyright = {
|
||||
shortName = "pyright",
|
||||
cmd = "pyright-langserver",
|
||||
args = {"--stdio"}
|
||||
},
|
||||
quicklintjs = {
|
||||
cmd = "quick-lint-js",
|
||||
args = {"--lsp"}
|
||||
},
|
||||
rubocop = {
|
||||
cmd = "rubocop",
|
||||
args = {"--lsp"}
|
||||
},
|
||||
rubylsp = {
|
||||
cmd = "ruby-lsp"
|
||||
},
|
||||
ruff = {
|
||||
cmd = "ruff",
|
||||
args = {"server"},
|
||||
onInitialized = function(client)
|
||||
-- does not give useful results
|
||||
client.serverCapabilities.hoverProvider = false
|
||||
end
|
||||
},
|
||||
rustAnalyzer = {
|
||||
shortName = "rust",
|
||||
cmd = "rust-analyzer"
|
||||
},
|
||||
solargraph = {
|
||||
cmd = "solargraph",
|
||||
args = {"stdio"}
|
||||
},
|
||||
zls = {
|
||||
cmd = "zls"
|
||||
}
|
||||
}
|
||||
|
||||
-- you don't need to care about this part but it's basically filling in defaults
|
||||
-- for all missing fields in language servers defined above
|
||||
defaultLanguageServerOptions.__index = defaultLanguageServerOptions
|
||||
for _, server in pairs(languageServer) do
|
||||
setmetatable(server, defaultLanguageServerOptions)
|
||||
end
|
||||
|
||||
|
||||
settings = {
|
||||
|
||||
-- Use LSP completion in place of micro's default Autocomplete action when
|
||||
-- available (you can bind `command:autocomplete` command to a different
|
||||
-- key in ~/.config/micro/bindings.json even if this setting is false)
|
||||
tabAutocomplete = true,
|
||||
|
||||
-- Automatically start language server(s) when a buffer with matching
|
||||
-- filetype is opened
|
||||
autostart = {
|
||||
-- Example #1: Start gopls when editing .go files:
|
||||
-- go = { languageServer.gopls },
|
||||
|
||||
-- Example #2: Start pylsp AND ruff-lsp when editing Python files:
|
||||
-- python = { languageServer.pylsp, languageServer.ruff },
|
||||
},
|
||||
|
||||
-- Language server to use when `lsp` command is executed without args
|
||||
defaultLanguageServer = {
|
||||
c = languageServer.clangd,
|
||||
["c++"] = languageServer.clangd,
|
||||
clojure = languageServer.clojurelsp,
|
||||
crystal = languageServer.crystalline,
|
||||
go = languageServer.gopls,
|
||||
haskell = languageServer.hls,
|
||||
javascript = languageServer.deno,
|
||||
julia = languageServer.julials,
|
||||
json = languageServer.deno,
|
||||
lua = languageServer.luals,
|
||||
markdown = languageServer.deno,
|
||||
python = languageServer.pylsp,
|
||||
ruby = languageServer.rubylsp,
|
||||
rust = languageServer.rustAnalyzer,
|
||||
typescript = languageServer.deno,
|
||||
zig = languageServer.zls,
|
||||
},
|
||||
|
||||
-- Which kinds of diagnostics to show in the gutter
|
||||
showDiagnostics = {
|
||||
error = false,
|
||||
warning = false,
|
||||
information = false,
|
||||
hint = false
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
|
||||
|
||||
-- by default the empty table ({} in lua) gets serialized into json array
|
||||
-- this hack allows us to use json.object when we want to emit an empty object
|
||||
-- instead
|
||||
local emptyMap_metatable = {}
|
||||
emptyMap_metatable.__index = emptyMap_metatable
|
||||
local emptyMap = {}
|
||||
setmetatable(emptyMap, emptyMap_metatable)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if (rawget(val, 1) ~= nil or next(val) == nil) and (getmetatable(val) ~= emptyMap_metatable) then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
local function decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
json = {
|
||||
_version = "0.2.0",
|
||||
object = emptyMap,
|
||||
decode = function(s) return decode(s) end,
|
||||
encode = function(o) return encode(o) end
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue