Compare commits

..

36 Commits

Author SHA1 Message Date
Zachary Yedidia
afd50e40c2 Disable fake cursor for Windows Terminal
Ref #1900
2020-11-06 13:43:40 -05:00
Zachary Yedidia
38f63ae432 Autocomplete for any non-whitespace 2020-08-30 15:44:19 -04:00
Zachary Yedidia
188b579b22 Show detail and doc 2020-08-16 17:20:17 -04:00
Zachary Yedidia
01f55a6c79 Ensure correct ordering of notifications 2020-08-16 12:35:08 -04:00
Zachary Yedidia
98b3ed0eec Fix undo autocomplete 2020-08-16 01:03:41 -04:00
Zachary Yedidia
724cedd37b Basic autocomplete box 2020-08-15 20:41:54 -04:00
Zachary Yedidia
132630a9a5 Apply additional edits if they exist 2020-08-15 18:20:10 -04:00
Zachary Yedidia
9999ef643f Use delta instead of textedit 2020-08-15 18:17:57 -04:00
Zachary Yedidia
68270773dd Use text edits for autocompletion 2020-08-15 18:05:29 -04:00
Zachary Yedidia
3821a7a075 Allow configuring lsp server list 2020-08-13 13:06:37 -04:00
Zachary Yedidia
a26dd63d93 Replace toml with yaml 2020-08-12 21:56:49 -04:00
Zachary Yedidia
25f65a5f7b LSP option and better LSP status 2020-08-12 21:40:20 -04:00
Zachary Yedidia
c822a16596 Shutdown lsp servers 2020-08-12 21:15:17 -04:00
Zachary Yedidia
eb5c123674 Fix usage of multireplace 2020-08-12 17:16:32 -04:00
Zachary Yedidia
8f6f336b6c Range format 2020-08-12 16:56:57 -04:00
Zachary Yedidia
0b49ffd7cb Fix nullwriter 2020-08-12 16:27:44 -04:00
Zachary Yedidia
3c50ac1666 Fix edit application in formatting 2020-08-12 16:21:05 -04:00
Zachary Yedidia
c1621086a2 Autoformatting 2020-08-12 16:03:23 -04:00
Zachary Yedidia
08f772b7d0 Better hover parsing 2020-08-12 16:03:23 -04:00
Zachary Yedidia
5ea8bd3aa1 Convert filetypes to language IDs 2020-08-12 16:03:23 -04:00
Zachary Yedidia
e3689ffbd8 Hover support 2020-08-12 16:03:23 -04:00
Zachary Yedidia
4af1dfcbd8 Handle initialization and didOpen properly 2020-08-12 16:03:23 -04:00
Zachary Yedidia
a4148d069a Fix issue with didChange position 2020-08-12 16:03:23 -04:00
Zachary Yedidia
f0b1158ab6 Run notifications in background to hide latency 2020-08-12 16:03:23 -04:00
Zachary Yedidia
c344f1bfce Fix notifications vs requests 2020-08-12 16:03:23 -04:00
Zachary Yedidia
053134af1c Basic non-compliant autocompletion via LSP 2020-08-12 16:03:23 -04:00
Zachary Yedidia
f6ba76424a Send didChange events 2020-08-12 16:03:23 -04:00
Zachary Yedidia
26442bdbbe Basic communication with lsp server 2020-08-12 16:03:23 -04:00
Zachary Yedidia
c5bafbc1c5 Merge 2020-08-12 01:18:18 -04:00
Zachary Yedidia
6b80870dfd Don't auto-relocate mouse events 2020-08-12 01:18:15 -04:00
Zachary Yedidia
5cb618c466 Improve showkey command 2020-08-11 22:18:10 -04:00
Ryan Westlund
a87370b111 Improve Rust syntax highlighting (#1820) 2020-08-11 21:39:57 -04:00
Zachary Yedidia
352f57cf11 Enable registering raw events
Fixes #1821
2020-08-11 14:36:58 -04:00
Zachary Yedidia
1e83e666fb Don't overwrite user bindings
This fix still needs more work.

Ref #1821
2020-08-11 01:43:41 -04:00
Zachary Yedidia
c837a7d0b7 Ref #1819 2020-08-10 20:34:10 -04:00
Zachary Yedidia
63d45bc9c5 Fix JobSend stdin 2020-08-10 12:24:29 -04:00
35 changed files with 1582 additions and 148 deletions

View File

@@ -12,7 +12,7 @@ type NullWriter struct{}
// Write is empty
func (NullWriter) Write(data []byte) (n int, err error) {
return 0, nil
return len(data), nil
}
// InitLog sets up the debug log system for micro if it has been enabled by compile-time variables

View File

@@ -10,6 +10,7 @@ import (
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/display"
"github.com/zyedidia/micro/v2/internal/lsp"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
@@ -34,6 +35,8 @@ func LuaImport(pkg string) *lua.LTable {
return luaImportMicroConfig()
case "micro/util":
return luaImportMicroUtil()
case "micro/lsp":
return luaImportMicroLsp()
default:
return ulua.Import(pkg)
}
@@ -153,3 +156,10 @@ func luaImportMicroUtil() *lua.LTable {
return pkg
}
func luaImportMicroLsp() *lua.LTable {
pkg := ulua.L.NewTable()
ulua.L.SetField(pkg, "GetLanguage", luar.New(ulua.L, lsp.GetLanguage))
return pkg
}

View File

@@ -20,6 +20,7 @@ import (
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/clipboard"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/lsp"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
@@ -218,6 +219,8 @@ func LoadInput(args []string) []*buffer.Buffer {
func main() {
defer func() {
lsp.ShutdownAllServers()
if util.Stdout.Len() > 0 {
fmt.Fprint(os.Stdout, util.Stdout.String())
}
@@ -250,6 +253,11 @@ func main() {
screen.TermMessage(err)
}
err = lsp.Init()
if err != nil {
screen.TermMessage(err)
}
// flag options
for k, v := range optionFlags {
if *v != "" {
@@ -405,6 +413,7 @@ func DoEvent() {
action.MainTab().Display()
action.InfoBar.Display()
screen.Screen.Show()
action.InfoBar.Message("")
// Check for new events
select {

2
go.mod
View File

@@ -19,6 +19,8 @@ require (
github.com/zyedidia/pty v2.0.0+incompatible // indirect
github.com/zyedidia/tcell v1.4.10
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415
go.lsp.dev/protocol v0.8.0
go.lsp.dev/uri v0.3.0
golang.org/x/text v0.3.2
gopkg.in/sourcemap.v1 v1.0.5 // indirect
gopkg.in/yaml.v2 v2.2.7

194
go.sum
View File

@@ -1,40 +1,136 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
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/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/p-e-w/go-runewidth v0.0.10-0.20200613030200-3e1705c5c059 h1:/+h2b6i15wh4EWsFkfdNdBE1jjGA872tpXEyhPM5aYg=
github.com/p-e-w/go-runewidth v0.0.10-0.20200613030200-3e1705c5c059/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff h1:+6NUiITWwE5q1KO6SAfUX918c+Tab0+tGAM/mtdlUyA=
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
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/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
@@ -56,22 +152,120 @@ github.com/zyedidia/tcell v1.4.10 h1:40iES9kNgiaTvp/wLTB4Elikx4uDPIPdV5fhI2EQiog
github.com/zyedidia/tcell v1.4.10/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415 h1:752dTQ5OatJ9M5ULK2+9lor+nzyZz+LYDo3WGngg3Rc=
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415/go.mod h1:8leT8G0Cm8NoJHdrrKHyR9MirWoF4YW7pZh06B6H+1E=
go.lsp.dev/jsonrpc2 v0.5.0 h1:nZfFY/G0SkMoogjAj2ltoWRvQ9xMzHDMIBWMS3CaUak=
go.lsp.dev/jsonrpc2 v0.5.0/go.mod h1:YPWQH63927Zzz1M+t4r3p/OrmQ3EfKjRLBd3S2E0e4g=
go.lsp.dev/protocol v0.8.0 h1:hSmnNllbCfvkRi0AjsKa8nua3EdCa4iAey75mDCpEv4=
go.lsp.dev/protocol v0.8.0/go.mod h1:SD+a8QoAIIR7H7/SAYPDLn6iQnEeHNEicfkFOR1j9E8=
go.lsp.dev/uri v0.3.0 h1:KcZJmh6nFIBeJzTugn5JTU6OOyG0lDOo3R9KwTxTYbo=
go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
layeh.com/gopher-luar v1.0.7 h1:53iv6CCkRs5wyofZ+qVXcyAYQOIG52s6pt4xkqZdq7k=
layeh.com/gopher-luar v1.0.7/go.mod h1:TPnIVCZ2RJBndm7ohXyaqfhzjlZ+OA2SZR/YwL8tECk=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@@ -10,10 +10,12 @@ import (
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/clipboard"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/lsp"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"go.lsp.dev/protocol"
)
// ScrollUp is not an action
@@ -92,6 +94,7 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
h.Cursor.StoreVisualX()
h.lastLoc = mouseLoc
h.Relocate()
return true
}
@@ -663,6 +666,13 @@ func (h *BufPane) Autocomplete() bool {
return false
}
// if there is an existing completion, always cycle it
if b.HasSuggestions {
h.cycleAutocomplete(true)
return true
}
// don't start a new completion unless the correct conditions are met
if h.Cursor.X == 0 {
return false
}
@@ -672,12 +682,14 @@ func (h *BufPane) Autocomplete() bool {
// don't autocomplete if cursor is on alpha numeric character (middle of a word)
return false
}
if b.HasSuggestions {
b.CycleAutocomplete(true)
return true
ret := true
if !b.Autocomplete(buffer.LSPComplete) {
ret = b.Autocomplete(buffer.BufferComplete)
}
return b.Autocomplete(buffer.BufferComplete)
if ret {
h.displayCompletionDoc()
}
return true
}
// CycleAutocompleteBack cycles back in the autocomplete suggestion list
@@ -687,12 +699,24 @@ func (h *BufPane) CycleAutocompleteBack() bool {
}
if h.Buf.HasSuggestions {
h.Buf.CycleAutocomplete(false)
h.cycleAutocomplete(false)
return true
}
return false
}
func (h *BufPane) cycleAutocomplete(forward bool) {
h.Buf.CycleAutocomplete(forward)
h.displayCompletionDoc()
}
func (h *BufPane) displayCompletionDoc() {
c := h.Buf.CurCompletion
if c >= 0 && c < len(h.Buf.Completions) {
InfoBar.Message(h.Buf.Completions[c].Doc)
}
}
// InsertTab inserts a tab or spaces
func (h *BufPane) InsertTab() bool {
b := h.Buf
@@ -1813,6 +1837,52 @@ func (h *BufPane) RemoveAllMultiCursors() bool {
return true
}
// SemanticInfo returns information about the identifier the cursor is on and
// displays the information in the infobar
// The information is fetched using the LSP server (must be enabled)
func (h *BufPane) SemanticInfo() bool {
info, err := h.Buf.Server.Hover(h.Buf.AbsPath, lsp.Position(h.Cursor.X, h.Cursor.Y))
if err != nil {
InfoBar.Error(err)
return false
}
info = strings.Split(info, "\n")[0]
InfoBar.Message(info)
return true
}
// AutoFormat automatically formats the document using LSP
func (h *BufPane) AutoFormat() bool {
var err error
var edits []protocol.TextEdit
if h.Cursor.HasSelection() {
edits, err = h.Buf.Server.DocumentRangeFormat(h.Buf.AbsPath, protocol.Range{
Start: lsp.Position(h.Cursor.CurSelection[0].X, h.Cursor.CurSelection[0].Y),
End: lsp.Position(h.Cursor.CurSelection[1].X, h.Cursor.CurSelection[1].Y),
}, protocol.FormattingOptions{
InsertSpaces: h.Buf.Settings["tabstospaces"].(bool),
TabSize: h.Buf.Settings["tabsize"].(float64),
})
} else {
edits, err = h.Buf.Server.DocumentFormat(h.Buf.AbsPath, protocol.FormattingOptions{
InsertSpaces: h.Buf.Settings["tabstospaces"].(bool),
TabSize: h.Buf.Settings["tabsize"].(float64),
})
}
if err != nil {
InfoBar.Error(err)
return false
}
h.Buf.ApplyEdits(edits)
return true
}
// None is an action that does nothing
func (h *BufPane) None() bool {
return true

View File

@@ -30,8 +30,6 @@ func createBindingsIfNotExist(fname string) {
// InitBindings intializes the bindings map by reading from bindings.json
func InitBindings() {
config.Bindings = DefaultBindings("buffer")
var parsed map[string]interface{}
filename := filepath.Join(config.ConfigDir, "bindings.json")
@@ -82,8 +80,11 @@ func BindKey(k, v string, bind func(e Event, a string)) {
event, err := findEvent(k)
if err != nil {
screen.TermMessage(err)
return
}
config.Bindings[event.Name()] = v
bind(event, v)
// switch e := event.(type) {

View File

@@ -484,9 +484,7 @@ func (h *BufPane) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
binds := h.Bindings()
action, _ := binds.NextEvent(e, te)
if action != nil {
if action(h) {
h.Relocate()
}
action(h)
binds.ResetEvents()
return true
}
@@ -690,6 +688,8 @@ var BufKeyActions = map[string]BufKeyAction{
"JumpLine": (*BufPane).JumpLine,
"Deselect": (*BufPane).Deselect,
"ClearInfo": (*BufPane).ClearInfo,
"SemanticInfo": (*BufPane).SemanticInfo,
"AutoFormat": (*BufPane).AutoFormat,
"None": (*BufPane).None,
// This was changed to InsertNewline but I don't want to break backwards compatibility

View File

@@ -641,7 +641,12 @@ func (h *BufPane) ShowKeyCmd(args []string) {
return
}
if action, ok := config.Bindings[args[0]]; ok {
event, err := findEvent(args[0])
if err != nil {
InfoBar.Error(err)
return
}
if action, ok := config.Bindings[event.Name()]; ok {
InfoBar.Message(action)
} else {
InfoBar.Message(args[0], " has no binding")

View File

@@ -72,6 +72,7 @@ var bufdefaults = map[string]string{
"Ctrl-w": "NextSplit",
"Ctrl-u": "ToggleMacro",
"Ctrl-j": "PlayMacro",
"Alt-i": "SemanticInfo",
"Insert": "ToggleOverwriteMode",
// Emacs-style keybindings

View File

@@ -15,7 +15,7 @@ import (
// for example with `vsplit filename`.
// CommandComplete autocompletes commands
func CommandComplete(b *buffer.Buffer) ([]string, []string) {
func CommandComplete(b *buffer.Buffer) []buffer.Completion {
c := b.GetActiveCursor()
input, argstart := buffer.GetArg(b)
@@ -32,11 +32,11 @@ func CommandComplete(b *buffer.Buffer) ([]string, []string) {
completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
}
return completions, suggestions
return buffer.ConvertCompletions(completions, suggestions, c)
}
// HelpComplete autocompletes help topics
func HelpComplete(b *buffer.Buffer) ([]string, []string) {
func HelpComplete(b *buffer.Buffer) []buffer.Completion {
c := b.GetActiveCursor()
input, argstart := buffer.GetArg(b)
@@ -54,7 +54,7 @@ func HelpComplete(b *buffer.Buffer) ([]string, []string) {
for i := range suggestions {
completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
}
return completions, suggestions
return buffer.ConvertCompletions(completions, suggestions, c)
}
// colorschemeComplete tab-completes names of colorschemes.
@@ -87,7 +87,7 @@ func contains(s []string, e string) bool {
}
// OptionComplete autocompletes options
func OptionComplete(b *buffer.Buffer) ([]string, []string) {
func OptionComplete(b *buffer.Buffer) []buffer.Completion {
c := b.GetActiveCursor()
input, argstart := buffer.GetArg(b)
@@ -97,22 +97,17 @@ func OptionComplete(b *buffer.Buffer) ([]string, []string) {
suggestions = append(suggestions, option)
}
}
// for option := range localSettings {
// if strings.HasPrefix(option, input) && !contains(suggestions, option) {
// suggestions = append(suggestions, option)
// }
// }
sort.Strings(suggestions)
completions := make([]string, len(suggestions))
for i := range suggestions {
completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
}
return completions, suggestions
return buffer.ConvertCompletions(completions, suggestions, c)
}
// OptionValueComplete completes values for various options
func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
func OptionValueComplete(b *buffer.Buffer) []buffer.Completion {
c := b.GetActiveCursor()
l := b.LineBytes(c.Y)
l = util.SliceStart(l, c.X)
@@ -128,12 +123,6 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
break
}
}
// for option := range localSettings {
// if option == string(args[len(args)-2]) {
// completeValue = true
// break
// }
// }
}
if !completeValue {
return OptionComplete(b)
@@ -150,11 +139,6 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
optionVal = option
}
}
// for k, option := range localSettings {
// if k == inputOpt {
// optionVal = option
// }
// }
switch optionVal.(type) {
case bool:
@@ -204,11 +188,11 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
for i := range suggestions {
completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
}
return completions, suggestions
return buffer.ConvertCompletions(completions, suggestions, c)
}
// PluginCmdComplete autocompletes the plugin command
func PluginCmdComplete(b *buffer.Buffer) ([]string, []string) {
func PluginCmdComplete(b *buffer.Buffer) []buffer.Completion {
c := b.GetActiveCursor()
input, argstart := buffer.GetArg(b)
@@ -224,11 +208,11 @@ func PluginCmdComplete(b *buffer.Buffer) ([]string, []string) {
for i := range suggestions {
completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
}
return completions, suggestions
return buffer.ConvertCompletions(completions, suggestions, c)
}
// PluginComplete completes values for the plugin command
func PluginComplete(b *buffer.Buffer) ([]string, []string) {
func PluginComplete(b *buffer.Buffer) []buffer.Completion {
c := b.GetActiveCursor()
l := b.LineBytes(c.Y)
l = util.SliceStart(l, c.X)
@@ -260,7 +244,7 @@ func PluginComplete(b *buffer.Buffer) ([]string, []string) {
for i := range suggestions {
completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
}
return completions, suggestions
return buffer.ConvertCompletions(completions, suggestions, c)
}
// PluginNameComplete completes with the names of loaded plugins

View File

@@ -141,13 +141,14 @@ func (k *KeyTree) RegisterMouseBinding(e Event, a PaneMouseAction) {
func (k *KeyTree) registerBinding(e Event, a TreeAction) {
switch ev := e.(type) {
case KeyEvent, MouseEvent:
case KeyEvent, MouseEvent, RawEvent:
newNode, ok := k.root.children[e]
if !ok {
newNode = NewKeyTreeNode()
k.root.children[e] = newNode
}
newNode.actions = append(newNode.actions, a)
// newNode.actions = append(newNode.actions, a)
newNode.actions = []TreeAction{a}
case KeySequenceEvent:
n := k.root
for _, key := range ev.keys {
@@ -159,7 +160,8 @@ func (k *KeyTree) registerBinding(e Event, a TreeAction) {
n = newNode
}
n.actions = append(n.actions, a)
// n.actions = append(n.actions, a)
n.actions = []TreeAction{a}
}
}

View File

@@ -7,7 +7,9 @@ import (
"sort"
"strings"
"github.com/zyedidia/micro/v2/internal/lsp"
"github.com/zyedidia/micro/v2/internal/util"
"go.lsp.dev/protocol"
)
// A Completer is a function that takes a buffer and returns info
@@ -17,49 +19,61 @@ import (
// the current cursor location if selected as well as a list of
// suggestion names which can be displayed in an autocomplete box or
// other UI element
type Completer func(*Buffer) ([]string, []string)
func (b *Buffer) GetSuggestions() {
type Completer func(*Buffer) []Completion
type Completion struct {
Edits []Delta
Label string
CommitChars []rune
Kind string
Filter string
Detail string
Doc string
}
// Autocomplete starts the autocomplete process
func (b *Buffer) Autocomplete(c Completer) bool {
b.Completions, b.Suggestions = c(b)
if len(b.Completions) != len(b.Suggestions) || len(b.Completions) == 0 {
b.Completions = c(b)
if len(b.Completions) == 0 {
return false
}
b.CurSuggestion = -1
b.CurCompletion = -1
b.CycleAutocomplete(true)
return true
}
// CycleAutocomplete moves to the next suggestion
func (b *Buffer) CycleAutocomplete(forward bool) {
prevSuggestion := b.CurSuggestion
prevCompletion := b.CurCompletion
if forward {
b.CurSuggestion++
b.CurCompletion++
} else {
b.CurSuggestion--
b.CurCompletion--
}
if b.CurSuggestion >= len(b.Suggestions) {
b.CurSuggestion = 0
} else if b.CurSuggestion < 0 {
b.CurSuggestion = len(b.Suggestions) - 1
if b.CurCompletion >= len(b.Completions) {
b.CurCompletion = 0
} else if b.CurCompletion < 0 {
b.CurCompletion = len(b.Completions) - 1
}
c := b.GetActiveCursor()
start := c.Loc
end := c.Loc
if prevSuggestion < len(b.Suggestions) && prevSuggestion >= 0 {
start = end.Move(-util.CharacterCountInString(b.Completions[prevSuggestion]), b)
} else {
// end = start.Move(1, b)
// undo prev completion
if prevCompletion != -1 {
prev := b.Completions[prevCompletion]
for i := 0; i < len(prev.Edits); i++ {
if len(prev.Edits[i].Text) != 0 {
b.UndoOneEvent()
}
if !prev.Edits[i].Start.Equal(prev.Edits[i].End) {
b.UndoOneEvent()
}
}
}
b.Replace(start, end, b.Completions[b.CurSuggestion])
if len(b.Suggestions) > 1 {
// apply current completion
comp := b.Completions[b.CurCompletion]
b.ApplyDeltas(comp.Edits)
if len(b.Completions) > 1 {
b.HasSuggestions = true
}
}
@@ -104,7 +118,7 @@ func GetArg(b *Buffer) (string, int) {
}
// FileComplete autocompletes filenames
func FileComplete(b *Buffer) ([]string, []string) {
func FileComplete(b *Buffer) []Completion {
c := b.GetActiveCursor()
input, argstart := GetArg(b)
@@ -123,7 +137,7 @@ func FileComplete(b *Buffer) ([]string, []string) {
}
if err != nil {
return nil, nil
return nil
}
var suggestions []string
@@ -149,16 +163,16 @@ func FileComplete(b *Buffer) ([]string, []string) {
completions[i] = util.SliceEndStr(complete, c.X-argstart)
}
return completions, suggestions
return ConvertCompletions(completions, suggestions, c)
}
// BufferComplete autocompletes based on previous words in the buffer
func BufferComplete(b *Buffer) ([]string, []string) {
func BufferComplete(b *Buffer) []Completion {
c := b.GetActiveCursor()
input, argstart := GetWord(b)
if argstart == -1 {
return []string{}, []string{}
return nil
}
inputLen := util.CharacterCount(input)
@@ -201,5 +215,97 @@ func BufferComplete(b *Buffer) ([]string, []string) {
completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
}
return completions, suggestions
return ConvertCompletions(completions, suggestions, c)
}
func LSPComplete(b *Buffer) []Completion {
if !b.HasLSP() {
return nil
}
c := b.GetActiveCursor()
pos := lsp.Position(c.X, c.Y)
items, err := b.Server.Completion(b.AbsPath, pos)
if err != nil {
return nil
}
completions := make([]Completion, len(items))
for i, item := range items {
completions[i] = Completion{
Label: item.Label,
Detail: item.Detail,
Kind: toKindStr(item.Kind),
Doc: getDoc(item.Documentation),
}
if item.TextEdit != nil && len(item.TextEdit.NewText) > 0 {
completions[i].Edits = []Delta{Delta{
Text: []byte(item.TextEdit.NewText),
Start: toLoc(item.TextEdit.Range.Start),
End: toLoc(item.TextEdit.Range.End),
}}
// for _, e := range item.AdditionalTextEdits {
// d := Delta{
// Text: []byte(e.NewText),
// Start: toLoc(e.Range.Start),
// End: toLoc(e.Range.End),
// }
// completions[i].Edits = append(completions[i].Edits, d)
// }
} else {
var t string
if len(item.InsertText) > 0 {
t = item.InsertText
} else {
t = item.Label
}
_, argstart := GetWord(b)
str := util.SliceEnd([]byte(t), c.X-argstart)
completions[i].Edits = []Delta{Delta{
Text: str,
Start: Loc{c.X, c.Y},
End: Loc{c.X, c.Y},
}}
}
}
return completions
}
// ConvertCompletions converts a list of insert text with suggestion labels
// to an array of completion objects ready for autocompletion
func ConvertCompletions(completions, suggestions []string, c *Cursor) []Completion {
comp := make([]Completion, len(completions))
for i := 0; i < len(completions); i++ {
comp[i] = Completion{
Label: suggestions[i],
}
comp[i].Edits = []Delta{Delta{
Text: []byte(completions[i]),
Start: Loc{c.X, c.Y},
End: Loc{c.X, c.Y},
}}
}
return comp
}
func toKindStr(k protocol.CompletionItemKind) string {
s := k.String()
return strings.ToLower(string(s[0]))
}
// returns documentation from a string | MarkupContent item
func getDoc(documentation interface{}) string {
var doc string
switch s := documentation.(type) {
case string:
doc = s
case protocol.MarkupContent:
doc = s.Value
}
return strings.Split(doc, "\n")[0]
}

View File

@@ -10,23 +10,26 @@ import (
"io/ioutil"
"os"
"path"
gopath "path"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"time"
luar "layeh.com/gopher-luar"
dmp "github.com/sergi/go-diff/diffmatchpatch"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/lsp"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/micro/v2/pkg/highlight"
lspt "go.lsp.dev/protocol"
"golang.org/x/text/encoding/htmlindex"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
luar "layeh.com/gopher-luar"
)
const backupTime = 8000
@@ -90,9 +93,8 @@ type SharedBuffer struct {
// Settings customized by the user
Settings map[string]interface{}
Suggestions []string
Completions []string
CurSuggestion int
Completions []Completion
CurCompletion int
Messages []*Message
@@ -123,6 +125,9 @@ type SharedBuffer struct {
// Hash of the original buffer -- empty if fastdirty is on
origHash [md5.Size]byte
Server *lsp.Server
version uint64
}
func (b *SharedBuffer) insert(pos Loc, value []byte) {
@@ -132,12 +137,37 @@ func (b *SharedBuffer) insert(pos Loc, value []byte) {
inslines := bytes.Count(value, []byte{'\n'})
b.MarkModified(pos.Y, pos.Y+inslines)
b.lspDidChange(pos, pos, string(value))
}
func (b *SharedBuffer) remove(start, end Loc) []byte {
b.isModified = true
b.HasSuggestions = false
defer b.MarkModified(start.Y, end.Y)
return b.LineArray.remove(start, end)
sub := b.LineArray.remove(start, end)
b.lspDidChange(start, end, "")
return sub
}
func (b *SharedBuffer) lspDidChange(start, end Loc, text string) {
b.version++
// TODO: convert to UTF16 codepoints
change := lspt.TextDocumentContentChangeEvent{
Range: &lspt.Range{
Start: lsp.Position(start.X, start.Y),
End: lsp.Position(end.X, end.Y),
},
Text: text,
}
if b.HasLSP() {
b.Server.DidChange(b.AbsPath, b.version, []lspt.TextDocumentContentChangeEvent{change})
}
}
// HasLSP returns whether this buffer is communicating with an LSP server
func (b *SharedBuffer) HasLSP() bool {
return b.Server != nil && b.Server.Active
}
// MarkModified marks the buffer as modified for this frame
@@ -369,9 +399,40 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
OpenBuffers = append(OpenBuffers, b)
if !found {
if btype == BTDefault && b.Settings["lsp"].(bool) {
b.lspInit()
}
}
return b
}
// initializes an LSP server if possible, or calls didOpen on an existing
// LSP server in this workspace
func (b *Buffer) lspInit() {
ft := lsp.Filetype(b.Settings["filetype"].(string))
l, ok := lsp.GetLanguage(ft)
if ok && l.Installed() {
b.Server = lsp.GetServer(l, gopath.Dir(b.AbsPath))
if b.Server == nil {
var err error
b.Server, err = lsp.StartServer(l)
if err == nil {
d, _ := os.Getwd()
b.Server.Initialize(d)
}
}
if b.HasLSP() {
bytes := b.Bytes()
if len(bytes) == 0 {
bytes = []byte{'\n'}
}
b.Server.DidOpen(b.AbsPath, ft, string(bytes), b.version)
}
}
}
// Close removes this buffer from the list of open buffers
func (b *Buffer) Close() {
for i, buf := range OpenBuffers {
@@ -396,6 +457,10 @@ func (b *Buffer) Fini() {
if b.Type == BTStdout {
fmt.Fprint(util.Stdout, string(b.Bytes()))
}
if b.HasLSP() {
b.Server.DidClose(b.AbsPath)
}
}
// GetName returns the name that should be displayed in the statusline
@@ -427,6 +492,7 @@ func (b *Buffer) Insert(start Loc, text string) {
b.EventHandler.Insert(start, text)
b.RequestBackup()
b.RelocateCursors()
}
}
@@ -438,6 +504,68 @@ func (b *Buffer) Remove(start, end Loc) {
b.EventHandler.Remove(start, end)
b.RequestBackup()
b.RelocateCursors()
}
}
// ApplyEdit performs a LSP text edit on the buffer
func (b *Buffer) ApplyEdit(e lspt.TextEdit) {
if len(e.NewText) == 0 {
// deletion
b.Remove(toLoc(e.Range.Start), toLoc(e.Range.End))
} else {
// insert/replace
b.Replace(toLoc(e.Range.Start), toLoc(e.Range.End), e.NewText)
}
}
func (b *Buffer) ApplyEdits(edits []lspt.TextEdit) {
if !b.Type.Readonly {
locs := make([]struct {
t string
start, end Loc
}, len(edits))
for i, e := range edits {
locs[i] = struct {
t string
start, end Loc
}{
t: e.NewText,
start: toLoc(e.Range.Start),
end: toLoc(e.Range.End),
}
}
// Since edit ranges are guaranteed by LSP to never overlap we can sort
// by last edit first and apply each edit in order
// Perhaps in the future we should make this more robust to a non-conforming
// server that sends overlapping ranges
sort.Slice(locs, func(i, j int) bool {
return locs[i].start.GreaterThan(locs[j].start)
})
for _, d := range locs {
if len(d.t) == 0 {
b.Remove(d.start, d.end)
} else {
b.Replace(d.start, d.end, d.t)
}
}
b.RelocateCursors()
}
}
func (b *Buffer) ApplyDeltas(deltas []Delta) {
if !b.Type.Readonly {
sort.Slice(deltas, func(i, j int) bool {
return deltas[i].Start.GreaterThan(deltas[j].Start)
})
for _, d := range deltas {
if len(d.Text) == 0 {
b.Remove(d.Start, d.End)
} else {
b.ReplaceBytes(d.Start, d.End, d.Text)
}
}
b.RelocateCursors()
}
}

View File

@@ -2,6 +2,7 @@ package buffer
import (
"bytes"
"log"
"time"
dmp "github.com/sergi/go-diff/diffmatchpatch"
@@ -52,6 +53,7 @@ func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) {
}
if len(t.Deltas) != 1 {
log.Println("Multiple deltas not supported")
return
}
@@ -230,6 +232,12 @@ func (eh *EventHandler) Replace(start, end Loc, replace string) {
eh.Insert(start, replace)
}
// ReplaceBytes deletes from start to end and replaces it with the given string
func (eh *EventHandler) ReplaceBytes(start, end Loc, replace []byte) {
eh.Remove(start, end)
eh.InsertBytes(start, replace)
}
// Execute a textevent and add it to the undo stack
func (eh *EventHandler) Execute(t *TextEvent) {
if eh.RedoStack.Len() > 0 {

View File

@@ -2,6 +2,7 @@ package buffer
import (
"github.com/zyedidia/micro/v2/internal/util"
"go.lsp.dev/protocol"
)
// Loc stores a location
@@ -47,6 +48,11 @@ func (l Loc) LessEqual(b Loc) bool {
return l == b
}
// Equal returns true if two locs are equal
func (l Loc) Equal(b Loc) bool {
return l.Y == b.Y && l.X == b.X
}
// The following functions require a buffer to know where newlines are
// Diff returns the distance between two locations
@@ -146,3 +152,10 @@ func clamp(pos Loc, la *LineArray) Loc {
}
return pos
}
func toLoc(r protocol.Position) Loc {
return Loc{
X: int(r.Character),
Y: int(r.Line),
}
}

View File

@@ -195,5 +195,10 @@ func (b *Buffer) saveToFile(filename string, withSudo bool) error {
b.AbsPath = absPath
b.isModified = false
b.UpdateRules()
if b.HasLSP() {
b.Server.DidSave(b.AbsPath)
}
return err
}

View File

@@ -39,6 +39,12 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error {
b.isModified = true
} else if option == "readonly" && b.Type.Kind == BTDefault.Kind {
b.Type.Readonly = nativeValue.(bool)
} else if option == "lsp" && b.Type.Kind == BTDefault.Kind {
if nativeValue.(bool) && !b.HasLSP() {
b.lspInit()
} else if b.HasLSP() {
b.Server.Shutdown()
}
}
return nil

View File

@@ -5,3 +5,7 @@ const (
)
var Bindings map[string]string
func init() {
Bindings = make(map[string]string)
}

File diff suppressed because one or more lines are too long

View File

@@ -272,6 +272,7 @@ var defaultCommonSettings = map[string]interface{}{
"ignorecase": false,
"indentchar": " ",
"keepautoindent": false,
"lsp": true,
"matchbrace": true,
"mkparents": false,
"permbackup": false,

View File

@@ -17,7 +17,8 @@ type BufWindow struct {
*View
// Buffer being shown in this window
Buf *buffer.Buffer
Buf *buffer.Buffer
completeBox buffer.Loc
active bool
@@ -583,6 +584,13 @@ func (w *BufWindow) displayBuffer() {
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style)
if w.Buf.HasSuggestions && len(w.Buf.Completions) > 0 {
compl := w.Buf.Completions[0].Edits[0].Start
if bloc.X == compl.X && bloc.Y == compl.Y {
w.completeBox = buffer.Loc{w.X + vloc.X, w.Y + vloc.Y}
}
}
if showcursor {
for _, c := range cursors {
if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() {
@@ -742,9 +750,60 @@ func (w *BufWindow) displayScrollBar() {
}
}
func (w *BufWindow) displayCompleteBox() {
if !w.Buf.HasSuggestions || w.Buf.NumCursors() > 1 {
return
}
labelw := 0
detailw := 0
kindw := 0
for _, comp := range w.Buf.Completions {
charcount := util.CharacterCountInString(comp.Label)
if charcount > labelw {
labelw = charcount
}
charcount = util.CharacterCountInString(comp.Detail)
if charcount > detailw {
detailw = charcount
}
charcount = util.CharacterCountInString(comp.Kind)
if charcount > kindw {
kindw = charcount
}
}
labelw++
kindw++
display := func(s string, width, x, y int, cur bool) {
for j := 0; j < width; j++ {
r := ' '
var combc []rune
var size int
if len(s) > 0 {
r, combc, size = util.DecodeCharacterInString(s)
s = s[size:]
}
st := config.DefStyle.Reverse(true)
if cur {
st = st.Reverse(false)
}
screen.SetContent(w.completeBox.X+x+j, w.completeBox.Y+y, r, combc, st)
}
}
for i, comp := range w.Buf.Completions {
cur := i == w.Buf.CurCompletion
display(comp.Label+" ", labelw, 0, i+1, cur)
display(comp.Kind+" ", kindw, labelw, i+1, cur)
display(comp.Detail, detailw, labelw+kindw, i+1, cur)
}
}
// Display displays the buffer and the statusline
func (w *BufWindow) Display() {
w.displayStatusLine()
w.displayScrollBar()
w.displayBuffer()
w.displayCompleteBox()
}

View File

@@ -179,8 +179,8 @@ func (i *InfoWindow) displayKeyMenu() {
func (i *InfoWindow) totalSize() int {
sum := 0
for _, n := range i.Suggestions {
sum += runewidth.StringWidth(n) + 1
for _, n := range i.Completions {
sum += runewidth.StringWidth(n.Label) + 1
}
return sum
}
@@ -189,9 +189,9 @@ func (i *InfoWindow) scrollToSuggestion() {
x := 0
s := i.totalSize()
for j, n := range i.Suggestions {
c := util.CharacterCountInString(n)
if j == i.CurSuggestion {
for j, n := range i.Completions {
c := util.CharacterCountInString(n.Label)
if j == i.CurCompletion {
if x+c >= i.hscroll+i.Width {
i.hscroll = util.Clamp(x+c+1-i.Width, 0, s-i.Width)
} else if x < i.hscroll {
@@ -236,7 +236,7 @@ func (i *InfoWindow) Display() {
}
}
if i.HasSuggestions && len(i.Suggestions) > 1 {
if i.HasSuggestions && len(i.Completions) > 1 {
i.scrollToSuggestion()
x := -i.hscroll
@@ -273,12 +273,12 @@ func (i *InfoWindow) Display() {
}
}
for j, s := range i.Suggestions {
for j, s := range i.Completions {
style := statusLineStyle
if i.CurSuggestion == j {
if i.CurCompletion == j {
style = style.Reverse(true)
}
for _, r := range s {
for _, r := range s.Label {
draw(r, style)
// screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style)
}

View File

@@ -98,44 +98,6 @@ func (s *StatusLine) Display() {
// We'll draw the line at the lowest line in the window
y := s.win.Height + s.win.Y - 1
b := s.win.Buf
// autocomplete suggestions (for the buffer, not for the infowindow)
if b.HasSuggestions && len(b.Suggestions) > 1 {
statusLineStyle := config.DefStyle.Reverse(true)
if style, ok := config.Colorscheme["statusline"]; ok {
statusLineStyle = style
}
keymenuOffset := 0
if config.GetGlobalOption("keymenu").(bool) {
keymenuOffset = len(keydisplay)
}
x := 0
for j, sug := range b.Suggestions {
style := statusLineStyle
if b.CurSuggestion == j {
style = style.Reverse(true)
}
for _, r := range sug {
screen.SetContent(x, y-keymenuOffset, r, nil, style)
x++
if x >= s.win.Width {
return
}
}
screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle)
x++
if x >= s.win.Width {
return
}
}
for x < s.win.Width {
screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle)
x++
}
return
}
formatter := func(match []byte) []byte {
name := match[2 : len(match)-1]
if bytes.HasPrefix(name, []byte("opt")) {

94
internal/lsp/install.go Normal file
View File

@@ -0,0 +1,94 @@
package lsp
import (
"errors"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/zyedidia/micro/v2/internal/config"
"gopkg.in/yaml.v2"
)
var ErrManualInstall = errors.New("Requires manual installation")
type Config struct {
Languages map[string]Language `yaml:"language"`
}
type Language struct {
Command string `yaml:"command"`
Args []string `yaml:"args"`
Install [][]string `yaml:"install"`
}
var conf *Config
func GetLanguage(lang string) (Language, bool) {
if conf != nil {
l, ok := conf.Languages[lang]
return l, ok
}
return Language{}, false
}
func Init() error {
var servers []byte
var err error
filename := filepath.Join(config.ConfigDir, "lsp.yaml")
if _, e := os.Stat(filename); e == nil {
servers, err = ioutil.ReadFile(filename)
if err != nil {
servers = servers_internal
}
} else {
err = ioutil.WriteFile(filename, servers_internal, 0644)
servers = servers_internal
}
conf, err = LoadConfig(servers)
return err
}
func LoadConfig(data []byte) (*Config, error) {
var conf Config
if err := yaml.Unmarshal(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
}

20
internal/lsp/languages.go Normal file
View File

@@ -0,0 +1,20 @@
package lsp
// mappings for when micro filetypes don't match LSP language identifiers
var languages = map[string]string{
"batch": "bat",
"c++": "cpp",
"git-rebase-todo": "git-rebase",
"html4": "html",
"html5": "html",
"python2": "python",
"shell": "shellscript",
// "tex": "latex",
}
func Filetype(ft string) string {
if l, ok := languages[ft]; ok {
return l
}
return ft
}

View File

@@ -0,0 +1,58 @@
package lsp
import (
lsp "go.lsp.dev/protocol"
"go.lsp.dev/uri"
)
func (s *Server) DidOpen(filename, language, text string, version uint64) {
doc := lsp.TextDocumentItem{
URI: uri.File(filename),
LanguageID: lsp.LanguageIdentifier(language),
Version: float64(version), // not sure why this is a float on go.lsp.dev
Text: text,
}
params := lsp.DidOpenTextDocumentParams{
TextDocument: doc,
}
go s.sendNotification(lsp.MethodTextDocumentDidOpen, params)
}
func (s *Server) DidSave(filename string) {
doc := lsp.TextDocumentIdentifier{
URI: uri.File(filename),
}
params := lsp.DidSaveTextDocumentParams{
TextDocument: doc,
}
go s.sendNotification(lsp.MethodTextDocumentDidSave, params)
}
func (s *Server) DidChange(filename string, version uint64, changes []lsp.TextDocumentContentChangeEvent) {
doc := lsp.VersionedTextDocumentIdentifier{
TextDocumentIdentifier: lsp.TextDocumentIdentifier{
URI: uri.File(filename),
},
Version: &version,
}
params := lsp.DidChangeTextDocumentParams{
TextDocument: doc,
ContentChanges: changes,
}
go s.sendNotification(lsp.MethodTextDocumentDidChange, params)
}
func (s *Server) DidClose(filename string) {
doc := lsp.TextDocumentIdentifier{
URI: uri.File(filename),
}
params := lsp.DidCloseTextDocumentParams{
TextDocument: doc,
}
go s.sendNotification(lsp.MethodTextDocumentDidClose, params)
}

198
internal/lsp/requests.go Normal file
View File

@@ -0,0 +1,198 @@
package lsp
import (
"encoding/json"
"errors"
lsp "go.lsp.dev/protocol"
"go.lsp.dev/uri"
)
var ErrNotSupported = errors.New("Operation not supported by language server")
type RPCCompletion struct {
RPCVersion string `json:"jsonrpc"`
ID int `json:"id"`
Result lsp.CompletionList `json:"result"`
}
type RPCCompletionAlternate struct {
RPCVersion string `json:"jsonrpc"`
ID int `json:"id"`
Result []lsp.CompletionItem `json:"result"`
}
type RPCHover struct {
RPCVersion string `json:"jsonrpc"`
ID int `json:"id"`
Result lsp.Hover `json:"result"`
}
type RPCFormat struct {
RPCVersion string `json:"jsonrpc"`
ID int `json:"id"`
Result []lsp.TextEdit `json:"result"`
}
type hoverAlternate struct {
// Contents is the hover's content
Contents []interface{} `json:"contents"`
// Range an optional range is a range inside a text document
// that is used to visualize a hover, e.g. by changing the background color.
Range lsp.Range `json:"range,omitempty"`
}
type RPCHoverAlternate struct {
RPCVersion string `json:"jsonrpc"`
ID int `json:"id"`
Result hoverAlternate `json:"result"`
}
func Position(x, y int) lsp.Position {
return lsp.Position{
Line: float64(y),
Character: float64(x),
}
}
func (s *Server) DocumentFormat(filename string, options lsp.FormattingOptions) ([]lsp.TextEdit, error) {
if !s.capabilities.DocumentFormattingProvider {
return nil, ErrNotSupported
}
doc := lsp.TextDocumentIdentifier{
URI: uri.File(filename),
}
params := lsp.DocumentFormattingParams{
Options: options,
TextDocument: doc,
}
resp, err := s.sendRequest(lsp.MethodTextDocumentFormatting, params)
if err != nil {
return nil, err
}
var r RPCFormat
err = json.Unmarshal(resp, &r)
if err != nil {
return nil, err
}
return r.Result, nil
}
func (s *Server) DocumentRangeFormat(filename string, r lsp.Range, options lsp.FormattingOptions) ([]lsp.TextEdit, error) {
if !s.capabilities.DocumentRangeFormattingProvider {
return nil, ErrNotSupported
}
doc := lsp.TextDocumentIdentifier{
URI: uri.File(filename),
}
params := lsp.DocumentRangeFormattingParams{
Options: options,
Range: r,
TextDocument: doc,
}
resp, err := s.sendRequest(lsp.MethodTextDocumentFormatting, params)
if err != nil {
return nil, err
}
var rpc RPCFormat
err = json.Unmarshal(resp, &rpc)
if err != nil {
return nil, err
}
return rpc.Result, nil
}
func (s *Server) Completion(filename string, pos lsp.Position) ([]lsp.CompletionItem, error) {
if s.capabilities.CompletionProvider == nil {
return nil, ErrNotSupported
}
cc := lsp.CompletionContext{
TriggerKind: lsp.Invoked,
}
docpos := lsp.TextDocumentPositionParams{
TextDocument: lsp.TextDocumentIdentifier{
URI: uri.File(filename),
},
Position: pos,
}
params := lsp.CompletionParams{
TextDocumentPositionParams: docpos,
Context: &cc,
}
resp, err := s.sendRequest(lsp.MethodTextDocumentCompletion, params)
if err != nil {
return nil, err
}
var r RPCCompletion
err = json.Unmarshal(resp, &r)
if err == nil {
return r.Result.Items, nil
}
var ra RPCCompletionAlternate
err = json.Unmarshal(resp, &ra)
if err != nil {
return nil, err
}
return ra.Result, nil
}
func (s *Server) CompletionResolve() {
}
func (s *Server) Hover(filename string, pos lsp.Position) (string, error) {
if !s.capabilities.HoverProvider {
return "", ErrNotSupported
}
params := lsp.TextDocumentPositionParams{
TextDocument: lsp.TextDocumentIdentifier{
URI: uri.File(filename),
},
Position: pos,
}
resp, err := s.sendRequest(lsp.MethodTextDocumentHover, params)
if err != nil {
return "", err
}
var r RPCHover
err = json.Unmarshal(resp, &r)
if err == nil {
return r.Result.Contents.Value, nil
}
var ra RPCHoverAlternate
err = json.Unmarshal(resp, &ra)
if err != nil {
return "", err
}
for _, c := range ra.Result.Contents {
switch t := c.(type) {
case string:
return t, nil
case map[string]interface{}:
s, ok := t["value"].(string)
if ok {
return s, nil
}
}
}
return "", nil
}

322
internal/lsp/server.go Normal file
View File

@@ -0,0 +1,322 @@
package lsp
import (
"bufio"
"encoding/json"
"errors"
"io"
"log"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"time"
lsp "go.lsp.dev/protocol"
"go.lsp.dev/uri"
)
var activeServers map[string]*Server
var slock sync.Mutex
func init() {
activeServers = make(map[string]*Server)
}
func GetServer(l Language, dir string) *Server {
s, ok := activeServers[l.Command+"-"+dir]
if ok && s.Active {
return s
}
return nil
}
func ShutdownAllServers() {
for _, s := range activeServers {
if s.Active {
s.Shutdown()
}
}
}
type Server struct {
cmd *exec.Cmd
stdin io.WriteCloser
stdout *bufio.Reader
language *Language
capabilities lsp.ServerCapabilities
root string
lock sync.Mutex
Active bool
requestID int
responses map[int]chan ([]byte)
}
type RPCRequest struct {
RPCVersion string `json:"jsonrpc"`
ID int `json:"id"`
Method string `json:"method"`
Params interface{} `json:"params"`
}
type RPCNotification struct {
RPCVersion string `json:"jsonrpc"`
Method string `json:"method"`
Params interface{} `json:"params"`
}
type RPCInit struct {
RPCVersion string `json:"jsonrpc"`
ID int `json:"id"`
Result lsp.InitializeResult `json:"result"`
}
type RPCResult struct {
RPCVersion string `json:"jsonrpc"`
ID int `json:"id,omitempty"`
Method string `json:"method,omitempty"`
}
func StartServer(l Language) (*Server, error) {
s := new(Server)
c := exec.Command(l.Command, l.Args...)
c.Stderr = log.Writer()
stdin, err := c.StdinPipe()
if err != nil {
log.Println("[micro-lsp]", err)
return nil, err
}
stdout, err := c.StdoutPipe()
if err != nil {
log.Println("[micro-lsp]", err)
return nil, err
}
err = c.Start()
if err != nil {
log.Println("[micro-lsp]", err)
return nil, err
}
s.cmd = c
s.stdin = stdin
s.stdout = bufio.NewReader(stdout)
s.language = &l
s.responses = make(map[int]chan []byte)
return s, nil
}
// Initialize performs the LSP initialization handshake
// The directory must be an absolute path
func (s *Server) Initialize(directory string) {
params := lsp.InitializeParams{
ProcessID: float64(os.Getpid()),
RootURI: uri.File(directory),
Capabilities: lsp.ClientCapabilities{
Workspace: &lsp.WorkspaceClientCapabilities{
WorkspaceEdit: &lsp.WorkspaceClientCapabilitiesWorkspaceEdit{
DocumentChanges: true,
ResourceOperations: []string{"create", "rename", "delete"},
},
ApplyEdit: true,
},
TextDocument: &lsp.TextDocumentClientCapabilities{
Formatting: &lsp.TextDocumentClientCapabilitiesFormatting{
DynamicRegistration: false,
},
Completion: &lsp.TextDocumentClientCapabilitiesCompletion{
DynamicRegistration: false,
CompletionItem: &lsp.TextDocumentClientCapabilitiesCompletionItem{
SnippetSupport: false,
CommitCharactersSupport: false,
DocumentationFormat: []lsp.MarkupKind{lsp.PlainText},
DeprecatedSupport: false,
PreselectSupport: false,
},
ContextSupport: false,
},
Hover: &lsp.TextDocumentClientCapabilitiesHover{
DynamicRegistration: false,
ContentFormat: []lsp.MarkupKind{lsp.PlainText},
},
},
},
}
activeServers[s.language.Command+"-"+directory] = s
s.Active = true
s.root = directory
go s.receive()
s.lock.Lock()
go func() {
resp, err := s.sendRequest(lsp.MethodInitialize, params)
if err != nil {
log.Println("[micro-lsp]", err)
s.Active = false
s.lock.Unlock()
return
}
// todo parse capabilities
log.Println("[micro-lsp] <<<", string(resp))
var r RPCInit
json.Unmarshal(resp, &r)
s.lock.Unlock()
err = s.sendNotification(lsp.MethodInitialized, struct{}{})
if err != nil {
log.Println("[micro-lsp]", err)
}
s.capabilities = r.Result.Capabilities
}()
}
func (s *Server) Shutdown() {
s.sendRequest(lsp.MethodShutdown, nil)
s.sendNotification(lsp.MethodExit, nil)
s.Active = false
}
func (s *Server) receive() {
for s.Active {
resp, err := s.receiveMessage()
if err == io.EOF {
log.Println("Received EOF, shutting down")
s.Active = false
return
}
if err != nil {
log.Println("[micro-lsp,error]", err)
continue
}
log.Println("[micro-lsp] <<<", string(resp))
var r RPCResult
err = json.Unmarshal(resp, &r)
if err != nil {
log.Println("[micro-lsp]", err)
continue
}
switch r.Method {
case lsp.MethodWindowLogMessage:
// TODO
case lsp.MethodTextDocumentPublishDiagnostics:
// TODO
case "":
// Response
if _, ok := s.responses[r.ID]; ok {
log.Println("[micro-lsp] Got response for", r.ID)
s.responses[r.ID] <- resp
}
}
}
}
func (s *Server) receiveMessage() ([]byte, error) {
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 := io.ReadFull(s.stdout, bytes)
if err != nil {
log.Println("[micro-lsp]", err)
}
return bytes, err
}
func (s *Server) sendNotification(method string, params interface{}) error {
m := RPCNotification{
RPCVersion: "2.0",
Method: method,
Params: params,
}
s.lock.Lock()
go s.sendMessageUnlock(m)
return nil
}
func (s *Server) sendRequest(method string, params interface{}) ([]byte, error) {
id := s.requestID
s.requestID++
r := make(chan []byte)
s.responses[id] = r
m := RPCRequest{
RPCVersion: "2.0",
ID: id,
Method: method,
Params: params,
}
err := s.sendMessage(m)
if err != nil {
return nil, err
}
var bytes []byte
select {
case bytes = <-r:
case <-time.After(5 * time.Second):
err = errors.New("Request timed out")
}
delete(s.responses, id)
return bytes, err
}
func (s *Server) sendMessage(m interface{}) error {
msg, err := json.Marshal(m)
if err != nil {
return err
}
log.Println("[micro-lsp] >>>", string(msg))
// encode header and proper line endings
msg = append(msg, '\r', '\n')
header := []byte("Content-Length: " + strconv.Itoa(len(msg)) + "\r\n\r\n")
msg = append(header, msg...)
_, err = s.stdin.Write(msg)
return err
}
func (s *Server) sendMessageUnlock(m interface{}) error {
defer s.lock.Unlock()
return s.sendMessage(m)
}

View File

@@ -0,0 +1,67 @@
package lsp
var servers_internal = []byte(`language:
rust:
command: rls
install: [["rustup", "update"], ["rustup", "component", "add", "rls", "rust-analysis", "rust-src"]]
javascript:
command: typescript-language-server
args: ["--stdio"]
install: [["npm", "install", "-g", "typescript-language-server"]]
typescript:
command: typescript-language-server
args: ["--stdio"]
install: [["npm", "install", "-g", "typescript-language-server"]]
html:
command: html-languageserver
args: ["--stdio"]
install: [["npm", "install", "-g", "vscode-html-languageserver-bin"]]
ocaml:
command: ocaml-language-server
args: ["--stdio"]
install: [["npm", "install", "-g", "ocaml-language-server"]]
python:
command: pyls
install: [["pip", "install", "python-language-server"]]
c:
command: clangd
args: []
cpp:
command: clangd
args: []
haskell:
command: hie
args: ["--lsp"]
go:
command: gopls
args: ["serve"]
install: [["go", "get", "-u", "golang.org/x/tools/gopls"]]
dart:
command: dart_language_server
install: [["pub", "global", "activate", "dart_language_server"]]
ruby:
command: solargraph
args: ["stdio"]
install: [["gem", "install", "solargraph"]]
css:
command: css-languageserver
args: ["--stdio"]
install: [["npm", "install", "-g", "vscode-css-languageserver-bin"]]
scss:
command: css-languageserver
args: ["--stdio"]
install: [["npm", "install", "-g", "vscode-css-languageserver-bin"]]
viml:
command: vim-language-server
args: ["--stdio"]
install: [["npm", "install", "-g", "vim-language-server"]]
purescript:
command: purescript-language-server
args: ["--stdio"]
install: [["npm", "install", "-g", "purescript-language-server"]]
verilog:
command: svls
install: [["cargo", "install", "svls"]]
d:
command: serve-d
`)

View File

@@ -37,6 +37,12 @@ type CallbackFile struct {
args []interface{}
}
// Job stores the executing command for the job, and the stdin pipe
type Job struct {
*exec.Cmd
Stdin io.WriteCloser
}
func (f *CallbackFile) Write(data []byte) (int, error) {
// This is either stderr or stdout
// In either case we create a new job function callback and put it in the jobs channel
@@ -47,13 +53,13 @@ func (f *CallbackFile) Write(data []byte) (int, error) {
// JobStart starts a shell command in the background with the given callbacks
// It returns an *exec.Cmd as the job id
func JobStart(cmd string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *exec.Cmd {
func JobStart(cmd string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *Job {
return JobSpawn("sh", []string{"-c", cmd}, onStdout, onStderr, onExit, userargs...)
}
// JobSpawn starts a process with args in the background with the given callbacks
// It returns an *exec.Cmd as the job id
func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *exec.Cmd {
func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *Job {
// Set up everything correctly if the functions have been provided
proc := exec.Command(cmdName, cmdArgs...)
var outbuf bytes.Buffer
@@ -67,6 +73,7 @@ func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(
} else {
proc.Stderr = &outbuf
}
stdin, _ := proc.StdinPipe()
go func() {
// Run the process in the background and create the onExit callback
@@ -75,20 +82,15 @@ func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(
Jobs <- jobFunc
}()
return proc
return &Job{proc, stdin}
}
// JobStop kills a job
func JobStop(cmd *exec.Cmd) {
cmd.Process.Kill()
func JobStop(j *Job) {
j.Process.Kill()
}
// JobSend sends the given data into the job's stdin stream
func JobSend(cmd *exec.Cmd, data string) {
stdin, err := cmd.StdinPipe()
if err != nil {
return
}
stdin.Write([]byte(data))
func JobSend(j *Job, data string) {
j.Stdin.Write([]byte(data))
}

View File

@@ -46,7 +46,8 @@ func init() {
fmt.Println("Invalid version: ", Version, err)
}
if runtime.GOOS == "windows" {
_, wt := os.LookupEnv("WT_SESSION")
if runtime.GOOS == "windows" && !wt {
FakeCursor = true
}
Stdout = new(bytes.Buffer)
@@ -423,7 +424,7 @@ func IsNonAlphaNumeric(c rune) bool {
}
func IsAutocomplete(c rune) bool {
return c == '.' || !IsNonAlphaNumeric(c)
return !unicode.IsSpace(c) || !IsNonAlphaNumeric(c)
}
func ParseSpecial(s string) string {

View File

@@ -394,6 +394,84 @@ Any option you set in the editor will be saved to the file
created for you. If you'd like to take your configuration with you to another
machine, simply copy the settings.json to the other machine.
## Settings.json file
The settings.json file should go in your configuration directory (by default
at `~/.config/micro`), and should contain only options which have been modified
from their default setting. Here is the full list of options in json format,
so that you can see what the formatting should look like.
```json
{
"autoclose": true,
"autoindent": true,
"autosave": 0,
"autosu": false,
"backup": true,
"backupdir": "",
"basename": false,
"clipboard": "external",
"colorcolumn": 0,
"colorscheme": "default",
"comment": true,
"cursorline": true,
"diff": true,
"diffgutter": false,
"divchars": "|-",
"divreverse": true,
"encoding": "utf-8",
"eofnewline": true,
"fastdirty": false,
"fileformat": "unix",
"filetype": "unknown",
"ftoptions": true,
"ignorecase": false,
"indentchar": " ",
"infobar": true,
"initlua": true,
"keepautoindent": false,
"keymenu": false,
"linter": true,
"literate": true,
"matchbrace": true,
"mkparents": false,
"mouse": true,
"parsecursor": false,
"paste": false,
"permbackup": false,
"pluginchannels": [
"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"
],
"pluginrepos": [],
"readonly": false,
"relativeruler": false,
"rmtrailingws": false,
"ruler": true,
"savecursor": false,
"savehistory": true,
"saveundo": false,
"scrollbar": false,
"scrollmargin": 3,
"scrollspeed": 2,
"smartpaste": true,
"softwrap": false,
"splitbottom": true,
"splitright": true,
"status": true,
"statusformatl": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
"statusformatr": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help",
"statusline": true,
"sucmd": "sudo",
"syntax": true,
"tabmovement": false,
"tabsize": 4,
"tabstospaces": false,
"test": true,
"useprimary": true,
"xterm": false
}
```
## Global and local settings
You can set these settings either globally or locally. Locally means that the

View File

@@ -13,6 +13,7 @@ function init()
micro.SetStatusInfoFn("status.lines")
micro.SetStatusInfoFn("status.bytes")
micro.SetStatusInfoFn("status.size")
micro.SetStatusInfoFn("status.lsp")
config.AddRuntimeFile("status", config.RTHelp, "help/status.md")
end
@@ -32,6 +33,25 @@ function size(b)
return humanize.Bytes(b:Size())
end
function lsp(b)
if not b.Settings["lsp"] then
return "disabled"
end
if b:HasLSP() then
return "on"
end
local lsp = import("micro/lsp")
local l, ok = lsp.GetLanguage(b.Settings["filetype"])
if not ok then
return "unsupported"
end
if not l:Installed() then
return l.Command .. " not installed"
end
return "off"
end
function branch(b)
if b.Type.Kind ~= buffer.BTInfo then
local shell = import("micro/shell")

View File

@@ -7,15 +7,19 @@ rules:
# function definition
- identifier: "fn [a-z0-9_]+"
# Reserved words
- statement: "\\b(abstract|alignof|as|become|box|break|const|continue|crate|do|else|enum|extern|false|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|offsetof|override|priv|pub|pure|ref|return|sizeof|static|self|struct|super|true|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\\b"
- statement: "\\b(abstract|alignof|as|become|box|break|const|continue|crate|do|dyn|else|enum|extern|false|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|offsetof|override|priv|pub|pure|ref|return|sizeof|static|self|struct|super|true|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\\b"
# macros
- special: "[a-z_]+!"
# Constants
- constant: "[A-Z][A-Z_]+"
- constant: "\\b[A-Z][A-Z_0-9]+\\b"
# Numbers
- constant.number: "\\b[0-9]+\\b"
# Booleans
- constant: "\\b(true|false)\\b"
# Traits/Enums/Structs/Types/etc.
- type: "[A-Z][a-z]+"
- type: "\\b[A-Z]+[a-zA-Z_0-9]*[a-z]+[a-zA-Z_0-9]*\\b"
# Builtin types that start with lowercase.
- type: "\\b(bool|str|isize|usize|((i|u)(8|16|32|64))|f32|f64)\\b"
- constant.string:
start: "\""