From a7208c9185c008c00a0c8d7eee71c97dfe9344f3 Mon Sep 17 00:00:00 2001 From: Cian Hughes Date: Wed, 22 Jan 2025 16:57:39 +0000 Subject: [PATCH] Removed micro plugin lsp (moving to submodule) --- dot_config/micro/plug/lsp/LICENSE | 21 - dot_config/micro/plug/lsp/README.md | 93 --- dot_config/micro/plug/lsp/help/lsp.md | 315 --------- dot_config/micro/plug/lsp/main.lua | 933 -------------------------- dot_config/micro/plug/lsp/repo.json | 78 --- 5 files changed, 1440 deletions(-) delete mode 100644 dot_config/micro/plug/lsp/LICENSE delete mode 100644 dot_config/micro/plug/lsp/README.md delete mode 100644 dot_config/micro/plug/lsp/help/lsp.md delete mode 100644 dot_config/micro/plug/lsp/main.lua delete mode 100644 dot_config/micro/plug/lsp/repo.json diff --git a/dot_config/micro/plug/lsp/LICENSE b/dot_config/micro/plug/lsp/LICENSE deleted file mode 100644 index 62df372..0000000 --- a/dot_config/micro/plug/lsp/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Robert Kunze - -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. diff --git a/dot_config/micro/plug/lsp/README.md b/dot_config/micro/plug/lsp/README.md deleted file mode 100644 index 758ac8b..0000000 --- a/dot_config/micro/plug/lsp/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# Micro Plugin LSP Client - -**Please note:** This software is very much not finished. It is more like a -proof of concept and might break if you call it names. - -Provides LSP methods as actions to Micro that can subsequently be mapped to key -bindings. - -Currently implemented methods: - -- textDocument/hover -- textDocument/definition -- textDocument/completion -- textDocument/formatting -- textDocument/references - -If possible, this plugin will register the following shortcuts: - -- Alt-k for hover -- Alt-d for definition lookup -- Alt-f for formatting -- Alt-r for looking up references -- Ctrl-space for completion - -## Installation - -Clone this repo into micro's plug folder: - -``` -$ git clone https://github.com/AndCake/micro-plugin-lsp ~/.config/micro/plug/lsp -``` - -## Configuration - -In your `settings.json`, you add the `lsp.server` option in order to enable -using it for your languages' server. - -Example: - -``` -{ - "lsp.server": "python=pyls,go=gopls,typescript=deno lsp,rust=rls", - "lsp.formatOnSave": true, - "lsp.ignoreMessages": "LS message1 to ignore|LS message 2 to ignore|...", - "lsp.tabcompletion": true, - "lsp.ignoreTriggerCharacters": "completion,signature", - "lsp.autocompleteDetails": false -} -``` - -The format for the `lsp.server` value is a comma-separated list for each file -type you want to boot up a language server: - -``` -=[=][,...] -``` - -You can also use an environment variable called `MICRO_LSP` to define the same -information. If set, it will override the `lsp.server` from the `settings.json`. -You can add a line such as the following to your shell profile (e.g. .bashrc): - -``` -export MICRO_LSP='python=pyls,go=gopls,typescript=deno lsp={"importMap":"import_map.json"},rust=rls' -``` - -If neither the MICRO_LSP nor the lsp.server is set, then the plugin falls back -to the following settings: - -``` -python=pylsp,go=gopls,typescript=deno lsp,javascript=deno lsp,markdown=deno lsp,json=deno lsp,jsonc=deno lsp,rust=rls,lua=lua-lsp,c++=clangd -``` - -The option `lsp.autocompleteDetails` allows for showing all auto-completions in -a horizontally split buffer view (true) instead of the status line (false). - -## Testing - -This plugin has been tested briefly with the following language servers: - -- C++ [clangd](https://clangd.llvm.org) / - [ccls](https://github.com/MaskRay/ccls) -- go: [gopls](https://pkg.go.dev/golang.org/x/tools/gopls#section-readme) -- markdown, JSON, typescript, javascript (including JSX/TSX): - [deno](https://deno.land/) -- python: pyls, [pylsp](https://github.com/python-lsp/python-lsp-server) -- rust: [rls](https://github.com/rust-lang/rls) -- lua: [lua-lsp](https://github.com/Alloyed/lua-lsp) -- zig: [zls](https://github.com/zigtools/zls) - -## Known issues - -Not all possible types of modification events to the file are currently being -sent to the language server. Saving the file will re-synchronize it, though. diff --git a/dot_config/micro/plug/lsp/help/lsp.md b/dot_config/micro/plug/lsp/help/lsp.md deleted file mode 100644 index dfc148a..0000000 --- a/dot_config/micro/plug/lsp/help/lsp.md +++ /dev/null @@ -1,315 +0,0 @@ -# Micro Plugin LSP Client - -LSP is a Language Server Protocol client. Features include function signatures -and jump to definition. - -This help page can be viewed in Micro editor with Ctrl-E 'help lsp' - -## Features and Shortcuts - -- Show function signature on status bar (alt-K) (textDocument/hover) -- Open function definition in a new tab (alt-D) (textDocument/definition) -- Format document (alt-F) (textDocument/formatting) -- Show references to the current symbol in a buffer (alt-R) - (textDocument/references), pressing return on the reference line, the - reference's location is opened in a new tab - -There is initial support for completion (ctrl-space) (textDocument/completion). - -## Supported languages - -Installation instructions for Go and Python are provided below. LSP Plugin has -been briefly tested with - -- C++: [clangd](https://clangd.llvm.org) / - [ccls](https://github.com/MaskRay/ccls) -- go: [gopls](https://pkg.go.dev/golang.org/x/tools/gopls#section-readme) -- markdown, JSON, typescript, javascript (including JSX/TSX): - [deno](https://deno.land/) -- python: pyls and [pylsp](https://github.com/python-lsp/python-lsp-server) -- rust: [rls](https://github.com/rust-lang/rls) -- lua: [lua-lsp](https://github.com/Alloyed/lua-lsp) - -## Install LSP plugin - - $ micro --plugin install lsp - -To configure the LSP Plugin, you can add two lines to settings.json - - $ micro settings.json - -Add lines - -```json -{ - "lsp.server": "python=pylsp,go=gopls,typescript=deno lsp={\"importMap\": \"./import_map.json\"}", - "lsp.formatOnSave": true -} -``` - -Remember to add comma to previous line. Depending on the language server, -automatic code formating can be quite opinionated. In that case, you can simply -set lsp.formatOnSave to false. - -For Python language server, the currently maintained fork is 'pylsp'. If you -wish to use the Palantir version (last updated in 2020) instead, set -"python=pyls" in lsp.server. - -If your lsp.server settings are autoremoved, you can - - $ export MICRO_LSP='python=pylsp,go=gopls,typescript=deno lsp={"importMap":"import_map.json"},rust=rls' - -The lsp.server default settings (if no others are defined) are: - -``` -python=pylsp,go=gopls,typescript=deno lsp,javascript=deno lsp,markdown=deno lsp,json=deno lsp,jsonc=deno lsp,rust=rls,lua=lua-lsp,c++=clangd -``` - -## Install Language Server - -To support each language, LSP plugin uses language servers. To use LSP plugin, -you must install at least one language server. - -If you want to quickly test LSP plugin, Go language server gopls is simple to -install. - -### gopls, Go language server - -You will need command 'gopls' - - $ gopls version - golang.org/x/tools/gopls v0.7.3 - -In Debian, this is installed with - - $ sudo apt-get update - $ sudo apt-get -y install golang-go gopls - -To test it, write a short go program - - $ micro hello.go - -```go -package main - -import "fmt" - -func main() { - fmt.Println("hello world") -} -``` - -Move cursor over Println and press alt-k. The function signature is shown on the -bottom of the screen, in Micro status bar. It shows you what parameters the -function can take. The signature should look similar to this: "func -fmt.Println(a ...interface{}) (n int, err error)Println formats using the -default formats..." - -Can you see the function signature with alt-k? If you can, you have succesfully -installed Micro LSP plugin and GoPLS language server. - -Keep your cursor over Println, and press alt-d. The file defining Println opens. -In this case, it's fmt/print.go. As Go reference documentation is in code -comments, this is very convenient. You can navigate between tabs with atl-, -(alt-comma) and alt-. (alt - full stop). To close the tab, press Ctrl-Q. - -### Markdown, JSON/JSONC, Typescript, Javascript - -The Deno LSP server will provide full support for Typescript and Javascript. -Additionally, it supports formatting for Markdown and JSON files. The -installation of this is fairly straight forward: - -On Mac/Linux: - - $ curl -fsSL https://deno.land/install.sh | sh - -On Powershell: - - $ iwr https://deno.land/install.ps1 -useb | iex - -### typescript-language-server - -This LSP server will allow for Javascript as well as Typescript support. For -using it, you first need to install it using NPM: - - $ npm install -g typescript-language-server typescript - -Once it has been installed, you can use it like so: - - $ micro hello.js - -Press ctrl-e and type in: - - set lsp.server "typescript=typescript-language-server --stdio,javascript=typescript-language-server --stdio" - -After you restarted micro, you can use the features for typescript and -javascript accordingly. - -### pylsp, Python language server - -Installing Python language server PyLSP is a bit more involved. - -You will need 'virtualenv' command to create virtual environments and 'pip' to -install Python packages. You can also use one of the many other commands for -keeping your 'pip' packages in order. - -In Debian, these are installed with - - $ sudo apt-get update - $ sudo apt-get install python-pip virtualenv - -Create a new virtual environment - - $ mkdir somePythonProject; cd somePythonProject - $ virtualenv -p python3 env/ - $ source env/bin/activate - -Your prompt likely shows "(env)" to confirm you're inside your virtual -environment. - -List the packages you want installed. - - $ micro requirements.txt - -This list is to provide the most useful suggestions. If you would like to get a -lot more opinionated advice, such as adding two empty lines between functions, -you could use "python-lsp-server[all]". The mypy package provides optional -static type checking. requirements.txt: - -``` -python-lsp-server[rope,pyflakes,mccabe,pylsp-mypy] -pylsp-mypy -``` - -And actually install - - $ pip install -r requirements.txt - -No you can test your Python environment - - $ micro hello.py - -```python -def helloWorld(): - return a -``` - -Save with Ctrl-S. A red warning sign ">>" lists up in the gutter, on the left -side of Micro. Move cursor to the line "return a". The status bar shows the -warning: "undefined name 'a'". Well done, you have now installed Python LSP -support for Micro. - -MyPy provides optional static type setting. You can write normally, and type -checking is ignored. You can define types for some functions, and you get -automatic warnings for incorrect use of types. This is how types are marked: - -```python -def square(x: int) -> int: - return x*x -``` - -Depending on your project, taste and installed linters, pylsp sometimes shows -warnings you would like to hide. Hiding messages is possible using -lsp.ignoreMessages, explained in later in this help document. - -### lua-lsp, Lua language server - -These are the initial installation instructions. This installation will support -linter messages in the gutter (on the left of editing area) and jump to -definition inside the same file (alt-D). All LSP features are not yet supported -with Lua. - -Install 'luarocks' command using your package manager. For example, on Debian - - $ sudo apt-get update - $ sudo apt-get -y install luarocks - -Use luarocks to install helper packages used by lua-lsp - - $ sudo luarocks install luacheck - $ sudo luarocks install Formatter - $ sudo luarocks install lcf - -Install lua-lsp, the Lua language server - - $ sudo luarocks install --server=ssh://luarocks.org/dev lua-lsp - -This command uses different URL from official lua-lsp instructions due to -[a change in how packages are downloaded](https://github.com/Alloyed/lua-lsp/issues/45). -This command uses ssh instead of http. - -To test it, open a Lua file - - $ micro $HOME/.config/micro/plug/lsp/main.lua - -Can you see some linter warnings ">>" in the gutter? Can you jump to functions -inside the same file with Alt-D? Well done, you've installed Lua LSP support for -micro. - -All features don't work yet with Lua LSP. - -### zls, ZIG language server - -The ZIG language server provides formatting, goto definition, auto-completion as -well as hover and references. It can be installed by following -[these instruction](https://github.com/zigtools/zls). - -Once installed, open micro, press ctrl+e and type the following command: - - set lsp.server zig=zls - -Close micro again and open a zig file. - -## Ignoring unhelpful messages - -In addition to providing assistance while coding, some language servers can show -spurious, unnecessary or too oppinionated messages. Sometimes, it's not obvious -how these messages are disable using language server settings. - -This plugin allows you to selectively ignore unwanted warnings while keeping -others. This is done my matching the start of the message. By default, nothing -is ignored. - -Consider a case where you're working with an external Python project that -indents with tabs. When joining an existing project, you might not want to -impose your own conventions to every code file. On the other hand, LSP support -is not useful if nearly every line is marked with a warning. - -Moving the cursor to a line with the warning, you see that the line starts with -"W191 indentation contains tabs". This, and similar unhelpful messages (in the -context of your current project) can be ignored by editing -~/.config/micro/settings.json - -```json -{ - "lsp.ignoreMessages": "Skipping analyzing |W191 indentation contains tabs|E101 indentation contains mixed spaces and tabs|See https://mypy.readthedocs.io/en" -} -``` - -As you now open the same file, you can see that warning "W191 indentation -contains tabs" is no longer shown. Also the warning mark ">>" in the gutter is -gone. Try referring to a variable that does not exist, and you can see a helpful -warning appear. You have now disabled the warnings you don't need, while keeping -the useful ones. - -## See also - -[Official repostory](https://github.com/AndCake/micro-plugin-lsp) - -[Usage examples with screenshots](https://terokarvinen.com/2022/micro-editor-lsp-support-python-and-go-jump-to-definition-show-function-signature/) - -[Language Server Protocol](https://microsoft.github.io/language-server-protocol/) - -[gopls - the Go language server](https://pkg.go.dev/golang.org/x/tools/gopls) - -[pylsp - Python LSP Server](https://github.com/python-lsp/python-lsp-server) - -[mypy - Optional Static Typing for Python](http://mypy-lang.org/) - -[rls - Rust Language Server](https://github.com/rust-lang/rls) - -[deno](https://deno.land/) - -[typescript-language-server](https://www.npmjs.com/package/typescript-language-server) - -[lua-lsp - A Lua language server](https://github.com/Alloyed/lua-lsp) diff --git a/dot_config/micro/plug/lsp/main.lua b/dot_config/micro/plug/lsp/main.lua deleted file mode 100644 index a0fa4c9..0000000 --- a/dot_config/micro/plug/lsp/main.lua +++ /dev/null @@ -1,933 +0,0 @@ -VERSION = "0.6.2" - -local micro = import("micro") -local config = import("micro/config") -local shell = import("micro/shell") -local util = import("micro/util") -local buffer = import("micro/buffer") -local fmt = import("fmt") -local go_os = import("os") -local path = import("path") -local filepath = import("path/filepath") - -local cmd = {} -local id = {} -local version = {} -local currentAction = {} -local capabilities = {} -local filetype = '' -local rootUri = '' -local message = '' -local completionCursor = 0 -local lastCompletion = {} -local splitBP = nil -local tabCount = 0 - -local json = {} - -function toBytes(str) - local result = {} - for i=1,#str do - local b = str:byte(i) - if b < 32 then - table.insert(result, b) - end - end - return result -end - -function getUriFromBuf(buf) - if buf == nil then return; end - local file = buf.AbsPath - local uri = fmt.Sprintf("file://%s", file) - return uri -end - -function mysplit (inputstr, sep) - if sep == nil then - sep = "%s" - end - local t={} - for str in string.gmatch(inputstr, "([^"..sep.."]+)") do - table.insert(t, str) - end - return t -end - -function parseOptions(inputstr) - local t = {} - inputstr = inputstr:gsub("[%w+_-]+=[^=,]+={.-}", function (str) - table.insert(t, str) - return ''; - end) - inputstr = inputstr:gsub("[%w+_-]+=[^=,]+", function (str) - table.insert(t, str) - return ''; - end) - return t -end - -function startServer(filetype, callback) - local wd, _ = go_os.Getwd() - rootUri = fmt.Sprintf("file://%s", wd) - local envSettings, _ = go_os.Getenv("MICRO_LSP") - local settings = config.GetGlobalOption("lsp.server") - local fallback = "python=pylsp,go=gopls,typescript=deno lsp,javascript=deno lsp,markdown=deno lsp,json=deno lsp,jsonc=deno lsp,rust=rls,lua=lua-lsp,c++=clangd" - if envSettings ~= nil and #envSettings > 0 then - settings = envSettings - end - if settings ~= nil and #settings > 0 then - settings = settings .. "," .. fallback - else - settings = fallback - end - local server = parseOptions(settings) - micro.Log("Server Options", server) - for i in pairs(server) do - local part = mysplit(server[i], "=") - local run = mysplit(part[2], "%s") - local initOptions = part[3] or '{}' - local runCmd = table.remove(run, 1) - local args = run - if filetype == part[1] then - local send = withSend(part[1]) - if cmd[part[1]] ~= nil then return; end - id[part[1]] = 0 - micro.Log("Starting server", part[1]) - cmd[part[1]] = shell.JobSpawn(runCmd, args, onStdout(part[1]), onStderr, onExit(part[1]), {}) - currentAction[part[1]] = { method = "initialize", response = function (bp, data) - send("initialized", "{}", true) - capabilities[filetype] = data.result and data.result.capabilities or {} - callback(bp.Buf, filetype) - end } - send(currentAction[part[1]].method, fmt.Sprintf('{"processId": %.0f, "rootUri": "%s", "workspaceFolders": [{"name": "root", "uri": "%s"}], "initializationOptions": %s, "capabilities": {"textDocument": {"hover": {"contentFormat": ["plaintext", "markdown"]}, "publishDiagnostics": {"relatedInformation": false, "versionSupport": false, "codeDescriptionSupport": true, "dataSupport": true}, "signatureHelp": {"signatureInformation": {"documentationFormat": ["plaintext", "markdown"]}}}}}', go_os.Getpid(), rootUri, rootUri, initOptions)) - return - end - end -end - -function init() - config.RegisterCommonOption("lsp", "server", "python=pylsp,go=gopls,typescript=deno lsp,javascript=deno lsp,markdown=deno lsp,json=deno lsp,jsonc=deno lsp,rust=rls,lua=lua-lsp,c++=clangd") - config.RegisterCommonOption("lsp", "formatOnSave", true) - config.RegisterCommonOption("lsp", "autocompleteDetails", false) - config.RegisterCommonOption("lsp", "ignoreMessages", "") - config.RegisterCommonOption("lsp", "tabcompletion", true) - config.RegisterCommonOption("lsp", "ignoreTriggerCharacters", "completion") - -- example to ignore all LSP server message starting with these strings: - -- "lsp.ignoreMessages": "Skipping analyzing |See https://" - - config.MakeCommand("hover", hoverAction, config.NoComplete) - config.MakeCommand("definition", definitionAction, config.NoComplete) - config.MakeCommand("lspcompletion", completionAction, config.NoComplete) - config.MakeCommand("format", formatAction, config.NoComplete) - config.MakeCommand("references", referencesAction, config.NoComplete) - - config.TryBindKey("Alt-k", "command:hover", false) - config.TryBindKey("Alt-d", "command:definition", false) - config.TryBindKey("Alt-f", "command:format", false) - config.TryBindKey("Alt-r", "command:references", false) - config.TryBindKey("CtrlSpace", "command:lspcompletion", false) - - config.AddRuntimeFile("lsp", config.RTHelp, "help/lsp.md") - - -- @TODO register additional actions here -end - -function withSend(filetype) - return function (method, params, isNotification) - if cmd[filetype] == nil then - return - end - - local msg = fmt.Sprintf('{"jsonrpc": "2.0", %s"method": "%s", "params": %s}', not isNotification and fmt.Sprintf('"id": %.0f, ', id[filetype]) or "", method, params) - id[filetype] = id[filetype] + 1 - msg = fmt.Sprintf("Content-Length: %.0f\r\n\r\n%s", #msg, msg) - --micro.Log("send", filetype, "sending", method or msg, msg) - shell.JobSend(cmd[filetype], msg) - end -end - -function preRune(bp, r) - if splitBP ~= nil then - pcall(function () splitBP:Unsplit(); end) - splitBP = nil - local cur = bp.Buf:GetActiveCursor() - cur:Deselect(false); - cur:GotoLoc(buffer.Loc(cur.X + 1, cur.Y)) - end -end - --- when a new character is types, the document changes -function onRune(bp, r) - local filetype = bp.Buf:FileType() - if cmd[filetype] == nil then - return - end - if splitBP ~= nil then - pcall(function () splitBP:Unsplit(); end) - splitBP = nil - end - - local send = withSend(filetype) - local uri = getUriFromBuf(bp.Buf) - if r ~= nil then - lastCompletion = {} - end - -- allow the document contents to be escaped properly for the JSON string - local content = util.String(bp.Buf:Bytes()):gsub("\\", "\\\\"):gsub("\n", "\\n"):gsub("\r", "\\r"):gsub('"', '\\"'):gsub("\t", "\\t") - -- increase change version - version[uri] = (version[uri] or 0) + 1 - send("textDocument/didChange", fmt.Sprintf('{"textDocument": {"version": %.0f, "uri": "%s"}, "contentChanges": [{"text": "%s"}]}', version[uri], uri, content), true) - local ignored = mysplit(config.GetGlobalOption("lsp.ignoreTriggerCharacters") or '', ",") - if r and capabilities[filetype] then - if not contains(ignored, "completion") and capabilities[filetype].completionProvider and capabilities[filetype].completionProvider.triggerCharacters and contains(capabilities[filetype].completionProvider.triggerCharacters, r) then - completionAction(bp) - elseif not contains(ignored, "signature") and capabilities[filetype].signatureHelpProvider and capabilities[filetype].signatureHelpProvider.triggerCharacters and contains(capabilities[filetype].signatureHelpProvider.triggerCharacters, r) then - hoverAction(bp) - end - end -end - --- alias functions for any kind of change to the document --- @TODO: add missing ones -function onBackspace(bp) onRune(bp); end -function onCut(bp) onRune(bp); end -function onCutLine(bp) onRune(bp); end -function onDuplicateLine(bp) onRune(bp); end -function onDeleteLine(bp) onRune(bp); end -function onDelete(bp) onRune(bp); end -function onUndo(bp) onRune(bp); end -function onRedo(bp) onRune(bp); end -function onIndent(bp) onRune(bp); end -function onIndentSelection(bp) onRune(bp); end -function onPaste(bp) onRune(bp); end -function onSave(bp) onRune(bp); end - -function onEscape(bp) - if splitBP ~= nil then - pcall(function () splitBP:Unsplit(); end) - splitBP = nil - end -end - -function preInsertNewline(bp) - if bp.Buf.Path == "References found" then - local cur = bp.Buf:GetActiveCursor() - cur:SelectLine() - local data = util.String(cur:GetSelection()) - local file, line, character = data:match("(./[^:]+):([^:]+):([^:]+)") - local doc, _ = file:gsub("^file://", "") - buf, _ = buffer.NewBufferFromFile(doc) - bp:AddTab() - micro.CurPane():OpenBuffer(buf) - buf:GetActiveCursor():GotoLoc(buffer.Loc(character * 1, line * 1)) - micro.CurPane():Center() - return false - end -end - -function preSave(bp) - if config.GetGlobalOption("lsp.formatOnSave") then - onRune(bp) - formatAction(bp, function () - bp:Save() - end) - end -end - -function handleInitialized(buf, filetype) - if cmd[filetype] == nil then return; end - micro.Log("Found running lsp server for ", filetype, "firing textDocument/didOpen...") - local send = withSend(filetype) - local uri = getUriFromBuf(buf) - local content = util.String(buf:Bytes()):gsub("\\", "\\\\"):gsub("\n", "\\n"):gsub("\r", "\\r"):gsub('"', '\\"'):gsub("\t", "\\t") - send("textDocument/didOpen", fmt.Sprintf('{"textDocument": {"uri": "%s", "languageId": "%s", "version": 1, "text": "%s"}}', uri, filetype, content), true) -end - -function onBufferOpen(buf) - local filetype = buf:FileType() - micro.Log("ONBUFFEROPEN", filetype) - if filetype ~= "unknown" and rootUri == "" and not cmd[filetype] then return startServer(filetype, handleInitialized); end - if cmd[filetype] then - handleInitialized(buf, filetype) - end -end - -function contains(list, x) - for _, v in pairs(list) do - if v == x then return true; end - end - return false -end - -function string.starts(String, Start) - return string.sub(String, 1, #Start) == Start -end - -function string.ends(String, End) - return string.sub(String, #String - (#End - 1), #String) == End -end - -function string.random(CharSet, Length, prefix) - - local _CharSet = CharSet or '.' - - if _CharSet == '' then - return '' - else - local Result = prefix or "" - math.randomseed(os.time()) - for Loop = 1,Length do - local char = math.random(1, #CharSet) - Result = Result .. CharSet:sub(char,char) - end - - return Result - end -end - -function string.parse(text) - if not text:find('"jsonrpc":') then return {}; end - local start,fin = text:find("\n%s*\n") - local cleanedText = text - if fin ~= nil then - cleanedText = text:sub(fin) - end - local status, res = pcall(json.parse, cleanedText) - if status then - return res - end - return false -end - -function isIgnoredMessage(msg) - -- Return true if msg matches one of the ignored starts of messages - -- Useful for linters that show spurious, hard to disable warnings - local ignoreList = mysplit(config.GetGlobalOption("lsp.ignoreMessages"), "|") - for i, ignore in pairs(ignoreList) do - if string.match(msg, ignore) then -- match from start of string - micro.Log("Ignore message: '", msg, "', because it matched: '", ignore, "'.") - return true -- ignore this message, dont show to user - end - end - return false -- show this message to user -end - -function onStdout(filetype) - return function (text) - if text:starts("Content-Length:") then - message = text - else - message = message .. text - end - if not text:ends("}") then - return - end - local data = message:parse() - if data == false then - return - end - - if data.method == "workspace/configuration" then - -- actually needs to respond with the same ID as the received JSON - local message = fmt.Sprintf('{"jsonrpc": "2.0", "id": %.0f, "result": [{"enable": true}]}', data.id) - shell.JobSend(cmd[filetype], fmt.Sprintf('Content-Length: %.0f\n\n%s', #message, message)) - elseif data.method == "textDocument/publishDiagnostics" or data.method == "textDocument\\/publishDiagnostics" then - -- react to server-published event - local bp = micro.CurPane().Buf - bp:ClearMessages("lsp") - bp:AddMessage(buffer.NewMessage("lsp", "", buffer.Loc(0, 10000000), buffer.Loc(0, 10000000), buffer.MTInfo)) - local uri = getUriFromBuf(bp) - if data.params.uri == uri then - for _, diagnostic in ipairs(data.params.diagnostics) do - local type = buffer.MTInfo - if diagnostic.severity == 1 then - type = buffer.MTError - elseif diagnostic.severity == 2 then - type = buffer.MTWarning - end - local mstart = buffer.Loc(diagnostic.range.start.character, diagnostic.range.start.line) - local mend = buffer.Loc(diagnostic.range["end"].character, diagnostic.range["end"].line) - - if not isIgnoredMessage(diagnostic.message) then - msg = buffer.NewMessage("lsp", diagnostic.message, mstart, mend, type) - bp:AddMessage(msg) - end - end - end - elseif currentAction[filetype] and currentAction[filetype].method and not data.method and currentAction[filetype].response and data.jsonrpc then -- react to custom action event - local bp = micro.CurPane() - micro.Log("Received message for ", filetype, data) - currentAction[filetype].response(bp, data) - currentAction[filetype] = {} - elseif data.method == "window/showMessage" or data.method == "window\\/showMessage" then - if filetype == micro.CurPane().Buf:FileType() then - micro.InfoBar():Message(data.params.message) - else - micro.Log(filetype .. " message " .. data.params.message) - end - elseif data.method == "window/logMessage" or data.method == "window\\/logMessage" then - micro.Log(data.params.message) - elseif message:starts("Content-Length:") then - if message:find('"') and not message:find('"result":null') then - micro.Log("Unhandled message 1", filetype, message) - end - else - -- enable for debugging purposes - micro.Log("Unhandled message 2", filetype, message) - end - end -end - -function onStderr(text) - micro.Log("ONSTDERR", text) - --micro.InfoBar():Message(text) -end - -function onExit(filetype) - return function (str) - currentAction[filetype] = nil - cmd[filetype] = nil - micro.Log("ONEXIT", filetype, str) - end -end - --- the actual hover action request and response --- the hoverActionResponse is hooked up in -function hoverAction(bp) - local filetype = bp.Buf:FileType() - if cmd[filetype] ~= nil then - local send = withSend(filetype) - local file = bp.Buf.AbsPath - local line = bp.Buf:GetActiveCursor().Y - local char = bp.Buf:GetActiveCursor().X - currentAction[filetype] = { method = "textDocument/hover", response = hoverActionResponse } - send(currentAction[filetype].method, fmt.Sprintf('{"textDocument": {"uri": "file://%s"}, "position": {"line": %.0f, "character": %.0f}}', file, line, char)) - end -end - -function hoverActionResponse(buf, data) - if data.result and data.result.contents ~= nil and data.result.contents ~= "" then - if data.result.contents.value then - micro.InfoBar():Message(data.result.contents.value) - elseif #data.result.contents > 0 then - micro.InfoBar():Message(data.result.contents[1].value) - end - end -end - --- the definition action request and response -function definitionAction(bp) - local filetype = bp.Buf:FileType() - if cmd[filetype] == nil then return; end - - local send = withSend(filetype) - local file = bp.Buf.AbsPath - local line = bp.Buf:GetActiveCursor().Y - local char = bp.Buf:GetActiveCursor().X - currentAction[filetype] = { method = "textDocument/definition", response = definitionActionResponse } - send(currentAction[filetype].method, fmt.Sprintf('{"textDocument": {"uri": "file://%s"}, "position": {"line": %.0f, "character": %.0f}}', file, line, char)) -end - -function definitionActionResponse(bp, data) - local results = data.result or data.partialResult - if results == nil then return; end - local file = bp.Buf.AbsPath - if results.uri ~= nil then - -- single result - results = { results } - end - if #results <= 0 then return; end - local uri = (results[1].uri or results[1].targetUri) - local doc = uri:gsub("^file://", "") - local buf = bp.Buf - if file ~= doc then - -- it's from a different file, so open it as a new tab - buf, _ = buffer.NewBufferFromFile(doc) - bp:AddTab() - micro.CurPane():OpenBuffer(buf) - -- shorten the displayed name in status bar - name = buf:GetName() - local wd, _ = go_os.Getwd() - if name:starts(wd) then - buf:SetName("." .. name:sub(#wd + 1, #name + 1)) - else - if #name > 30 then - buf:SetName("..." .. name:sub(-30, #name + 1)) - end - end - end - local range = results[1].range or results[1].targetSelectionRange - buf:GetActiveCursor():GotoLoc(buffer.Loc(range.start.character, range.start.line)) - bp:Center() -end - -function completionAction(bp) - local filetype = bp.Buf:FileType() - local send = withSend(filetype) - local file = bp.Buf.AbsPath - local line = bp.Buf:GetActiveCursor().Y - local char = bp.Buf:GetActiveCursor().X - - if lastCompletion[1] == file and lastCompletion[2] == line and lastCompletion[3] == char then - completionCursor = completionCursor + 1 - else - completionCursor = 0 - if bp.Cursor:HasSelection() then - -- we have a selection - -- assume we want to indent the selection - bp:IndentSelection() - return - end - if char == 0 then - -- we are at the very first character of a line - -- assume we want to indent - bp:IndentLine() - return - end - local cur = bp.Buf:GetActiveCursor() - cur:SelectLine() - local lineContent = util.String(cur:GetSelection()) - cur:ResetSelection() - cur:GotoLoc(buffer.Loc(char, line)) - local startOfLine = "" .. lineContent:sub(1, char) - if startOfLine:match("^%s+$") then - -- we are at the beginning of a line - -- assume we want to indent the line - bp:IndentLine() - return - end - end - if cmd[filetype] == nil then return; end - lastCompletion = {file, line, char} - currentAction[filetype] = { method = "textDocument/completion", response = completionActionResponse } - send(currentAction[filetype].method, fmt.Sprintf('{"textDocument": {"uri": "file://%s"}, "position": {"line": %.0f, "character": %.0f}}', file, line, char)) -end - -table.filter = function(t, filterIter) - local out = {} - - for k, v in pairs(t) do - if filterIter(v, k, t) then table.insert(out, v) end - end - - return out -end - -function findCommon(input, list) - local commonLen = 0 - local prefixList = {} - local str = input.textEdit and input.textEdit.newText or input.label - for i = 1,#str,1 do - local prefix = str:sub(1, i) - prefixList[prefix] = 0 - for idx, entry in ipairs(list) do - local currentEntry = entry.textEdit and entry.textEdit.newText or entry.label - if currentEntry:starts(prefix) then - prefixList[prefix] = prefixList[prefix] + 1 - end - end - end - local longest = "" - for idx, entry in pairs(prefixList) do - if entry >= #list then - if #longest < #idx then - longest = idx - end - end - end - if #list == 1 then - return list[1].textEdit and list[1].textEdit.newText or list[1].label - end - return longest -end - -function completionActionResponse(bp, data) - local results = data.result - if results == nil then - return - end - if results.items then - results = results.items - end - - local xy = buffer.Loc(bp.Cursor.X, bp.Cursor.Y) - local start = xy - if bp.Cursor:HasSelection() then - bp.Cursor:DeleteSelection() - end - - local found = false - local prefix = "" - local reversed = "" - -- if we have no defined ranges in the result - -- try to find out what our prefix is we want to filter against - if not results[1] or not results[1].textEdit or not results[1].textEdit.range then - if capabilities[bp.Buf:FileType()] and capabilities[bp.Buf:FileType()].completionProvider and capabilities[bp.Buf:FileType()].completionProvider.triggerCharacters then - local cur = bp.Buf:GetActiveCursor() - cur:SelectLine() - local lineContent = util.String(cur:GetSelection()) - reversed = string.reverse(lineContent:gsub("\r?\n$", ""):sub(1, xy.X)) - local triggerChars = capabilities[bp.Buf:FileType()].completionProvider.triggerCharacters - for i = 1,#reversed,1 do - local char = reversed:sub(i,i) - -- try to find a trigger character or any other non-word character - if contains(triggerChars, char) or contains({" ", ":", "/", "-", "\t", ";"}, char) then - found = true - start = buffer.Loc(#reversed - (i - 1), bp.Cursor.Y) - bp.Cursor:SetSelectionStart(start) - bp.Cursor:SetSelectionEnd(xy) - prefix = util.String(cur:GetSelection()) - bp.Cursor:DeleteSelection() - bp.Cursor:ResetSelection() - break - end - end - if not found then - prefix = lineContent:gsub("\r?\n$", '') - end - end - -- if we have found a prefix - if prefix ~= "" then - -- filter it down to what is suggested by the prefix - results = table.filter(results, function (entry) - return entry.label:starts(prefix) - end) - end - end - - table.sort(results, function (left, right) - return (left.sortText or left.label) < (right.sortText or right.label) - end) - - entry = results[(completionCursor % #results) + 1] - -- if no matching results are found - if entry == nil then - -- reposition cursor and stop - bp.Cursor:GotoLoc(xy) - return - end - local commonStart = '' - local toInsert = entry.textEdit and entry.textEdit.newText or entry.label - local isTabCompletion = config.GetGlobalOption("lsp.tabcompletion") - if isTabCompletion and not entry.textEdit then - commonStart = findCommon(entry, results) - bp.Buf:Insert(start, commonStart) - if prefix ~= commonStart then - return - end - start = buffer.Loc(start.X + #prefix, start.Y) - else - prefix = '' - end - - if entry.textEdit and entry.textEdit.range then - start = buffer.Loc(entry.textEdit.range.start.character, entry.textEdit.range.start.line) - bp.Cursor:SetSelectionStart(start) - bp.Cursor:SetSelectionEnd(xy) - bp.Cursor:DeleteSelection() - bp.Cursor:ResetSelection() - elseif capabilities[bp.Buf:FileType()] and capabilities[bp.Buf:FileType()].completionProvider and capabilities[bp.Buf:FileType()].completionProvider.triggerCharacters then - if not found then - -- we found nothing - so assume we need the beginning of the line - if reversed:starts(" ") or reversed:starts("\t") then - -- if we end with some indentation, keep it - start = buffer.Loc(#reversed, bp.Cursor.Y) - else - start = buffer.Loc(0, bp.Cursor.Y) - end - bp.Cursor:SetSelectionStart(start) - bp.Cursor:SetSelectionEnd(xy) - bp.Cursor:DeleteSelection() - bp.Cursor:ResetSelection() - end - end - local inserting = "" .. toInsert:gsub(prefix, "") - bp.Buf:Insert(start, inserting) - - if #results > 1 then - if entry.textEdit then - bp.Cursor:GotoLoc(start) - bp.Cursor:SetSelectionStart(start) - else - -- if we had to calculate everything outselves - -- go back to the original location - bp.Cursor:GotoLoc(xy) - bp.Cursor:SetSelectionStart(xy) - end - bp.Cursor:SetSelectionEnd(buffer.Loc(start.X + #toInsert, start.Y)) - else - bp.Cursor:GotoLoc(buffer.Loc(start.X + #inserting, start.Y)) - end - - local startLoc = buffer.Loc(0, 0) - local endLoc = buffer.Loc(0, 0) - local msg = '' - local insertion = '' - if entry.detail or entry.documentation then - insertion = fmt.Sprintf("%s", entry.detail or entry.documentation or '') - for idx, result in ipairs(results) do - if #msg > 0 then - msg = msg .. "\n" - end - local insertion = fmt.Sprintf("%s %s", result.detail or '', result.documentation or '') - if idx == (completionCursor % #results) + 1 then - local msglines = mysplit(msg, "\n") - startLoc = buffer.Loc(0, #msglines) - endLoc = buffer.Loc(#insertion - 1, #msglines) - end - msg = msg .. insertion - end - else - insertion = entry.label - for idx, result in ipairs(results) do - if #msg > 0 then - local msglines = mysplit(msg, "\n") - local lastLine = msglines[#msglines] - local len = #result.label + 4 - if #lastLine + len >= bp:GetView().Width then - msg = msg .. "\n " - else - msg = msg .. ' ' - end - else - msg = " " - end - if idx == (completionCursor % #results) + 1 then - local msglines = mysplit(msg, "\n") - local prefixLen = 0 - if #msglines > 0 then - prefixLen = #msglines[#msglines] - else - prefixLen = #msg - end - startLoc = buffer.Loc(prefixLen or 0, #msglines - 1) - endLoc = buffer.Loc(prefixLen + #result.label, #msglines - 1) - end - msg = msg .. result.label - end - end - if config.GetGlobalOption("lsp.autocompleteDetails") then - if not splitBP then - local tmpName = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"):random(32) - local logBuf = buffer.NewBuffer(msg, tmpName) - splitBP = bp:HSplitBuf(logBuf) - bp:NextSplit() - else - splitBP:SelectAll() - splitBP.Cursor:DeleteSelection() - splitBP.Cursor:ResetSelection() - splitBP.Buf:insert(buffer.Loc(1, 1), msg) - end - splitBP.Cursor:ResetSelection() - splitBP.Cursor:SetSelectionStart(startLoc) - splitBP.Cursor:SetSelectionEnd(endLoc) - else - if entry.detail or entry.documentation then - micro.InfoBar():Message(insertion) - else - local cleaned = " " .. msg:gsub("%s+", " ") - local replaced, _ = cleaned:gsub(".*%s" .. insertion .. "%s?", " [" .. insertion .. "] ") - micro.InfoBar():Message(replaced) - end - end -end - -function formatAction(bp, callback) - local filetype = bp.Buf:FileType() - if cmd[filetype] == nil then return; end - local send = withSend(filetype) - local file = bp.Buf.AbsPath - - currentAction[filetype] = { method = "textDocument/formatting", response = formatActionResponse(callback) } - send(currentAction[filetype].method, fmt.Sprintf('{"textDocument": {"uri": "file://%s"}, "options": {"tabSize": 4, "insertSpaces": true}}', file)) -end - -function formatActionResponse(callback) - return function (bp, data) - if data.result == nil then return; end - local edits = data.result - -- make sure we apply the changes from back to front - -- this allows for changes to not need position updates - table.sort(edits, function (left, right) - -- go by lines first - return left.range['end'].line > right.range['end'].line or - -- if lines match, go by end character - left.range['end'].line == right.range['end'].line and left.range['end'].character > right.range['end'].character or - -- if they match too, go by start character - left.range['end'].line == right.range['end'].line and left.range['end'].character == right.range['end'].character and left.range.start.line == left.range['end'].line and left.range.start.character > right.range.start.character - end) - - -- save original cursor position - local xy = buffer.Loc(bp.Cursor.X, bp.Cursor.Y) - for _idx, edit in ipairs(edits) do - rangeStart = buffer.Loc(edit.range.start.character, edit.range.start.line) - rangeEnd = buffer.Loc(edit.range['end'].character, edit.range['end'].line) - -- apply each change - bp.Cursor:GotoLoc(rangeStart) - bp.Cursor:SetSelectionStart(rangeStart) - bp.Cursor:SetSelectionEnd(rangeEnd) - bp.Cursor:DeleteSelection() - bp.Cursor:ResetSelection() - - if edit.newText ~= "" then - bp.Buf:insert(rangeStart, edit.newText) - end - end - -- put the cursor back where it was - bp.Cursor:GotoLoc(xy) - -- if any changes were applied - if #edits > 0 then - -- tell the server about the changed document - onRune(bp) - end - - if callback ~= nil then - callback(bp) - end - end -end - --- the references action request and response -function referencesAction(bp) - local filetype = bp.Buf:FileType() - if cmd[filetype] == nil then return; end - - local send = withSend(filetype) - local file = bp.Buf.AbsPath - local line = bp.Buf:GetActiveCursor().Y - local char = bp.Buf:GetActiveCursor().X - currentAction[filetype] = { method = "textDocument/references", response = referencesActionResponse } - send(currentAction[filetype].method, fmt.Sprintf('{"textDocument": {"uri": "file://%s"}, "position": {"line": %.0f, "character": %.0f}, "context": {"includeDeclaration":true}}', file, line, char)) -end - -function referencesActionResponse(bp, data) - if data.result == nil then return; end - local results = data.result or data.partialResult - if results == nil or #results <= 0 then return; end - - local file = bp.Buf.AbsPath - - local msg = '' - for _idx, ref in ipairs(results) do - if msg ~= '' then msg = msg .. '\n'; end - local doc = (ref.uri or ref.targetUri) - msg = msg .. "." .. doc:sub(#rootUri + 1, #doc) .. ":" .. ref.range.start.line .. ":" .. ref.range.start.character - end - - local logBuf = buffer.NewBuffer(msg, "References found") - local splitBP = bp:HSplitBuf(logBuf) -end - --- --- @TODO implement additional functions here... --- - - - --- --- JSON --- --- Internal functions. - -local function kind_of(obj) - if type(obj) ~= 'table' then return type(obj) end - local i = 1 - for _ in pairs(obj) do - if obj[i] ~= nil then i = i + 1 else return 'table' end - end - if i == 1 then return 'table' else return 'array' end -end - -local function escape_str(s) - local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'} - local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'} - for i, c in ipairs(in_char) do - s = s:gsub(c, '\\' .. out_char[i]) - end - return s -end - --- Returns pos, did_find; there are two cases: --- 1. Delimiter found: pos = pos after leading space + delim; did_find = true. --- 2. Delimiter not found: pos = pos after leading space; did_find = false. --- This throws an error if err_if_missing is true and the delim is not found. -local function skip_delim(str, pos, delim, err_if_missing) - pos = pos + #str:match('^%s*', pos) - if str:sub(pos, pos) ~= delim then - if err_if_missing then - error('Expected ' .. delim .. ' near position ' .. pos) - end - return pos, false - end - return pos + 1, true -end - --- Expects the given pos to be the first character after the opening quote. --- Returns val, pos; the returned pos is after the closing quote character. -local function parse_str_val(str, pos, val) - val = val or '' - local early_end_error = 'End of input found while parsing string.' - if pos > #str then error(early_end_error) end - local c = str:sub(pos, pos) - if c == '"' then return val, pos + 1 end - if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end - -- We must have a \ character. - local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'} - local nextc = str:sub(pos + 1, pos + 1) - if not nextc then error(early_end_error) end - return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc)) -end - --- Returns val, pos; the returned pos is after the number's final character. -local function parse_num_val(str, pos) - local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos) - local val = tonumber(num_str) - if not val then error('Error parsing number at position ' .. pos .. '.') end - return val, pos + #num_str -end - -json.null = {} -- This is a one-off table to represent the null value. - -function json.parse(str, pos, end_delim) - pos = pos or 1 - if pos > #str then error('Reached unexpected end of input.' .. str) end - local pos = pos + #str:match('^%s*', pos) -- Skip whitespace. - local first = str:sub(pos, pos) - if first == '{' then -- Parse an object. - local obj, key, delim_found = {}, true, true - pos = pos + 1 - while true do - key, pos = json.parse(str, pos, '}') - if key == nil then return obj, pos end - if not delim_found then error('Comma missing between object items.') end - pos = skip_delim(str, pos, ':', true) -- true -> error if missing. - obj[key], pos = json.parse(str, pos) - pos, delim_found = skip_delim(str, pos, ',') - end - elseif first == '[' then -- Parse an array. - local arr, val, delim_found = {}, true, true - pos = pos + 1 - while true do - val, pos = json.parse(str, pos, ']') - if val == nil then return arr, pos end - if not delim_found then error('Comma missing between array items.') end - arr[#arr + 1] = val - pos, delim_found = skip_delim(str, pos, ',') - end - elseif first == '"' then -- Parse a string. - return parse_str_val(str, pos + 1) - elseif first == '-' or first:match('%d') then -- Parse a number. - return parse_num_val(str, pos) - elseif first == end_delim then -- End of an object or array. - return nil, pos + 1 - else -- Parse true, false, or null. - local literals = {['true'] = true, ['false'] = false, ['null'] = json.null} - for lit_str, lit_val in pairs(literals) do - local lit_end = pos + #lit_str - 1 - if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end - end - local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10) - error('Invalid json syntax starting at ' .. pos_info_str .. ': ' .. str) - end -end diff --git a/dot_config/micro/plug/lsp/repo.json b/dot_config/micro/plug/lsp/repo.json deleted file mode 100644 index 15ad866..0000000 --- a/dot_config/micro/plug/lsp/repo.json +++ /dev/null @@ -1,78 +0,0 @@ -[{ - "Name": "lsp", - "Description": "Generic LSP Client for Micro", - "Website": "https://github.com/AndCake/micro-plugin-lsp", - "Tags": ["lsp"], - "Versions": [ - { - "Version": "0.4.1", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.4.1.zip", - "Require": { - "micro": ">=2.0.10" - } - }, - { - "Version": "0.4.2", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.4.2.zip", - "Require": { - "micro": ">=2.0.8" - } - }, - { - "Version": "0.4.3", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.4.3.zip", - "Require": { - "micro": ">=2.0.8" - } - }, - { - "Version": "0.5.0", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.5.0.zip", - "Require": { - "micro": ">=2.0.8" - } - }, - { - "Version": "0.5.1", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.5.1.zip", - "Require": { - "micro": ">=2.0.8" - } - }, - { - "Version": "0.5.2", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.5.2.zip", - "Require": { - "micro": ">=2.0.8" - } - }, - { - "Version": "0.5.3", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.5.3.zip", - "Require": { - "micro": ">=2.0.8" - } - }, - { - "Version": "0.6.0", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.6.0.zip", - "Require": { - "micro": ">=2.0.8" - } - }, - { - "Version": "0.6.1", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.6.1.zip", - "Require": { - "micro": ">=2.0.8" - } - }, - { - "Version": "0.6.2", - "Url": "https://github.com/AndCake/micro-plugin-lsp/archive/v0.6.2.zip", - "Require": { - "micro": ">=2.0.8" - } - } - ] -}]