mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-30 14:47:16 +09:00
Basic communication with lsp server
This commit is contained in:
2
go.mod
2
go.mod
@@ -1,6 +1,7 @@
|
|||||||
module github.com/zyedidia/micro/v2
|
module github.com/zyedidia/micro/v2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/BurntSushi/toml v0.3.1
|
||||||
github.com/blang/semver v3.5.1+incompatible
|
github.com/blang/semver v3.5.1+incompatible
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/go-errors/errors v1.0.1
|
github.com/go-errors/errors v1.0.1
|
||||||
@@ -10,6 +11,7 @@ require (
|
|||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff
|
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff
|
||||||
github.com/sergi/go-diff v1.1.0
|
github.com/sergi/go-diff v1.1.0
|
||||||
|
github.com/sourcegraph/go-lsp v0.0.0-20200429204803-219e11d77f5d
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb
|
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb
|
||||||
github.com/zyedidia/clipboard v1.0.3
|
github.com/zyedidia/clipboard v1.0.3
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
@@ -31,6 +33,8 @@ github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff h1:+6NUiITWwE5q1
|
|||||||
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
|
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
|
github.com/sourcegraph/go-lsp v0.0.0-20200429204803-219e11d77f5d h1:afLbh+ltiygTOB37ymZVwKlJwWZn+86syPTbrrOAydY=
|
||||||
|
github.com/sourcegraph/go-lsp v0.0.0-20200429204803-219e11d77f5d/go.mod h1:SULmZY7YNBsvNiQbrb/BEDdEJ84TGnfyUQxaHt8t8rY=
|
||||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
gopath "path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -20,6 +21,7 @@ import (
|
|||||||
|
|
||||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||||
"github.com/zyedidia/micro/v2/internal/config"
|
"github.com/zyedidia/micro/v2/internal/config"
|
||||||
|
"github.com/zyedidia/micro/v2/internal/lsp"
|
||||||
ulua "github.com/zyedidia/micro/v2/internal/lua"
|
ulua "github.com/zyedidia/micro/v2/internal/lua"
|
||||||
"github.com/zyedidia/micro/v2/internal/screen"
|
"github.com/zyedidia/micro/v2/internal/screen"
|
||||||
"github.com/zyedidia/micro/v2/internal/util"
|
"github.com/zyedidia/micro/v2/internal/util"
|
||||||
@@ -123,6 +125,9 @@ type SharedBuffer struct {
|
|||||||
|
|
||||||
// Hash of the original buffer -- empty if fastdirty is on
|
// Hash of the original buffer -- empty if fastdirty is on
|
||||||
origHash [md5.Size]byte
|
origHash [md5.Size]byte
|
||||||
|
|
||||||
|
server *lsp.Server
|
||||||
|
version int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *SharedBuffer) insert(pos Loc, value []byte) {
|
func (b *SharedBuffer) insert(pos Loc, value []byte) {
|
||||||
@@ -369,6 +374,18 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
|
|||||||
|
|
||||||
OpenBuffers = append(OpenBuffers, b)
|
OpenBuffers = append(OpenBuffers, b)
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
if btype == BTDefault {
|
||||||
|
ft := b.Settings["filetype"].(string)
|
||||||
|
l, ok := lsp.GetLanguage(ft)
|
||||||
|
if ok && l.Installed() {
|
||||||
|
b.server, _ = lsp.StartServer(l)
|
||||||
|
b.server.Initialize(gopath.Dir(b.AbsPath))
|
||||||
|
b.server.DidOpen(b.AbsPath, ft, string(b.Bytes()), b.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
72
internal/lsp/install.go
Normal file
72
internal/lsp/install.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrManualInstall = errors.New("Requires manual installation")
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Languages map[string]Language `toml:"language"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Language struct {
|
||||||
|
Command string `toml:"command"`
|
||||||
|
Args []string `toml:"args"`
|
||||||
|
Install [][]string `toml:"install"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var conf *Config
|
||||||
|
|
||||||
|
func GetLanguage(lang string) (Language, bool) {
|
||||||
|
l, ok := conf.Languages[lang]
|
||||||
|
return l, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
conf, _ = LoadConfig([]byte(servers))
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfig(data []byte) (*Config, error) {
|
||||||
|
var conf Config
|
||||||
|
if _, err := toml.Decode(string(data), &conf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Language) Installed() bool {
|
||||||
|
_, err := exec.LookPath(l.Command)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Language) DoInstall(w io.Writer) error {
|
||||||
|
if l.Installed() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(l.Install) == 0 {
|
||||||
|
return ErrManualInstall
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range l.Install {
|
||||||
|
io.WriteString(w, strings.Join(c, " ")+"\n")
|
||||||
|
cmd := exec.Command(c[0], c[1:]...)
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
29
internal/lsp/requests.go
Normal file
29
internal/lsp/requests.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/sourcegraph/go-lsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) DidOpen(filename, language, text string, version int) error {
|
||||||
|
doc := lsp.TextDocumentItem{
|
||||||
|
URI: lsp.DocumentURI("file://" + filename),
|
||||||
|
LanguageID: language,
|
||||||
|
Version: version,
|
||||||
|
Text: text,
|
||||||
|
}
|
||||||
|
|
||||||
|
params := lsp.DidOpenTextDocumentParams{
|
||||||
|
TextDocument: doc,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.SendMessage("textDocument/didOpen", params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Received", string(resp))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
212
internal/lsp/server.go
Normal file
212
internal/lsp/server.go
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sourcegraph/go-lsp"
|
||||||
|
"github.com/zyedidia/micro/v2/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ActiveServers map[string]*Server
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ActiveServers = make(map[string]*Server)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
cmd *exec.Cmd
|
||||||
|
stdin io.WriteCloser
|
||||||
|
stdout *bufio.Reader
|
||||||
|
language *Language
|
||||||
|
capabilities lsp.ServerCapabilities
|
||||||
|
}
|
||||||
|
|
||||||
|
type RPCMessage struct {
|
||||||
|
RPCVersion string `json:"jsonrpc"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Params interface{} `json:"params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RPCInit struct {
|
||||||
|
RPCVersion string `json:"jsonrpc"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Result lsp.InitializeResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartServer(l Language) (*Server, error) {
|
||||||
|
c := exec.Command(l.Command, l.Args...)
|
||||||
|
|
||||||
|
log.Println("Running", l.Command, l.Args)
|
||||||
|
|
||||||
|
stdin, err := c.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout, err := c.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.Start()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := new(Server)
|
||||||
|
s.cmd = c
|
||||||
|
s.stdin = stdin
|
||||||
|
s.stdout = bufio.NewReader(stdout)
|
||||||
|
s.language = &l
|
||||||
|
|
||||||
|
// ActiveServers[l.Command] = s
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Initialize(directory string) error {
|
||||||
|
params := lsp.InitializeParams{
|
||||||
|
ProcessID: os.Getpid(),
|
||||||
|
RootURI: lsp.DocumentURI("file://" + directory),
|
||||||
|
ClientInfo: lsp.ClientInfo{
|
||||||
|
Name: "micro",
|
||||||
|
Version: util.Version,
|
||||||
|
},
|
||||||
|
Trace: "off",
|
||||||
|
Capabilities: lsp.ClientCapabilities{
|
||||||
|
Workspace: lsp.WorkspaceClientCapabilities{
|
||||||
|
WorkspaceEdit: struct {
|
||||||
|
DocumentChanges bool `json:"documentChanges,omitempty"`
|
||||||
|
ResourceOperations []string `json:"resourceOperations,omitempty"`
|
||||||
|
}{
|
||||||
|
DocumentChanges: true,
|
||||||
|
ResourceOperations: []string{"create", "rename", "delete"},
|
||||||
|
},
|
||||||
|
ApplyEdit: true,
|
||||||
|
},
|
||||||
|
TextDocument: lsp.TextDocumentClientCapabilities{
|
||||||
|
Formatting: &struct {
|
||||||
|
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
|
||||||
|
}{
|
||||||
|
DynamicRegistration: true,
|
||||||
|
},
|
||||||
|
Completion: struct {
|
||||||
|
CompletionItem struct {
|
||||||
|
DocumentationFormat []lsp.DocumentationFormat `json:"documentationFormat,omitempty"`
|
||||||
|
SnippetSupport bool `json:"snippetSupport,omitempty"`
|
||||||
|
} `json:"completionItem,omitempty"`
|
||||||
|
|
||||||
|
CompletionItemKind struct {
|
||||||
|
ValueSet []lsp.CompletionItemKind `json:"valueSet,omitempty"`
|
||||||
|
} `json:"completionItemKind,omitempty"`
|
||||||
|
|
||||||
|
ContextSupport bool `json:"contextSupport,omitempty"`
|
||||||
|
}{
|
||||||
|
CompletionItem: struct {
|
||||||
|
DocumentationFormat []lsp.DocumentationFormat `json:"documentationFormat,omitempty"`
|
||||||
|
SnippetSupport bool `json:"snippetSupport,omitempty"`
|
||||||
|
}{
|
||||||
|
DocumentationFormat: []lsp.DocumentationFormat{lsp.DFPlainText},
|
||||||
|
SnippetSupport: false,
|
||||||
|
},
|
||||||
|
ContextSupport: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Window: lsp.WindowClientCapabilities{
|
||||||
|
WorkDoneProgress: false,
|
||||||
|
},
|
||||||
|
Experimental: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.SendMessage("initialize", params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo parse capabilities
|
||||||
|
log.Println("Received", string(resp))
|
||||||
|
|
||||||
|
var r RPCInit
|
||||||
|
err = json.Unmarshal(resp, &r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(r)
|
||||||
|
s.capabilities = r.Result.Capabilities
|
||||||
|
fmt.Println(s.capabilities)
|
||||||
|
|
||||||
|
_, err = s.SendMessage("initialized", struct{}{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) SendMessage(method string, params interface{}) ([]byte, error) {
|
||||||
|
m := RPCMessage{
|
||||||
|
RPCVersion: "2.0",
|
||||||
|
ID: os.Getpid(),
|
||||||
|
Method: method,
|
||||||
|
Params: params,
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = append(msg, '\r', '\n')
|
||||||
|
|
||||||
|
header := []byte("Content-Length: " + strconv.Itoa(len(msg)) + "\r\n\r\n")
|
||||||
|
msg = append(header, msg...)
|
||||||
|
|
||||||
|
log.Println("Sending", string(msg))
|
||||||
|
|
||||||
|
s.stdin.Write(msg)
|
||||||
|
|
||||||
|
n := -1
|
||||||
|
for {
|
||||||
|
b, err := s.stdout.ReadBytes('\n')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
headerline := strings.TrimSpace(string(b))
|
||||||
|
if len(headerline) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(headerline, "Content-Length:") {
|
||||||
|
split := strings.Split(headerline, ":")
|
||||||
|
if len(split) <= 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n, err = strconv.Atoi(strings.TrimSpace(split[1]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n <= 0 {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := make([]byte, n)
|
||||||
|
_, err = s.stdout.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes, nil
|
||||||
|
}
|
||||||
79
internal/lsp/servers_toml.go
Normal file
79
internal/lsp/servers_toml.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package lsp
|
||||||
|
|
||||||
|
var servers = `[language.rust]
|
||||||
|
command = "rls"
|
||||||
|
install = [
|
||||||
|
["rustup", "update"],
|
||||||
|
["rustup", "component", "add", "rls", "rust-analysis", "rust-src"],
|
||||||
|
]
|
||||||
|
|
||||||
|
[language.javascript]
|
||||||
|
command = "typescript-language-server"
|
||||||
|
args = ["--stdio"]
|
||||||
|
install = [["npm", "install", "-g", "typescript-language-server"]]
|
||||||
|
|
||||||
|
[language.typescript]
|
||||||
|
command = "typescript-language-server"
|
||||||
|
args = ["--stdio"]
|
||||||
|
install = [["npm", "install", "-g", "typescript-language-server"]]
|
||||||
|
|
||||||
|
[language.html]
|
||||||
|
command = "html-languageserver"
|
||||||
|
args = ["--stdio"]
|
||||||
|
install = [["npm", "install", "-g", "vscode-html-languageserver-bin"]]
|
||||||
|
|
||||||
|
[language.ocaml]
|
||||||
|
command = "ocaml-language-server"
|
||||||
|
args = ["--stdio"]
|
||||||
|
install = [["npm", "install", "-g", "ocaml-language-server"]]
|
||||||
|
|
||||||
|
[language.python]
|
||||||
|
command = "pyls"
|
||||||
|
install = [["pip", "install", "python-language-server"]]
|
||||||
|
|
||||||
|
[language.c]
|
||||||
|
command = "clangd"
|
||||||
|
args = ["--log=verbose"]
|
||||||
|
|
||||||
|
[language.cpp]
|
||||||
|
command = "clangd"
|
||||||
|
args = []
|
||||||
|
|
||||||
|
[language.haskell]
|
||||||
|
command = "hie"
|
||||||
|
args = ["--lsp"]
|
||||||
|
|
||||||
|
[language.go]
|
||||||
|
command = "gopls"
|
||||||
|
args = ["serve"]
|
||||||
|
install = [["go", "get", "-u", "golang.org/x/tools/gopls"]]
|
||||||
|
|
||||||
|
[language.dart]
|
||||||
|
command = "dart_language_server"
|
||||||
|
install = [["pub", "global", "activate", "dart_language_server"]]
|
||||||
|
|
||||||
|
[language.ruby]
|
||||||
|
command = "solargraph"
|
||||||
|
args = ["stdio"]
|
||||||
|
install = [["gem", "install", "solargraph"]]
|
||||||
|
|
||||||
|
[language.css]
|
||||||
|
command = "css-languageserver"
|
||||||
|
args = ["--stdio"]
|
||||||
|
install = [["npm", "install", "-g", "vscode-css-languageserver-bin"]]
|
||||||
|
|
||||||
|
[language.scss]
|
||||||
|
command = "css-languageserver"
|
||||||
|
args = ["--stdio"]
|
||||||
|
install = [["npm", "install", "-g", "vscode-css-languageserver-bin"]]
|
||||||
|
|
||||||
|
[language.viml]
|
||||||
|
command = "vim-language-server"
|
||||||
|
args = ["--stdio"]
|
||||||
|
install = [["npm", "install", "-g", "vim-language-server"]]
|
||||||
|
|
||||||
|
[language.purescript]
|
||||||
|
command = "purescript-language-server"
|
||||||
|
args = ["--stdio"]
|
||||||
|
install = [["npm", "install", "-g", "purescript-language-server"]]
|
||||||
|
`
|
||||||
Reference in New Issue
Block a user