diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8ff025..0ba2c0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,7 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: '1.13' + go-version: '1.16.9' - name: Build and Test run: | diff --git a/.gitignore b/.gitignore index a20487f..fceacbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +.old build.sh -arduino-language-server* \ No newline at end of file +arduino-language-server* diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 03d41d8..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Go", - "type": "go", - "request": "launch", - "mode": "auto", - "program": "${fileDirname}", - "env": {}, - "args": [], - "dlvLoadConfig": { - "followPointers": true, - "maxVariableRecurse": 1, - "maxStringLen": 256, - "maxArrayValues": 64, - "maxStructFields": -1 - } - } - ] -} \ No newline at end of file diff --git a/README.md b/README.md index 473a0e7..d23d246 100644 --- a/README.md +++ b/README.md @@ -62,20 +62,28 @@ go test -v ./... ``` ## Usage -To run the Arduino Language Server you need: +The language server it's not intended for direct usage by humans via the command line terminal. +The purpose of this program is to provide C++/.ino language-related functionality to the IDEs so, in general, it's the IDE that talks to the language server via stdin/stdout using the slightly modified JSONRPC protocol defined in the LSP specification. + +The prerequisites to run the Arduino Language Server are: - [Arduino CLI](https://github.com/arduino/arduino-cli) +- [clangd](we suggest the builds here https://github.com/clangd/clangd/releases) -After building, call: +To start the language server the IDE must provide the path to Arduino CLI and clangd with the following flags in addition to the target board FQBN: ``` -./arduino-language-server -cli-config -``` -For example: -``` -./arduino-language-server -cli-config $HOME/.arduino15/arduino-cli.yaml +./arduino-language-server \ + -clangd /usr/local/bin/clangd \ + -cli /usr/local/bin/arduino-cli \ + -cli-config $HOME/.arduino15/arduino-cli.yaml \ + -fqbn arduino:mbed:nanorp2040connect ``` -Note: If you do not have an Arduino CLI config file, you can create one by running: + +The -fqbn flag represents the board you're actually working on (different boards may implement different features/API, if you change board you need to restart the language server with another fqbn). +The support for the board must be installed with the `arduino-cli core install ...` command before starting the language server. + +If you do not have an Arduino CLI config file, you can create one by running: ``` arduino-cli config init ``` diff --git a/go.mod b/go.mod index 07add5b..030da01 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,14 @@ module github.com/arduino/arduino-language-server -go 1.12 +go 1.16 require ( - github.com/arduino/arduino-cli v0.0.0-20201215104024-6a177ebf56f2 - github.com/arduino/go-paths-helper v1.5.0 - github.com/fatih/color v1.7.0 + github.com/arduino/arduino-cli v0.0.0-20211111113528-bf4a7844a79b + github.com/arduino/go-paths-helper v1.6.1 + github.com/fatih/color v1.13.0 github.com/pkg/errors v0.9.1 - github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.7.0 + go.bug.st/json v1.15.6 + go.bug.st/lsp v0.0.0-20211202163946-3ad3994172a0 + google.golang.org/grpc v1.42.0 ) diff --git a/go.sum b/go.sum index 82a9597..e42d045 100644 --- a/go.sum +++ b/go.sum @@ -1,133 +1,214 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/arduino/arduino-cli v0.0.0-20201215104024-6a177ebf56f2 h1:J+EUtAwSXK7AFSkK0Tbw85rLbcGf1ykuerssDUD9LxY= -github.com/arduino/arduino-cli v0.0.0-20201215104024-6a177ebf56f2/go.mod h1:RZsAJUrAIHFaSj71SNJ/hSRUqNrjDw+3WFT2xw9NnRM= -github.com/arduino/board-discovery v0.0.0-20180823133458-1ba29327fb0c h1:agh2JT96G8egU7FEb13L4dq3fnCN7lxXhJ86t69+W7s= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/arduino/arduino-cli v0.0.0-20211111113528-bf4a7844a79b h1:fa5vlawr+0hTBaMQxeQB6Mgy0hsTUcdJUlZuGDA+440= +github.com/arduino/arduino-cli v0.0.0-20211111113528-bf4a7844a79b/go.mod h1:cU/LNzIePCU/dvW39BClyZVPuz19Zsrt2dAIbWPtTAA= github.com/arduino/board-discovery v0.0.0-20180823133458-1ba29327fb0c/go.mod h1:HK7SpkEax/3P+0w78iRQx1sz1vCDYYw9RXwHjQTB5i8= -github.com/arduino/go-paths-helper v1.0.1 h1:utYXLM2RfFlc9qp/MJTIYp3t6ux/xM6mWjeEb/WLK4Q= github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= -github.com/arduino/go-paths-helper v1.4.0 h1:ilnseAdxmN1bFnLxxXHRtcdmt9jBf3O4jtYfWfqule4= -github.com/arduino/go-paths-helper v1.4.0/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU= -github.com/arduino/go-paths-helper v1.5.0 h1:RVo189hD+GhUS1rQ3gixwK1nSbvVR8MGIGa7Gxv2bdM= -github.com/arduino/go-paths-helper v1.5.0/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU= -github.com/arduino/go-properties-orderedmap v1.3.0 h1:4No/vQopB36e7WUIk6H6TxiSEJPiMrVOCZylYmua39o= -github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= -github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b h1:9hDi4F2st6dbLC3y4i02zFT5quS4X6iioWifGlVwfy4= +github.com/arduino/go-paths-helper v1.6.1 h1:lha+/BuuBsx0qTZ3gy6IO1kU23lObWdQ/UItkzVWQ+0= +github.com/arduino/go-paths-helper v1.6.1/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU= +github.com/arduino/go-properties-orderedmap v1.6.0 h1:gp2JoWRETtqwsZ+UHu/PBuYWYH2x2+d+uipDxS4WmvM= +github.com/arduino/go-properties-orderedmap v1.6.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b/go.mod h1:uwGy5PpN4lqW97FiLnbcx+xx8jly5YuPMJWfVwwjJiQ= github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b h1:3PjgYG5gVPA7cipp7vIR2lF96KkEJIFBJ+ANnuv6J20= github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b/go.mod h1:iIPnclBMYm1g32Q5kXoqng4jLhMStReIP7ZxaoUC2y8= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/cmaglie/go.rice v1.0.3 h1:ZBLmBdQp6ejc+n8eMNH0uuRSKkg6kKe6ORjXKnyHBYw= -github.com/cmaglie/go.rice v1.0.3/go.mod h1:AF3bOWkvdOpp8/S3UL8qbQ4N7DiISIbJtj54GWFPAsc= -github.com/cmaglie/pb v1.0.27 h1:ynGj8vBXR+dtj4B7Q/W/qGt31771Ux5iFfRQBnwdQiA= github.com/cmaglie/pb v1.0.27/go.mod h1:GilkKZMXYjBA4NxItWFfO+lwkp59PLHQ+IOW/b/kmZI= -github.com/codeclysm/cc v1.2.2 h1:1ChS4EvWTjw6bH2sd6QiMcmih0itVVrWdh9MmOliX/I= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codeclysm/cc v1.2.2/go.mod h1:XtW4ArCNgQwFphcRGG9+sPX5WM1J6/u0gMy5ZdV3obA= github.com/codeclysm/extract/v3 v3.0.2 h1:sB4LcE3Php7LkhZwN0n2p8GCwZe92PEQutdbGURf5xc= github.com/codeclysm/extract/v3 v3.0.2/go.mod h1:NKsw+hqua9H+Rlwy/w/3Qgt9jDonYEgB6wJu+25eOKw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0= -github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= +github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= -github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/facchinm/gohex v0.0.0-20201008150446-be2a6be25790 h1:GTx/F+6TdhQAMtaDrpGDgnAzxYJhTVuTdSKpMsUbMNA= -github.com/facchinm/gohex v0.0.0-20201008150446-be2a6be25790/go.mod h1:sErAiirjQXs3P13DBW7oPmJ6Q0WsFjYXVAhz7Xt59UQ= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fluxio/iohelpers v0.0.0-20160419043813-3a4dd67a94d2 h1:C6sOwknxwWfLBEQ91zhmptlfxf7pVEs5s6wOnDxNpS4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fluxio/iohelpers v0.0.0-20160419043813-3a4dd67a94d2/go.mod h1:c7sGIpDbBo0JZZ1tKyC1p5smWf8QcUjK4bFtZjHAecg= -github.com/fluxio/multierror v0.0.0-20160419044231-9c68d39025e5 h1:R8jFW6G/bjoXjWPFrEfw9G5YQDlYhwV4AC+Eonu6wmk= github.com/fluxio/multierror v0.0.0-20160419044231-9c68d39025e5/go.mod h1:BEUDl7FG1cc76sM0J0x8dqr6RhiL4uqvk6oFkwuNyuM= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 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/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/h2non/filetype v1.0.6/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU= github.com/h2non/filetype v1.0.8 h1:le8gpf+FQA0/DlDABbtisA1KiTS0Xi+YSC/E8yY3Y14= github.com/h2non/filetype v1.0.8/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/imjasonmiller/godice v0.1.2 h1:T1/sW/HoDzFeuwzOOuQjmeMELz9CzZ53I2CnD+08zD4= -github.com/imjasonmiller/godice v0.1.2/go.mod h1:8cTkdnVI+NglU2d6sv+ilYcNaJ5VSTBwvMbFULJd/QQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/clock v0.0.0-20180524022203-d293bb356ca4/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= @@ -141,14 +222,12 @@ github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0 h1:+WWUkhnTjV6RNOxkcw github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0/go.mod h1:hpGvhGHPVbNBraRLZEhoQwFLMrjK8PSlO4D3nDjKYXo= github.com/juju/utils v0.0.0-20180808125547-9dfc6dbfb02b/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk= github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 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.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= @@ -157,198 +236,449 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leonelquinteros/gotext v1.4.0 h1:2NHPCto5IoMXbrT0bldPrxj0qM5asOCwtb1aUQZ1tys= github.com/leonelquinteros/gotext v1.4.0/go.mod h1:yZGXREmoGTtBvZHNcc+Yfug49G/2spuF/i/Qlsvz1Us= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/marcinbor85/gohex v0.0.0-20210308104911-55fb1c624d84/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdlayher/genetlink v0.0.0-20190313224034-60417448a851/go.mod h1:EsbsAEUEs15qC1cosAwxgCWV0Qhd8TmkxnA9Kw1Vhl4= github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/taskstats v0.0.0-20190313225729-7cbba52ee072/go.mod h1:sGdS7A6CAETR53zkdjGkgoFlh1vSm7MtX+i8XfEsTMA= -github.com/miekg/dns v1.0.5 h1:MQBGf2JEJDu0rg9WOpQZzeO+zW8UKwgkvP3R1dUU1Yw= -github.com/miekg/dns v1.0.5/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228 h1:Cvfd2dOlXIPTeEkOT/h8PyK4phBngOM4at9/jlgy7d4= github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228/go.mod h1:MGuVJ1+5TX1SCoO2Sx0eAnjpdRytYla2uC1YIZfkC9c= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 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/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583 h1:ogHi8YLNeIxABOaH6UgtbwkODheuAK+ErP8gWXYQVj0= github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583/go.mod h1:sFPiU/UgDcsQVu3vkqpZLCXWFwUoQRpHGu9ATihPAl0= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= -github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e h1:uO75wNGioszjmIzcY/tvdDYKRLVvzggtAmmJkn9j4GQ= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e/go.mod h1:tm/wZFQ8e24NYaBGIlnO2WGCAi67re4HHuOm0sftE/M= github.com/segmentio/objconv v1.0.1/go.mod h1:auayaH5k3137Cl4SoXTgrzQcuQDmvuVtZgS0fb1Ahys= -github.com/segmentio/stats/v4 v4.5.3 h1:Y/DSUWZ4c8ICgqJ9rQohzKvGqGWbLPWad5zmxVoKN+Y= github.com/segmentio/stats/v4 v4.5.3/go.mod h1:LsaahUJR7iiSs8mnkvQvdQ/RLHAS5adGLxuntg0ydGo= 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/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 h1:marA1XQDC7N870zmSFIoHZpIUduK80USeY0Rkuflgp4= -github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37/go.mod h1:ZafdZgk/axhT1cvZAPOhw+95nz2I/Ra5qMlU4gTRwIo= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.0.1-0.20200710201246-675ae5f5a98c h1:/dP/1GnfVIlWnB0YDImenSmneUCw3wjyq2RMgAG1e2o= -github.com/spf13/cobra v1.0.1-0.20200710201246-675ae5f5a98c/go.mod h1:aeNIJzz/GSSVlS+gpCpQWZ83BKbsoW57mr90+YthtkQ= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E= -github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= -github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= +github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.bug.st/cleanup v1.0.0 h1:XVj1HZxkBXeq3gMT7ijWUpHyIC1j8XAoNSyQ06CskgA= go.bug.st/cleanup v1.0.0/go.mod h1:EqVmTg2IBk4znLbPD28xne3abjsJftMdqqJEjhn70bk= -go.bug.st/downloader/v2 v2.1.0 h1:VqGOrJrjgz8c0c8ExvF9dvvcpcrbo2IrI+rOoXKD6nQ= -go.bug.st/downloader/v2 v2.1.0/go.mod h1:VZW2V1iGKV8rJL2ZEGIDzzBeKowYv34AedJz13RzVII= +go.bug.st/downloader/v2 v2.1.1 h1:nyqbUizo3E2IxCCm4YFac4FtSqqFpqWP+Aae5GCMuw4= +go.bug.st/downloader/v2 v2.1.1/go.mod h1:VZW2V1iGKV8rJL2ZEGIDzzBeKowYv34AedJz13RzVII= +go.bug.st/json v1.15.6 h1:pvSpotu6f5JoCbx1TnKn6asVH7o9Tg2/GKsZSVzBOsc= +go.bug.st/json v1.15.6/go.mod h1:bh58F9adz5ePlNqtvbuXuXcf9k6IrDLKH6lJUsHP3TI= +go.bug.st/lsp v0.0.0-20211130152916-c597b0a0439f h1:Rj7FdBdROWh9mMra/16G/5d7u/QE0Wwq487NZt+Evjg= +go.bug.st/lsp v0.0.0-20211130152916-c597b0a0439f/go.mod h1:oYTh1uf5hI1teV5crrWut41Pk8vD/NqIjs4zD+No5FE= +go.bug.st/lsp v0.0.0-20211202163946-3ad3994172a0 h1:/SnZ7aZ3bmfUKWbhckiK6L4mv9vyf9HgwV6X0dm+/is= +go.bug.st/lsp v0.0.0-20211202163946-3ad3994172a0/go.mod h1:oYTh1uf5hI1teV5crrWut41Pk8vD/NqIjs4zD+No5FE= go.bug.st/relaxed-semver v0.0.0-20190922224835-391e10178d18 h1:F1qxtaFuewctYc/SsHRn+Q7Dtwi+yJGPgVq8YLtQz98= go.bug.st/relaxed-semver v0.0.0-20190922224835-391e10178d18/go.mod h1:Cx1VqMtEhE9pIkEyUj3LVVVPkv89dgW8aCKrRPDR/uE= -go.bug.st/serial v1.1.1 h1:5J1DpaIaSIruBi7jVnKXnhRS+YQ9+2PLJMtIZKoIgnc= -go.bug.st/serial v1.1.1/go.mod h1:VmYBeyJWp5BnJ0tw2NUJHZdJTGl2ecBGABHlzRK1knY= -go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45 h1:mACY1anK6HNCZtm/DK2Rf2ZPHggVqeB0+7rY9Gl6wyI= +go.bug.st/serial v1.3.2/go.mod h1:jDkjqASf/qSjmaOxHSHljwUQ6eHo/ZX/bxJLQqSlvZg= go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45/go.mod h1:dRSl/CVCTf56CkXgJMDOdSwNfo2g1orOGE/gBGdvjZw= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y= -golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/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-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -357,36 +687,45 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= -gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/handler/builder.go b/handler/builder.go deleted file mode 100644 index 1d7a260..0000000 --- a/handler/builder.go +++ /dev/null @@ -1,156 +0,0 @@ -package handler - -import ( - "bytes" - "context" - "encoding/json" - "log" - "strings" - "time" - - "github.com/arduino/arduino-cli/arduino/libraries" - "github.com/arduino/arduino-cli/executils" - "github.com/arduino/arduino-language-server/lsp" - "github.com/arduino/arduino-language-server/streams" - "github.com/arduino/go-paths-helper" - "github.com/pkg/errors" -) - -func (handler *InoHandler) scheduleRebuildEnvironment() { - handler.rebuildSketchDeadlineMutex.Lock() - defer handler.rebuildSketchDeadlineMutex.Unlock() - d := time.Now().Add(time.Second) - handler.rebuildSketchDeadline = &d -} - -func (handler *InoHandler) rebuildEnvironmentLoop() { - defer streams.CatchAndLogPanic() - - grabDeadline := func() *time.Time { - handler.rebuildSketchDeadlineMutex.Lock() - defer handler.rebuildSketchDeadlineMutex.Unlock() - - res := handler.rebuildSketchDeadline - handler.rebuildSketchDeadline = nil - return res - } - - for { - // Wait for someone to schedule a preprocessing... - time.Sleep(100 * time.Millisecond) - deadline := grabDeadline() - if deadline == nil { - continue - } - - for time.Now().Before(*deadline) { - time.Sleep(100 * time.Millisecond) - - if d := grabDeadline(); d != nil { - deadline = d - } - } - - // Regenerate preprocessed sketch! - done := make(chan bool) - go func() { - defer streams.CatchAndLogPanic() - - handler.progressHandler.Create("arduinoLanguageServerRebuild") - handler.progressHandler.Begin("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressBegin{ - Title: "Building sketch", - }) - - count := 0 - dots := []string{".", "..", "..."} - for { - select { - case <-time.After(time.Millisecond * 400): - msg := "compiling" + dots[count%3] - count++ - handler.progressHandler.Report("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressReport{Message: &msg}) - case <-done: - msg := "done" - handler.progressHandler.End("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressEnd{Message: &msg}) - return - } - } - }() - - handler.dataLock("RBLD---") - handler.initializeWorkbench(context.Background(), nil) - handler.dataUnlock("RBLD---") - done <- true - close(done) - } -} - -func (handler *InoHandler) generateBuildEnvironment(buildPath *paths.Path) error { - sketchDir := handler.sketchRoot - fqbn := handler.config.SelectedBoard.Fqbn - - // Export temporary files - type overridesFile struct { - Overrides map[string]string `json:"overrides"` - } - data := overridesFile{Overrides: map[string]string{}} - for uri, trackedFile := range handler.docs { - rel, err := paths.New(uri).RelFrom(handler.sketchRoot) - if err != nil { - return errors.WithMessage(err, "dumping tracked files") - } - data.Overrides[rel.String()] = trackedFile.Text - } - var overridesJSON *paths.Path - if jsonBytes, err := json.MarshalIndent(data, "", " "); err != nil { - return errors.WithMessage(err, "dumping tracked files") - } else if tmpFile, err := paths.WriteToTempFile(jsonBytes, nil, ""); err != nil { - return errors.WithMessage(err, "dumping tracked files") - } else { - overridesJSON = tmpFile - defer tmpFile.Remove() - } - - // XXX: do this from IDE or via gRPC - args := []string{globalCliPath, - "--config-file", globalCliConfigPath, - "compile", - "--fqbn", fqbn, - "--only-compilation-database", - "--clean", - "--source-override", overridesJSON.String(), - "--build-path", buildPath.String(), - "--format", "json", - sketchDir.String(), - } - cmd, err := executils.NewProcess(args...) - if err != nil { - return errors.Errorf("running %s: %s", strings.Join(args, " "), err) - } - cmdOutput := &bytes.Buffer{} - cmd.RedirectStdoutTo(cmdOutput) - cmd.SetDirFromPath(sketchDir) - log.Println("running: ", strings.Join(args, " ")) - if err := cmd.Run(); err != nil { - return errors.Errorf("running %s: %s", strings.Join(args, " "), err) - } - - // Currently those values are not used, keeping here for future improvements - type cmdBuilderRes struct { - BuildPath *paths.Path `json:"build_path"` - UsedLibraries []*libraries.Library - } - type cmdRes struct { - CompilerOut string `json:"compiler_out"` - CompilerErr string `json:"compiler_err"` - BuilderResult cmdBuilderRes `json:"builder_result"` - Success bool `json:"success"` - } - var res cmdRes - if err := json.Unmarshal(cmdOutput.Bytes(), &res); err != nil { - return errors.Errorf("parsing arduino-cli output: %s", err) - } - log.Println("arduino-cli output:", cmdOutput) - - return nil -} diff --git a/handler/handler.go b/handler/handler.go deleted file mode 100644 index 3a33155..0000000 --- a/handler/handler.go +++ /dev/null @@ -1,1966 +0,0 @@ -package handler - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "log" - "os" - "regexp" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/arduino/arduino-cli/arduino/builder" - "github.com/arduino/arduino-cli/executils" - "github.com/arduino/arduino-language-server/handler/sourcemapper" - "github.com/arduino/arduino-language-server/handler/textutils" - "github.com/arduino/arduino-language-server/lsp" - "github.com/arduino/arduino-language-server/streams" - "github.com/arduino/go-paths-helper" - "github.com/fatih/color" - "github.com/pkg/errors" - "github.com/sourcegraph/jsonrpc2" -) - -var globalCliPath string -var globalCliConfigPath string -var globalClangdPath string -var globalFormatterConf *paths.Path -var enableLogging bool - -// Setup initializes global variables. -func Setup(cliPath, cliConfigPath, clangdPath, formatFilePath string, _enableLogging bool) { - globalCliPath = cliPath - globalCliConfigPath = cliConfigPath - globalClangdPath = clangdPath - if formatFilePath != "" { - globalFormatterConf = paths.New(formatFilePath) - } - enableLogging = _enableLogging -} - -// CLangdStarter starts clangd and returns its stdin/out/err -type CLangdStarter func() (stdin io.WriteCloser, stdout io.ReadCloser, stderr io.ReadCloser) - -// InoHandler is a JSON-RPC handler that delegates messages to clangd. -type InoHandler struct { - StdioConn *jsonrpc2.Conn - ClangdConn *jsonrpc2.Conn - - stdioNotificationCount int64 - clangdNotificationCount int64 - progressHandler *ProgressProxyHandler - - closing chan bool - clangdStarted *sync.Cond - dataMux sync.RWMutex - lspInitializeParams *lsp.InitializeParams - buildPath *paths.Path - buildSketchRoot *paths.Path - buildSketchCpp *paths.Path - buildSketchCppVersion int - buildSketchSymbols []lsp.DocumentSymbol - buildSketchIncludesCanary string - buildSketchSymbolsCanary string - buildSketchSymbolsLoad bool - buildSketchSymbolsCheck bool - rebuildSketchDeadline *time.Time - rebuildSketchDeadlineMutex sync.Mutex - sketchRoot *paths.Path - sketchName string - sketchMapper *sourcemapper.InoMapper - sketchTrackedFilesCount int - docs map[string]*lsp.TextDocumentItem - inoDocsWithDiagnostics map[lsp.DocumentURI]bool - - config lsp.BoardConfig -} - -var yellow = color.New(color.FgHiYellow) - -func (handler *InoHandler) dataLock(msg string) { - handler.dataMux.Lock() - log.Println(msg + yellow.Sprintf(" locked")) -} - -func (handler *InoHandler) dataUnlock(msg string) { - log.Println(msg + yellow.Sprintf(" unlocked")) - handler.dataMux.Unlock() -} - -func (handler *InoHandler) dataRLock(msg string) { - handler.dataMux.RLock() - log.Println(msg + yellow.Sprintf(" read-locked")) -} - -func (handler *InoHandler) dataRUnlock(msg string) { - log.Println(msg + yellow.Sprintf(" read-unlocked")) - handler.dataMux.RUnlock() -} - -func (handler *InoHandler) waitClangdStart(prefix string) error { - if handler.ClangdConn != nil { - return nil - } - - log.Printf(prefix + "(throttled: waiting for clangd)") - log.Println(prefix + yellow.Sprintf(" unlocked (waiting clangd)")) - handler.clangdStarted.Wait() - log.Println(prefix + yellow.Sprintf(" locked (waiting clangd)")) - - if handler.ClangdConn == nil { - log.Printf(prefix + "clangd startup failed: aborting call") - return errors.New("could not start clangd, aborted") - } - return nil -} - -// NewInoHandler creates and configures an InoHandler. -func NewInoHandler(stdio io.ReadWriteCloser, board lsp.Board) *InoHandler { - handler := &InoHandler{ - docs: map[string]*lsp.TextDocumentItem{}, - inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, - closing: make(chan bool), - config: lsp.BoardConfig{ - SelectedBoard: board, - }, - } - handler.clangdStarted = sync.NewCond(&handler.dataMux) - - if buildPath, err := paths.MkTempDir("", "arduino-language-server"); err != nil { - log.Fatalf("Could not create temp folder: %s", err) - } else { - handler.buildPath = buildPath.Canonical() - handler.buildSketchRoot = handler.buildPath.Join("sketch") - } - if enableLogging { - log.Println("Initial board configuration:", board) - log.Println("Language server build path:", handler.buildPath) - log.Println("Language server build sketch root:", handler.buildSketchRoot) - } - - stdStream := jsonrpc2.NewBufferedStream(stdio, jsonrpc2.VSCodeObjectCodec{}) - var stdHandler jsonrpc2.Handler = jsonrpc2.HandlerWithError(handler.HandleMessageFromIDE) - handler.StdioConn = jsonrpc2.NewConn(context.Background(), stdStream, stdHandler, - jsonrpc2.OnRecv(streams.JSONRPCConnLogOnRecv("IDE --> LS CL:")), - jsonrpc2.OnSend(streams.JSONRPCConnLogOnSend("IDE <-- LS CL:")), - ) - - handler.progressHandler = NewProgressProxy(handler.StdioConn) - - go handler.rebuildEnvironmentLoop() - return handler -} - -// FileData gathers information on a .ino source file. -type FileData struct { - sourceText string - sourceURI lsp.DocumentURI - targetURI lsp.DocumentURI - sourceMap *sourcemapper.InoMapper - version int -} - -// Close closes all the json-rpc connections. -func (handler *InoHandler) Close() { - if handler.ClangdConn != nil { - handler.ClangdConn.Close() - handler.ClangdConn = nil - } - if handler.closing != nil { - close(handler.closing) - handler.closing = nil - } -} - -// CloseNotify returns a channel that is closed when the InoHandler is closed -func (handler *InoHandler) CloseNotify() <-chan bool { - return handler.closing -} - -// CleanUp performs cleanup of the workspace and temp files create by the language server -func (handler *InoHandler) CleanUp() { - if handler.buildPath != nil { - log.Printf("removing buildpath") - handler.buildPath.RemoveAll() - handler.buildPath = nil - } -} - -// HandleMessageFromIDE handles a message received from the IDE client (via stdio). -func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (interface{}, error) { - defer streams.CatchAndLogPanic() - - prefix := "IDE --> " - if req.Notif { - n := atomic.AddInt64(&handler.stdioNotificationCount, 1) - prefix += fmt.Sprintf("%s notif%d ", req.Method, n) - } else { - prefix += fmt.Sprintf("%s %v ", req.Method, req.ID) - } - - params, err := lsp.ReadParams(req.Method, req.Params) - if err != nil { - return nil, err - } - if params == nil { - params = req.Params - } - - // Set up RWLocks and wait for clangd startup - switch req.Method { - case // Write lock - NO clangd required - "initialize": - handler.dataLock(prefix) - defer handler.dataUnlock(prefix) - case // Write lock - clangd required - "textDocument/didOpen", - "textDocument/didChange", - "textDocument/didClose": - handler.dataLock(prefix) - defer handler.dataUnlock(prefix) - handler.waitClangdStart(prefix) - case // Read lock - NO clangd required - "initialized": - handler.dataRLock(prefix) - defer handler.dataRUnlock(prefix) - default: // Read lock - clangd required - handler.dataRLock(prefix) - // if clangd is not started... - if handler.ClangdConn == nil { - // Release the read lock and acquire a write lock - // (this is required to wait on condition variable). - handler.dataRUnlock(prefix) - handler.dataLock(prefix) - defer handler.dataUnlock(prefix) - handler.waitClangdStart(prefix) - } else { - defer handler.dataRUnlock(prefix) - } - } - - // Handle LSP methods: transform parameters and send to clangd - var inoURI, cppURI lsp.DocumentURI - - switch p := params.(type) { - case *lsp.InitializeParams: - // method "initialize" - - go func() { - defer streams.CatchAndLogPanic() - prefix := "INIT--- " - log.Printf(prefix + "initializing workbench") - - // Start clangd asynchronously - handler.dataLock(prefix) - defer handler.dataUnlock(prefix) - - handler.initializeWorkbench(ctx, p) - - // clangd should be running now... - handler.clangdStarted.Broadcast() - - log.Printf(prefix + "initializing workbench (done)") - }() - - T := true - F := false - return &lsp.InitializeResult{ - Capabilities: lsp.ServerCapabilities{ - TextDocumentSync: &lsp.TextDocumentSyncOptionsOrKind{Kind: &lsp.TDSKIncremental}, - HoverProvider: true, - CompletionProvider: &lsp.CompletionOptions{ - TriggerCharacters: []string{".", "\u003e", ":"}, - }, - SignatureHelpProvider: &lsp.SignatureHelpOptions{ - TriggerCharacters: []string{"(", ","}, - }, - DefinitionProvider: true, - ReferencesProvider: false, // TODO: true - DocumentHighlightProvider: true, - DocumentSymbolProvider: true, - WorkspaceSymbolProvider: true, - CodeActionProvider: &lsp.BoolOrCodeActionOptions{IsProvider: &T}, - DocumentFormattingProvider: true, - DocumentRangeFormattingProvider: true, - DocumentOnTypeFormattingProvider: &lsp.DocumentOnTypeFormattingOptions{ - FirstTriggerCharacter: "\n", - }, - RenameProvider: &lsp.BoolOrRenameOptions{IsProvider: &F}, // TODO: &T - ExecuteCommandProvider: &lsp.ExecuteCommandOptions{ - Commands: []string{"clangd.applyFix", "clangd.applyTweak"}, - }, - }, - }, nil - - case *lsp.InitializedParams: - // method "initialized" - log.Println(prefix + "notification is not propagated to clangd") - return nil, nil // Do not propagate to clangd - - case *lsp.DidOpenTextDocumentParams: - // method "textDocument/didOpen" - inoURI = p.TextDocument.URI - log.Printf(prefix+"(%s@%d as '%s')", p.TextDocument.URI, p.TextDocument.Version, p.TextDocument.LanguageID) - - if res, e := handler.didOpen(p); e != nil { - params = nil - err = e - } else if res == nil { - log.Println(prefix + "notification is not propagated to clangd") - return nil, nil // do not propagate to clangd - } else { - log.Printf(prefix+"to clang: didOpen(%s@%d as '%s')", res.TextDocument.URI, res.TextDocument.Version, res.TextDocument.LanguageID) - params = res - } - - case *lsp.DidCloseTextDocumentParams: - // Method: "textDocument/didClose" - inoURI = p.TextDocument.URI - log.Printf("--> didClose(%s)", p.TextDocument.URI) - - if res, e := handler.didClose(p); e != nil { - } else if res == nil { - log.Println(" --X notification is not propagated to clangd") - return nil, nil // do not propagate to clangd - } else { - log.Printf(" --> didClose(%s)", res.TextDocument.URI) - params = res - } - - case *lsp.DidChangeTextDocumentParams: - // notification "textDocument/didChange" - inoURI = p.TextDocument.URI - log.Printf("--> didChange(%s@%d)", p.TextDocument.URI, p.TextDocument.Version) - for _, change := range p.ContentChanges { - log.Printf(" > %s -> %s", change.Range, strconv.Quote(change.Text)) - } - - if res, err := handler.didChange(ctx, p); err != nil { - log.Printf(" --E error: %s", err) - return nil, err - } else if res == nil { - log.Println(" --X notification is not propagated to clangd") - return nil, err // do not propagate to clangd - } else { - p = res - } - - log.Printf(" --> didChange(%s@%d)", p.TextDocument.URI, p.TextDocument.Version) - for _, change := range p.ContentChanges { - log.Printf(" > %s -> %s", change.Range, strconv.Quote(change.Text)) - } - err = handler.ClangdConn.Notify(ctx, req.Method, p) - return nil, err - - case *lsp.CompletionParams: - // method: "textDocument/completion" - log.Printf("--> completion(%s:%d:%d)\n", p.TextDocument.URI, p.Position.Line, p.Position.Character) - - if res, e := handler.ino2cppTextDocumentPositionParams(&p.TextDocumentPositionParams); e == nil { - p.TextDocumentPositionParams = *res - log.Printf(" --> completion(%s:%d:%d)\n", p.TextDocument.URI, p.Position.Line, p.Position.Character) - } else { - err = e - } - inoURI = p.TextDocument.URI - - case *lsp.CodeActionParams: - // method "textDocument/codeAction" - inoURI = p.TextDocument.URI - log.Printf("--> codeAction(%s:%s)", p.TextDocument.URI, p.Range.Start) - - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument) - if err != nil { - break - } - if p.TextDocument.URI.AsPath().EquivalentTo(handler.buildSketchCpp) { - p.Range = handler.sketchMapper.InoToCppLSPRange(inoURI, p.Range) - for index := range p.Context.Diagnostics { - r := &p.Context.Diagnostics[index].Range - *r = handler.sketchMapper.InoToCppLSPRange(inoURI, *r) - } - } - log.Printf(" --> codeAction(%s:%s)", p.TextDocument.URI, p.Range.Start) - - case *lsp.HoverParams: - // method: "textDocument/hover" - doc := &p.TextDocumentPositionParams - log.Printf("--> hover(%s:%d:%d)\n", doc.TextDocument.URI, doc.Position.Line, doc.Position.Character) - - if res, e := handler.ino2cppTextDocumentPositionParams(doc); e == nil { - p.TextDocumentPositionParams = *res - log.Printf(" --> hover(%s:%d:%d)\n", doc.TextDocument.URI, doc.Position.Line, doc.Position.Character) - } else { - err = e - } - inoURI = p.TextDocument.URI - - case *lsp.DocumentSymbolParams: - // method "textDocument/documentSymbol" - inoURI = p.TextDocument.URI - log.Printf("--> documentSymbol(%s)", p.TextDocument.URI) - - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument) - log.Printf(" --> documentSymbol(%s)", p.TextDocument.URI) - - case *lsp.DocumentFormattingParams: - // method "textDocument/formatting" - inoURI = p.TextDocument.URI - log.Printf("--> formatting(%s)", p.TextDocument.URI) - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument) - cppURI = p.TextDocument.URI - log.Printf(" --> formatting(%s)", p.TextDocument.URI) - if cleanup, e := handler.createClangdFormatterConfig(cppURI); e != nil { - err = e - } else { - defer cleanup() - } - - case *lsp.DocumentRangeFormattingParams: - // Method: "textDocument/rangeFormatting" - log.Printf("--> %s(%s:%s)", req.Method, p.TextDocument.URI, p.Range) - inoURI = p.TextDocument.URI - if cppParams, e := handler.ino2cppDocumentRangeFormattingParams(p); e == nil { - params = cppParams - cppURI = cppParams.TextDocument.URI - log.Printf(" --> %s(%s:%s)", req.Method, cppParams.TextDocument.URI, cppParams.Range) - if cleanup, e := handler.createClangdFormatterConfig(cppURI); e != nil { - err = e - } else { - defer cleanup() - } - } else { - err = e - } - - case *lsp.TextDocumentPositionParams: - // Method: "textDocument/signatureHelp" - // Method: "textDocument/definition" - // Method: "textDocument/typeDefinition" - // Method: "textDocument/implementation" - // Method: "textDocument/documentHighlight" - log.Printf("--> %s(%s:%s)", req.Method, p.TextDocument.URI, p.Position) - inoURI = p.TextDocument.URI - if res, e := handler.ino2cppTextDocumentPositionParams(p); e == nil { - cppURI = res.TextDocument.URI - params = res - log.Printf(" --> %s(%s:%s)", req.Method, res.TextDocument.URI, res.Position) - } else { - err = e - } - - case *lsp.DidSaveTextDocumentParams: - // Method: "textDocument/didSave" - log.Printf("--> %s(%s)", req.Method, p.TextDocument.URI) - inoURI = p.TextDocument.URI - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument) - cppURI = p.TextDocument.URI - if cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) { - log.Printf(" --| didSave not forwarded to clangd") - return nil, nil - } - log.Printf(" --> %s(%s)", req.Method, p.TextDocument.URI) - - case *lsp.ReferenceParams: // "textDocument/references": - log.Printf("--X " + req.Method) - return nil, nil - inoURI = p.TextDocument.URI - _, err = handler.ino2cppTextDocumentPositionParams(&p.TextDocumentPositionParams) - case *lsp.DocumentOnTypeFormattingParams: // "textDocument/onTypeFormatting": - log.Printf("--X " + req.Method) - return nil, nil - inoURI = p.TextDocument.URI - err = handler.ino2cppDocumentOnTypeFormattingParams(p) - case *lsp.RenameParams: // "textDocument/rename": - log.Printf("--X " + req.Method) - return nil, nil - inoURI = p.TextDocument.URI - err = handler.ino2cppRenameParams(p) - case *lsp.DidChangeWatchedFilesParams: // "workspace/didChangeWatchedFiles": - log.Printf("--X " + req.Method) - return nil, nil - err = handler.ino2cppDidChangeWatchedFilesParams(p) - case *lsp.ExecuteCommandParams: // "workspace/executeCommand": - log.Printf("--X " + req.Method) - return nil, nil - err = handler.ino2cppExecuteCommand(p) - } - if err != nil { - log.Printf(prefix+"Error: %s", err) - return nil, err - } - - var result interface{} - if req.Notif { - log.Printf(prefix + "sent to Clang") - err = handler.ClangdConn.Notify(ctx, req.Method, params) - } else { - log.Printf(prefix + "sent to Clang") - result, err = lsp.SendRequest(ctx, handler.ClangdConn, req.Method, params) - } - if err == nil && handler.buildSketchSymbolsLoad { - handler.buildSketchSymbolsLoad = false - handler.buildSketchSymbolsCheck = false - log.Println(prefix + "Queued resfreshing document symbols") - go handler.LoadCppDocumentSymbols() - } - if err == nil && handler.buildSketchSymbolsCheck { - handler.buildSketchSymbolsCheck = false - log.Println(prefix + "Queued check document symbols") - go handler.CheckCppDocumentSymbols() - } - if err != nil { - // Exit the process and trigger a restart by the client in case of a severe error - if err.Error() == "context deadline exceeded" { - log.Println(prefix + "Timeout exceeded while waiting for a reply from clangd.") - log.Println(prefix + "Please restart the language server.") - handler.Close() - } - if strings.Contains(err.Error(), "non-added document") || strings.Contains(err.Error(), "non-added file") { - log.Printf(prefix + "The clangd process has lost track of the open document.") - log.Printf(prefix+" %s", err) - log.Println(prefix + "Please restart the language server.") - handler.Close() - } - } - - // Transform and return the result - if result != nil { - result = handler.transformClangdResult(req.Method, inoURI, cppURI, result) - } - return result, err -} - -func (handler *InoHandler) initializeWorkbench(ctx context.Context, params *lsp.InitializeParams) error { - currCppTextVersion := 0 - if params != nil { - log.Printf(" --> initialize(%s)\n", params.RootURI) - handler.lspInitializeParams = params - handler.sketchRoot = params.RootURI.AsPath() - handler.sketchName = handler.sketchRoot.Base() - } else { - log.Printf(" --> RE-initialize()\n") - currCppTextVersion = handler.sketchMapper.CppText.Version - } - - if err := handler.generateBuildEnvironment(handler.buildPath); err != nil { - return err - } - handler.buildSketchCpp = handler.buildSketchRoot.Join(handler.sketchName + ".ino.cpp") - handler.buildSketchCppVersion = 1 - handler.lspInitializeParams.RootPath = handler.buildSketchRoot.String() - handler.lspInitializeParams.RootURI = lsp.NewDocumentURIFromPath(handler.buildSketchRoot) - - if cppContent, err := handler.buildSketchCpp.ReadFile(); err == nil { - handler.sketchMapper = sourcemapper.CreateInoMapper(cppContent) - handler.sketchMapper.CppText.Version = currCppTextVersion + 1 - } else { - return errors.WithMessage(err, "reading generated cpp file from sketch") - } - - canonicalizeCompileCommandsJSON(handler.buildPath) - - if params == nil { - // If we are restarting re-synchronize clangd - cppURI := lsp.NewDocumentURIFromPath(handler.buildSketchCpp) - cppTextDocumentIdentifier := lsp.TextDocumentIdentifier{URI: cppURI} - - syncEvent := &lsp.DidChangeTextDocumentParams{ - TextDocument: lsp.VersionedTextDocumentIdentifier{ - TextDocumentIdentifier: cppTextDocumentIdentifier, - Version: handler.sketchMapper.CppText.Version, - }, - ContentChanges: []lsp.TextDocumentContentChangeEvent{ - {Text: handler.sketchMapper.CppText.Text}, // Full text change - }, - } - - if err := handler.ClangdConn.Notify(ctx, "textDocument/didChange", syncEvent); err != nil { - log.Println(" error reinitilizing clangd:", err) - return err - } - } else { - // Otherwise start clangd! - dataFolder, err := extractDataFolderFromArduinoCLI() - if err != nil { - log.Printf(" error: %s", err) - } - clangdStdout, clangdStdin, clangdStderr := startClangd(handler.buildPath, handler.buildSketchCpp, dataFolder) - clangdStdio := streams.NewReadWriteCloser(clangdStdin, clangdStdout) - if enableLogging { - clangdStdio = streams.LogReadWriteCloserAs(clangdStdio, "inols-clangd.log") - go io.Copy(streams.OpenLogFileAs("inols-clangd-err.log"), clangdStderr) - } else { - go io.Copy(os.Stderr, clangdStderr) - } - - clangdStream := jsonrpc2.NewBufferedStream(clangdStdio, jsonrpc2.VSCodeObjectCodec{}) - clangdHandler := AsyncHandler{jsonrpc2.HandlerWithError(handler.FromClangd)} - handler.ClangdConn = jsonrpc2.NewConn(context.Background(), clangdStream, clangdHandler, - jsonrpc2.OnRecv(streams.JSONRPCConnLogOnRecv("IDE LS <-- CL:")), - jsonrpc2.OnSend(streams.JSONRPCConnLogOnSend("IDE LS --> CL:"))) - go func() { - <-handler.ClangdConn.DisconnectNotify() - log.Printf("Lost connection with clangd!") - handler.Close() - }() - - // Send initialization command to clangd - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - var resp lsp.InitializeResult - if err := handler.ClangdConn.Call(ctx, "initialize", handler.lspInitializeParams, &resp); err != nil { - log.Println(" error initilizing clangd:", err) - return err - } - - if err := handler.ClangdConn.Notify(ctx, "initialized", lsp.InitializedParams{}); err != nil { - log.Println(" error sending initialize to clangd:", err) - return err - } - } - - handler.buildSketchSymbolsLoad = true - return nil -} - -func extractDataFolderFromArduinoCLI() (*paths.Path, error) { - // XXX: do this from IDE or via gRPC - args := []string{globalCliPath, - "--config-file", globalCliConfigPath, - "config", - "dump", - "--format", "json", - } - cmd, err := executils.NewProcess(args...) - if err != nil { - return nil, errors.Errorf("running %s: %s", strings.Join(args, " "), err) - } - cmdOutput := &bytes.Buffer{} - cmd.RedirectStdoutTo(cmdOutput) - log.Println("running: ", strings.Join(args, " ")) - if err := cmd.Run(); err != nil { - return nil, errors.Errorf("running %s: %s", strings.Join(args, " "), err) - } - - type cmdRes struct { - Directories struct { - Data string `json:"data"` - } `json:"directories"` - } - var res cmdRes - if err := json.Unmarshal(cmdOutput.Bytes(), &res); err != nil { - return nil, errors.Errorf("parsing arduino-cli output: %s", err) - } - // Return only the build path - log.Println("Arduino Data Dir -> ", res.Directories.Data) - return paths.New(res.Directories.Data), nil -} - -func (handler *InoHandler) refreshCppDocumentSymbols(prefix string) error { - // Query source code symbols - cppURI := lsp.NewDocumentURIFromPath(handler.buildSketchCpp) - log.Printf(prefix+"requesting documentSymbol for %s", cppURI) - - handler.dataRUnlock(prefix) - result, err := lsp.SendRequest(context.Background(), handler.ClangdConn, "textDocument/documentSymbol", &lsp.DocumentSymbolParams{ - TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, - }) - handler.dataRLock(prefix) - - if err != nil { - log.Printf(prefix+"error: %s", err) - return errors.WithMessage(err, "quering source code symbols") - } - - symbolResult, ok := result.(*lsp.DocumentSymbolArrayOrSymbolInformationArray) - if !ok || symbolResult.DocumentSymbolArray == nil { - log.Printf(prefix + "error: expected DocumenSymbol array from clangd") - return errors.New("expected array from clangd") - } - symbols := *symbolResult.DocumentSymbolArray - - // Filter non-functions symbols - i := 0 - for _, symbol := range symbols { - if symbol.Kind != lsp.SKFunction { - continue - } - symbols[i] = symbol - i++ - } - symbols = symbols[:i] - handler.buildSketchSymbols = symbols - - symbolsCanary := "" - for _, symbol := range symbols { - log.Printf(prefix+" symbol: %s %s %s", symbol.Kind, symbol.Name, symbol.Range) - if symbolText, err := textutils.ExtractRange(handler.sketchMapper.CppText.Text, symbol.Range); err != nil { - log.Printf(prefix+" > invalid range: %s", err) - symbolsCanary += "/" - } else if end := strings.Index(symbolText, "{"); end != -1 { - log.Printf(prefix+" TRIMMED> %s", symbolText[:end]) - symbolsCanary += symbolText[:end] - } else { - log.Printf(prefix+" > %s", symbolText) - symbolsCanary += symbolText - } - } - handler.buildSketchSymbolsCanary = symbolsCanary - return nil -} - -func (handler *InoHandler) LoadCppDocumentSymbols() error { - prefix := "SYLD--- " - defer log.Printf(prefix + "(done)") - handler.dataRLock(prefix) - defer handler.dataRUnlock(prefix) - return handler.refreshCppDocumentSymbols(prefix) -} - -func (handler *InoHandler) CheckCppDocumentSymbols() error { - prefix := "SYCK--- " - defer log.Printf(prefix + "(done)") - handler.dataRLock(prefix) - defer handler.dataRUnlock(prefix) - - oldSymbols := handler.buildSketchSymbols - canary := handler.buildSketchSymbolsCanary - if err := handler.refreshCppDocumentSymbols(prefix); err != nil { - return err - } - if len(oldSymbols) != len(handler.buildSketchSymbols) || canary != handler.buildSketchSymbolsCanary { - log.Println(prefix + "function symbols change detected, triggering sketch rebuild!") - handler.scheduleRebuildEnvironment() - } - return nil -} - -func (handler *InoHandler) CheckCppIncludesChanges() { - prefix := "INCK--- " - - includesCanary := "" - for _, line := range strings.Split(handler.sketchMapper.CppText.Text, "\n") { - if strings.Contains(line, "#include ") { - includesCanary += line - } - } - - if includesCanary != handler.buildSketchIncludesCanary { - handler.buildSketchIncludesCanary = includesCanary - log.Println(prefix + "#include change detected, triggering sketch rebuild!") - handler.scheduleRebuildEnvironment() - } -} - -func canonicalizeCompileCommandsJSON(compileCommandsDir *paths.Path) map[string]bool { - // Open compile_commands.json and find the main cross-compiler executable - compileCommandsJSONPath := compileCommandsDir.Join("compile_commands.json") - compileCommands, err := builder.LoadCompilationDatabase(compileCommandsJSONPath) - if err != nil { - panic("could not find compile_commands.json") - } - compilers := map[string]bool{} - for i, cmd := range compileCommands.Contents { - if len(cmd.Arguments) == 0 { - panic("invalid empty argument field in compile_commands.json") - } - - // clangd requires full path to compiler (including extension .exe on Windows!) - compilerPath := paths.New(cmd.Arguments[0]).Canonical() - compiler := compilerPath.String() - if runtime.GOOS == "windows" && strings.ToLower(compilerPath.Ext()) != ".exe" { - compiler += ".exe" - } - compileCommands.Contents[i].Arguments[0] = compiler - - compilers[compiler] = true - } - - // Save back compile_commands.json with OS native file separator and extension - compileCommands.SaveToFile() - - return compilers -} - -func startClangd(compileCommandsDir, sketchCpp *paths.Path, dataFolder *paths.Path) (io.WriteCloser, io.ReadCloser, io.ReadCloser) { - // Start clangd - args := []string{ - globalClangdPath, - "-log=verbose", - fmt.Sprintf(`--compile-commands-dir=%s`, compileCommandsDir), - } - if dataFolder != nil { - args = append(args, fmt.Sprintf("-query-driver=%s", dataFolder.Join("packages", "**"))) - } - if enableLogging { - log.Println(" Starting clangd:", strings.Join(args, " ")) - } - if clangdCmd, err := executils.NewProcess(args...); err != nil { - panic("starting clangd: " + err.Error()) - } else if clangdIn, err := clangdCmd.StdinPipe(); err != nil { - panic("getting clangd stdin: " + err.Error()) - } else if clangdOut, err := clangdCmd.StdoutPipe(); err != nil { - panic("getting clangd stdout: " + err.Error()) - } else if clangdErr, err := clangdCmd.StderrPipe(); err != nil { - panic("getting clangd stderr: " + err.Error()) - } else if err := clangdCmd.Start(); err != nil { - panic("running clangd: " + err.Error()) - } else { - return clangdIn, clangdOut, clangdErr - } -} - -func (handler *InoHandler) didOpen(inoDidOpen *lsp.DidOpenTextDocumentParams) (*lsp.DidOpenTextDocumentParams, error) { - // Add the TextDocumentItem in the tracked files list - inoItem := inoDidOpen.TextDocument - handler.docs[inoItem.URI.AsPath().String()] = &inoItem - - // If we are tracking a .ino... - if inoItem.URI.Ext() == ".ino" { - handler.sketchTrackedFilesCount++ - log.Printf(" increasing .ino tracked files count: %d", handler.sketchTrackedFilesCount) - - // notify clang that sketchCpp has been opened only once - if handler.sketchTrackedFilesCount != 1 { - return nil, nil - } - } - - cppItem, err := handler.ino2cppTextDocumentItem(inoItem) - return &lsp.DidOpenTextDocumentParams{ - TextDocument: cppItem, - }, err -} - -func (handler *InoHandler) didClose(inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { - inoIdentifier := inoDidClose.TextDocument - if _, exist := handler.docs[inoIdentifier.URI.AsPath().String()]; exist { - delete(handler.docs, inoIdentifier.URI.AsPath().String()) - } else { - log.Printf(" didClose of untracked document: %s", inoIdentifier.URI) - return nil, unknownURI(inoIdentifier.URI) - } - - // If we are tracking a .ino... - if inoIdentifier.URI.Ext() == ".ino" { - handler.sketchTrackedFilesCount-- - log.Printf(" decreasing .ino tracked files count: %d", handler.sketchTrackedFilesCount) - - // notify clang that sketchCpp has been close only once all .ino are closed - if handler.sketchTrackedFilesCount != 0 { - return nil, nil - } - } - - cppIdentifier, err := handler.ino2cppTextDocumentIdentifier(inoIdentifier) - return &lsp.DidCloseTextDocumentParams{ - TextDocument: cppIdentifier, - }, err -} - -func (handler *InoHandler) ino2cppTextDocumentItem(inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { - cppURI, err := handler.ino2cppDocumentURI(inoItem.URI) - if err != nil { - return cppItem, err - } - cppItem.URI = cppURI - - if cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) { - cppItem.LanguageID = "cpp" - cppItem.Text = handler.sketchMapper.CppText.Text - cppItem.Version = handler.sketchMapper.CppText.Version - } else { - cppItem.LanguageID = inoItem.LanguageID - inoPath := inoItem.URI.AsPath().String() - cppItem.Text = handler.docs[inoPath].Text - cppItem.Version = handler.docs[inoPath].Version - } - - return cppItem, nil -} - -func (handler *InoHandler) didChange(ctx context.Context, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { - doc := req.TextDocument - - trackedDoc, ok := handler.docs[doc.URI.AsPath().String()] - if !ok { - return nil, unknownURI(doc.URI) - } - textutils.ApplyLSPTextDocumentContentChangeEvent(trackedDoc, req.ContentChanges, doc.Version) - - // If changes are applied to a .ino file we increment the global .ino.cpp versioning - // for each increment of the single .ino file. - if doc.URI.Ext() == ".ino" { - - cppChanges := []lsp.TextDocumentContentChangeEvent{} - for _, inoChange := range req.ContentChanges { - cppRange, ok := handler.sketchMapper.InoToCppLSPRangeOk(doc.URI, *inoChange.Range) - if !ok { - return nil, errors.Errorf("invalid change range %s:%s", doc.URI, *inoChange.Range) - } - - // Detect changes in critical lines (for example function definitions) - // and trigger arduino-preprocessing + clangd restart. - dirty := false - for _, sym := range handler.buildSketchSymbols { - if sym.SelectionRange.Overlaps(cppRange) { - dirty = true - log.Println("--! DIRTY CHANGE detected using symbol tables, force sketch rebuild!") - break - } - } - if handler.sketchMapper.ApplyTextChange(doc.URI, inoChange) { - dirty = true - log.Println("--! DIRTY CHANGE detected with sketch mapper, force sketch rebuild!") - } - if dirty { - handler.scheduleRebuildEnvironment() - } - - // log.Println("New version:----------") - // log.Println(handler.sketchMapper.CppText.Text) - // log.Println("----------------------") - - cppChange := lsp.TextDocumentContentChangeEvent{ - Range: &cppRange, - RangeLength: inoChange.RangeLength, - Text: inoChange.Text, - } - cppChanges = append(cppChanges, cppChange) - } - - handler.CheckCppIncludesChanges() - - // build a cpp equivalent didChange request - cppReq := &lsp.DidChangeTextDocumentParams{ - ContentChanges: cppChanges, - TextDocument: lsp.VersionedTextDocumentIdentifier{ - TextDocumentIdentifier: lsp.TextDocumentIdentifier{ - URI: lsp.NewDocumentURIFromPath(handler.buildSketchCpp), - }, - Version: handler.sketchMapper.CppText.Version, - }, - } - - return cppReq, nil - } - - // If changes are applied to other files pass them by converting just the URI - cppDoc, err := handler.ino2cppVersionedTextDocumentIdentifier(req.TextDocument) - if err != nil { - return nil, err - } - cppReq := &lsp.DidChangeTextDocumentParams{ - TextDocument: cppDoc, - ContentChanges: req.ContentChanges, - } - return cppReq, err -} - -func (handler *InoHandler) handleError(ctx context.Context, err error) error { - errorStr := err.Error() - var message string - if strings.Contains(errorStr, "#error") { - exp, regexpErr := regexp.Compile("#error \"(.*)\"") - if regexpErr != nil { - panic(regexpErr) - } - submatch := exp.FindStringSubmatch(errorStr) - message = submatch[1] - } else if strings.Contains(errorStr, "platform not installed") || strings.Contains(errorStr, "no FQBN provided") { - if len(handler.config.SelectedBoard.Name) > 0 { - board := handler.config.SelectedBoard.Name - message = "Editor support may be inaccurate because the core for the board `" + board + "` is not installed." - message += " Use the Boards Manager to install it." - } else { - // This case happens most often when the app is started for the first time and no - // board is selected yet. Don't bother the user with an error then. - return err - } - } else if strings.Contains(errorStr, "No such file or directory") { - exp, regexpErr := regexp.Compile("([\\w\\.\\-]+): No such file or directory") - if regexpErr != nil { - panic(regexpErr) - } - submatch := exp.FindStringSubmatch(errorStr) - message = "Editor support may be inaccurate because the header `" + submatch[1] + "` was not found." - message += " If it is part of a library, use the Library Manager to install it." - } else { - message = "Could not start editor support.\n" + errorStr - } - go handler.showMessage(ctx, lsp.MTError, message) - return errors.New(message) -} - -func (handler *InoHandler) ino2cppVersionedTextDocumentIdentifier(doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { - cppURI, err := handler.ino2cppDocumentURI(doc.URI) - res := doc - res.URI = cppURI - return res, err -} - -func (handler *InoHandler) ino2cppTextDocumentIdentifier(doc lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { - cppURI, err := handler.ino2cppDocumentURI(doc.URI) - res := doc - res.URI = cppURI - return res, err -} - -func (handler *InoHandler) ino2cppDocumentURI(inoURI lsp.DocumentURI) (lsp.DocumentURI, error) { - // Sketchbook/Sketch/Sketch.ino -> build-path/sketch/Sketch.ino.cpp - // Sketchbook/Sketch/AnotherTab.ino -> build-path/sketch/Sketch.ino.cpp (different section from above) - // Sketchbook/Sketch/AnotherFile.cpp -> build-path/sketch/AnotherFile.cpp (1:1) - // another/path/source.cpp -> unchanged - - // Convert sketch path to build path - inoPath := inoURI.AsPath() - if inoPath.Ext() == ".ino" { - return lsp.NewDocumentURIFromPath(handler.buildSketchCpp), nil - } - - inside, err := inoPath.IsInsideDir(handler.sketchRoot) - if err != nil { - log.Printf(" could not determine if '%s' is inside '%s'", inoPath, handler.sketchRoot) - return lsp.NilURI, unknownURI(inoURI) - } - if !inside { - log.Printf(" '%s' not inside sketchroot '%s', passing doc identifier to as-is", handler.sketchRoot, inoPath) - return inoURI, nil - } - - rel, err := handler.sketchRoot.RelTo(inoPath) - if err == nil { - cppPath := handler.buildSketchRoot.JoinPath(rel) - log.Printf(" URI: '%s' -> '%s'", inoPath, cppPath) - return lsp.NewDocumentURIFromPath(cppPath), nil - } - - log.Printf(" could not determine rel-path of '%s' in '%s': %s", inoPath, handler.sketchRoot, err) - return lsp.NilURI, err -} - -func (handler *InoHandler) inoDocumentURIFromInoPath(inoPath string) (lsp.DocumentURI, error) { - if inoPath == sourcemapper.NotIno.File { - return sourcemapper.NotInoURI, nil - } - doc, ok := handler.docs[inoPath] - if !ok { - log.Printf(" !!! Unresolved .ino path: %s", inoPath) - log.Printf(" !!! Known doc paths are:") - for p := range handler.docs { - log.Printf(" !!! > %s", p) - } - uri := lsp.NewDocumentURI(inoPath) - return uri, unknownURI(uri) - } - return doc.URI, nil -} - -func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { - // TODO: Split this function into 2 - // - Cpp2inoSketchDocumentURI: converts sketch (cppURI, cppRange) -> (inoURI, inoRange) - // - Cpp2inoDocumentURI : converts non-sketch (cppURI) -> (inoURI) [range is the same] - - // Sketchbook/Sketch/Sketch.ino <- build-path/sketch/Sketch.ino.cpp - // Sketchbook/Sketch/AnotherTab.ino <- build-path/sketch/Sketch.ino.cpp (different section from above) - // Sketchbook/Sketch/AnotherFile.cpp <- build-path/sketch/AnotherFile.cpp (1:1) - // another/path/source.cpp <- unchanged - - // Convert build path to sketch path - cppPath := cppURI.AsPath() - if cppPath.EquivalentTo(handler.buildSketchCpp) { - inoPath, inoRange, err := handler.sketchMapper.CppToInoRangeOk(cppRange) - if err == nil { - if handler.sketchMapper.IsPreprocessedCppLine(cppRange.Start.Line) { - inoPath = sourcemapper.NotIno.File - log.Printf(" URI: is in preprocessed section") - log.Printf(" converted %s to %s:%s", cppRange, inoPath, inoRange) - } else { - log.Printf(" URI: converted %s to %s:%s", cppRange, inoPath, inoRange) - } - } else if _, ok := err.(sourcemapper.AdjustedRangeErr); ok { - log.Printf(" URI: converted %s to %s:%s (END LINE ADJUSTED)", cppRange, inoPath, inoRange) - err = nil - } else { - log.Printf(" URI: ERROR: %s", err) - handler.sketchMapper.DebugLogAll() - return lsp.NilURI, lsp.NilRange, err - } - inoURI, err := handler.inoDocumentURIFromInoPath(inoPath) - return inoURI, inoRange, err - } - - inside, err := cppPath.IsInsideDir(handler.buildSketchRoot) - if err != nil { - log.Printf(" could not determine if '%s' is inside '%s'", cppPath, handler.buildSketchRoot) - return lsp.NilURI, lsp.NilRange, err - } - if !inside { - log.Printf(" '%s' is not inside '%s'", cppPath, handler.buildSketchRoot) - log.Printf(" keep doc identifier to '%s' as-is", cppPath) - return cppURI, cppRange, nil - } - - rel, err := handler.buildSketchRoot.RelTo(cppPath) - if err == nil { - inoPath := handler.sketchRoot.JoinPath(rel).String() - log.Printf(" URI: '%s' -> '%s'", cppPath, inoPath) - inoURI, err := handler.inoDocumentURIFromInoPath(inoPath) - log.Printf(" as URI: '%s'", inoURI) - return inoURI, cppRange, err - } - - log.Printf(" could not determine rel-path of '%s' in '%s': %s", cppPath, handler.buildSketchRoot, err) - return lsp.NilURI, lsp.NilRange, err -} - -func (handler *InoHandler) ino2cppTextDocumentPositionParams(inoParams *lsp.TextDocumentPositionParams) (*lsp.TextDocumentPositionParams, error) { - cppDoc, err := handler.ino2cppTextDocumentIdentifier(inoParams.TextDocument) - if err != nil { - return nil, err - } - cppPosition := inoParams.Position - inoURI := inoParams.TextDocument.URI - if inoURI.Ext() == ".ino" { - if cppLine, ok := handler.sketchMapper.InoToCppLineOk(inoURI, inoParams.Position.Line); ok { - cppPosition.Line = cppLine - } else { - log.Printf(" invalid line requested: %s:%d", inoURI, inoParams.Position.Line) - return nil, unknownURI(inoURI) - } - } - return &lsp.TextDocumentPositionParams{ - TextDocument: cppDoc, - Position: cppPosition, - }, nil -} - -func (handler *InoHandler) ino2cppRange(inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { - cppURI, err := handler.ino2cppDocumentURI(inoURI) - if err != nil { - return lsp.NilURI, lsp.Range{}, err - } - if cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) { - cppRange := handler.sketchMapper.InoToCppLSPRange(inoURI, inoRange) - return cppURI, cppRange, nil - } - return cppURI, inoRange, nil -} - -func (handler *InoHandler) ino2cppDocumentRangeFormattingParams(inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { - cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(inoParams.TextDocument) - if err != nil { - return nil, err - } - - _, cppRange, err := handler.ino2cppRange(inoParams.TextDocument.URI, inoParams.Range) - return &lsp.DocumentRangeFormattingParams{ - TextDocument: cppTextDocument, - Range: cppRange, - Options: inoParams.Options, - }, err -} - -func (handler *InoHandler) ino2cppDocumentOnTypeFormattingParams(params *lsp.DocumentOnTypeFormattingParams) error { - panic("not implemented") - // handler.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) - // if data, ok := handler.data[params.TextDocument.URI]; ok { - // params.Position.Line = data.sourceMap.InoToCppLine(data.sourceURI, params.Position.Line) - // return nil - // } - return unknownURI(params.TextDocument.URI) -} - -func (handler *InoHandler) ino2cppRenameParams(params *lsp.RenameParams) error { - panic("not implemented") - // handler.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) - // if data, ok := handler.data[params.TextDocument.URI]; ok { - // params.Position.Line = data.sourceMap.InoToCppLine(data.sourceURI, params.Position.Line) - // return nil - // } - return unknownURI(params.TextDocument.URI) -} - -func (handler *InoHandler) ino2cppDidChangeWatchedFilesParams(params *lsp.DidChangeWatchedFilesParams) error { - panic("not implemented") - // for index := range params.Changes { - // fileEvent := ¶ms.Changes[index] - // if data, ok := handler.data[fileEvent.URI]; ok { - // fileEvent.URI = data.targetURI - // } - // } - return nil -} - -func (handler *InoHandler) ino2cppExecuteCommand(executeCommand *lsp.ExecuteCommandParams) error { - panic("not implemented") - // if len(executeCommand.Arguments) == 1 { - // arg := handler.parseCommandArgument(executeCommand.Arguments[0]) - // if workspaceEdit, ok := arg.(*lsp.WorkspaceEdit); ok { - // executeCommand.Arguments[0] = handler.ino2cppWorkspaceEdit(workspaceEdit) - // } - // } - return nil -} - -func (handler *InoHandler) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { - panic("not implemented") - newEdit := lsp.WorkspaceEdit{Changes: make(map[lsp.DocumentURI][]lsp.TextEdit)} - // for uri, edit := range origEdit.Changes { - // if data, ok := handler.data[lsp.DocumentURI(uri)]; ok { - // newValue := make([]lsp.TextEdit, len(edit)) - // for index := range edit { - // newValue[index] = lsp.TextEdit{ - // NewText: edit[index].NewText, - // Range: data.sourceMap.InoToCppLSPRange(data.sourceURI, edit[index].Range), - // } - // } - // newEdit.Changes[string(data.targetURI)] = newValue - // } else { - // newEdit.Changes[uri] = edit - // } - // } - return &newEdit -} - -func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI lsp.DocumentURI, result interface{}) interface{} { - cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp) - - switch r := result.(type) { - case *lsp.Hover: - // method "textDocument/hover" - if len(r.Contents.Value) == 0 { - return nil - } - if cppToIno { - _, *r.Range = handler.sketchMapper.CppToInoRange(*r.Range) - } - log.Printf("<-- hover(%s)", strconv.Quote(r.Contents.Value)) - return r - - case *lsp.CompletionList: - // method "textDocument/completion" - newItems := make([]lsp.CompletionItem, 0) - - for _, item := range r.Items { - if !strings.HasPrefix(item.InsertText, "_") { - if cppToIno && item.TextEdit != nil { - _, item.TextEdit.Range = handler.sketchMapper.CppToInoRange(item.TextEdit.Range) - } - newItems = append(newItems, item) - } - } - r.Items = newItems - log.Printf("<-- completion(%d items) cppToIno=%v", len(r.Items), cppToIno) - return r - - case *lsp.DocumentSymbolArrayOrSymbolInformationArray: - // method "textDocument/documentSymbol" - - if r.DocumentSymbolArray != nil { - // Treat the input as []DocumentSymbol - log.Printf(" <-- documentSymbol(%d document symbols)", len(*r.DocumentSymbolArray)) - return handler.cpp2inoDocumentSymbols(*r.DocumentSymbolArray, inoURI) - } else if r.SymbolInformationArray != nil { - // Treat the input as []SymbolInformation - log.Printf(" <-- documentSymbol(%d symbol information)", len(*r.SymbolInformationArray)) - return handler.cpp2inoSymbolInformation(*r.SymbolInformationArray) - } else { - // Treat the input as null - log.Printf(" <-- null documentSymbol") - } - - case *[]lsp.CommandOrCodeAction: - // method "textDocument/codeAction" - log.Printf(" <-- codeAction(%d elements)", len(*r)) - for i, item := range *r { - if item.Command != nil { - log.Printf(" > Command: %s", item.Command.Title) - } - if item.CodeAction != nil { - log.Printf(" > CodeAction: %s", item.CodeAction.Title) - } - (*r)[i] = lsp.CommandOrCodeAction{ - Command: handler.Cpp2InoCommand(item.Command), - CodeAction: handler.cpp2inoCodeAction(item.CodeAction, inoURI), - } - } - log.Printf("<-- codeAction(%d elements)", len(*r)) - - case *[]lsp.TextEdit: - // Method: "textDocument/rangeFormatting" - // Method: "textDocument/onTypeFormatting" - // Method: "textDocument/formatting" - log.Printf(" <-- %s %s textEdit(%d elements)", method, cppURI, len(*r)) - for _, edit := range *r { - log.Printf(" > %s -> %s", edit.Range, strconv.Quote(edit.NewText)) - } - sketchEdits, err := handler.cpp2inoTextEdits(cppURI, *r) - if err != nil { - log.Printf("ERROR converting textEdits: %s", err) - return nil - } - - inoEdits, ok := sketchEdits[inoURI] - if !ok { - inoEdits = []lsp.TextEdit{} - } - log.Printf("<-- %s %s textEdit(%d elements)", method, inoURI, len(inoEdits)) - for _, edit := range inoEdits { - log.Printf(" > %s -> %s", edit.Range, strconv.Quote(edit.NewText)) - } - return &inoEdits - - case *[]lsp.Location: - // Method: "textDocument/definition" - // Method: "textDocument/typeDefinition" - // Method: "textDocument/implementation" - // Method: "textDocument/references" - inoLocations := []lsp.Location{} - for _, cppLocation := range *r { - inoLocation, err := handler.cpp2inoLocation(cppLocation) - if err != nil { - log.Printf("ERROR converting location %s:%s: %s", cppLocation.URI, cppLocation.Range, err) - return nil - } - inoLocations = append(inoLocations, inoLocation) - } - return &inoLocations - - case *[]lsp.SymbolInformation: - // Method: "workspace/symbol" - - inoSymbols := []lsp.SymbolInformation{} - for _, cppSymbolInfo := range *r { - cppLocation := cppSymbolInfo.Location - inoLocation, err := handler.cpp2inoLocation(cppLocation) - if err != nil { - log.Printf("ERROR converting location %s:%s: %s", cppLocation.URI, cppLocation.Range, err) - return nil - } - inoSymbolInfo := cppSymbolInfo - inoSymbolInfo.Location = inoLocation - inoSymbols = append(inoSymbols, inoSymbolInfo) - } - return &inoSymbols - - case *[]lsp.DocumentHighlight: - // Method: "textDocument/documentHighlight" - res := []lsp.DocumentHighlight{} - for _, cppHL := range *r { - inoHL, err := handler.cpp2inoDocumentHighlight(&cppHL, cppURI) - if err != nil { - log.Printf("ERROR converting location %s:%s: %s", cppURI, cppHL.Range, err) - return nil - } - res = append(res, *inoHL) - } - return &res - - case *lsp.WorkspaceEdit: // "textDocument/rename": - return handler.cpp2inoWorkspaceEdit(r) - } - return result -} - -func (handler *InoHandler) cpp2inoCodeAction(codeAction *lsp.CodeAction, uri lsp.DocumentURI) *lsp.CodeAction { - if codeAction == nil { - return nil - } - inoCodeAction := &lsp.CodeAction{ - Title: codeAction.Title, - Kind: codeAction.Kind, - Edit: handler.cpp2inoWorkspaceEdit(codeAction.Edit), - Diagnostics: codeAction.Diagnostics, - Command: handler.Cpp2InoCommand(codeAction.Command), - } - if uri.Ext() == ".ino" { - for i, diag := range inoCodeAction.Diagnostics { - _, inoCodeAction.Diagnostics[i].Range = handler.sketchMapper.CppToInoRange(diag.Range) - } - } - return inoCodeAction -} - -func (handler *InoHandler) Cpp2InoCommand(command *lsp.Command) *lsp.Command { - if command == nil { - return nil - } - inoCommand := &lsp.Command{ - Title: command.Title, - Command: command.Command, - Arguments: command.Arguments, - } - if command.Command == "clangd.applyTweak" { - for i := range command.Arguments { - v := struct { - TweakID string `json:"tweakID"` - File lsp.DocumentURI `json:"file"` - Selection lsp.Range `json:"selection"` - }{} - if err := json.Unmarshal(command.Arguments[0], &v); err == nil { - if v.TweakID == "ExtractVariable" { - log.Println(" > converted clangd ExtractVariable") - if v.File.AsPath().EquivalentTo(handler.buildSketchCpp) { - inoFile, inoSelection := handler.sketchMapper.CppToInoRange(v.Selection) - v.File = lsp.NewDocumentURI(inoFile) - v.Selection = inoSelection - } - } - } - - converted, err := json.Marshal(v) - if err != nil { - panic("Internal Error: json conversion of codeAcion command arguments") - } - inoCommand.Arguments[i] = converted - } - } - return inoCommand -} - -func (handler *InoHandler) cpp2inoWorkspaceEdit(cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { - if cppWorkspaceEdit == nil { - return nil - } - inoWorkspaceEdit := &lsp.WorkspaceEdit{ - Changes: map[lsp.DocumentURI][]lsp.TextEdit{}, - } - for editURI, edits := range cppWorkspaceEdit.Changes { - // if the edits are not relative to sketch file... - if !editURI.AsPath().EquivalentTo(handler.buildSketchCpp) { - // ...pass them through... - inoWorkspaceEdit.Changes[editURI] = edits - continue - } - - // ...otherwise convert edits to the sketch.ino.cpp into multilpe .ino edits - for _, edit := range edits { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(editURI, edit.Range) - if err != nil { - log.Printf(" error converting edit %s:%s: %s", editURI, edit.Range, err) - continue - } - //inoFile, inoRange := handler.sketchMapper.CppToInoRange(edit.Range) - //inoURI := lsp.NewDocumentURI(inoFile) - if _, have := inoWorkspaceEdit.Changes[inoURI]; !have { - inoWorkspaceEdit.Changes[inoURI] = []lsp.TextEdit{} - } - inoWorkspaceEdit.Changes[inoURI] = append(inoWorkspaceEdit.Changes[inoURI], lsp.TextEdit{ - NewText: edit.NewText, - Range: inoRange, - }) - } - } - log.Printf(" done converting workspaceEdit") - return inoWorkspaceEdit -} - -func (handler *InoHandler) cpp2inoLocation(cppLocation lsp.Location) (lsp.Location, error) { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(cppLocation.URI, cppLocation.Range) - return lsp.Location{ - URI: inoURI, - Range: inoRange, - }, err -} - -func (handler *InoHandler) cpp2inoDocumentHighlight(cppHighlight *lsp.DocumentHighlight, cppURI lsp.DocumentURI) (*lsp.DocumentHighlight, error) { - _, inoRange, err := handler.cpp2inoDocumentURI(cppURI, cppHighlight.Range) - if err != nil { - return nil, err - } - return &lsp.DocumentHighlight{ - Kind: cppHighlight.Kind, - Range: inoRange, - }, nil -} - -func (handler *InoHandler) cpp2inoTextEdits(cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { - res := map[lsp.DocumentURI][]lsp.TextEdit{} - for _, cppEdit := range cppEdits { - inoURI, inoEdit, err := handler.cpp2inoTextEdit(cppURI, cppEdit) - if err != nil { - return nil, err - } - inoEdits, ok := res[inoURI] - if !ok { - inoEdits = []lsp.TextEdit{} - } - inoEdits = append(inoEdits, inoEdit) - res[inoURI] = inoEdits - } - return res, nil -} - -func (handler *InoHandler) cpp2inoTextEdit(cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(cppURI, cppEdit.Range) - inoEdit := cppEdit - inoEdit.Range = inoRange - return inoURI, inoEdit, err -} - -func (handler *InoHandler) cpp2inoDocumentSymbols(cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { - inoRequested := inoRequestedURI.AsPath().String() - log.Printf(" filtering for requested ino file: %s", inoRequested) - if inoRequestedURI.Ext() != ".ino" || len(cppSymbols) == 0 { - return cppSymbols - } - - inoSymbols := []lsp.DocumentSymbol{} - for _, symbol := range cppSymbols { - log.Printf(" > convert %s %s", symbol.Kind, symbol.Range) - if handler.sketchMapper.IsPreprocessedCppLine(symbol.Range.Start.Line) { - log.Printf(" symbol is in the preprocessed section of the sketch.ino.cpp") - continue - } - - inoFile, inoRange := handler.sketchMapper.CppToInoRange(symbol.Range) - inoSelectionURI, inoSelectionRange := handler.sketchMapper.CppToInoRange(symbol.SelectionRange) - - if inoFile != inoSelectionURI { - log.Printf(" ERROR: symbol range and selection belongs to different URI!") - log.Printf(" symbol %s != selection %s", symbol.Range, symbol.SelectionRange) - log.Printf(" %s:%s != %s:%s", inoFile, inoRange, inoSelectionURI, inoSelectionRange) - continue - } - - if inoFile != inoRequested { - log.Printf(" skipping symbol related to %s", inoFile) - continue - } - - inoSymbols = append(inoSymbols, lsp.DocumentSymbol{ - Name: symbol.Name, - Detail: symbol.Detail, - Deprecated: symbol.Deprecated, - Kind: symbol.Kind, - Range: inoRange, - SelectionRange: inoSelectionRange, - Children: handler.cpp2inoDocumentSymbols(symbol.Children, inoRequestedURI), - }) - } - - return inoSymbols -} - -func (handler *InoHandler) cpp2inoSymbolInformation(syms []lsp.SymbolInformation) []lsp.SymbolInformation { - panic("not implemented") - // // Much like in cpp2inoDocumentSymbols we de-duplicate symbols based on file in-file location. - // idx := make(map[string]*lsp.SymbolInformation) - // for _, sym := range syms { - // handler.cpp2inoLocation(&sym.Location) - - // nme := fmt.Sprintf("%s::%s", sym.ContainerName, sym.Name) - // other, duplicate := idx[nme] - // if duplicate && other.Location.Range.Start.Line < sym.Location.Range.Start.Line { - // continue - // } - - // idx[nme] = sym - // } - - // var j int - // symbols := make([]lsp.SymbolInformation, len(idx)) - // for _, sym := range idx { - // symbols[j] = *sym - // j++ - // } - // return symbols -} - -func (handler *InoHandler) cpp2inoDiagnostics(cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { - inoDiagsParam := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} - - cppURI := cppDiags.URI - isSketch := cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) - if isSketch { - for inoURI := range handler.inoDocsWithDiagnostics { - inoDiagsParam[inoURI] = &lsp.PublishDiagnosticsParams{ - URI: inoURI, - Diagnostics: []lsp.Diagnostic{}, - } - } - handler.inoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} - } else { - inoURI, _, err := handler.cpp2inoDocumentURI(cppURI, lsp.NilRange) - if err != nil { - return nil, err - } - inoDiagsParam[inoURI] = &lsp.PublishDiagnosticsParams{ - URI: inoURI, - Diagnostics: []lsp.Diagnostic{}, - } - } - - for _, cppDiag := range cppDiags.Diagnostics { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(cppURI, cppDiag.Range) - if err != nil { - return nil, err - } - if inoURI.String() == sourcemapper.NotInoURI.String() { - continue - } - - inoDiagParam, created := inoDiagsParam[inoURI] - if !created { - inoDiagParam = &lsp.PublishDiagnosticsParams{ - URI: inoURI, - Diagnostics: []lsp.Diagnostic{}, - } - inoDiagsParam[inoURI] = inoDiagParam - } - - inoDiag := cppDiag - inoDiag.Range = inoRange - inoDiagParam.Diagnostics = append(inoDiagParam.Diagnostics, inoDiag) - - if isSketch { - handler.inoDocsWithDiagnostics[inoURI] = true - - // If we have an "undefined reference" in the .ino code trigger a - // check for newly created symbols (that in turn may trigger a - // new arduino-preprocessing of the sketch). - if inoDiag.Code == "undeclared_var_use_suggest" || - inoDiag.Code == "undeclared_var_use" || - inoDiag.Code == "ovl_no_viable_function_in_call" || - inoDiag.Code == "pp_file_not_found" { - handler.buildSketchSymbolsCheck = true - } - } - } - - inoDiagParams := []*lsp.PublishDiagnosticsParams{} - for _, v := range inoDiagsParam { - inoDiagParams = append(inoDiagParams, v) - } - return inoDiagParams, nil -} - -// FromClangd handles a message received from clangd. -func (handler *InoHandler) FromClangd(ctx context.Context, connection *jsonrpc2.Conn, req *jsonrpc2.Request) (interface{}, error) { - defer streams.CatchAndLogPanic() - - prefix := "CLG <-- " - if req.Notif { - n := atomic.AddInt64(&handler.clangdNotificationCount, 1) - prefix += fmt.Sprintf("%s notif%d ", req.Method, n) - } else { - prefix += fmt.Sprintf("%s %v ", req.Method, req.ID) - } - - if req.Method == "window/workDoneProgress/create" { - params := lsp.WorkDoneProgressCreateParams{} - if err := json.Unmarshal(*req.Params, ¶ms); err != nil { - log.Printf(prefix+"error decoding window/workDoneProgress/create: %v", err) - return nil, err - } - handler.progressHandler.Create(params.Token) - return &lsp.WorkDoneProgressCreateResult{}, nil - } - - if req.Method == "$/progress" { - // data may be of many different types... - params := lsp.ProgressParams{} - if err := json.Unmarshal(*req.Params, ¶ms); err != nil { - log.Printf(prefix+"error decoding progress: %v", err) - return nil, err - } - id := params.Token - - var begin lsp.WorkDoneProgressBegin - if err := json.Unmarshal(*params.Value, &begin); err == nil { - // log.Printf(prefix+"begin %s %v", id, begin) - handler.progressHandler.Begin(id, &begin) - return nil, nil - } - - var report lsp.WorkDoneProgressReport - if err := json.Unmarshal(*params.Value, &report); err == nil { - // log.Printf(prefix+"report %s %v", id, report) - handler.progressHandler.Report(id, &report) - return nil, nil - } - - var end lsp.WorkDoneProgressEnd - if err := json.Unmarshal(*params.Value, &end); err == nil { - // log.Printf(prefix+"end %s %v", id, end) - handler.progressHandler.End(id, &end) - return nil, nil - } - - log.Printf(prefix + "error unsupported $/progress: " + string(*params.Value)) - return nil, errors.New("unsupported $/progress: " + string(*params.Value)) - } - - // Default to read lock - log.Printf(prefix + "(queued)") - handler.dataRLock(prefix) - defer handler.dataRUnlock(prefix) - log.Printf(prefix + "(running)") - - params, err := lsp.ReadParams(req.Method, req.Params) - if err != nil { - log.Println(prefix+"parsing clang message:", err) - return nil, errors.WithMessage(err, "parsing JSON message from clangd") - } - - switch p := params.(type) { - case *lsp.PublishDiagnosticsParams: - // "textDocument/publishDiagnostics" - log.Printf(prefix+"publishDiagnostics(%s):", p.URI) - for _, diag := range p.Diagnostics { - log.Printf(prefix+"> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) - } - - // the diagnostics on sketch.cpp.ino once mapped into their - // .ino counter parts may span over multiple .ino files... - inoDiagnostics, err := handler.cpp2inoDiagnostics(p) - if err != nil { - return nil, err - } - - // Push back to IDE the converted diagnostics - for _, inoDiag := range inoDiagnostics { - - log.Printf(prefix+"to IDE: publishDiagnostics(%s):", inoDiag.URI) - for _, diag := range inoDiag.Diagnostics { - log.Printf(prefix+"> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) - } - if err := handler.StdioConn.Notify(ctx, "textDocument/publishDiagnostics", inoDiag); err != nil { - return nil, err - } - } - return nil, err - - case *lsp.ApplyWorkspaceEditParams: - // "workspace/applyEdit" - p.Edit = *handler.cpp2inoWorkspaceEdit(&p.Edit) - } - if err != nil { - log.Println("From clangd: Method:", req.Method, "Error:", err) - return nil, err - } - - if params == nil { - // passthrough - log.Printf(prefix + "passing through message") - params = req.Params - } - - var result interface{} - if req.Notif { - log.Println(prefix + "to IDE") - err = handler.StdioConn.Notify(ctx, req.Method, params) - } else { - log.Println(prefix + "to IDE") - result, err = lsp.SendRequest(ctx, handler.StdioConn, req.Method, params) - } - return result, err -} - -func (handler *InoHandler) createClangdFormatterConfig(cppuri lsp.DocumentURI) (func(), error) { - // clangd looks for a .clang-format configuration file on the same directory - // pointed by the uri passed in the lsp command parameters. - // https://github.com/llvm/llvm-project/blob/64d06ed9c9e0389cd27545d2f6e20455a91d89b1/clang-tools-extra/clangd/ClangdLSPServer.cpp#L856-L868 - // https://github.com/llvm/llvm-project/blob/64d06ed9c9e0389cd27545d2f6e20455a91d89b1/clang-tools-extra/clangd/ClangdServer.cpp#L402-L404 - - config := `# See: https://releases.llvm.org/11.0.1/tools/clang/docs/ClangFormatStyleOptions.html ---- -Language: Cpp -# LLVM is the default style setting, used when a configuration option is not set here -BasedOnStyle: LLVM -AccessModifierOffset: -2 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveBitFields: false -AlignConsecutiveDeclarations: false -AlignConsecutiveMacros: false -AlignEscapedNewlines: DontAlign -AlignOperands: Align -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true -AllowAllConstructorInitializersOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: Always -AllowShortCaseLabelsOnASingleLine: true -AllowShortEnumsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: Always -AllowShortLambdasOnASingleLine: Empty -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: No -BinPackArguments: true -BinPackParameters: true -# Only used when "BreakBeforeBraces" set to "Custom" -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - #AfterObjCDeclaration: - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyRecord: false - SplitEmptyNamespace: false -# Java-specific -#BreakAfterJavaFieldAnnotations: -BreakBeforeBinaryOperators: NonAssignment -BreakBeforeBraces: Attach -BreakBeforeTernaryOperators: true -BreakConstructorInitializers: BeforeColon -BreakInheritanceList: BeforeColon -BreakStringLiterals: false -ColumnLimit: 0 -# "" matches none -CommentPragmas: "" -CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: true -ConstructorInitializerIndentWidth: 2 -ContinuationIndentWidth: 2 -Cpp11BracedListStyle: false -DeriveLineEnding: true -DerivePointerAlignment: true -DisableFormat: false -# Docs say "Do not use this in config files". The default (LLVM 11.0.1) is "false". -#ExperimentalAutoDetectBinPacking: -FixNamespaceComments: false -ForEachMacros: [] -IncludeBlocks: Preserve -IncludeCategories: [] -# "" matches none -IncludeIsMainRegex: "" -IncludeIsMainSourceRegex: "" -IndentCaseBlocks: true -IndentCaseLabels: true -IndentExternBlock: Indent -IndentGotoLabels: false -IndentPPDirectives: None -IndentWidth: 2 -IndentWrappedFunctionNames: false -InsertTrailingCommas: None -# Java-specific -#JavaImportGroups: -# JavaScript-specific -#JavaScriptQuotes: -#JavaScriptWrapImports -KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: "" -MacroBlockEnd: "" -# Set to a large number to effectively disable -MaxEmptyLinesToKeep: 100000 -NamespaceIndentation: None -NamespaceMacros: [] -# Objective C-specific -#ObjCBinPackProtocolList: -#ObjCBlockIndentWidth: -#ObjCBreakBeforeNestedBlockParam: -#ObjCSpaceAfterProperty: -#ObjCSpaceBeforeProtocolList -PenaltyBreakAssignment: 1 -PenaltyBreakBeforeFirstCallParameter: 1 -PenaltyBreakComment: 1 -PenaltyBreakFirstLessLess: 1 -PenaltyBreakString: 1 -PenaltyBreakTemplateDeclaration: 1 -PenaltyExcessCharacter: 1 -PenaltyReturnTypeOnItsOwnLine: 1 -# Used as a fallback if alignment style can't be detected from code (DerivePointerAlignment: true) -PointerAlignment: Right -RawStringFormats: [] -ReflowComments: false -SortIncludes: false -SortUsingDeclarations: false -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: true -SpaceBeforeSquareBrackets: false -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 2 -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInConditionalStatement: false -SpacesInContainerLiterals: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Auto -StatementMacros: [] -TabWidth: 2 -TypenameMacros: [] -# Default to LF if line endings can't be detected from the content (DeriveLineEnding). -UseCRLF: false -UseTab: Never -WhitespaceSensitiveMacros: [] -` - - try := func(conf *paths.Path) bool { - if c, err := conf.ReadFile(); err != nil { - log.Printf(" error reading custom formatter config file %s: %s", conf, err) - } else { - log.Printf(" using custom formatter config file %s", conf) - config = string(c) - } - return true - } - - if sketchFormatterConf := handler.sketchRoot.Join(".clang-format"); sketchFormatterConf.Exist() { - // If a custom config is present in the sketch folder, use that one - try(sketchFormatterConf) - } else if globalFormatterConf != nil && globalFormatterConf.Exist() { - // Otherwise if a global config file is present, use that one - try(globalFormatterConf) - } - - targetFile := cppuri.AsPath() - if targetFile.IsNotDir() { - targetFile = targetFile.Parent() - } - targetFile = targetFile.Join(".clang-format") - cleanup := func() { - targetFile.Remove() - log.Printf(" formatter config cleaned") - } - log.Printf(" writing formatter config in: %s", targetFile) - err := targetFile.WriteFile([]byte(config)) - return cleanup, err -} - -func (handler *InoHandler) showMessage(ctx context.Context, msgType lsp.MessageType, message string) { - defer streams.CatchAndLogPanic() - - params := lsp.ShowMessageParams{ - Type: msgType, - Message: message, - } - handler.StdioConn.Notify(ctx, "window/showMessage", ¶ms) -} - -func unknownURI(uri lsp.DocumentURI) error { - return errors.New("Document is not available: " + uri.String()) -} diff --git a/handler/syncer.go b/handler/syncer.go deleted file mode 100644 index 0a60cdc..0000000 --- a/handler/syncer.go +++ /dev/null @@ -1,23 +0,0 @@ -package handler - -import ( - "context" - - "github.com/sourcegraph/jsonrpc2" -) - -// AsyncHandler wraps a Handler such that each request is handled in its own goroutine. -type AsyncHandler struct { - handler jsonrpc2.Handler -} - -// Handle handles a request or notification -func (ah AsyncHandler) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) { - switch req.Method { - case // Request that should not be parallelized - "window/workDoneProgress/create", "$/progress": - ah.handler.Handle(ctx, conn, req) - default: // By default process all requests in parallel - go ah.handler.Handle(ctx, conn, req) - } -} diff --git a/handler/textutils/textutils.go b/handler/textutils/textutils.go deleted file mode 100644 index 22b0c91..0000000 --- a/handler/textutils/textutils.go +++ /dev/null @@ -1,125 +0,0 @@ -package textutils - -import ( - "fmt" - - "github.com/arduino/arduino-language-server/lsp" -) - -// ApplyLSPTextDocumentContentChangeEvent applies the LSP change in the given text -func ApplyLSPTextDocumentContentChangeEvent(textDoc *lsp.TextDocumentItem, changes []lsp.TextDocumentContentChangeEvent, version int) error { - newText := textDoc.Text - for _, change := range changes { - if t, err := ApplyTextChange(newText, *change.Range, change.Text); err == nil { - newText = t - } else { - return err - } - } - textDoc.Text = newText - textDoc.Version = version - return nil -} - -// ApplyTextChange replaces startingText substring specified by replaceRange with insertText -func ApplyTextChange(startingText string, replaceRange lsp.Range, insertText string) (res string, err error) { - start, err := GetOffset(startingText, replaceRange.Start) - if err != nil { - return "", err - } - end, err := GetOffset(startingText, replaceRange.End) - if err != nil { - return "", err - } - - return startingText[:start] + insertText + startingText[end:], nil -} - -// GetOffset computes the offset in the text expressed by the lsp.Position. -// Returns OutOfRangeError if the position is out of range. -func GetOffset(text string, pos lsp.Position) (int, error) { - // Find line - lineOffset, err := GetLineOffset(text, pos.Line) - if err != nil { - return -1, err - } - character := pos.Character - if character == 0 { - return lineOffset, nil - } - - // Find the character and return its offset within the text - var count = len(text[lineOffset:]) - for offset, c := range text[lineOffset:] { - if character == offset { - // We've found the character - return lineOffset + offset, nil - } - if c == '\n' { - // We've reached the end of line. LSP spec says we should default back to the line length. - // See https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#position - if character > offset { - return lineOffset + offset, nil - } - count = offset - break - } - } - if character > 0 { - // We've reached the end of the last line. Default to the text length (see above). - return len(text), nil - } - - // We haven't found the character in the text (character index was negative) - return -1, OutOfRangeError{"Character", count, character} -} - -// GetLineOffset finds the offset/position of the beginning of a line within the text. -// For example: -// text := "foo\nfoobar\nbaz" -// GetLineOffset(text, 0) == 0 -// GetLineOffset(text, 1) == 4 -// GetLineOffset(text, 2) == 11 -func GetLineOffset(text string, line int) (int, error) { - if line == 0 { - return 0, nil - } - - // Find the line and return its offset within the text - var count int - for offset, c := range text { - if c == '\n' { - count++ - if count == line { - return offset + 1, nil - } - } - } - - // We haven't found the line in the text - return -1, OutOfRangeError{"Line", count, line} -} - -// ExtractRange extract a piece of text from a text document given the range -func ExtractRange(text string, textRange lsp.Range) (string, error) { - start, err := GetOffset(text, textRange.Start) - if err != nil { - return "", err - } - end, err := GetOffset(text, textRange.End) - if err != nil { - return "", err - } - return text[start:end], nil -} - -// OutOfRangeError returned if one attempts to access text out of its range -type OutOfRangeError struct { - Type string - Max int - Req int -} - -func (oor OutOfRangeError) Error() string { - return fmt.Sprintf("%s access out of range: max=%d requested=%d", oor.Type, oor.Max, oor.Req) -} diff --git a/handler/textutils/textutils_test.go b/handler/textutils/textutils_test.go deleted file mode 100644 index c314760..0000000 --- a/handler/textutils/textutils_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package textutils - -import ( - "strings" - "testing" - - "github.com/arduino/arduino-language-server/lsp" -) - -func TestApplyTextChange(t *testing.T) { - tests := []struct { - InitialText string - Range lsp.Range - Insertion string - Expectation string - Err error - }{ - { - "foo\nbar\nbaz\n!", - lsp.Range{ - Start: lsp.Position{Line: 1, Character: 1}, - End: lsp.Position{Line: 2, Character: 2}, - }, - "i", - "foo\nbiz\n!", - nil, - }, - { - "foo\nbar\nbaz\n!", - lsp.Range{ - Start: lsp.Position{Line: 1, Character: 1}, - End: lsp.Position{Line: 1, Character: 2}, - }, - "ee", - "foo\nbeer\nbaz\n!", - nil, - }, - { - "foo\nbar\nbaz\n!", - lsp.Range{ - Start: lsp.Position{Line: 1, Character: 1}, - End: lsp.Position{Line: 1, Character: 1}, - }, - "eer from the st", - "foo\nbeer from the star\nbaz\n!", - nil, - }, - { - "foo\nbar\nbaz\n!", - lsp.Range{ - Start: lsp.Position{Line: 0, Character: 10}, - End: lsp.Position{Line: 2, Character: 20}, - }, - "i", - "fooi\n!", - nil, - }, - { - "foo\nbar\nbaz\n!", - lsp.Range{ - Start: lsp.Position{Line: 0, Character: 100}, - End: lsp.Position{Line: 2, Character: 0}, - }, - "i", - "fooibaz\n!", - nil, - }, - { - "foo\nbar\nbaz\n!", - lsp.Range{ - // out of range start offset - Start: lsp.Position{Line: 20, Character: 0}, - End: lsp.Position{Line: 2, Character: 0}, - }, - "i", - "", - OutOfRangeError{"Line", 3, 20}, - }, - { - "foo\nbar\nbaz\n!", - lsp.Range{ - // out of range end offset - Start: lsp.Position{Line: 0, Character: 0}, - End: lsp.Position{Line: 20, Character: 0}, - }, - "i", - "", - OutOfRangeError{"Line", 3, 20}, - }, - } - - for _, test := range tests { - initial := strings.ReplaceAll(test.InitialText, "\n", "\\n") - insertion := strings.ReplaceAll(test.Insertion, "\n", "\\n") - expectation := strings.ReplaceAll(test.Expectation, "\n", "\\n") - - t.Logf("applyTextChange(\"%s\", %v, \"%s\") == \"%s\"", initial, test.Range, insertion, expectation) - act, err := ApplyTextChange(test.InitialText, test.Range, test.Insertion) - if act != test.Expectation { - t.Errorf("applyTextChange(\"%s\", %v, \"%s\") != \"%s\", got \"%s\"", initial, test.Range, insertion, expectation, strings.ReplaceAll(act, "\n", "\\n")) - } - if err != test.Err { - t.Errorf("applyTextChange(\"%s\", %v, \"%s\") error != %v, got %v instead", initial, test.Range, insertion, test.Err, err) - } - } -} - -func TestGetOffset(t *testing.T) { - tests := []struct { - Text string - Line int - Char int - Exp int - Err error - }{ - {"foo\nfoobar\nbaz", 0, 0, 0, nil}, - {"foo\nfoobar\nbaz", 1, 0, 4, nil}, - {"foo\nfoobar\nbaz", 1, 3, 7, nil}, - {"foo\nba\nr\nbaz\n!", 3, 0, 9, nil}, - {"foo\nba\nr\nbaz\n!", 1, 10, 6, nil}, - {"foo\nba\nr\nbaz\n!", -1, 0, -1, OutOfRangeError{"Line", 4, -1}}, - {"foo\nba\nr\nbaz\n!", 1, -1, -1, OutOfRangeError{"Character", 2, -1}}, - {"foo\nba\nr\nbaz\n!", 4, 20, 14, nil}, - {"foo\nba\nr\nbaz!\n", 4, 0, 14, nil}, - } - - for _, test := range tests { - st := strings.Replace(test.Text, "\n", "\\n", -1) - - t.Logf("getOffset(\"%s\", {Line: %d, Character: %d}) == %d", st, test.Line, test.Char, test.Exp) - act, err := GetOffset(test.Text, lsp.Position{Line: test.Line, Character: test.Char}) - if act != test.Exp { - t.Errorf("getOffset(\"%s\", {Line: %d, Character: %d}) != %d, got %d instead", st, test.Line, test.Char, test.Exp, act) - } - if err != test.Err { - t.Errorf("getOffset(\"%s\", {Line: %d, Character: %d}) error != %v, got %v instead", st, test.Line, test.Char, test.Err, err) - } - } -} - -func TestGetLineOffset(t *testing.T) { - tests := []struct { - Text string - Line int - Exp int - Err error - }{ - {"foo\nfoobar\nbaz", 0, 0, nil}, - {"foo\nfoobar\nbaz", 1, 4, nil}, - {"foo\nfoobar\nbaz", 2, 11, nil}, - {"foo\nfoobar\nbaz", 3, -1, OutOfRangeError{"Line", 2, 3}}, - {"foo\nba\nr\nbaz\n!", 3, 9, nil}, - {"foo\nba\nr\nbaz\n!", -1, -1, OutOfRangeError{"Line", 4, -1}}, - {"foo\nba\nr\nbaz\n!", 20, -1, OutOfRangeError{"Line", 4, 20}}, - } - - for _, test := range tests { - st := strings.Replace(test.Text, "\n", "\\n", -1) - - t.Logf("getLineOffset(\"%s\", %d) == %d", st, test.Line, test.Exp) - act, err := GetLineOffset(test.Text, test.Line) - if act != test.Exp { - t.Errorf("getLineOffset(\"%s\", %d) != %d, got %d instead", st, test.Line, test.Exp, act) - } - if err != test.Err { - t.Errorf("getLineOffset(\"%s\", %d) error != %v, got %v instead", st, test.Line, test.Err, err) - } - } -} diff --git a/ls/builder.go b/ls/builder.go new file mode 100644 index 0000000..f4c2a47 --- /dev/null +++ b/ls/builder.go @@ -0,0 +1,317 @@ +package ls + +import ( + "bytes" + "context" + "fmt" + "io" + "runtime" + "strings" + "sync" + "time" + + "github.com/arduino/arduino-cli/arduino/builder" + "github.com/arduino/arduino-cli/arduino/libraries" + "github.com/arduino/arduino-cli/executils" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/arduino-language-server/sourcemapper" + "github.com/arduino/arduino-language-server/streams" + "github.com/arduino/go-paths-helper" + "github.com/fatih/color" + "github.com/pkg/errors" + "go.bug.st/json" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" + "google.golang.org/grpc" +) + +type SketchRebuilder struct { + ls *INOLanguageServer + trigger chan bool + cancel func() + mutex sync.Mutex +} + +func NewSketchBuilder(ls *INOLanguageServer) *SketchRebuilder { + res := &SketchRebuilder{ + trigger: make(chan bool, 1), + cancel: func() {}, + ls: ls, + } + go func() { + defer streams.CatchAndLogPanic() + res.rebuilderLoop() + }() + return res +} + +func (ls *INOLanguageServer) triggerRebuild() { + ls.sketchRebuilder.TriggerRebuild() +} + +func (r *SketchRebuilder) TriggerRebuild() { + r.mutex.Lock() + defer r.mutex.Unlock() + + r.cancel() // Stop possibly already running builds + select { + case r.trigger <- true: + default: + } +} + +func (r *SketchRebuilder) rebuilderLoop() { + logger := NewLSPFunctionLogger(color.HiMagentaString, "SKETCH REBUILD: ") + for { + <-r.trigger + + for { + // Concede a 200ms delay to accumulate bursts of changes + select { + case <-r.trigger: + continue + case <-time.After(time.Second): + } + break + } + + r.ls.progressHandler.Create("arduinoLanguageServerRebuild") + r.ls.progressHandler.Begin("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressBegin{Title: "Building sketch"}) + + ctx, cancel := context.WithCancel(context.Background()) + r.mutex.Lock() + logger.Logf("Sketch rebuild started") + r.cancel = cancel + r.mutex.Unlock() + + if err := r.doRebuild(ctx, logger); err != nil { + logger.Logf("Error: %s", err) + } + + cancel() + r.ls.progressHandler.End("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressEnd{Message: "done"}) + } +} + +func (r *SketchRebuilder) doRebuild(ctx context.Context, logger jsonrpc.FunctionLogger) error { + ls := r.ls + + if success, err := ls.generateBuildEnvironment(ctx, logger); err != nil { + return err + } else if !success { + return fmt.Errorf("build failed") + } + + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) + + // Check one last time if the process has been canceled + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + if err := ls.buildPath.Join("compile_commands.json").CopyTo(ls.compileCommandsDir.Join("compile_commands.json")); err != nil { + logger.Logf("ERROR: updating compile_commands: %s", err) + } + + if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { + oldVesrion := ls.sketchMapper.CppText.Version + ls.sketchMapper = sourcemapper.CreateInoMapper(cppContent) + ls.sketchMapper.CppText.Version = oldVesrion + 1 + ls.sketchMapper.DebugLogAll() + } else { + return errors.WithMessage(err, "reading generated cpp file from sketch") + } + + // Send didSave to notify clang that the source cpp is changed + logger.Logf("Sending 'didSave' notification to Clangd") + cppURI := lsp.NewDocumentURIFromPath(ls.buildSketchCpp) + didSaveParams := &lsp.DidSaveTextDocumentParams{ + TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, + } + if err := ls.Clangd.conn.TextDocumentDidSave(didSaveParams); err != nil { + logger.Logf("error reinitilizing clangd:", err) + return err + } + + // Send the full text to clang + logger.Logf("Sending full-text 'didChange' notification to Clangd") + didChangeParams := &lsp.DidChangeTextDocumentParams{ + TextDocument: lsp.VersionedTextDocumentIdentifier{ + TextDocumentIdentifier: lsp.TextDocumentIdentifier{URI: cppURI}, + Version: ls.sketchMapper.CppText.Version, + }, + ContentChanges: []lsp.TextDocumentContentChangeEvent{ + {Text: ls.sketchMapper.CppText.Text}, + }, + } + if err := ls.Clangd.conn.TextDocumentDidChange(didChangeParams); err != nil { + logger.Logf("error reinitilizing clangd:", err) + return err + } + + return nil +} + +func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logger jsonrpc.FunctionLogger) (bool, error) { + // Extract all build information from language server status + ls.readLock(logger, false) + sketchRoot := ls.sketchRoot + buildPath := ls.buildPath + config := ls.config + type overridesFile struct { + Overrides map[string]string `json:"overrides"` + } + data := overridesFile{Overrides: map[string]string{}} + for uri, trackedFile := range ls.trackedIdeDocs { + rel, err := paths.New(uri).RelFrom(sketchRoot) + if err != nil { + ls.readUnlock(logger) + return false, errors.WithMessage(err, "dumping tracked files") + } + data.Overrides[rel.String()] = trackedFile.Text + } + ls.readUnlock(logger) + + var success bool + if config.CliPath == nil { + // Establish a connection with the arduino-cli gRPC server + conn, err := grpc.Dial(config.CliDaemonAddress, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + return false, fmt.Errorf("error connecting to arduino-cli rpc server: %w", err) + } + defer conn.Close() + client := rpc.NewArduinoCoreServiceClient(conn) + + compileReq := &rpc.CompileRequest{ + Instance: &rpc.Instance{Id: int32(config.CliInstanceNumber)}, + Fqbn: config.Fqbn, + SketchPath: sketchRoot.String(), + SourceOverride: data.Overrides, + BuildPath: buildPath.String(), + CreateCompilationDatabaseOnly: true, + Verbose: true, + } + compileReqJson, _ := json.MarshalIndent(compileReq, "", " ") + logger.Logf("Running build with: %s", string(compileReqJson)) + + compRespStream, err := client.Compile(context.Background(), compileReq) + if err != nil { + return false, fmt.Errorf("error running compile: %w", err) + } + + // Loop and consume the server stream until all the operations are done. + stdout := "" + stderr := "" + for { + compResp, err := compRespStream.Recv() + if err == io.EOF { + success = true + logger.Logf("Compile successful!") + break + } + if err != nil { + logger.Logf("build stdout:") + logger.Logf(stdout) + logger.Logf("build stderr:") + logger.Logf(stderr) + return false, fmt.Errorf("error running compile: %w", err) + } + + if resp := compResp.GetOutStream(); resp != nil { + stdout += string(resp) + } + if resperr := compResp.GetErrStream(); resperr != nil { + stderr += string(resperr) + } + } + + } else { + + // Dump overrides into a temporary json file + for filename, override := range data.Overrides { + logger.Logf("Dumping %s override:\n%s", filename, override) + } + var overridesJSON *paths.Path + if jsonBytes, err := json.MarshalIndent(data, "", " "); err != nil { + return false, errors.WithMessage(err, "dumping tracked files") + } else if tmp, err := paths.WriteToTempFile(jsonBytes, nil, ""); err != nil { + return false, errors.WithMessage(err, "dumping tracked files") + } else { + overridesJSON = tmp + defer tmp.Remove() + } + + // Run arduino-cli to perform the build + args := []string{config.CliPath.String(), + "--config-file", config.CliConfigPath.String(), + "compile", + "--fqbn", config.Fqbn, + "--only-compilation-database", + "--source-override", overridesJSON.String(), + "--build-path", buildPath.String(), + "--format", "json", + //"--clean", + sketchRoot.String(), + } + cmd, err := executils.NewProcess(args...) + if err != nil { + return false, errors.Errorf("running %s: %s", strings.Join(args, " "), err) + } + cmdOutput := &bytes.Buffer{} + cmd.RedirectStdoutTo(cmdOutput) + cmd.SetDirFromPath(sketchRoot) + logger.Logf("running: %s", strings.Join(args, " ")) + if err := cmd.RunWithinContext(ctx); err != nil { + return false, errors.Errorf("running %s: %s", strings.Join(args, " "), err) + } + + // Currently those values are not used, keeping here for future improvements + type cmdBuilderRes struct { + BuildPath *paths.Path `json:"build_path"` + UsedLibraries []*libraries.Library + } + type cmdRes struct { + CompilerOut string `json:"compiler_out"` + CompilerErr string `json:"compiler_err"` + BuilderResult cmdBuilderRes `json:"builder_result"` + Success bool `json:"success"` + } + var res cmdRes + if err := json.Unmarshal(cmdOutput.Bytes(), &res); err != nil { + return false, errors.Errorf("parsing arduino-cli output: %s", err) + } + logger.Logf("arduino-cli output: %s", cmdOutput) + success = res.Success + } + + // TODO: do canonicalization directly in `arduino-cli` + canonicalizeCompileCommandsJSON(buildPath.Join("compile_commands.json")) + + return success, nil +} + +func canonicalizeCompileCommandsJSON(compileCommandsJSONPath *paths.Path) { + compileCommands, err := builder.LoadCompilationDatabase(compileCommandsJSONPath) + if err != nil { + panic("could not find compile_commands.json") + } + for i, cmd := range compileCommands.Contents { + if len(cmd.Arguments) == 0 { + panic("invalid empty argument field in compile_commands.json") + } + + // clangd requires full path to compiler (including extension .exe on Windows!) + compilerPath := paths.New(cmd.Arguments[0]).Canonical() + compiler := compilerPath.String() + if runtime.GOOS == "windows" && strings.ToLower(compilerPath.Ext()) != ".exe" { + compiler += ".exe" + } + compileCommands.Contents[i].Arguments[0] = compiler + } + + // Save back compile_commands.json with OS native file separator and extension + compileCommands.SaveToFile() +} diff --git a/ls/ls.go b/ls/ls.go new file mode 100644 index 0000000..2bc638c --- /dev/null +++ b/ls/ls.go @@ -0,0 +1,1523 @@ +package ls + +import ( + "bytes" + "context" + "fmt" + "io" + "log" + "os" + "strconv" + "strings" + "sync" + "time" + + "github.com/arduino/arduino-cli/executils" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/settings/v1" + "github.com/arduino/arduino-language-server/sourcemapper" + "github.com/arduino/arduino-language-server/streams" + "github.com/arduino/go-paths-helper" + "github.com/fatih/color" + "github.com/pkg/errors" + "go.bug.st/json" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" + "go.bug.st/lsp/textedits" + "google.golang.org/grpc" +) + +// INOLanguageServer is a JSON-RPC handler that delegates messages to clangd. +type INOLanguageServer struct { + config *Config + IDE *IDELSPServer + Clangd *ClangdLSPClient + + progressHandler *ProgressProxyHandler + closing chan bool + clangdStarted *sync.Cond + dataMux sync.RWMutex + compileCommandsDir *paths.Path + buildPath *paths.Path + buildSketchRoot *paths.Path + buildSketchCpp *paths.Path + sketchRoot *paths.Path + sketchName string + sketchMapper *sourcemapper.SketchMapper + sketchTrackedFilesCount int + trackedIdeDocs map[string]lsp.TextDocumentItem + ideInoDocsWithDiagnostics map[lsp.DocumentURI]bool + sketchRebuilder *SketchRebuilder +} + +// Config describes the language server configuration. +type Config struct { + Fqbn string + CliPath *paths.Path + CliConfigPath *paths.Path + ClangdPath *paths.Path + CliDaemonAddress string + CliInstanceNumber int + FormatterConf *paths.Path + EnableLogging bool +} + +var yellow = color.New(color.FgHiYellow) + +func (ls *INOLanguageServer) writeLock(logger jsonrpc.FunctionLogger, requireClangd bool) { + ls.dataMux.Lock() + logger.Logf(yellow.Sprintf("write-locked")) + if requireClangd && ls.Clangd == nil { + // if clangd is not started... + logger.Logf("(throttled: waiting for clangd)") + logger.Logf(yellow.Sprintf("unlocked (waiting clangd)")) + ls.clangdStarted.Wait() + logger.Logf(yellow.Sprintf("locked (waiting clangd)")) + + if ls.Clangd == nil { + logger.Logf("clangd startup failed: quitting Language server") + ls.Close() + os.Exit(2) + } + } +} + +func (ls *INOLanguageServer) writeUnlock(logger jsonrpc.FunctionLogger) { + logger.Logf(yellow.Sprintf("write-unlocked")) + ls.dataMux.Unlock() +} + +func (ls *INOLanguageServer) readLock(logger jsonrpc.FunctionLogger, requireClangd bool) { + ls.dataMux.RLock() + logger.Logf(yellow.Sprintf("read-locked")) + + for requireClangd && ls.Clangd == nil { + // if clangd is not started... + + // Release the read lock and acquire a write lock + // (this is required to wait on condition variable and restart clang). + logger.Logf(yellow.Sprintf("clang not started: read-unlocking...")) + ls.dataMux.RUnlock() + + ls.writeLock(logger, true) + ls.writeUnlock(logger) + + ls.dataMux.RLock() + logger.Logf(yellow.Sprintf("testing again if clang started: read-locked...")) + } +} + +func (ls *INOLanguageServer) readUnlock(logger jsonrpc.FunctionLogger) { + logger.Logf(yellow.Sprintf("read-unlocked")) + ls.dataMux.RUnlock() +} + +// NewINOLanguageServer creates and configures an Arduino Language Server. +func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, config *Config) *INOLanguageServer { + logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") + ls := &INOLanguageServer{ + trackedIdeDocs: map[string]lsp.TextDocumentItem{}, + ideInoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, + closing: make(chan bool), + config: config, + } + ls.clangdStarted = sync.NewCond(&ls.dataMux) + ls.sketchRebuilder = NewSketchBuilder(ls) + + if tmp, err := paths.MkTempDir("", "arduino-language-server"); err != nil { + log.Fatalf("Could not create temp folder: %s", err) + } else { + ls.compileCommandsDir = tmp.Canonical() + } + + if tmp, err := paths.MkTempDir("", "arduino-language-server"); err != nil { + log.Fatalf("Could not create temp folder: %s", err) + } else { + ls.buildPath = tmp.Canonical() + ls.buildSketchRoot = ls.buildPath.Join("sketch") + } + + logger.Logf("Initial board configuration: %s", ls.config.Fqbn) + logger.Logf("Language server build path: %s", ls.buildPath) + logger.Logf("Language server build sketch root: %s", ls.buildSketchRoot) + logger.Logf("Language server compile-commands: %s", ls.compileCommandsDir.Join("compile_commands.json")) + + ls.IDE = NewIDELSPServer(logger, stdin, stdout, ls) + ls.progressHandler = NewProgressProxy(ls.IDE.conn) + go func() { + defer streams.CatchAndLogPanic() + ls.IDE.Run() + logger.Logf("Lost connection with IDE!") + ls.Close() + }() + + return ls +} + +func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { + go func() { + defer streams.CatchAndLogPanic() + // Unlock goroutines waiting for clangd + defer ls.clangdStarted.Broadcast() + + logger := NewLSPFunctionLogger(color.HiCyanString, "INIT --- ") + logger.Logf("initializing workbench: %s", ideParams.RootURI) + + ls.sketchRoot = ideParams.RootURI.AsPath() + ls.sketchName = ls.sketchRoot.Base() + ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") + + if success, err := ls.generateBuildEnvironment(context.Background(), logger); err != nil { + logger.Logf("error starting clang: %s", err) + return + } else if !success { + logger.Logf("bootstrap build failed!") + return + } + + if err := ls.buildPath.Join("compile_commands.json").CopyTo(ls.compileCommandsDir.Join("compile_commands.json")); err != nil { + logger.Logf("ERROR: updating compile_commands: %s", err) + } + + if inoCppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { + ls.sketchMapper = sourcemapper.CreateInoMapper(inoCppContent) + ls.sketchMapper.CppText.Version = 1 + } else { + logger.Logf("error starting clang: reading generated cpp file from sketch: %s", err) + return + } + + // Retrieve data folder + dataFolder, err := ls.extractDataFolderFromArduinoCLI(logger) + if err != nil { + logger.Logf("error retrieving data folder from arduino-cli: %s", err) + return + } + + // Start clangd + ls.Clangd = NewClangdLSPClient(logger, dataFolder, ls) + go func() { + defer streams.CatchAndLogPanic() + ls.Clangd.Run() + logger.Logf("Lost connection with clangd!") + ls.Close() + }() + + // Send initialization command to clangd (1 sec. timeout) + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + clangInitializeParams := *ideParams + clangInitializeParams.RootPath = ls.buildSketchRoot.String() + clangInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) + if clangInitializeResult, clangErr, err := ls.Clangd.conn.Initialize(ctx, &clangInitializeParams); err != nil { + logger.Logf("error initilizing clangd: %v", err) + return + } else if clangErr != nil { + logger.Logf("error initilizing clangd: %v", clangErr.AsError()) + return + } else { + logger.Logf("clangd successfully started: %s", string(lsp.EncodeMessage(clangInitializeResult))) + } + + if err := ls.Clangd.conn.Initialized(&lsp.InitializedParams{}); err != nil { + logger.Logf("error sending initialized notification to clangd: %v", err) + return + } + + logger.Logf("Done initializing workbench") + }() + /* + Clang 12 capabilities: + + ✓ "textDocumentSync": { + ✓ "openClose": true, + ✓ "change": 2, (incremental) + ✓ "save": {} + ✓ }, + ✓ "completionProvider": { + ✓ "triggerCharacters": [ ".", "<", ">", ":", "\"", "/" ], + ✓ "allCommitCharacters": [ + ✓ " ", "\t","(", ")", "[", "]", "{", "}", "<", + ✓ ">", ":", ";", ",", "+", "-", "/", "*", "%", + ✓ "^", "&", "#", "?", ".", "=", "\"","'", "|" + ✓ ], + ✓ "completionItem": {} + ✓ }, + ✓ "hoverProvider": {}, + ✓ "signatureHelpProvider": { + ✓ "triggerCharacters": [ "(", "," ] + ✓ }, + ✓ "declarationProvider": {}, + ✓ "definitionProvider": {}, + ✓ "implementationProvider": {}, + ✓ "referencesProvider": {}, + ✓ "documentHighlightProvider": {}, + ✓ "documentSymbolProvider": {}, + ✓ "codeActionProvider": { + ✓ "codeActionKinds": [ "quickfix", "refactor", "info" ] + ✓ }, + ✓ "documentLinkProvider": {}, + ✓ "documentFormattingProvider": {}, + ✓ "documentRangeFormattingProvider": {}, + ✓ "documentOnTypeFormattingProvider": { + ✓ "firstTriggerCharacter": "\n" + ✓ }, + ✓ "renameProvider": { + ✓ "prepareProvider": true + ✓ }, + ✓ "executeCommandProvider": { + ✓ "commands": [ "clangd.applyFix", "clangd.applyTweak" ] + ✓ }, + ✓ "selectionRangeProvider": {}, + ✓ "callHierarchyProvider": {}, + ✓ "semanticTokensProvider": { + ✓ "legend": { + ✓ "tokenTypes": [ + ✓ "variable", "variable", "parameter", "function", "method", + ✓ "function", "property", "variable", "class", "enum", + ✓ "enumMember", "type", "dependent", "dependent", "namespace", + ✓ "typeParameter", "concept", "type", "macro", "comment" + ✓ ], + ✓ "tokenModifiers": [] + ✓ }, + ✓ "range": false, + ✓ "full": { + ✓ "delta": true + ✓ } + ✓ }, + ✓ "workspaceSymbolProvider": {} + */ + resp := &lsp.InitializeResult{ + Capabilities: lsp.ServerCapabilities{ + TextDocumentSync: &lsp.TextDocumentSyncOptions{ + OpenClose: true, + Change: lsp.TextDocumentSyncKindIncremental, + Save: &lsp.SaveOptions{ + IncludeText: true, + }, + }, + CompletionProvider: &lsp.CompletionOptions{ + TriggerCharacters: []string{".", "<", ">", ":", "\"", "/"}, + AllCommitCharacters: []string{ + " ", "\t", "(", ")", "[", "]", "{", "}", "<", ">", + ":", ";", ",", "+", "-", "/", "*", "%", "^", "&", + "#", "?", ".", "=", "\"", "'", "|"}, + ResolveProvider: false, + CompletionItem: &lsp.CompletionItemOptions{}, + }, + HoverProvider: &lsp.HoverOptions{}, + SignatureHelpProvider: &lsp.SignatureHelpOptions{ + TriggerCharacters: []string{"(", ","}, + }, + // DeclarationProvider: &lsp.DeclarationRegistrationOptions{}, + DefinitionProvider: &lsp.DefinitionOptions{}, + // ImplementationProvider: &lsp.ImplementationRegistrationOptions{}, + // ReferencesProvider: &lsp.ReferenceOptions{}, + DocumentHighlightProvider: &lsp.DocumentHighlightOptions{}, + DocumentSymbolProvider: &lsp.DocumentSymbolOptions{}, + CodeActionProvider: &lsp.CodeActionOptions{ + CodeActionKinds: []lsp.CodeActionKind{ + lsp.CodeActionKindQuickFix, + lsp.CodeActionKindRefactor, + "info", + }, + }, + // DocumentLinkProvider: &lsp.DocumentLinkOptions{ResolveProvider: false}, + DocumentFormattingProvider: &lsp.DocumentFormattingOptions{}, + DocumentRangeFormattingProvider: &lsp.DocumentRangeFormattingOptions{}, + // SelectionRangeProvider: &lsp.SelectionRangeRegistrationOptions{}, + DocumentOnTypeFormattingProvider: &lsp.DocumentOnTypeFormattingOptions{ + FirstTriggerCharacter: "\n", + }, + RenameProvider: &lsp.RenameOptions{ + // PrepareProvider: true, + }, + ExecuteCommandProvider: &lsp.ExecuteCommandOptions{ + Commands: []string{"clangd.applyFix", "clangd.applyTweak"}, + }, + // SelectionRangeProvider: &lsp.SelectionRangeOptions{}, + // CallHierarchyProvider: &lsp.CallHierarchyOptions{}, + // SemanticTokensProvider: &lsp.SemanticTokensOptions{ + // Legend: lsp.SemanticTokensLegend{ + // TokenTypes: []string{ + // "variable", "variable", "parameter", "function", "method", + // "function", "property", "variable", "class", "enum", + // "enumMember", "type", "dependent", "dependent", "namespace", + // "typeParameter", "concept", "type", "macro", "comment", + // }, + // TokenModifiers: []string{}, + // }, + // Range: false, + // Full: &lsp.SemantiTokenFullOptions{ + // Delta: true, + // }, + // }, + WorkspaceSymbolProvider: &lsp.WorkspaceSymbolOptions{}, + }, + ServerInfo: &lsp.InitializeResultServerInfo{ + Name: "arduino-language-server", + Version: "0.5.0-beta", + }, + } + logger.Logf("initialization parameters: %s", string(lsp.EncodeMessage(resp))) + return resp, nil +} + +func (ls *INOLanguageServer) ShutdownReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger) *jsonrpc.ResponseError { + _, _ = ls.Clangd.conn.Shutdown(context.Background()) + return nil +} + +func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.CompletionParams) (*lsp.CompletionList, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) + + clangTextDocPositionParams, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + clangParams := &lsp.CompletionParams{ + TextDocumentPositionParams: clangTextDocPositionParams, + Context: ideParams.Context, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + PartialResultParams: ideParams.PartialResultParams, + } + + clangCompletionList, clangErr, err := ls.Clangd.conn.TextDocumentCompletion(ctx, clangParams) + if err != nil { + logger.Logf("clangd connection error: %v", err) + ls.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + ideCompletionList := &lsp.CompletionList{ + IsIncomplete: clangCompletionList.IsIncomplete, + } + for _, clangItem := range clangCompletionList.Items { + if strings.HasPrefix(clangItem.InsertText, "_") { + // XXX: Should be really ignored? + continue + } + + var ideTextEdit *lsp.TextEdit + if clangItem.TextEdit != nil { + if ideURI, _ideTextEdit, isPreprocessed, err := ls.cpp2inoTextEdit(logger, clangParams.TextDocument.URI, *clangItem.TextEdit); err != nil { + logger.Logf("Error converting textedit: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } else if ideURI != ideParams.TextDocument.URI || isPreprocessed { + err := fmt.Errorf("text edit is in preprocessed section or is mapped to another file") + logger.Logf("Error converting textedit: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } else { + ideTextEdit = &_ideTextEdit + } + } + var ideAdditionalTextEdits []lsp.TextEdit + if len(clangItem.AdditionalTextEdits) > 0 { + _ideAdditionalTextEdits, err := ls.cland2IdeTextEdits(logger, clangParams.TextDocument.URI, clangItem.AdditionalTextEdits) + if err != nil { + logger.Logf("Error converting textedit: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + ideAdditionalTextEdits = _ideAdditionalTextEdits[ideParams.TextDocument.URI] + } + + var ideCommand *lsp.Command + if clangItem.Command != nil { + c := ls.clang2IdeCommand(logger, *clangItem.Command) + if c == nil { + continue // Skit item with unsupported command convertion + } + ideCommand = c + } + + ideCompletionList.Items = append(ideCompletionList.Items, lsp.CompletionItem{ + Label: clangItem.Label, + LabelDetails: clangItem.LabelDetails, + Kind: clangItem.Kind, + Tags: clangItem.Tags, + Detail: clangItem.Detail, + Documentation: clangItem.Documentation, + Deprecated: clangItem.Deprecated, + Preselect: clangItem.Preselect, + SortText: clangItem.SortText, + FilterText: clangItem.FilterText, + InsertText: clangItem.InsertText, + InsertTextFormat: clangItem.InsertTextFormat, + InsertTextMode: clangItem.InsertTextMode, + CommitCharacters: clangItem.CommitCharacters, + Data: clangItem.Data, + Command: ideCommand, + TextEdit: ideTextEdit, + AdditionalTextEdits: ideAdditionalTextEdits, + }) + } + logger.Logf("<-- completion(%d items)", len(ideCompletionList.Items)) + return ideCompletionList, nil +} + +func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.HoverParams) (*lsp.Hover, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) + + clangTextDocPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + clangParams := &lsp.HoverParams{ + TextDocumentPositionParams: clangTextDocPosition, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + } + clangResp, clangErr, err := ls.Clangd.conn.TextDocumentHover(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + if clangResp == nil { + logger.Logf("null response") + return nil, nil + } + + var ideRange *lsp.Range + if clangResp.Range != nil { + _, r, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, clangParams.TextDocument.URI, *clangResp.Range) + if err != nil { + logger.Logf("error during range conversion: %v", err) + ls.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if inPreprocessed { + return nil, nil + } + ideRange = &r + } + ideResp := lsp.Hover{ + Contents: clangResp.Contents, + Range: ideRange, + } + logger.Logf("Hover content: %s", strconv.Quote(ideResp.Contents.Value)) + return &ideResp, nil +} + +func (ls *INOLanguageServer) TextDocumentSignatureHelpReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.SignatureHelpParams) (*lsp.SignatureHelp, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) + + clangTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + clangParams := &lsp.SignatureHelpParams{ + TextDocumentPositionParams: clangTextDocumentPosition, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + Context: ideParams.Context, + } + clangSignatureHelp, clangErr, err := ls.Clangd.conn.TextDocumentSignatureHelp(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + // No need to convert back to inoSignatureHelp + ideSignatureHelp := clangSignatureHelp + return ideSignatureHelp, nil +} + +func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) + + clangTextDocPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + if err != nil { + logger.Logf("Error: %s", err) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + clangParams := &lsp.DefinitionParams{ + TextDocumentPositionParams: clangTextDocPosition, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + PartialResultParams: ideParams.PartialResultParams, + } + clangLocations, clangLocationLinks, clangErr, err := ls.Clangd.conn.TextDocumentDefinition(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + var ideLocations []lsp.Location + if clangLocations != nil { + ideLocations, err = ls.clang2IdeLocationsArray(logger, clangLocations) + if err != nil { + logger.Logf("Error: %v", err) + ls.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + } + + var ideLocationLinks []lsp.LocationLink + if clangLocationLinks != nil { + panic("unimplemented") + } + + return ideLocations, ideLocationLinks, nil +} + +func (ls *INOLanguageServer) TextDocumentTypeDefinitionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + // XXX: This capability is not advertised in the initialization message (clangd + // does not advetise it either, so maybe we should just not implement it) + ls.readLock(logger, true) + defer ls.readUnlock(logger) + + cppTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + if err != nil { + logger.Logf("Error: %s", err) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + clangParams := &lsp.TypeDefinitionParams{ + TextDocumentPositionParams: cppTextDocumentPosition, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + PartialResultParams: ideParams.PartialResultParams, + } + clangLocations, clangLocationLinks, clangErr, err := ls.Clangd.conn.TextDocumentTypeDefinition(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + var ideLocations []lsp.Location + if clangLocations != nil { + ideLocations, err = ls.clang2IdeLocationsArray(logger, clangLocations) + if err != nil { + logger.Logf("Error: %v", err) + ls.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + } + + var ideLocationLinks []lsp.LocationLink + if clangLocationLinks != nil { + panic("unimplemented") + } + + return ideLocations, ideLocationLinks, nil +} + +func (ls *INOLanguageServer) TextDocumentImplementationReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.ImplementationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) + + clangTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + if err != nil { + logger.Logf("Error: %s", err) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + clangParams := &lsp.ImplementationParams{ + TextDocumentPositionParams: clangTextDocumentPosition, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + PartialResultParams: ideParams.PartialResultParams, + } + clangLocations, clangLocationLinks, clangErr, err := ls.Clangd.conn.TextDocumentImplementation(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + var ideLocations []lsp.Location + if clangLocations != nil { + ideLocations, err = ls.clang2IdeLocationsArray(logger, clangLocations) + if err != nil { + logger.Logf("Error: %v", err) + ls.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + } + + var inoLocationLinks []lsp.LocationLink + if clangLocationLinks != nil { + panic("unimplemented") + } + + return ideLocations, inoLocationLinks, nil +} + +func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) + + clangTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + if err != nil { + logger.Logf("ERROR: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + clangURI := clangTextDocumentPosition.TextDocument.URI + + clangParams := &lsp.DocumentHighlightParams{ + TextDocumentPositionParams: clangTextDocumentPosition, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + PartialResultParams: ideParams.PartialResultParams, + } + clangHighlights, clangErr, err := ls.Clangd.conn.TextDocumentDocumentHighlight(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication ERROR: %v", err) + ls.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response ERROR: %v", clangErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + if clangHighlights == nil { + logger.Logf("null response from clangd") + return nil, nil + } + + ideHighlights := []lsp.DocumentHighlight{} + for _, clangHighlight := range clangHighlights { + ideHighlight, inPreprocessed, err := ls.clang2IdeDocumentHighlight(logger, clangHighlight, clangURI) + if inPreprocessed { + continue + } + if err != nil { + logger.Logf("ERROR converting highlight %s:%s: %s", clangURI, clangHighlight.Range, err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + ideHighlights = append(ideHighlights, ideHighlight) + } + return ideHighlights, nil +} + +func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, []lsp.SymbolInformation, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) + + // Convert request for clang + clangTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideParams.TextDocument) + if err != nil { + logger.Logf("Error: %s", err) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + // Send request to clang + clangParams := &lsp.DocumentSymbolParams{ + TextDocument: clangTextDocument, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + PartialResultParams: ideParams.PartialResultParams, + } + clangDocSymbols, clangSymbolsInformation, clangErr, err := ls.Clangd.conn.TextDocumentDocumentSymbol(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + // Convert response for IDE + var ideDocSymbols []lsp.DocumentSymbol + if clangDocSymbols != nil { + if s, err := ls.clang2IdeDocumentSymbols(logger, clangDocSymbols, clangParams.TextDocument.URI, ideParams.TextDocument.URI); err != nil { + logger.Logf("Error: %s", err) + ls.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } else { + ideDocSymbols = s + } + } + var ideSymbolsInformation []lsp.SymbolInformation + if clangSymbolsInformation != nil { + ideSymbolsInformation = ls.clang2IdeSymbolsInformation(logger, clangSymbolsInformation) + } + return ideDocSymbols, ideSymbolsInformation, nil +} + +func (ls *INOLanguageServer) TextDocumentCodeActionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.CodeActionParams) ([]lsp.CommandOrCodeAction, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) + + ideTextDocument := ideParams.TextDocument + ideURI := ideTextDocument.URI + logger.Logf("--> codeAction(%s:%s)", ideTextDocument, ideParams.Range.Start) + + clangURI, clangRange, err := ls.ide2ClangRange(logger, ideURI, ideParams.Range) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + clangContext, err := ls.ide2ClangCodeActionContext(logger, ideURI, ideParams.Context) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + clangParams := &lsp.CodeActionParams{ + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + PartialResultParams: ideParams.PartialResultParams, + TextDocument: lsp.TextDocumentIdentifier{URI: clangURI}, + Range: clangRange, + Context: clangContext, + } + logger.Logf(" --> codeAction(%s:%s)", clangParams.TextDocument, ideParams.Range.Start) + + clangCommandsOrCodeActions, clangErr, err := ls.Clangd.conn.TextDocumentCodeAction(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + // TODO: Create a function for this one? + ideCommandsOrCodeActions := []lsp.CommandOrCodeAction{} + if clangCommandsOrCodeActions != nil { + return ideCommandsOrCodeActions, nil + } + logger.Logf(" <-- codeAction(%d elements)", len(clangCommandsOrCodeActions)) + for _, clangItem := range clangCommandsOrCodeActions { + ideItem := lsp.CommandOrCodeAction{} + switch i := clangItem.Get().(type) { + case lsp.Command: + logger.Logf(" > Command: %s", i.Title) + ideCommand := ls.clang2IdeCommand(logger, i) + if ideCommand == nil { + continue // Skip unsupported command + } + ideItem.Set(*ideCommand) + case lsp.CodeAction: + logger.Logf(" > CodeAction: %s", i.Title) + ideCodeAction := ls.clang2IdeCodeAction(logger, i, ideURI) + if ideCodeAction == nil { + continue // Skip unsupported code action + } + ideItem.Set(*ideCodeAction) + } + ideCommandsOrCodeActions = append(ideCommandsOrCodeActions, ideItem) + } + logger.Logf("<-- codeAction(%d elements)", len(ideCommandsOrCodeActions)) + return ideCommandsOrCodeActions, nil +} + +func (ls *INOLanguageServer) TextDocumentFormattingReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) + + ideTextDocument := ideParams.TextDocument + ideURI := ideTextDocument.URI + + clangTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + clangURI := clangTextDocument.URI + + if cleanup, err := ls.createClangdFormatterConfig(logger, clangURI); err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } else { + defer cleanup() + } + + clangParams := &lsp.DocumentFormattingParams{ + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + Options: ideParams.Options, + TextDocument: clangTextDocument, + } + clangEdits, clangErr, err := ls.Clangd.conn.TextDocumentFormatting(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + if clangEdits == nil { + return nil, nil + } + + ideEdits, err := ls.cland2IdeTextEdits(logger, clangURI, clangEdits) + if err != nil { + logger.Logf("ERROR converting textEdits: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + // Edits may span over multiple .ino files, filter only the edits relative to the currently displayed file + if inoEdits, ok := ideEdits[ideURI]; !ok { + return []lsp.TextEdit{}, nil + } else { + return inoEdits, nil + } +} + +func (ls *INOLanguageServer) TextDocumentRangeFormattingReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) + + ideURI := ideParams.TextDocument.URI + clangURI, clangRange, err := ls.ide2ClangRange(logger, ideURI, ideParams.Range) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + clangParams := &lsp.DocumentRangeFormattingParams{ + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + Options: ideParams.Options, + TextDocument: lsp.TextDocumentIdentifier{URI: clangURI}, + Range: clangRange, + } + + if cleanup, e := ls.createClangdFormatterConfig(logger, clangURI); e != nil { + logger.Logf("cannot create formatter config file: %v", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } else { + defer cleanup() + } + + clangEdits, clangErr, err := ls.Clangd.conn.TextDocumentRangeFormatting(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + if clangEdits == nil { + return nil, nil + } + + sketchEdits, err := ls.cland2IdeTextEdits(logger, clangURI, clangEdits) + if err != nil { + logger.Logf("ERROR converting textEdits: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + // Edits may span over multiple .ino files, filter only the edits relative to the currently displayed file + if inoEdits, ok := sketchEdits[ideURI]; !ok { + return []lsp.TextEdit{}, nil + } else { + return inoEdits, nil + } +} + +func (ls *INOLanguageServer) InitializedNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.InitializedParams) { + logger.Logf("Notification is not propagated to clangd") +} + +func (ls *INOLanguageServer) ExitNotifFromIDE(logger jsonrpc.FunctionLogger) { + ls.Clangd.conn.Exit() + logger.Logf("Arduino Language Server is shutting down.") + os.Exit(0) +} + +func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.FunctionLogger, ideParam *lsp.DidOpenTextDocumentParams) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) + + ls.triggerRebuild() + + // Add the TextDocumentItem in the tracked files list + ideTextDocItem := ideParam.TextDocument + ls.trackedIdeDocs[ideTextDocItem.URI.AsPath().String()] = ideTextDocItem + + // If we are tracking a .ino... + if ideTextDocItem.URI.Ext() == ".ino" { + ls.sketchTrackedFilesCount++ + logger.Logf("Increasing .ino tracked files count to %d", ls.sketchTrackedFilesCount) + + // Notify clangd that sketchCpp has been opened only once + if ls.sketchTrackedFilesCount != 1 { + logger.Logf("Clang already notified, do not notify it anymore") + return + } + } + + clangURI, _, err := ls.ide2ClangDocumentURI(logger, ideTextDocItem.URI) + if err != nil { + logger.Logf("Error: %s", err) + return + } + clangTextDocItem := lsp.TextDocumentItem{ + URI: clangURI, + } + if ls.clangURIRefersToIno(clangURI) { + clangTextDocItem.LanguageID = "cpp" + clangTextDocItem.Text = ls.sketchMapper.CppText.Text + clangTextDocItem.Version = ls.sketchMapper.CppText.Version + } else { + clangText, err := clangURI.AsPath().ReadFile() + if err != nil { + logger.Logf("Error opening sketch file %s: %s", clangURI.AsPath(), err) + } + clangTextDocItem.LanguageID = ideTextDocItem.LanguageID + clangTextDocItem.Version = ideTextDocItem.Version + clangTextDocItem.Text = string(clangText) + } + + if err := ls.Clangd.conn.TextDocumentDidOpen(&lsp.DidOpenTextDocumentParams{ + TextDocument: clangTextDocItem, + }); err != nil { + // Exit the process and trigger a restart by the client in case of a severe error + logger.Logf("Error sending notification to clangd server: %v", err) + logger.Logf("Please restart the language server.") + ls.Close() + } +} + +func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.DidChangeTextDocumentParams) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) + + ls.triggerRebuild() + + logger.Logf("didChange(%s)", ideParams.TextDocument) + for _, change := range ideParams.ContentChanges { + logger.Logf(" > %s", change) + } + + // Clear all RangeLengths: it's a deprecated field and if the byte count is wrong the + // source text file will be unloaded from clangd without notice, leading to a "non-added + // document" error for all subsequent requests. + // https://github.com/clangd/clangd/issues/717#issuecomment-793220007 + for i := range ideParams.ContentChanges { + ideParams.ContentChanges[i].RangeLength = nil + } + + ideTextDocIdentifier := ideParams.TextDocument + + // Apply the change to the tracked sketch file. + trackedIdeDocID := ideTextDocIdentifier.URI.AsPath().String() + if doc, ok := ls.trackedIdeDocs[trackedIdeDocID]; !ok { + logger.Logf("Error: %s", &UnknownURI{ideTextDocIdentifier.URI}) + return + } else if updatedDoc, err := textedits.ApplyLSPTextDocumentContentChangeEvent(doc, ideParams); err != nil { + logger.Logf("Error: %s", err) + return + } else { + ls.trackedIdeDocs[trackedIdeDocID] = updatedDoc + logger.Logf("-----Tracked SKETCH file-----\n" + updatedDoc.Text + "\n-----------------------------") + } + + clangChanges := []lsp.TextDocumentContentChangeEvent{} + var clangURI *lsp.DocumentURI + var clangParams *lsp.DidChangeTextDocumentParams + for _, ideChange := range ideParams.ContentChanges { + if ideChange.Range == nil { + panic("full-text change not implemented") + } + + clangRangeURI, clangRange, err := ls.ide2ClangRange(logger, ideTextDocIdentifier.URI, *ideChange.Range) + if err != nil { + logger.Logf("Error: %s", err) + return + } + + // all changes should refer to the same URI + if clangURI == nil { + clangURI = &clangRangeURI + } else if *clangURI != clangRangeURI { + logger.Logf("Error: change maps to %s URI, but %s was expected", clangRangeURI, *clangURI) + return + } + + // If we are applying changes to a .ino, update the sketchmapper + if ideTextDocIdentifier.URI.Ext() == ".ino" { + _ = ls.sketchMapper.ApplyTextChange(ideTextDocIdentifier.URI, ideChange) + } + + clangChanges = append(clangChanges, lsp.TextDocumentContentChangeEvent{ + Range: &clangRange, + RangeLength: ideChange.RangeLength, + Text: ideChange.Text, + }) + } + + clangVersion := ideTextDocIdentifier.Version + if ideTextDocIdentifier.URI.Ext() == ".ino" { + // If changes are applied to a .ino file we increment the global .ino.cpp versioning + // for each increment of the single .ino file. + clangVersion = ls.sketchMapper.CppText.Version + ls.sketchMapper.DebugLogAll() + } + + // build a cpp equivalent didChange request + clangParams = &lsp.DidChangeTextDocumentParams{ + TextDocument: lsp.VersionedTextDocumentIdentifier{ + TextDocumentIdentifier: lsp.TextDocumentIdentifier{URI: *clangURI}, + Version: clangVersion, + }, + ContentChanges: clangChanges, + } + + logger.Logf("to Clang: didChange(%s)", clangParams.TextDocument) + for _, change := range clangParams.ContentChanges { + logger.Logf(" > %s", change) + } + if err := ls.Clangd.conn.TextDocumentDidChange(clangParams); err != nil { + logger.Logf("Connection error with clangd server: %v", err) + logger.Logf("Please restart the language server.") + ls.Close() + } +} + +func (ls *INOLanguageServer) TextDocumentDidSaveNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.DidSaveTextDocumentParams) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) + + // clangd looks in the build directory (where a copy of the preprocessed sketch resides) + // so we will not forward notification on saves in the sketch folder. + logger.Logf("notification is not forwarded to clang") + + ls.triggerRebuild() +} + +func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.DidCloseTextDocumentParams) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) + + ls.triggerRebuild() + + inoIdentifier := ideParams.TextDocument + if _, exist := ls.trackedIdeDocs[inoIdentifier.URI.AsPath().String()]; exist { + delete(ls.trackedIdeDocs, inoIdentifier.URI.AsPath().String()) + } else { + logger.Logf("didClose of untracked document: %s", inoIdentifier.URI) + return + } + + // If we are tracking a .ino... + if inoIdentifier.URI.Ext() == ".ino" { + ls.sketchTrackedFilesCount-- + logger.Logf("decreasing .ino tracked files count: %d", ls.sketchTrackedFilesCount) + + // notify clang that sketch.cpp.ino has been closed only once all .ino are closed + if ls.sketchTrackedFilesCount != 0 { + logger.Logf("--X Notification is not propagated to clangd") + return + } + } + + clangIdentifier, err := ls.ide2ClangTextDocumentIdentifier(logger, inoIdentifier) + if err != nil { + logger.Logf("Error: %s", err) + } + clangParams := &lsp.DidCloseTextDocumentParams{ + TextDocument: clangIdentifier, + } + + logger.Logf("--> didClose(%s)", clangParams.TextDocument) + if err := ls.Clangd.conn.TextDocumentDidClose(clangParams); err != nil { + // Exit the process and trigger a restart by the client in case of a severe error + logger.Logf("Error sending notification to clangd server: %v", err) + logger.Logf("Please restart the language server.") + ls.Close() + } +} + +func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.FunctionLogger, clangParams *lsp.PublishDiagnosticsParams) { + ls.readLock(logger, false) + defer ls.readUnlock(logger) + + logger.Logf("%s (%d diagnostics):", clangParams.URI, len(clangParams.Diagnostics)) + for _, diag := range clangParams.Diagnostics { + logger.Logf(" > %s - %s: %s", diag.Range.Start, diag.Severity, string(diag.Code)) + } + + // the diagnostics on sketch.cpp.ino once mapped into their + // .ino counter parts may span over multiple .ino files... + allIdeParams, err := ls.clang2IdeDiagnostics(logger, clangParams) + if err != nil { + logger.Logf("Error converting diagnostics to .ino: %s", err) + return + } + + // If the incoming diagnostics are from sketch.cpp.ino then... + if ls.clangURIRefersToIno(clangParams.URI) { + // ...add all the new diagnostics... + for ideInoURI := range allIdeParams { + ls.ideInoDocsWithDiagnostics[ideInoURI] = true + } + + // .. and cleanup all previouse diagnostics that are no longer valid... + for ideInoURI := range ls.ideInoDocsWithDiagnostics { + if _, ok := allIdeParams[ideInoURI]; ok { + continue + } + allIdeParams[ideInoURI] = &lsp.PublishDiagnosticsParams{ + URI: ideInoURI, + Diagnostics: []lsp.Diagnostic{}, + } + delete(ls.ideInoDocsWithDiagnostics, ideInoURI) + } + } + + // Push back to IDE the converted diagnostics + logger.Logf("diagnostics to IDE:") + for _, ideParams := range allIdeParams { + logger.Logf(" - %s (%d diagnostics):", ideParams.URI, len(ideParams.Diagnostics)) + for _, diag := range ideParams.Diagnostics { + logger.Logf(" > %s - %s: %s", diag.Range.Start, diag.Severity, diag.Code) + } + if err := ls.IDE.conn.TextDocumentPublishDiagnostics(ideParams); err != nil { + logger.Logf("Error sending diagnostics to IDE: %s", err) + return + } + } +} + +func (ls *INOLanguageServer) TextDocumentRenameReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.RenameParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + ls.writeLock(logger, false) + defer ls.writeUnlock(logger) + + clangTextDocPositionParams, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + clangParams := &lsp.RenameParams{ + TextDocumentPositionParams: clangTextDocPositionParams, + NewName: ideParams.NewName, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + } + clangWorkspaceEdit, clangErr, err := ls.Clangd.conn.TextDocumentRename(ctx, clangParams) + if err != nil { + logger.Logf("clangd communication error: %v", err) + ls.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if clangErr != nil { + logger.Logf("clangd response error: %v", clangErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + + ideWorkspaceEdit, err := ls.clang2IdeWorkspaceEdit(logger, clangWorkspaceEdit) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + // Check if all edits belongs to the sketch + for ideURI := range ideWorkspaceEdit.Changes { + if !ls.ideURIIsPartOfTheSketch(ideURI) { + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInvalidParams, Message: "Could not rename symbol, it requires changes outside the sketch."} + } + } + return ideWorkspaceEdit, nil +} + +func (ls *INOLanguageServer) ideURIIsPartOfTheSketch(ideURI lsp.DocumentURI) bool { + res, _ := ideURI.AsPath().IsInsideDir(ls.sketchRoot) + return res +} + +func (ls *INOLanguageServer) ProgressNotifFromClangd(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { + var token string + if err := json.Unmarshal(progress.Token, &token); err != nil { + logger.Logf("error decoding progess token: %s", err) + return + } + switch value := progress.TryToDecodeWellKnownValues().(type) { + case lsp.WorkDoneProgressBegin: + logger.Logf("%s %s", token, value) + ls.progressHandler.Begin(token, &value) + case lsp.WorkDoneProgressReport: + logger.Logf("%s %s", token, value) + ls.progressHandler.Report(token, &value) + case lsp.WorkDoneProgressEnd: + logger.Logf("%s %s", token, value) + ls.progressHandler.End(token, &value) + default: + logger.Logf("error unsupported $/progress: " + string(progress.Value)) + } +} + +func (ls *INOLanguageServer) WindowWorkDoneProgressCreateReqFromClangd(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCreateParams) *jsonrpc.ResponseError { + var token string + if err := json.Unmarshal(params.Token, &token); err != nil { + logger.Logf("error decoding progress token: %s", err) + return &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + ls.progressHandler.Create(token) + return nil +} + +func (ls *INOLanguageServer) SetTraceNotifFromIDE(logger jsonrpc.FunctionLogger, params *lsp.SetTraceParams) { + logger.Logf("Notification level set to: %s", params.Value) + ls.Clangd.conn.SetTrace(params) +} + +// Close closes all the json-rpc connections and clean-up temp folders. +func (ls *INOLanguageServer) Close() { + if ls.Clangd != nil { + ls.Clangd.Close() + ls.Clangd = nil + } + if ls.closing != nil { + close(ls.closing) + ls.closing = nil + } + if ls.buildPath != nil { + ls.buildPath.RemoveAll() + } + if ls.compileCommandsDir != nil { + ls.compileCommandsDir.RemoveAll() + } +} + +// CloseNotify returns a channel that is closed when the InoHandler is closed +func (ls *INOLanguageServer) CloseNotify() <-chan bool { + return ls.closing +} + +// CleanUp performs cleanup of the workspace and temp files create by the language server +func (ls *INOLanguageServer) CleanUp() { + if ls.buildPath != nil { + ls.buildPath.RemoveAll() + ls.buildPath = nil + } +} + +func (ls *INOLanguageServer) extractDataFolderFromArduinoCLI(logger jsonrpc.FunctionLogger) (*paths.Path, error) { + var dataDir string + if ls.config.CliPath == nil { + // Establish a connection with the arduino-cli gRPC server + conn, err := grpc.Dial(ls.config.CliDaemonAddress, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + return nil, fmt.Errorf("error connecting to arduino-cli rpc server: %w", err) + } + defer conn.Close() + client := rpc.NewSettingsServiceClient(conn) + + resp, err := client.GetValue(context.Background(), &rpc.GetValueRequest{ + Key: "directories.data", + }) + if err != nil { + return nil, fmt.Errorf("error getting arduino data dir: %w", err) + } + if err := json.Unmarshal([]byte(resp.JsonData), &dataDir); err != nil { + return nil, fmt.Errorf("error getting arduino data dir: %w", err) + } + logger.Logf("Arduino Data Dir -> %s", dataDir) + } else { + args := []string{ls.config.CliPath.String(), + "--config-file", ls.config.CliConfigPath.String(), + "config", + "dump", + "--format", "json", + } + cmd, err := executils.NewProcess(args...) + if err != nil { + return nil, errors.Errorf("running %s: %s", strings.Join(args, " "), err) + } + cmdOutput := &bytes.Buffer{} + cmd.RedirectStdoutTo(cmdOutput) + logger.Logf("running: %s", strings.Join(args, " ")) + if err := cmd.Run(); err != nil { + return nil, errors.Errorf("running %s: %s", strings.Join(args, " "), err) + } + + type cmdRes struct { + Directories struct { + Data string `json:"data"` + } `json:"directories"` + } + var res cmdRes + if err := json.Unmarshal(cmdOutput.Bytes(), &res); err != nil { + return nil, errors.Errorf("parsing arduino-cli output: %s", err) + } + // Return only the build path + logger.Logf("Arduino Data Dir -> %s", res.Directories.Data) + dataDir = res.Directories.Data + } + + dataDirPath := paths.New(dataDir) + return dataDirPath.Canonical(), nil +} + +func (ls *INOLanguageServer) clang2IdeCodeAction(logger jsonrpc.FunctionLogger, clangCodeAction lsp.CodeAction, origIdeURI lsp.DocumentURI) *lsp.CodeAction { + ideCodeAction := &lsp.CodeAction{ + Title: clangCodeAction.Title, + Kind: clangCodeAction.Kind, + Diagnostics: clangCodeAction.Diagnostics, + IsPreferred: clangCodeAction.IsPreferred, + Disabled: clangCodeAction.Disabled, + Edit: ls.cpp2inoWorkspaceEdit(logger, clangCodeAction.Edit), + } + if clangCodeAction.Command != nil { + inoCommand := ls.clang2IdeCommand(logger, *clangCodeAction.Command) + if inoCommand == nil { + return nil + } + ideCodeAction.Command = inoCommand + } + if origIdeURI.Ext() == ".ino" { + for i, diag := range ideCodeAction.Diagnostics { + _, ideCodeAction.Diagnostics[i].Range = ls.sketchMapper.CppToInoRange(diag.Range) + } + } + return ideCodeAction +} + +func (ls *INOLanguageServer) clang2IdeCommand(logger jsonrpc.FunctionLogger, clangCommand lsp.Command) *lsp.Command { + switch clangCommand.Command { + case "clangd.applyTweak": + logger.Logf("> Command: clangd.applyTweak") + ideCommand := &lsp.Command{ + Title: clangCommand.Title, + Command: clangCommand.Command, + Arguments: clangCommand.Arguments, + } + for i := range clangCommand.Arguments { + v := struct { + TweakID string `json:"tweakID"` + File lsp.DocumentURI `json:"file"` + Selection lsp.Range `json:"selection"` + }{} + + if err := json.Unmarshal(clangCommand.Arguments[0], &v); err == nil { + if v.TweakID == "ExtractVariable" { + logger.Logf(" > converted clangd ExtractVariable") + if v.File.AsPath().EquivalentTo(ls.buildSketchCpp) { + inoFile, inoSelection := ls.sketchMapper.CppToInoRange(v.Selection) + v.File = lsp.NewDocumentURI(inoFile) + v.Selection = inoSelection + } + } + } + + converted, err := json.Marshal(v) + if err != nil { + panic("Internal Error: json conversion of codeAcion command arguments") + } + ideCommand.Arguments[i] = converted + } + return ideCommand + default: + logger.Logf("ERROR: could not convert Command '%s'", clangCommand.Command) + return nil + } +} + +func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { + if cppWorkspaceEdit == nil { + return nil + } + inoWorkspaceEdit := &lsp.WorkspaceEdit{ + Changes: map[lsp.DocumentURI][]lsp.TextEdit{}, + } + for editURI, edits := range cppWorkspaceEdit.Changes { + // if the edits are not relative to sketch file... + if !editURI.AsPath().EquivalentTo(ls.buildSketchCpp) { + // ...pass them through... + inoWorkspaceEdit.Changes[editURI] = edits + continue + } + + // ...otherwise convert edits to the sketch.ino.cpp into multilpe .ino edits + for _, edit := range edits { + inoURI, inoRange, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, editURI, edit.Range) + if err != nil { + logger.Logf(" error converting edit %s:%s: %s", editURI, edit.Range, err) + continue + } + if inPreprocessed { + // XXX: ignore + logger.Logf(" ignored in-preprocessed-section change") + continue + } + //inoFile, inoRange := ls.sketchMapper.CppToInoRange(edit.Range) + //inoURI := lsp.NewDocumentURI(inoFile) + if _, have := inoWorkspaceEdit.Changes[inoURI]; !have { + inoWorkspaceEdit.Changes[inoURI] = []lsp.TextEdit{} + } + inoWorkspaceEdit.Changes[inoURI] = append(inoWorkspaceEdit.Changes[inoURI], lsp.TextEdit{ + NewText: edit.NewText, + Range: inoRange, + }) + } + } + logger.Logf(" done converting workspaceEdit") + return inoWorkspaceEdit +} + +func (ls *INOLanguageServer) cpp2inoTextEdit(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, bool, error) { + inoURI, inoRange, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, cppEdit.Range) + + if err != nil { + if strings.HasPrefix(cppEdit.NewText, "\n") && cppEdit.Range.Start.Line < cppEdit.Range.End.Line { + // Special case: the text-edit may start from the very end of a not-ino section and fallthrough + // in the .ino section with a '\n...' at the beginning of the replacement text. + nextLine := lsp.Position{Line: cppEdit.Range.Start.Line + 1, Character: 0} + startOffset, err1 := textedits.GetOffset(ls.sketchMapper.CppText.Text, cppEdit.Range.Start) + nextOffset, err2 := textedits.GetOffset(ls.sketchMapper.CppText.Text, nextLine) + if err1 == nil && err2 == nil && startOffset+1 == nextOffset { + // In this can we can generate an equivalent text-edit that fits entirely in the .ino section + // by removing the redundant '\n' and by offsetting the start location to the beginning of the + // next line. + cppEdit.Range.Start = nextLine + cppEdit.NewText = cppEdit.NewText[1:] + return ls.cpp2inoTextEdit(logger, cppURI, cppEdit) + } + } + } + + inoEdit := cppEdit + inoEdit.Range = inoRange + return inoURI, inoEdit, inPreprocessed, err +} + +type UnknownURI struct { + URI lsp.DocumentURI +} + +func (e *UnknownURI) Error() string { + return "Document is not available: " + e.URI.String() +} diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go new file mode 100644 index 0000000..b3b2c8c --- /dev/null +++ b/ls/ls_clang_to_ide.go @@ -0,0 +1,346 @@ +package ls + +import ( + "strconv" + + "github.com/arduino/arduino-language-server/sourcemapper" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" +) + +func (ls *INOLanguageServer) clangURIRefersToIno(clangURI lsp.DocumentURI) bool { + return clangURI.AsPath().EquivalentTo(ls.buildSketchCpp) +} + +// Convert Range and DocumentURI from Clang to IDE. +// Returns: +// - The IDE DocumentURI and Range +// - a boolean that is true if the clang range is in the preprocessed area of the sketch +// - an error +func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangRange lsp.Range) (lsp.DocumentURI, lsp.Range, bool, error) { + // Sketchbook/Sketch/Sketch.ino <-> build-path/sketch/Sketch.ino.cpp + // Sketchbook/Sketch/AnotherTab.ino <-> build-path/sketch/Sketch.ino.cpp (different section from above) + if ls.clangURIRefersToIno(clangURI) { + // We are converting from preprocessed sketch.ino.cpp back to a sketch.ino file + idePath, ideRange, err := ls.sketchMapper.CppToInoRangeOk(clangRange) + if _, ok := err.(sourcemapper.AdjustedRangeErr); ok { + logger.Logf("Range has been END LINE ADJSUTED") + } else if err != nil { + logger.Logf("Range conversion ERROR: %s", err) + ls.sketchMapper.DebugLogAll() + return lsp.NilURI, lsp.NilRange, false, err + } + ideURI, err := ls.idePathToIdeURI(logger, idePath) + if err != nil { + logger.Logf("Range conversion ERROR: %s", err) + ls.sketchMapper.DebugLogAll() + return lsp.NilURI, lsp.NilRange, false, err + } + inPreprocessed := ls.sketchMapper.IsPreprocessedCppLine(clangRange.Start.Line) + if inPreprocessed { + logger.Logf("Range is in PREPROCESSED section of the sketch") + } + logger.Logf("Range: %s:%s -> %s:%s (.ino)", clangURI, clangRange, ideURI, ideRange) + return ideURI, ideRange, inPreprocessed, err + } + + // /another/global/path/to/source.cpp <-> /another/global/path/to/source.cpp (same range) + ideRange := clangRange + clangPath := clangURI.AsPath() + inside, err := clangPath.IsInsideDir(ls.buildSketchRoot) + if err != nil { + logger.Logf("ERROR: could not determine if '%s' is inside '%s'", clangURI, ls.buildSketchRoot) + return lsp.NilURI, lsp.NilRange, false, err + } + if !inside { + ideURI := clangURI + logger.Logf("Range: %s:%s -> %s:%s (ext file)", clangURI, clangRange, ideURI, ideRange) + return clangURI, clangRange, false, nil + } + + // Sketchbook/Sketch/AnotherFile.cpp <-> build-path/sketch/AnotherFile.cpp (one line offset) + rel, err := ls.buildSketchRoot.RelTo(clangPath) + if err != nil { + logger.Logf("ERROR: could not transform '%s' into a relative path on '%s': %s", clangURI, ls.buildSketchRoot, err) + return lsp.NilURI, lsp.NilRange, false, err + } + idePath := ls.sketchRoot.JoinPath(rel).String() + ideURI, err := ls.idePathToIdeURI(logger, idePath) + if ideRange.End.Line > 0 { + ideRange.End.Line-- + } + if ideRange.Start.Line > 0 { + ideRange.Start.Line-- + } + logger.Logf("Range: %s:%s -> %s:%s (.cpp/.h)", clangURI, clangRange, ideURI, ideRange) + return ideURI, ideRange, false, err +} + +func (ls *INOLanguageServer) clang2IdeDocumentURI(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI) (lsp.DocumentURI, error) { + // Sketchbook/Sketch/Sketch.ino <-> build-path/sketch/Sketch.ino.cpp + // Sketchbook/Sketch/AnotherTab.ino <-> build-path/sketch/Sketch.ino.cpp (different section from above) + if ls.clangURIRefersToIno(clangURI) { + // the URI may refer to any .ino, without a range reference pick the first tracked .ino + for _, ideDoc := range ls.trackedIdeDocs { + if ideDoc.URI.Ext() == ".ino" { + logger.Logf("%s -> %s", clangURI, ideDoc.URI) + return ideDoc.URI, nil + } + } + return lsp.DocumentURI{}, &UnknownURI{URI: clangURI} + } + + // /another/global/path/to/source.cpp <-> /another/global/path/to/source.cpp + clangPath := clangURI.AsPath() + inside, err := clangPath.IsInsideDir(ls.buildSketchRoot) + if err != nil { + logger.Logf("ERROR: could not determine if '%s' is inside '%s'", clangURI, ls.buildSketchRoot) + return lsp.DocumentURI{}, err + } + if !inside { + ideURI := clangURI + logger.Logf("%s -> %s", clangURI, ideURI) + return ideURI, nil + } + + // Sketchbook/Sketch/AnotherFile.cpp <-> build-path/sketch/AnotherFile.cpp + rel, err := ls.buildSketchRoot.RelTo(clangPath) + if err != nil { + logger.Logf("ERROR: could not transform '%s' into a relative path on '%s': %s", clangURI, ls.buildSketchRoot, err) + return lsp.DocumentURI{}, err + } + idePath := ls.sketchRoot.JoinPath(rel).String() + ideURI, err := ls.idePathToIdeURI(logger, idePath) + logger.Logf("%s -> %s", clangURI, ideURI) + return ideURI, err +} + +func (ls *INOLanguageServer) clang2IdeDocumentHighlight(logger jsonrpc.FunctionLogger, clangHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, bool, error) { + _, ideRange, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, clangHighlight.Range) + if err != nil || inPreprocessed { + return lsp.DocumentHighlight{}, inPreprocessed, err + } + return lsp.DocumentHighlight{ + Kind: clangHighlight.Kind, + Range: ideRange, + }, false, nil +} + +func (ls *INOLanguageServer) clang2IdeDiagnostics(logger jsonrpc.FunctionLogger, clangDiagsParams *lsp.PublishDiagnosticsParams) (map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams, error) { + // If diagnostics comes from sketch.ino.cpp they may refer to multiple .ino files, + // so we collect all of the into a map. + allIdeDiagsParams := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} + + // Convert empty diagnostic directly (otherwise they will be missed from the next loop) + if len(clangDiagsParams.Diagnostics) == 0 { + ideURI, err := ls.clang2IdeDocumentURI(logger, clangDiagsParams.URI) + if err != nil { + return nil, err + } + allIdeDiagsParams[ideURI] = &lsp.PublishDiagnosticsParams{ + URI: ideURI, + Version: clangDiagsParams.Version, + Diagnostics: []lsp.Diagnostic{}, + } + return allIdeDiagsParams, nil + } + + // Collect all diagnostics into different sets + for _, clangDiagnostic := range clangDiagsParams.Diagnostics { + ideURI, ideDiagnostic, inPreprocessed, err := ls.clang2IdeDiagnostic(logger, clangDiagsParams.URI, clangDiagnostic) + if err != nil { + return nil, err + } + if inPreprocessed { + continue + } + if _, ok := allIdeDiagsParams[ideURI]; !ok { + allIdeDiagsParams[ideURI] = &lsp.PublishDiagnosticsParams{URI: ideURI} + } + allIdeDiagsParams[ideURI].Diagnostics = append(allIdeDiagsParams[ideURI].Diagnostics, ideDiagnostic) + } + + return allIdeDiagsParams, nil +} + +func (ls *INOLanguageServer) clang2IdeDiagnostic(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangDiagnostic lsp.Diagnostic) (lsp.DocumentURI, lsp.Diagnostic, bool, error) { + ideURI, ideRange, inPreproccesed, err := ls.clang2IdeRangeAndDocumentURI(logger, clangURI, clangDiagnostic.Range) + if err != nil || inPreproccesed { + return lsp.DocumentURI{}, lsp.Diagnostic{}, inPreproccesed, err + } + + ideDiagnostic := clangDiagnostic + ideDiagnostic.Range = ideRange + + if len(clangDiagnostic.RelatedInformation) > 0 { + ideInfos, err := ls.clang2IdeDiagnosticRelatedInformationArray(logger, clangDiagnostic.RelatedInformation) + if err != nil { + return lsp.DocumentURI{}, lsp.Diagnostic{}, false, err + } + ideDiagnostic.RelatedInformation = ideInfos + } + return ideURI, ideDiagnostic, false, nil +} + +func (ls *INOLanguageServer) clang2IdeDiagnosticRelatedInformationArray(logger jsonrpc.FunctionLogger, clangInfos []lsp.DiagnosticRelatedInformation) ([]lsp.DiagnosticRelatedInformation, error) { + ideInfos := []lsp.DiagnosticRelatedInformation{} + for _, clangInfo := range clangInfos { + ideLocation, inPreprocessed, err := ls.clang2IdeLocation(logger, clangInfo.Location) + if err != nil { + return nil, err + } + if inPreprocessed { + logger.Logf("Ignoring in-preprocessed-section diagnostic related information") + continue + } + ideInfos = append(ideInfos, lsp.DiagnosticRelatedInformation{ + Message: clangInfo.Message, + Location: ideLocation, + }) + } + return ideInfos, nil +} + +func (ls *INOLanguageServer) clang2IdeDocumentSymbols(logger jsonrpc.FunctionLogger, clangSymbols []lsp.DocumentSymbol, clangURI lsp.DocumentURI, origIdeURI lsp.DocumentURI) ([]lsp.DocumentSymbol, error) { + logger.Logf("%s (%d document symbols)", clangURI, len(clangSymbols)) + + ideSymbols := []lsp.DocumentSymbol{} + for _, clangSymbol := range clangSymbols { + logger.Logf(" > convert %s %s", clangSymbol.Kind, clangSymbol.Range) + ideURI, ideRange, isPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, clangURI, clangSymbol.Range) + if err != nil { + return nil, err + } + if isPreprocessed { + logger.Logf(" symbol is in the preprocessed section of the sketch.ino.cpp, skipping") + continue + } + if ideURI != origIdeURI { + logger.Logf(" filtering out symbol related to %s", ideURI) + continue + } + ideSelectionURI, ideSelectionRange, isSelectionPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, clangURI, clangSymbol.SelectionRange) + if err != nil { + return nil, err + } + if ideSelectionURI != ideURI || isSelectionPreprocessed { + logger.Logf(" ERROR: doc of symbol-selection-range does not match doc of symbol-range") + logger.Logf(" range %s > %s:%s", clangSymbol.Range, ideURI, ideRange) + logger.Logf(" selection %s > %s:%s", clangSymbol.SelectionRange, ideSelectionURI, ideSelectionRange) + continue + } + + ideChildren, err := ls.clang2IdeDocumentSymbols(logger, clangSymbol.Children, clangURI, origIdeURI) + if err != nil { + return nil, err + } + + ideSymbols = append(ideSymbols, lsp.DocumentSymbol{ + Name: clangSymbol.Name, + Detail: clangSymbol.Detail, + Deprecated: clangSymbol.Deprecated, + Kind: clangSymbol.Kind, + Range: ideRange, + SelectionRange: ideSelectionRange, + Children: ideChildren, + Tags: ls.clang2IdeSymbolTags(logger, clangSymbol.Tags), + }) + } + + return ideSymbols, nil +} + +func (ls *INOLanguageServer) cland2IdeTextEdits(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangTextEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { + logger.Logf("%s clang/textEdit (%d elements)", clangURI, len(clangTextEdits)) + allIdeTextEdits := map[lsp.DocumentURI][]lsp.TextEdit{} + for _, clangTextEdit := range clangTextEdits { + ideURI, ideTextEdit, inPreprocessed, err := ls.cpp2inoTextEdit(logger, clangURI, clangTextEdit) + if err != nil { + return nil, err + } + logger.Logf(" > %s:%s -> %s", clangURI, clangTextEdit.Range, strconv.Quote(clangTextEdit.NewText)) + if inPreprocessed { + logger.Logf((" ignoring in-preprocessed-section edit")) + continue + } + allIdeTextEdits[ideURI] = append(allIdeTextEdits[ideURI], ideTextEdit) + } + + logger.Logf("converted to:") + + for ideURI, ideTextEdits := range allIdeTextEdits { + logger.Logf(" %s ino/textEdit (%d elements)", ideURI, len(ideTextEdits)) + for _, ideTextEdit := range ideTextEdits { + logger.Logf(" > %s:%s -> %s", ideURI, ideTextEdit.Range, strconv.Quote(ideTextEdit.NewText)) + } + } + return allIdeTextEdits, nil +} + +func (ls *INOLanguageServer) clang2IdeLocationsArray(logger jsonrpc.FunctionLogger, clangLocations []lsp.Location) ([]lsp.Location, error) { + ideLocations := []lsp.Location{} + for _, clangLocation := range clangLocations { + ideLocation, inPreprocessed, err := ls.clang2IdeLocation(logger, clangLocation) + if err != nil { + logger.Logf("ERROR converting location %s: %s", clangLocation, err) + return nil, err + } + if inPreprocessed { + logger.Logf("ignored in-preprocessed-section location") + continue + } + ideLocations = append(ideLocations, ideLocation) + } + return ideLocations, nil +} + +func (ls *INOLanguageServer) clang2IdeLocation(logger jsonrpc.FunctionLogger, clangLocation lsp.Location) (lsp.Location, bool, error) { + ideURI, ideRange, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, clangLocation.URI, clangLocation.Range) + return lsp.Location{ + URI: ideURI, + Range: ideRange, + }, inPreprocessed, err +} + +func (ls *INOLanguageServer) clang2IdeSymbolTags(logger jsonrpc.FunctionLogger, clangSymbolTags []lsp.SymbolTag) []lsp.SymbolTag { + if len(clangSymbolTags) == 0 || clangSymbolTags == nil { + return clangSymbolTags + } + panic("not implemented") +} + +func (ls *INOLanguageServer) clang2IdeSymbolsInformation(logger jsonrpc.FunctionLogger, clangSymbolsInformation []lsp.SymbolInformation) []lsp.SymbolInformation { + logger.Logf("SymbolInformation (%d elements):", len(clangSymbolsInformation)) + panic("not implemented") +} + +func (ls *INOLanguageServer) clang2IdeWorkspaceEdit(logger jsonrpc.FunctionLogger, clangWorkspaceEdit *lsp.WorkspaceEdit) (*lsp.WorkspaceEdit, error) { + ideChanges := map[lsp.DocumentURI][]lsp.TextEdit{} + for clangURI, clangChanges := range clangWorkspaceEdit.Changes { + for _, clangTextEdit := range clangChanges { + ideURI, ideTextEdit, isPreprocessed, err := ls.clang2IdeTextEdit(logger, clangURI, clangTextEdit) + if isPreprocessed { + logger.Logf("- ignore edit in preprocessed area") + continue + } + if err != nil { + return nil, err + } + ideChanges[ideURI] = append(ideChanges[ideURI], ideTextEdit) + } + } + ideWorkspaceEdit := &lsp.WorkspaceEdit{ + Changes: ideChanges, + ChangeAnnotations: clangWorkspaceEdit.ChangeAnnotations, + } + return ideWorkspaceEdit, nil +} + +func (ls *INOLanguageServer) clang2IdeTextEdit(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangTextEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, bool, error) { + ideURI, ideRange, isPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, clangURI, clangTextEdit.Range) + ideTextEdit := lsp.TextEdit{ + NewText: clangTextEdit.NewText, + Range: ideRange, + } + return ideURI, ideTextEdit, isPreprocessed, err +} diff --git a/ls/ls_formatter.go b/ls/ls_formatter.go new file mode 100644 index 0000000..b49c12d --- /dev/null +++ b/ls/ls_formatter.go @@ -0,0 +1,191 @@ +package ls + +import ( + "github.com/arduino/go-paths-helper" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" +) + +func (ls *INOLanguageServer) createClangdFormatterConfig(logger jsonrpc.FunctionLogger, cppuri lsp.DocumentURI) (func(), error) { + // clangd looks for a .clang-format configuration file on the same directory + // pointed by the uri passed in the lsp command parameters. + // https://github.com/llvm/llvm-project/blob/64d06ed9c9e0389cd27545d2f6e20455a91d89b1/clang-tools-extra/clangd/ClangdLSPServer.cpp#L856-L868 + // https://github.com/llvm/llvm-project/blob/64d06ed9c9e0389cd27545d2f6e20455a91d89b1/clang-tools-extra/clangd/ClangdServer.cpp#L402-L404 + + config := `# See: https://releases.llvm.org/11.0.1/tools/clang/docs/ClangFormatStyleOptions.html +--- +Language: Cpp +# LLVM is the default style setting, used when a configuration option is not set here +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveBitFields: false +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: false +AlignEscapedNewlines: DontAlign +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: No +BinPackArguments: true +BinPackParameters: true +# Only used when "BreakBeforeBraces" set to "Custom" +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + #AfterObjCDeclaration: + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +# Java-specific +#BreakAfterJavaFieldAnnotations: +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: false +ColumnLimit: 0 +# "" matches none +CommentPragmas: "" +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: false +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +# Docs say "Do not use this in config files". The default (LLVM 11.0.1) is "false". +#ExperimentalAutoDetectBinPacking: +FixNamespaceComments: false +ForEachMacros: [] +IncludeBlocks: Preserve +IncludeCategories: [] +# "" matches none +IncludeIsMainRegex: "" +IncludeIsMainSourceRegex: "" +IndentCaseBlocks: true +IndentCaseLabels: true +IndentExternBlock: Indent +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +# Java-specific +#JavaImportGroups: +# JavaScript-specific +#JavaScriptQuotes: +#JavaScriptWrapImports +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: "" +MacroBlockEnd: "" +# Set to a large number to effectively disable +MaxEmptyLinesToKeep: 100000 +NamespaceIndentation: None +NamespaceMacros: [] +# Objective C-specific +#ObjCBinPackProtocolList: +#ObjCBlockIndentWidth: +#ObjCBreakBeforeNestedBlockParam: +#ObjCSpaceAfterProperty: +#ObjCSpaceBeforeProtocolList +PenaltyBreakAssignment: 1 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 1 +PenaltyBreakFirstLessLess: 1 +PenaltyBreakString: 1 +PenaltyBreakTemplateDeclaration: 1 +PenaltyExcessCharacter: 1 +PenaltyReturnTypeOnItsOwnLine: 1 +# Used as a fallback if alignment style can't be detected from code (DerivePointerAlignment: true) +PointerAlignment: Right +RawStringFormats: [] +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementMacros: [] +TabWidth: 2 +TypenameMacros: [] +# Default to LF if line endings can't be detected from the content (DeriveLineEnding). +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: [] +` + try := func(conf *paths.Path) bool { + if c, err := conf.ReadFile(); err != nil { + logger.Logf(" error reading custom formatter config file %s: %s", conf, err) + } else { + logger.Logf(" using custom formatter config file %s", conf) + config = string(c) + } + return true + } + + if sketchFormatterConf := ls.sketchRoot.Join(".clang-format"); sketchFormatterConf.Exist() { + // If a custom config is present in the sketch folder, use that one + try(sketchFormatterConf) + } else if ls.config.FormatterConf != nil && ls.config.FormatterConf.Exist() { + // Otherwise if a global config file is present, use that one + try(ls.config.FormatterConf) + } + + targetFile := cppuri.AsPath() + if targetFile.IsNotDir() { + targetFile = targetFile.Parent() + } + targetFile = targetFile.Join(".clang-format") + cleanup := func() { + targetFile.Remove() + logger.Logf(" formatter config cleaned") + } + logger.Logf(" writing formatter config in: %s", targetFile) + err := targetFile.WriteFile([]byte(config)) + return cleanup, err +} diff --git a/ls/ls_ide_to_clang.go b/ls/ls_ide_to_clang.go new file mode 100644 index 0000000..fc49771 --- /dev/null +++ b/ls/ls_ide_to_clang.go @@ -0,0 +1,179 @@ +package ls + +import ( + "fmt" + + "github.com/arduino/arduino-language-server/sourcemapper" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" +) + +func (ls *INOLanguageServer) idePathToIdeURI(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { + if inoPath == sourcemapper.NotIno.File { + return sourcemapper.NotInoURI, nil + } + doc, ok := ls.trackedIdeDocs[inoPath] + if !ok { + logger.Logf(" !!! Unresolved .ino path: %s", inoPath) + logger.Logf(" !!! Known doc paths are:") + for p := range ls.trackedIdeDocs { + logger.Logf(" !!! > %s", p) + } + uri := lsp.NewDocumentURI(inoPath) + return uri, &UnknownURI{uri} + } + return doc.URI, nil +} + +func (ls *INOLanguageServer) ide2ClangTextDocumentIdentifier(logger jsonrpc.FunctionLogger, ideTextDocIdentifier lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { + clangURI, _, err := ls.ide2ClangDocumentURI(logger, ideTextDocIdentifier.URI) + return lsp.TextDocumentIdentifier{URI: clangURI}, err +} + +func (ls *INOLanguageServer) ide2ClangDocumentURI(logger jsonrpc.FunctionLogger, ideURI lsp.DocumentURI) (lsp.DocumentURI, bool, error) { + // Sketchbook/Sketch/Sketch.ino -> build-path/sketch/Sketch.ino.cpp + // Sketchbook/Sketch/AnotherTab.ino -> build-path/sketch/Sketch.ino.cpp (different section from above) + idePath := ideURI.AsPath() + if idePath.Ext() == ".ino" { + clangURI := lsp.NewDocumentURIFromPath(ls.buildSketchCpp) + logger.Logf("URI: %s -> %s", ideURI, clangURI) + return clangURI, true, nil + } + + // another/path/source.cpp -> another/path/source.cpp (unchanged) + inside, err := idePath.IsInsideDir(ls.sketchRoot) + if err != nil { + logger.Logf("ERROR: could not determine if '%s' is inside '%s'", idePath, ls.sketchRoot) + return lsp.NilURI, false, &UnknownURI{ideURI} + } + if !inside { + clangURI := ideURI + logger.Logf("URI: %s -> %s", ideURI, clangURI) + return clangURI, false, nil + } + + // Sketchbook/Sketch/AnotherFile.cpp -> build-path/sketch/AnotherFile.cpp + rel, err := ls.sketchRoot.RelTo(idePath) + if err != nil { + logger.Logf("ERROR: could not determine rel-path of '%s' in '%s': %s", idePath, ls.sketchRoot, err) + return lsp.NilURI, false, err + } + + clangPath := ls.buildSketchRoot.JoinPath(rel) + clangURI := lsp.NewDocumentURIFromPath(clangPath) + logger.Logf("URI: %s -> %s", ideURI, clangURI) + return clangURI, true, nil +} + +func (ls *INOLanguageServer) ide2ClangTextDocumentPositionParams(logger jsonrpc.FunctionLogger, ideParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { + clangURI, clangPosition, err := ls.ide2ClangPosition(logger, ideParams.TextDocument.URI, ideParams.Position) + if err != nil { + logger.Logf("Error converting position %s: %s", ideParams, err) + return lsp.TextDocumentPositionParams{}, err + } + clangParams := lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{URI: clangURI}, + Position: clangPosition, + } + logger.Logf("%s -> %s", ideParams, clangParams) + return clangParams, nil +} + +func (ls *INOLanguageServer) ide2ClangPosition(logger jsonrpc.FunctionLogger, ideURI lsp.DocumentURI, idePosition lsp.Position) (lsp.DocumentURI, lsp.Position, error) { + clangURI, clangRange, err := ls.ide2ClangRange(logger, ideURI, lsp.Range{Start: idePosition, End: idePosition}) + return clangURI, clangRange.Start, err +} + +func (ls *INOLanguageServer) ide2ClangRange(logger jsonrpc.FunctionLogger, ideURI lsp.DocumentURI, ideRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { + clangURI, inSketch, err := ls.ide2ClangDocumentURI(logger, ideURI) + if err != nil { + return lsp.DocumentURI{}, lsp.Range{}, err + } + + // Convert .ino ranges using sketchmapper + if ls.clangURIRefersToIno(clangURI) { + if clangRange, ok := ls.sketchMapper.InoToCppLSPRangeOk(ideURI, ideRange); ok { + return clangURI, clangRange, nil + } else { + return lsp.DocumentURI{}, lsp.Range{}, fmt.Errorf("invalid range %s:%s: could not be mapped to Arduino-preprocessed sketck.ino.cpp", ideURI, ideRange) + } + } else if inSketch { + // Convert other sketch file ranges (.cpp/.h) + clangRange := ideRange + clangRange.Start.Line++ + clangRange.End.Line++ + return clangURI, clangRange, nil + } else { + // Outside sketch: keep range as is + clangRange := ideRange + return clangURI, clangRange, nil + } +} + +func (ls *INOLanguageServer) ide2ClangVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, ideVersionedDoc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { + clangURI, _, err := ls.ide2ClangDocumentURI(logger, ideVersionedDoc.URI) + return lsp.VersionedTextDocumentIdentifier{ + TextDocumentIdentifier: lsp.TextDocumentIdentifier{URI: clangURI}, + Version: ideVersionedDoc.Version, + }, err +} + +func (ls *INOLanguageServer) ide2ClangDiagnosticRelatedInformationArray(logger jsonrpc.FunctionLogger, ideInfos []lsp.DiagnosticRelatedInformation) ([]lsp.DiagnosticRelatedInformation, error) { + clangInfos := []lsp.DiagnosticRelatedInformation{} + for _, ideInfo := range ideInfos { + clangLocation, err := ls.ide2ClangLocation(logger, ideInfo.Location) + if err != nil { + return nil, err + } + clangInfos = append(clangInfos, lsp.DiagnosticRelatedInformation{ + Message: ideInfo.Message, + Location: clangLocation, + }) + } + return clangInfos, nil +} + +func (ls *INOLanguageServer) ide2ClangLocation(logger jsonrpc.FunctionLogger, ideLocation lsp.Location) (lsp.Location, error) { + clangURI, clangRange, err := ls.ide2ClangRange(logger, ideLocation.URI, ideLocation.Range) + return lsp.Location{ + URI: clangURI, + Range: clangRange, + }, err +} + +func (ls *INOLanguageServer) ide2ClangDiagnostic(logger jsonrpc.FunctionLogger, ideURI lsp.DocumentURI, ideDiag lsp.Diagnostic) (lsp.DocumentURI, lsp.Diagnostic, error) { + clangURI, clangRange, err := ls.ide2ClangRange(logger, ideURI, ideDiag.Range) + if err != nil { + return lsp.DocumentURI{}, lsp.Diagnostic{}, err + } + clangDiagRelatedInfo, err := ls.ide2ClangDiagnosticRelatedInformationArray(logger, ideDiag.RelatedInformation) + if err != nil { + return lsp.DocumentURI{}, lsp.Diagnostic{}, err + } + return clangURI, lsp.Diagnostic{ + Range: clangRange, + RelatedInformation: clangDiagRelatedInfo, + Severity: ideDiag.Severity, + Code: ideDiag.Code, + CodeDescription: ideDiag.CodeDescription, + Source: ideDiag.Source, + Message: ideDiag.Message, + Tags: ideDiag.Tags, + Data: ideDiag.Data, + }, nil +} + +func (ls *INOLanguageServer) ide2ClangCodeActionContext(logger jsonrpc.FunctionLogger, ideURI lsp.DocumentURI, ideContext lsp.CodeActionContext) (lsp.CodeActionContext, error) { + clangDiagnostics := []lsp.Diagnostic{} + for _, ideDiag := range ideContext.Diagnostics { + _, clangDiag, err := ls.ide2ClangDiagnostic(logger, ideURI, ideDiag) + if err != nil { + return lsp.CodeActionContext{}, err + } + clangDiagnostics = append(clangDiagnostics, clangDiag) + } + return lsp.CodeActionContext{ + Diagnostics: clangDiagnostics, + Only: ideContext.Only, + }, nil +} diff --git a/ls/lsp_client_clangd.go b/ls/lsp_client_clangd.go new file mode 100644 index 0000000..e98f94c --- /dev/null +++ b/ls/lsp_client_clangd.go @@ -0,0 +1,145 @@ +package ls + +import ( + "context" + "fmt" + "io" + "os" + "strings" + + "github.com/arduino/arduino-cli/executils" + "github.com/arduino/arduino-language-server/streams" + "github.com/arduino/go-paths-helper" + "github.com/fatih/color" + "go.bug.st/json" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" +) + +type ClangdLSPClient struct { + conn *lsp.Client + ls *INOLanguageServer +} + +func NewClangdLSPClient(logger jsonrpc.FunctionLogger, dataFolder *paths.Path, ls *INOLanguageServer) *ClangdLSPClient { + // Start clangd + args := []string{ + ls.config.ClangdPath.String(), + "-log=verbose", + fmt.Sprintf(`--compile-commands-dir=%s`, ls.buildPath), + } + if dataFolder != nil { + args = append(args, fmt.Sprintf("-query-driver=%s", dataFolder.Join("packages", "**"))) + } + + logger.Logf(" Starting clangd: %s", strings.Join(args, " ")) + var clangdStdin io.WriteCloser + var clangdStdout, clangdStderr io.ReadCloser + if clangdCmd, err := executils.NewProcess(args...); err != nil { + panic("starting clangd: " + err.Error()) + } else if cin, err := clangdCmd.StdinPipe(); err != nil { + panic("getting clangd stdin: " + err.Error()) + } else if cout, err := clangdCmd.StdoutPipe(); err != nil { + panic("getting clangd stdout: " + err.Error()) + } else if cerr, err := clangdCmd.StderrPipe(); err != nil { + panic("getting clangd stderr: " + err.Error()) + } else if err := clangdCmd.Start(); err != nil { + panic("running clangd: " + err.Error()) + } else { + clangdStdin = cin + clangdStdout = cout + clangdStderr = cerr + } + + clangdStdio := streams.NewReadWriteCloser(clangdStdout, clangdStdin) + if ls.config.EnableLogging { + clangdStdio = streams.LogReadWriteCloserAs(clangdStdio, "inols-clangd.log") + go io.Copy(streams.OpenLogFileAs("inols-clangd-err.log"), clangdStderr) + } else { + go io.Copy(os.Stderr, clangdStderr) + } + + client := &ClangdLSPClient{ + ls: ls, + } + client.conn = lsp.NewClient(clangdStdio, clangdStdio, client) + client.conn.SetLogger(&LSPLogger{ + IncomingPrefix: "IDE LS <-- Clangd", + OutgoingPrefix: "IDE LS --> Clangd", + HiColor: color.HiRedString, + LoColor: color.RedString, + ErrorColor: color.New(color.BgHiMagenta, color.FgHiWhite, color.BlinkSlow).Sprintf, + }) + return client +} + +func (client *ClangdLSPClient) Run() { + client.conn.Run() +} + +func (client *ClangdLSPClient) Close() { + client.conn.Exit() // send "exit" notification to Clangd + // TODO: kill client.conn +} + +// The following are events incoming from Clangd + +func (client *ClangdLSPClient) WindowShowMessageRequest(context.Context, jsonrpc.FunctionLogger, *lsp.ShowMessageRequestParams) (*lsp.MessageActionItem, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdLSPClient) WindowShowDocument(context.Context, jsonrpc.FunctionLogger, *lsp.ShowDocumentParams) (*lsp.ShowDocumentResult, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdLSPClient) WindowWorkDoneProgressCreate(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCreateParams) *jsonrpc.ResponseError { + return client.ls.WindowWorkDoneProgressCreateReqFromClangd(ctx, logger, params) +} + +func (client *ClangdLSPClient) ClientRegisterCapability(context.Context, jsonrpc.FunctionLogger, *lsp.RegistrationParams) *jsonrpc.ResponseError { + panic("unimplemented") +} + +func (client *ClangdLSPClient) ClientUnregisterCapability(context.Context, jsonrpc.FunctionLogger, *lsp.UnregistrationParams) *jsonrpc.ResponseError { + panic("unimplemented") +} + +func (client *ClangdLSPClient) WorkspaceWorkspaceFolders(context.Context, jsonrpc.FunctionLogger) ([]lsp.WorkspaceFolder, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdLSPClient) WorkspaceConfiguration(context.Context, jsonrpc.FunctionLogger, *lsp.ConfigurationParams) ([]json.RawMessage, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdLSPClient) WorkspaceApplyEdit(context.Context, jsonrpc.FunctionLogger, *lsp.ApplyWorkspaceEditParams) (*lsp.ApplyWorkspaceEditResult, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdLSPClient) WorkspaceCodeLensRefresh(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { + panic("unimplemented") +} + +func (client *ClangdLSPClient) Progress(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { + client.ls.ProgressNotifFromClangd(logger, progress) +} + +func (client *ClangdLSPClient) LogTrace(jsonrpc.FunctionLogger, *lsp.LogTraceParams) { + panic("unimplemented") +} + +func (client *ClangdLSPClient) WindowShowMessage(jsonrpc.FunctionLogger, *lsp.ShowMessageParams) { + panic("unimplemented") +} + +func (client *ClangdLSPClient) WindowLogMessage(jsonrpc.FunctionLogger, *lsp.LogMessageParams) { + panic("unimplemented") +} + +func (client *ClangdLSPClient) TelemetryEvent(jsonrpc.FunctionLogger, json.RawMessage) { + panic("unimplemented") +} + +func (client *ClangdLSPClient) TextDocumentPublishDiagnostics(logger jsonrpc.FunctionLogger, params *lsp.PublishDiagnosticsParams) { + go client.ls.PublishDiagnosticsNotifFromClangd(logger, params) +} diff --git a/ls/lsp_logger.go b/ls/lsp_logger.go new file mode 100644 index 0000000..2e9f417 --- /dev/null +++ b/ls/lsp_logger.go @@ -0,0 +1,77 @@ +package ls + +import ( + "fmt" + "log" + + "github.com/fatih/color" + "go.bug.st/json" + "go.bug.st/lsp/jsonrpc" +) + +type LSPLogger struct { + IncomingPrefix, OutgoingPrefix string + HiColor, LoColor func(format string, a ...interface{}) string + ErrorColor func(format string, a ...interface{}) string +} + +func (l *LSPLogger) LogOutgoingRequest(id string, method string, params json.RawMessage) { + log.Print(l.HiColor("%s REQU %s %s", l.OutgoingPrefix, method, id)) +} +func (l *LSPLogger) LogOutgoingCancelRequest(id string) { + log.Print(l.LoColor("%s CANCEL %s", l.OutgoingPrefix, id)) +} +func (l *LSPLogger) LogIncomingResponse(id string, method string, resp json.RawMessage, respErr *jsonrpc.ResponseError) { + e := "" + if respErr != nil { + e = l.ErrorColor(" ERROR: %s", respErr.AsError()) + } + log.Print(l.LoColor("%s RESP %s %s%s", l.IncomingPrefix, method, id, e)) +} +func (l *LSPLogger) LogOutgoingNotification(method string, params json.RawMessage) { + log.Print(l.HiColor("%s NOTIF %s", l.OutgoingPrefix, method)) +} + +func (l *LSPLogger) LogIncomingRequest(id string, method string, params json.RawMessage) jsonrpc.FunctionLogger { + spaces := " " + log.Print(l.HiColor(fmt.Sprintf("%s REQU %s %s", l.IncomingPrefix, method, id))) + return &LSPFunctionLogger{ + colorFunc: l.HiColor, + prefix: fmt.Sprintf("%s %s %s", spaces[:len(l.IncomingPrefix)], method, id), + } +} +func (l *LSPLogger) LogIncomingCancelRequest(id string) { + log.Print(l.LoColor("%s CANCEL %s", l.IncomingPrefix, id)) +} +func (l *LSPLogger) LogOutgoingResponse(id string, method string, resp json.RawMessage, respErr *jsonrpc.ResponseError) { + e := "" + if respErr != nil { + e = l.ErrorColor(" ERROR: %s", respErr.AsError()) + } + log.Print(l.LoColor("%s RESP %s %s%s", l.OutgoingPrefix, method, id, e)) +} +func (l *LSPLogger) LogIncomingNotification(method string, params json.RawMessage) jsonrpc.FunctionLogger { + spaces := " " + log.Print(l.HiColor(fmt.Sprintf("%s NOTIF %s", l.IncomingPrefix, method))) + return &LSPFunctionLogger{ + colorFunc: l.HiColor, + prefix: fmt.Sprintf("%s %s", spaces[:len(l.IncomingPrefix)], method), + } +} + +type LSPFunctionLogger struct { + colorFunc func(format string, a ...interface{}) string + prefix string +} + +func NewLSPFunctionLogger(colofFunction func(format string, a ...interface{}) string, prefix string) *LSPFunctionLogger { + color.NoColor = false + return &LSPFunctionLogger{ + colorFunc: colofFunction, + prefix: prefix, + } +} + +func (l *LSPFunctionLogger) Logf(format string, a ...interface{}) { + log.Print(l.colorFunc(l.prefix+": "+format, a...)) +} diff --git a/ls/lsp_server_ide.go b/ls/lsp_server_ide.go new file mode 100644 index 0000000..2f295d5 --- /dev/null +++ b/ls/lsp_server_ide.go @@ -0,0 +1,269 @@ +package ls + +import ( + "context" + "io" + + "github.com/fatih/color" + "go.bug.st/json" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" +) + +type IDELSPServer struct { + conn *lsp.Server + ls *INOLanguageServer +} + +func NewIDELSPServer(logger jsonrpc.FunctionLogger, in io.Reader, out io.Writer, ls *INOLanguageServer) *IDELSPServer { + server := &IDELSPServer{ + ls: ls, + } + server.conn = lsp.NewServer(in, out, server) + server.conn.SetLogger(&LSPLogger{ + IncomingPrefix: "IDE --> LS", + OutgoingPrefix: "IDE <-- LS", + HiColor: color.HiGreenString, + LoColor: color.GreenString, + ErrorColor: color.New(color.BgHiMagenta, color.FgHiWhite, color.BlinkSlow).Sprintf, + }) + return server +} + +func (server *IDELSPServer) Run() { + server.conn.Run() +} + +func (server *IDELSPServer) Initialize(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { + return server.ls.InitializeReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) Shutdown(ctx context.Context, logger jsonrpc.FunctionLogger) *jsonrpc.ResponseError { + return server.ls.ShutdownReqFromIDE(ctx, logger) +} + +func (server *IDELSPServer) WorkspaceSymbol(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceExecuteCommand(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.ExecuteCommandParams) (json.RawMessage, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceWillCreateFiles(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CreateFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceWillRenameFiles(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.RenameFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceWillDeleteFiles(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DeleteFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentWillSaveWaitUntil(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WillSaveTextDocumentParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentCompletion(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CompletionParams) (*lsp.CompletionList, *jsonrpc.ResponseError) { + return server.ls.TextDocumentCompletionReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) CompletionItemResolve(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CompletionItem) (*lsp.CompletionItem, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentHover(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.HoverParams) (*lsp.Hover, *jsonrpc.ResponseError) { + return server.ls.TextDocumentHoverReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentSignatureHelp(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.SignatureHelpParams) (*lsp.SignatureHelp, *jsonrpc.ResponseError) { + return server.ls.TextDocumentSignatureHelpReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentDeclaration(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DeclarationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + return server.ls.TextDocumentDefinitionReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentTypeDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + return server.ls.TextDocumentTypeDefinitionReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentImplementation(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.ImplementationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + return server.ls.TextDocumentImplementationReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentReferences(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.ReferenceParams) ([]lsp.Location, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentDocumentHighlight(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, *jsonrpc.ResponseError) { + return server.ls.TextDocumentDocumentHighlightReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentDocumentSymbol(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, []lsp.SymbolInformation, *jsonrpc.ResponseError) { + return server.ls.TextDocumentDocumentSymbolReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentCodeAction(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CodeActionParams) ([]lsp.CommandOrCodeAction, *jsonrpc.ResponseError) { + return server.ls.TextDocumentCodeActionReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) CodeActionResolve(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CodeAction) (*lsp.CodeAction, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentCodeLens(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CodeLensParams) ([]lsp.CodeLens, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) CodeLensResolve(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CodeLens) (*lsp.CodeLens, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentDocumentLink(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DocumentLinkParams) ([]lsp.DocumentLink, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) DocumentLinkResolve(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DocumentLink) (*lsp.DocumentLink, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentDocumentColor(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DocumentColorParams) ([]lsp.ColorInformation, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentColorPresentation(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.ColorPresentationParams) ([]lsp.ColorPresentation, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + return server.ls.TextDocumentFormattingReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentRangeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + return server.ls.TextDocumentRangeFormattingReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentOnTypeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.DocumentOnTypeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentRename(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.RenameParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + return server.ls.TextDocumentRenameReqFromIDE(ctx, logger, params) +} + +func (server *IDELSPServer) TextDocumentFoldingRange(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.FoldingRangeParams) ([]lsp.FoldingRange, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentSelectionRange(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.SelectionRangeParams) ([]lsp.SelectionRange, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentPrepareCallHierarchy(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CallHierarchyPrepareParams) ([]lsp.CallHierarchyItem, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) CallHierarchyIncomingCalls(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CallHierarchyIncomingCallsParams) ([]lsp.CallHierarchyIncomingCall, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) CallHierarchyOutgoingCalls(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.CallHierarchyOutgoingCallsParams) ([]lsp.CallHierarchyOutgoingCall, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentSemanticTokensFull(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.SemanticTokensParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentSemanticTokensFullDelta(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.SemanticTokensDeltaParams) (*lsp.SemanticTokens, *lsp.SemanticTokensDelta, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentSemanticTokensRange(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.SemanticTokensRangeParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceSemanticTokensRefresh(ctx context.Context, logger jsonrpc.FunctionLogger) *jsonrpc.ResponseError { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentLinkedEditingRange(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.LinkedEditingRangeParams) (*lsp.LinkedEditingRanges, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentMoniker(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.MonikerParams) ([]lsp.Moniker, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +// Notifications -> + +func (server *IDELSPServer) Progress(logger jsonrpc.FunctionLogger, params *lsp.ProgressParams) { + panic("unimplemented") +} + +func (server *IDELSPServer) Initialized(logger jsonrpc.FunctionLogger, params *lsp.InitializedParams) { + server.ls.InitializedNotifFromIDE(logger, params) +} + +func (server *IDELSPServer) Exit(logger jsonrpc.FunctionLogger) { + server.ls.ExitNotifFromIDE(logger) +} + +func (server *IDELSPServer) SetTrace(logger jsonrpc.FunctionLogger, params *lsp.SetTraceParams) { + server.ls.SetTraceNotifFromIDE(logger, params) +} + +func (server *IDELSPServer) WindowWorkDoneProgressCancel(logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCancelParams) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceDidChangeWorkspaceFolders(logger jsonrpc.FunctionLogger, params *lsp.DidChangeWorkspaceFoldersParams) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceDidChangeConfiguration(logger jsonrpc.FunctionLogger, params *lsp.DidChangeConfigurationParams) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceDidChangeWatchedFiles(logger jsonrpc.FunctionLogger, params *lsp.DidChangeWatchedFilesParams) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceDidCreateFiles(logger jsonrpc.FunctionLogger, params *lsp.CreateFilesParams) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceDidRenameFiles(logger jsonrpc.FunctionLogger, params *lsp.RenameFilesParams) { + panic("unimplemented") +} + +func (server *IDELSPServer) WorkspaceDidDeleteFiles(logger jsonrpc.FunctionLogger, params *lsp.DeleteFilesParams) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentDidOpen(logger jsonrpc.FunctionLogger, params *lsp.DidOpenTextDocumentParams) { + server.ls.TextDocumentDidOpenNotifFromIDE(logger, params) +} + +func (server *IDELSPServer) TextDocumentDidChange(logger jsonrpc.FunctionLogger, params *lsp.DidChangeTextDocumentParams) { + server.ls.TextDocumentDidChangeNotifFromIDE(logger, params) +} + +func (server *IDELSPServer) TextDocumentWillSave(logger jsonrpc.FunctionLogger, params *lsp.WillSaveTextDocumentParams) { + panic("unimplemented") +} + +func (server *IDELSPServer) TextDocumentDidSave(logger jsonrpc.FunctionLogger, params *lsp.DidSaveTextDocumentParams) { + server.ls.TextDocumentDidSaveNotifFromIDE(logger, params) +} + +func (server *IDELSPServer) TextDocumentDidClose(logger jsonrpc.FunctionLogger, params *lsp.DidCloseTextDocumentParams) { + server.ls.TextDocumentDidCloseNotifFromIDE(logger, params) +} diff --git a/handler/progress.go b/ls/progress.go similarity index 79% rename from handler/progress.go rename to ls/progress.go index ea2b997..8df523c 100644 --- a/handler/progress.go +++ b/ls/progress.go @@ -1,17 +1,16 @@ -package handler +package ls import ( "context" "log" "sync" - "github.com/arduino/arduino-language-server/lsp" "github.com/arduino/arduino-language-server/streams" - "github.com/sourcegraph/jsonrpc2" + "go.bug.st/lsp" ) type ProgressProxyHandler struct { - conn *jsonrpc2.Conn + conn *lsp.Server mux sync.Mutex actionRequiredCond *sync.Cond proxies map[string]*progressProxy @@ -35,19 +34,20 @@ type progressProxy struct { endReq *lsp.WorkDoneProgressEnd } -func NewProgressProxy(conn *jsonrpc2.Conn) *ProgressProxyHandler { +func NewProgressProxy(conn *lsp.Server) *ProgressProxyHandler { res := &ProgressProxyHandler{ conn: conn, proxies: map[string]*progressProxy{}, } res.actionRequiredCond = sync.NewCond(&res.mux) - go res.handlerLoop() + go func() { + defer streams.CatchAndLogPanic() + res.handlerLoop() + }() return res } func (p *ProgressProxyHandler) handlerLoop() { - defer streams.CatchAndLogPanic() - p.mux.Lock() defer p.mux.Unlock() @@ -70,24 +70,27 @@ func (p *ProgressProxyHandler) handlerLoop() { } func (p *ProgressProxyHandler) handleProxy(id string, proxy *progressProxy) { - ctx := context.Background() switch proxy.currentStatus { case progressProxyNew: p.mux.Unlock() - var res lsp.WorkDoneProgressCreateResult - err := p.conn.Call(ctx, "window/workDoneProgress/create", &lsp.WorkDoneProgressCreateParams{Token: id}, &res) - p.mux.Lock() - + respErr, err := p.conn.WindowWorkDoneProgressCreate(context.Background(), &lsp.WorkDoneProgressCreateParams{ + Token: lsp.EncodeMessage(id), + }) if err != nil { log.Printf("ProgressHandler: error creating token %s: %v", id, err) - } else { - proxy.currentStatus = progressProxyCreated + break + } + if respErr != nil { + log.Printf("ProgressHandler: error creating token %s: %v", id, respErr.AsError()) + break } + p.mux.Lock() + proxy.currentStatus = progressProxyCreated case progressProxyCreated: - err := p.conn.Notify(ctx, "$/progress", lsp.ProgressParams{ - Token: id, - Value: lsp.Raw(proxy.beginReq), + err := p.conn.Progress(&lsp.ProgressParams{ + Token: lsp.EncodeMessage(id), + Value: lsp.EncodeMessage(proxy.beginReq), }) proxy.beginReq = nil @@ -99,9 +102,10 @@ func (p *ProgressProxyHandler) handleProxy(id string, proxy *progressProxy) { case progressProxyBegin: if proxy.requiredStatus == progressProxyReport { - err := p.conn.Notify(ctx, "$/progress", &lsp.ProgressParams{ - Token: id, - Value: lsp.Raw(proxy.reportReq)}) + err := p.conn.Progress(&lsp.ProgressParams{ + Token: lsp.EncodeMessage(id), + Value: lsp.EncodeMessage(proxy.reportReq), + }) proxy.reportReq = nil if err != nil { @@ -111,9 +115,9 @@ func (p *ProgressProxyHandler) handleProxy(id string, proxy *progressProxy) { } } else if proxy.requiredStatus == progressProxyEnd { - err := p.conn.Notify(ctx, "$/progress", &lsp.ProgressParams{ - Token: id, - Value: lsp.Raw(proxy.endReq), + err := p.conn.Progress(&lsp.ProgressParams{ + Token: lsp.EncodeMessage(id), + Value: lsp.EncodeMessage(proxy.endReq), }) proxy.endReq = nil diff --git a/ls/unused.go b/ls/unused.go new file mode 100644 index 0000000..d870bbc --- /dev/null +++ b/ls/unused.go @@ -0,0 +1,58 @@ +package ls + +import ( + "regexp" + "strings" + + "github.com/arduino/arduino-language-server/streams" + "github.com/pkg/errors" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" +) + +func (ls *INOLanguageServer) handleError(logger jsonrpc.FunctionLogger, err error) error { + errorStr := err.Error() + var message string + if strings.Contains(errorStr, "#error") { + exp, regexpErr := regexp.Compile("#error \"(.*)\"") + if regexpErr != nil { + panic(regexpErr) + } + submatch := exp.FindStringSubmatch(errorStr) + message = submatch[1] + } else if strings.Contains(errorStr, "platform not installed") || strings.Contains(errorStr, "no FQBN provided") { + if ls.config.Fqbn != "" { + message = "Editor support may be inaccurate because the core for the board `" + ls.config.Fqbn + "` is not installed." + message += " Use the Boards Manager to install it." + } else { + // This case happens most often when the app is started for the first time and no + // board is selected yet. Don't bother the user with an error then. + return err + } + } else if strings.Contains(errorStr, "No such file or directory") { + exp, regexpErr := regexp.Compile(`([\w\.\-]+): No such file or directory`) + if regexpErr != nil { + panic(regexpErr) + } + submatch := exp.FindStringSubmatch(errorStr) + message = "Editor support may be inaccurate because the header `" + submatch[1] + "` was not found." + message += " If it is part of a library, use the Library Manager to install it." + } else { + message = "Could not start editor support.\n" + errorStr + } + go func() { + defer streams.CatchAndLogPanic() + ls.showMessage(logger, lsp.MessageTypeError, message) + }() + return errors.New(message) +} + +func (ls *INOLanguageServer) showMessage(logger jsonrpc.FunctionLogger, msgType lsp.MessageType, message string) { + params := lsp.ShowMessageParams{ + Type: msgType, + Message: message, + } + if err := ls.IDE.conn.WindowShowMessage(¶ms); err != nil { + logger.Logf("error sending showMessage notification: %s", err) + } +} diff --git a/lsp/LICENSE b/lsp/LICENSE deleted file mode 100644 index da96b67..0000000 --- a/lsp/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2018 Sourcegraph - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lsp/README.md b/lsp/README.md deleted file mode 100644 index 1a48ef0..0000000 --- a/lsp/README.md +++ /dev/null @@ -1,10 +0,0 @@ -This module has been imported from: https://github.com/sourcegraph/go-lsp - -# go-lsp - -Package lsp contains Go types for the messages used in the Language Server -Protocol. - -See -https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md -for more information. diff --git a/lsp/jsonrpc2.go b/lsp/jsonrpc2.go deleted file mode 100644 index 11e2b86..0000000 --- a/lsp/jsonrpc2.go +++ /dev/null @@ -1,52 +0,0 @@ -package lsp - -import ( - "encoding/json" - "strconv" -) - -// ID represents a JSON-RPC 2.0 request ID, which may be either a -// string or number (or null, which is unsupported). -type ID struct { - // At most one of Num or Str may be nonzero. If both are zero - // valued, then IsNum specifies which field's value is to be used - // as the ID. - Num uint64 - Str string - - // IsString controls whether the Num or Str field's value should be - // used as the ID, when both are zero valued. It must always be - // set to true if the request ID is a string. - IsString bool -} - -func (id ID) String() string { - if id.IsString { - return strconv.Quote(id.Str) - } - return strconv.FormatUint(id.Num, 10) -} - -// MarshalJSON implements json.Marshaler. -func (id ID) MarshalJSON() ([]byte, error) { - if id.IsString { - return json.Marshal(id.Str) - } - return json.Marshal(id.Num) -} - -// UnmarshalJSON implements json.Unmarshaler. -func (id *ID) UnmarshalJSON(data []byte) error { - // Support both uint64 and string IDs. - var v uint64 - if err := json.Unmarshal(data, &v); err == nil { - *id = ID{Num: v} - return nil - } - var v2 string - if err := json.Unmarshal(data, &v2); err != nil { - return err - } - *id = ID{Str: v2, IsString: true} - return nil -} diff --git a/lsp/protocol.go b/lsp/protocol.go deleted file mode 100644 index 7857db8..0000000 --- a/lsp/protocol.go +++ /dev/null @@ -1,330 +0,0 @@ -package lsp - -import ( - "context" - "encoding/json" - - "github.com/sourcegraph/jsonrpc2" -) - -func ReadParams(method string, raw *json.RawMessage) (interface{}, error) { - switch method { - case "initialize": - params := new(InitializeParams) - err := json.Unmarshal(*raw, params) - return params, err - case "initialized": - return &InitializedParams{}, nil - case "textDocument/didOpen": - params := new(DidOpenTextDocumentParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/didChange": - params := new(DidChangeTextDocumentParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/didSave": - params := new(DidSaveTextDocumentParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/didClose": - params := new(DidCloseTextDocumentParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/completion": - params := new(CompletionParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/codeAction": - params := new(CodeActionParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/signatureHelp": - fallthrough - case "textDocument/hover": - params := new(HoverParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/definition": - fallthrough - case "textDocument/typeDefinition": - fallthrough - case "textDocument/implementation": - fallthrough - case "textDocument/documentHighlight": - params := new(TextDocumentPositionParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/references": - params := new(ReferenceParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/formatting": - params := new(DocumentFormattingParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/rangeFormatting": - params := new(DocumentRangeFormattingParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/onTypeFormatting": - params := new(DocumentOnTypeFormattingParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/documentSymbol": - params := new(DocumentSymbolParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/rename": - params := new(RenameParams) - err := json.Unmarshal(*raw, params) - return params, err - case "workspace/symbol": - params := new(WorkspaceSymbolParams) - err := json.Unmarshal(*raw, params) - return params, err - case "workspace/didChangeWatchedFiles": - params := new(DidChangeWatchedFilesParams) - err := json.Unmarshal(*raw, params) - return params, err - case "workspace/executeCommand": - params := new(ExecuteCommandParams) - err := json.Unmarshal(*raw, params) - return params, err - case "workspace/applyEdit": - params := new(ApplyWorkspaceEditParams) - err := json.Unmarshal(*raw, params) - return params, err - case "textDocument/publishDiagnostics": - params := new(PublishDiagnosticsParams) - err := json.Unmarshal(*raw, params) - return params, err - case "arduino/selectedBoard": - params := new(BoardConfig) - err := json.Unmarshal(*raw, params) - return params, err - } - return nil, nil -} - -func SendRequest(ctx context.Context, conn *jsonrpc2.Conn, method string, params interface{}) (interface{}, error) { - switch method { - case "initialize": - result := new(InitializeResult) - err := conn.Call(ctx, method, params, result) - return result, err - case "textDocument/completion": - result := new(CompletionList) - err := conn.Call(ctx, method, params, result) - return result, err - case "textDocument/codeAction": - result := new([]CommandOrCodeAction) - err := conn.Call(ctx, method, params, result) - return result, err - case "completionItem/resolve": - result := new(CompletionItem) - err := conn.Call(ctx, method, params, result) - return result, err - case "textDocument/signatureHelp": - result := new(SignatureHelp) - err := conn.Call(ctx, method, params, result) - return result, err - case "textDocument/hover": - result := new(Hover) - err := conn.Call(ctx, method, params, result) - return result, err - case "textDocument/definition": - fallthrough - case "textDocument/typeDefinition": - fallthrough - case "textDocument/implementation": - fallthrough - case "textDocument/references": - result := new([]Location) - err := conn.Call(ctx, method, params, result) - return result, err - case "textDocument/documentHighlight": - result := new([]DocumentHighlight) - err := conn.Call(ctx, method, params, result) - return result, err - case "textDocument/formatting": - fallthrough - case "textDocument/rangeFormatting": - fallthrough - case "textDocument/onTypeFormatting": - result := new([]TextEdit) - err := conn.Call(ctx, method, params, result) - return result, err - case "textDocument/documentSymbol": - result := new(DocumentSymbolArrayOrSymbolInformationArray) - err := conn.Call(ctx, method, params, result) - return result, err - case "textDocument/rename": - result := new(WorkspaceEdit) - err := conn.Call(ctx, method, params, result) - return result, err - case "workspace/symbol": - result := new([]SymbolInformation) - err := conn.Call(ctx, method, params, result) - return result, err - case "window/showMessageRequest": - result := new(MessageActionItem) - err := conn.Call(ctx, method, params, result) - return result, err - case "workspace/executeCommand": - result := new(string) - err := conn.Call(ctx, method, params, result) - return result, err - case "workspace/applyEdit": - result := new(ApplyWorkspaceEditResponse) - err := conn.Call(ctx, method, params, result) - return result, err - } - var result interface{} - err := conn.Call(ctx, method, params, result) - return result, err -} - -// CodeAction structure according to LSP -type CodeAction struct { - Title string `json:"title"` - Kind string `json:"kind,omitempty"` - Diagnostics []Diagnostic `json:"diagnostics,omitempty"` - Edit *WorkspaceEdit `json:"edit,omitempty"` - Command *Command `json:"command,omitempty"` -} - -type CommandOrCodeAction struct { - Command *Command - CodeAction *CodeAction -} - -func (entry *CommandOrCodeAction) UnmarshalJSON(raw []byte) error { - command := new(Command) - err := json.Unmarshal(raw, command) - if err == nil && len(command.Command) > 0 { - entry.Command = command - return nil - } - codeAction := new(CodeAction) - err = json.Unmarshal(raw, codeAction) - if err != nil { - return err - } - entry.CodeAction = codeAction - return nil -} - -func (entry *CommandOrCodeAction) MarshalJSON() ([]byte, error) { - if entry.Command != nil { - return json.Marshal(entry.Command) - } - if entry.CodeAction != nil { - return json.Marshal(entry.CodeAction) - } - return nil, nil -} - -// Hover structure according to LSP -type Hover struct { - Contents MarkupContent `json:"contents"` - Range *Range `json:"range,omitempty"` -} - -// HoverParams structure according to LSP -type HoverParams struct { - TextDocumentPositionParams - // WorkDoneProgressParams -} - -// MarkupContent structure according to LSP -type MarkupContent struct { - Kind string `json:"kind"` - Value string `json:"value"` -} - -// DocumentSymbol structure according to LSP -type DocumentSymbol struct { - Name string `json:"name"` - Detail string `json:"detail,omitempty"` - Kind SymbolKind `json:"kind"` - Deprecated bool `json:"deprecated,omitempty"` - Range Range `json:"range"` - SelectionRange Range `json:"selectionRange"` - Children []DocumentSymbol `json:"children,omitempty"` -} - -type DocumentSymbolArrayOrSymbolInformationArray struct { - DocumentSymbolArray *[]DocumentSymbol - SymbolInformationArray *[]SymbolInformation -} - -func (entry *DocumentSymbolArrayOrSymbolInformationArray) UnmarshalJSON(raw []byte) error { - intermediate := []json.RawMessage{} - if err := json.Unmarshal(raw, &intermediate); err != nil { - return err - } - if len(intermediate) == 0 { - return nil - } - discriminator := struct { - Range *Range `json:"range,omitempty"` - Location *Location `json:"location,omitempty"` - }{} - if err := json.Unmarshal(intermediate[0], &discriminator); err != nil { - return err - } - if discriminator.Range != nil { - res := make([]DocumentSymbol, len(intermediate)) - for i, item := range intermediate { - if err := json.Unmarshal(item, &res[i]); err != nil { - return err - } - } - entry.DocumentSymbolArray = &res - } - if discriminator.Location != nil { - res := make([]SymbolInformation, len(intermediate)) - for i, item := range intermediate { - if err := json.Unmarshal(item, &res[i]); err != nil { - return err - } - } - entry.SymbolInformationArray = &res - } - return nil -} - -func (entry DocumentSymbolArrayOrSymbolInformationArray) MarshalJSON() ([]byte, error) { - if entry.DocumentSymbolArray != nil { - return json.Marshal(entry.DocumentSymbolArray) - } - if entry.SymbolInformationArray != nil { - return json.Marshal(entry.SymbolInformationArray) - } - return []byte("[]"), nil -} - -// ApplyWorkspaceEditParams structure according to LSP -type ApplyWorkspaceEditParams struct { - Label string `json:"label,omitempty"` - Edit WorkspaceEdit `json:"edit"` -} - -// ApplyWorkspaceEditResponse structure according to LSP -type ApplyWorkspaceEditResponse struct { - Applied bool `json:"applied"` - FailureReason string `json:"failureReason,omitempty"` -} - -// BoardConfig describes the board and port selected by the user. -type BoardConfig struct { - SelectedBoard Board `json:"selectedBoard"` - SelectedPort string `json:"selectedPort"` -} - -// Board structure. -type Board struct { - Name string `json:"name"` - Fqbn string `json:"fqbn"` -} diff --git a/lsp/protocol_test.go b/lsp/protocol_test.go deleted file mode 100644 index 8599f1c..0000000 --- a/lsp/protocol_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package lsp - -import ( - "encoding/json" - "fmt" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestDocumentSymbolParse(t *testing.T) { - docin := ` - [ - { - "kind":12, - "name":"setup", - "range": {"end": {"character":11,"line":6},"start": {"character":0,"line":6}}, - "selectionRange":{"end":{"character":10,"line":6},"start":{"character":5,"line":6}} - },{ - "kind":12, - "name":"newfunc", - "range":{"end":{"character":13,"line":8},"start":{"character":0,"line":8}}, - "selectionRange":{"end":{"character":12,"line":8},"start":{"character":5,"line":8}} - },{ - "kind":12, - "name":"loop", - "range":{"end":{"character":10,"line":10},"start":{"character":0,"line":10}}, - "selectionRange":{"end":{"character":9,"line":10},"start":{"character":5,"line":10}} - },{ - "kind":12, - "name":"secondFunction", - "range":{"end":{"character":20,"line":12},"start":{"character":0,"line":12}}, - "selectionRange":{"end":{"character":19,"line":12},"start":{"character":5,"line":12}} - },{ - "kind":12, - "name":"setup", - "range":{"end":{"character":0,"line":21},"start":{"character":0,"line":14}}, - "selectionRange":{"end":{"character":10,"line":14},"start":{"character":5,"line":14}} - },{ - "kind":12, - "name":"newfunc", - "range":{"end":{"character":16,"line":23},"start":{"character":0,"line":23}}, - "selectionRange":{"end":{"character":12,"line":23},"start":{"character":5,"line":23}} - },{ - "kind":12, - "name":"loop", - "range":{"end":{"character":0,"line":26},"start":{"character":0,"line":24}}, - "selectionRange":{"end":{"character":9,"line":24},"start":{"character":5,"line":24}} - },{ - "kind":12, - "name":"secondFunction", - "range":{"end":{"character":0,"line":32},"start":{"character":0,"line":30}}, - "selectionRange":{"end":{"character":19,"line":30},"start":{"character":5,"line":30}} - } - ]` - var res DocumentSymbolArrayOrSymbolInformationArray - err := json.Unmarshal([]byte(docin), &res) - require.NoError(t, err) - require.NotNil(t, res.DocumentSymbolArray) - symbols := *res.DocumentSymbolArray - require.Equal(t, SymbolKind(12), symbols[2].Kind) - require.Equal(t, "loop", symbols[2].Name) - require.Equal(t, "10:0-10:10", symbols[2].Range.String()) - require.Equal(t, "10:5-10:9", symbols[2].SelectionRange.String()) - fmt.Printf("%+v\n", res) -} - -func TestVariousMessages(t *testing.T) { - t.Run("ProgressParamsMarshalUnmarshal", func(t *testing.T) { - x := &ProgressParams{ - Token: "token", - Value: Raw(WorkDoneProgressBegin{ - Title: "some work", - }), - } - data, err := json.Marshal(&x) - require.NoError(t, err) - require.JSONEq(t, `{"token":"token", "value":{"kind":"begin","title":"some work"}}`, string(data)) - }) - - t.Run("WorkDoneProgressBegin", func(t *testing.T) { - var begin WorkDoneProgressBegin - err := json.Unmarshal([]byte(`{"kind":"begin","title":"some work"}`), &begin) - require.NoError(t, err) - }) - - t.Run("WorkDoneProgressReport", func(t *testing.T) { - var report WorkDoneProgressReport - err := json.Unmarshal([]byte(`{"kind":"report","message":"28/29","percentage":96.551724137931032}`), &report) - require.NoError(t, err) - }) - - t.Run("InitializeResult", func(t *testing.T) { - msg := `{ - "capabilities":{ - "codeActionProvider":{ - "codeActionKinds":["quickfix","refactor","info"]}, - "completionProvider":{ - "allCommitCharacters":[" ","\t","(",")","[","]","{","}","<",">",":",";",",","+","-","/","*","%","^","&","#","?",".","=","\"","'","|"], - "resolveProvider":false, - "triggerCharacters":[".","<",">",":","\"","/"]}, - "declarationProvider":true, - "definitionProvider":true, - "documentFormattingProvider":true, - "documentHighlightProvider":true, - "documentLinkProvider":{"resolveProvider":false}, - "documentOnTypeFormattingProvider":{ - "firstTriggerCharacter":"\n", - "moreTriggerCharacter":[]}, - "documentRangeFormattingProvider":true, - "documentSymbolProvider":true, - "executeCommandProvider":{"commands":["clangd.applyFix","clangd.applyTweak"]}, - "hoverProvider":true, - "referencesProvider":true, - "renameProvider":{"prepareProvider":true}, - "selectionRangeProvider":true, - "semanticTokensProvider":{ - "full":{"delta":true}, - "legend":{ - "tokenModifiers":[], - "tokenTypes":["variable","variable","parameter","function","member","function","member","variable","class","enum","enumConstant","type","dependent","dependent","namespace","typeParameter","concept","type","macro","comment"] - }, - "range":false}, - "signatureHelpProvider":{"triggerCharacters":["(",","]}, - "textDocumentSync":{ - "change":2, - "openClose":true, - "save":true - }, - "typeHierarchyProvider":true, - "workspaceSymbolProvider":true - }, - "serverInfo":{"name":"clangd","version":"clangd version 11.0.0 (https://github.com/llvm/llvm-project 176249bd6732a8044d457092ed932768724a6f06)"}}` - var init InitializeResult - err := json.Unmarshal([]byte(msg), &init) - require.NoError(t, err) - }) - - t.Run("DocumentSymbol", func(t *testing.T) { - msg := `[{"kind":12,"name":"setup","range":{"end":{"character":12,"line":5},"start":{"character":0,"line":5}},"selectionRange": - {"end":{"character":10,"line":5},"start":{"character":5,"line":5}}},{"kind":12,"name":"newfunc","range":{"end":{"character":14,"line":7}, - "start":{"character":0,"line":7}},"selectionRange":{"end":{"character":12,"line":7},"start":{"character":5,"line":7}}},{"kind":12,"name": - "altro","range":{"end":{"character":12,"line":9},"start":{"character":0,"line":9}},"selectionRange":{"end":{"character":10,"line":9},"start": - {"character":5,"line":9}}},{"kind":12,"name":"ancora","range":{"end":{"character":18,"line":11},"start":{"character":0,"line":11}}, - "selectionRange":{"end":{"character":11,"line":11},"start":{"character":5,"line":11}}},{"kind":12,"name":"loop","range":{"end":{ - "character":11,"line":13},"start":{"character":0,"line":13}},"selectionRange":{"end":{"character":9,"line":13},"start":{"character":5, - "line":13}}},{"kind":12,"name":"secondFunction","range":{"end":{"character":21,"line":15},"start":{"character":0,"line":15}}, - "selectionRange":{"end":{"character":19,"line":15},"start":{"character":5,"line":15}}},{"kind":12,"name":"setup","range":{"end":{ - "character":1,"line":34},"start":{"character":0,"line":17}},"selectionRange":{"end":{"character":10,"line":17},"start":{"character":5, - "line":17}}},{"kind":12,"name":"newfunc","range":{"end":{"character":1,"line":40},"start":{"character":0,"line":36}},"selectionRange": - {"end":{"character":12,"line":36},"start":{"character":5,"line":36}}},{"kind":12,"name":"altro","range":{"end":{"character":38,"line":42}, - "start":{"character":0,"line":42}},"selectionRange":{"end":{"character":10,"line":42},"start":{"character":5,"line":42}}},{"kind":12, - "name":"ancora","range":{"end":{"character":21,"line":47},"start":{"character":0,"line":47}},"selectionRange":{"end":{"character":11, - "line":47},"start":{"character":5,"line":47}}},{"kind":12,"name":"loop","range":{"end":{"character":24,"line":49},"start":{"character":0, - "line":49}},"selectionRange":{"end":{"character":9,"line":49},"start":{"character":5,"line":49}}},{"kind":12,"name":"secondFunction", - "range":{"end":{"character":38,"line":53},"start":{"character":0,"line":53}},"selectionRange":{"end":{"character":19,"line":53},"start": - {"character":5,"line":53}}}]` - var symbol DocumentSymbolArrayOrSymbolInformationArray - err := json.Unmarshal([]byte(msg), &symbol) - require.NoError(t, err) - }) - - t.Run("EmptyDocumentSymbolMarshalUnmarshal", func(t *testing.T) { - var symbol DocumentSymbolArrayOrSymbolInformationArray - err := json.Unmarshal([]byte(`[]`), &symbol) - require.NoError(t, err) - data, err := json.Marshal(symbol) - require.Equal(t, "[]", string(data)) - data, err = json.Marshal(&symbol) - require.Equal(t, "[]", string(data)) - }) -} diff --git a/lsp/service.go b/lsp/service.go deleted file mode 100644 index de2e2a9..0000000 --- a/lsp/service.go +++ /dev/null @@ -1,1198 +0,0 @@ -package lsp - -import ( - "bytes" - "encoding/base64" - "encoding/binary" - "encoding/json" - "errors" - "strings" -) - -type None struct{} - -type InitializeParams struct { - ProcessID int `json:"processId,omitempty"` - - // RootPath is DEPRECATED in favor of the RootURI field. - RootPath string `json:"rootPath,omitempty"` - - RootURI DocumentURI `json:"rootUri,omitempty"` - ClientInfo ClientInfo `json:"clientInfo,omitempty"` - Trace Trace `json:"trace,omitempty"` - InitializationOptions interface{} `json:"initializationOptions,omitempty"` - Capabilities ClientCapabilities `json:"capabilities"` - - WorkDoneToken string `json:"workDoneToken,omitempty"` -} - -type InitializedParams struct{} - -type ClientInfo struct { - Name string `json:"name,omitempty"` - Version string `json:"version,omitempty"` -} - -type Trace string - -type ClientCapabilities struct { - Workspace WorkspaceClientCapabilities `json:"workspace,omitempty"` - TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"` - Window WindowClientCapabilities `json:"window,omitempty"` - Experimental interface{} `json:"experimental,omitempty"` - - // Below are Sourcegraph extensions. They do not live in lspext since - // they are extending the field InitializeParams.Capabilities - - // XFilesProvider indicates the client provides support for - // workspace/xfiles. This is a Sourcegraph extension. - XFilesProvider bool `json:"xfilesProvider,omitempty"` - - // XContentProvider indicates the client provides support for - // textDocument/xcontent. This is a Sourcegraph extension. - XContentProvider bool `json:"xcontentProvider,omitempty"` - - // XCacheProvider indicates the client provides support for cache/get - // and cache/set. - XCacheProvider bool `json:"xcacheProvider,omitempty"` -} - -type WorkspaceClientCapabilities struct { - WorkspaceEdit struct { - DocumentChanges bool `json:"documentChanges,omitempty"` - ResourceOperations []string `json:"resourceOperations,omitempty"` - } `json:"workspaceEdit,omitempty"` - - ApplyEdit bool `json:"applyEdit,omitempty"` - - Symbol struct { - SymbolKind struct { - ValueSet []int `json:"valueSet,omitempty"` - } `json:"symbolKind,omitempty"` - } `json:"symbol,omitempty"` - - ExecuteCommand *struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - } `json:"executeCommand,omitempty"` - - DidChangeWatchedFiles *struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - } `json:"didChangeWatchedFiles,omitempty"` - - WorkspaceFolders bool `json:"workspaceFolders,omitempty"` - - Configuration bool `json:"configuration,omitempty"` -} - -type TextDocumentClientCapabilities struct { - Declaration *struct { - LinkSupport bool `json:"linkSupport,omitempty"` - } `json:"declaration,omitempty"` - - Definition *struct { - LinkSupport bool `json:"linkSupport,omitempty"` - } `json:"definition,omitempty"` - - Implementation *struct { - LinkSupport bool `json:"linkSupport,omitempty"` - - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - } `json:"implementation,omitempty"` - - TypeDefinition *struct { - LinkSupport bool `json:"linkSupport,omitempty"` - } `json:"typeDefinition,omitempty"` - - Synchronization *struct { - WillSave bool `json:"willSave,omitempty"` - DidSave bool `json:"didSave,omitempty"` - WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"` - } `json:"synchronization,omitempty"` - - DocumentSymbol struct { - SymbolKind struct { - ValueSet []int `json:"valueSet,omitempty"` - } `json:"symbolKind,omitempty"` - - HierarchicalDocumentSymbolSupport bool `json:"hierarchicalDocumentSymbolSupport,omitempty"` - } `json:"documentSymbol,omitempty"` - - Formatting *struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - } `json:"formatting,omitempty"` - - RangeFormatting *struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - } `json:"rangeFormatting,omitempty"` - - Rename *struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - - PrepareSupport bool `json:"prepareSupport,omitempty"` - } `json:"rename,omitempty"` - - SemanticHighlightingCapabilities *struct { - SemanticHighlighting bool `json:"semanticHighlighting,omitempty"` - } `json:"semanticHighlightingCapabilities,omitempty"` - - CodeAction struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - - IsPreferredSupport bool `json:"isPreferredSupport,omitempty"` - - CodeActionLiteralSupport struct { - CodeActionKind struct { - ValueSet []CodeActionKind `json:"valueSet,omitempty"` - } `json:"codeActionKind,omitempty"` - } `json:"codeActionLiteralSupport,omitempty"` - } `json:"codeAction,omitempty"` - - Completion struct { - CompletionItem struct { - DocumentationFormat []DocumentationFormat `json:"documentationFormat,omitempty"` - SnippetSupport bool `json:"snippetSupport,omitempty"` - } `json:"completionItem,omitempty"` - - CompletionItemKind struct { - ValueSet []CompletionItemKind `json:"valueSet,omitempty"` - } `json:"completionItemKind,omitempty"` - - ContextSupport bool `json:"contextSupport,omitempty"` - } `json:"completion,omitempty"` - - SignatureHelp *struct { - SignatureInformation struct { - ParameterInformation struct { - LabelOffsetSupport bool `json:"labelOffsetSupport,omitempty"` - } `json:"parameterInformation,omitempty"` - } `json:"signatureInformation,omitempty"` - } `json:"signatureHelp,omitempty"` - - DocumentLink *struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - - TooltipSupport bool `json:"tooltipSupport,omitempty"` - } `json:"documentLink,omitempty"` - - Hover *struct { - ContentFormat []string `json:"contentFormat,omitempty"` - } `json:"hover,omitempty"` - - FoldingRange *struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - - RangeLimit interface{} `json:"rangeLimit,omitempty"` - - LineFoldingOnly bool `json:"lineFoldingOnly,omitempty"` - } `json:"foldingRange,omitempty"` - - CallHierarchy *struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - } `json:"callHierarchy,omitempty"` - - ColorProvider *struct { - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - } `json:"colorProvider,omitempty"` -} - -type WindowClientCapabilities struct { - WorkDoneProgress bool `json:"workDoneProgress,omitempty"` -} - -type InitializeResult struct { - Capabilities ServerCapabilities `json:"capabilities,omitempty"` -} - -type InitializeError struct { - Retry bool `json:"retry"` -} - -type ResourceOperation string - -const ( - ROCreate ResourceOperation = "create" - RODelete ResourceOperation = "delete" - RORename ResourceOperation = "rename" -) - -// TextDocumentSyncKind is a DEPRECATED way to describe how text -// document syncing works. Use TextDocumentSyncOptions instead (or the -// Options field of TextDocumentSyncOptionsOrKind if you need to -// support JSON-(un)marshaling both). -type TextDocumentSyncKind int - -var ( - TDSKNone TextDocumentSyncKind = 0 - TDSKFull TextDocumentSyncKind = 1 - TDSKIncremental TextDocumentSyncKind = 2 -) - -type TextDocumentSyncOptions struct { - OpenClose bool `json:"openClose,omitempty"` - Change TextDocumentSyncKind `json:"change"` - WillSave bool `json:"willSave,omitempty"` - WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"` - Save *BoolOrSaveOptions `json:"save,omitempty"` -} - -type BoolOrSaveOptions struct { - Save *bool - SaveOptions *SaveOptions -} - -// MarshalJSON implements json.Marshaler. -func (v *BoolOrSaveOptions) MarshalJSON() ([]byte, error) { - if v.Save != nil { - return json.Marshal(v.Save) - } - if v.SaveOptions != nil { - return json.Marshal(v.SaveOptions) - } - return []byte("null"), nil -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *BoolOrSaveOptions) UnmarshalJSON(data []byte) error { - if bytes.Equal(data, []byte("null")) { - v.Save = nil - v.SaveOptions = nil - return nil - } - var save bool - if err := json.Unmarshal(data, &save); err == nil { - v.Save = &save - v.SaveOptions = nil - return nil - } - var saveOpts SaveOptions - if err := json.Unmarshal(data, &saveOpts); err != nil { - return err - } - v.Save = nil - v.SaveOptions = &saveOpts - return nil -} - -// TextDocumentSyncOptions holds either a TextDocumentSyncKind or -// TextDocumentSyncOptions. The LSP API allows either to be specified -// in the (ServerCapabilities).TextDocumentSync field. -type TextDocumentSyncOptionsOrKind struct { - Kind *TextDocumentSyncKind - Options *TextDocumentSyncOptions -} - -// MarshalJSON implements json.Marshaler. -func (v *TextDocumentSyncOptionsOrKind) MarshalJSON() ([]byte, error) { - if v == nil { - return []byte("null"), nil - } - if v.Kind != nil { - return json.Marshal(v.Kind) - } - return json.Marshal(v.Options) -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *TextDocumentSyncOptionsOrKind) UnmarshalJSON(data []byte) error { - if bytes.Equal(data, []byte("null")) { - *v = TextDocumentSyncOptionsOrKind{} - return nil - } - var kind TextDocumentSyncKind - if err := json.Unmarshal(data, &kind); err == nil { - // Create equivalent TextDocumentSyncOptions using the same - // logic as in vscode-languageclient. Also set the Kind field - // so that JSON-marshaling and unmarshaling are inverse - // operations (for backward compatibility, preserving the - // original input but accepting both). - *v = TextDocumentSyncOptionsOrKind{ - Options: &TextDocumentSyncOptions{OpenClose: true, Change: kind}, - Kind: &kind, - } - return nil - } - var tmp TextDocumentSyncOptions - if err := json.Unmarshal(data, &tmp); err != nil { - return err - } - *v = TextDocumentSyncOptionsOrKind{Options: &tmp} - return nil -} - -type SaveOptions struct { - IncludeText bool `json:"includeText"` -} - -type ServerCapabilities struct { - TextDocumentSync *TextDocumentSyncOptionsOrKind `json:"textDocumentSync,omitempty"` - HoverProvider bool `json:"hoverProvider,omitempty"` - CompletionProvider *CompletionOptions `json:"completionProvider,omitempty"` - SignatureHelpProvider *SignatureHelpOptions `json:"signatureHelpProvider,omitempty"` - DefinitionProvider bool `json:"definitionProvider,omitempty"` - TypeDefinitionProvider bool `json:"typeDefinitionProvider,omitempty"` - ReferencesProvider bool `json:"referencesProvider,omitempty"` - DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"` - DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` - WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` - ImplementationProvider bool `json:"implementationProvider,omitempty"` - CodeActionProvider *BoolOrCodeActionOptions `json:"codeActionProvider,omitempty"` - CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"` - DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` - DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` - DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"` - RenameProvider *BoolOrRenameOptions `json:"renameProvider,omitempty"` - ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"` - SemanticHighlighting *SemanticHighlightingOptions `json:"semanticHighlighting,omitempty"` - - // XWorkspaceReferencesProvider indicates the server provides support for - // xworkspace/references. This is a Sourcegraph extension. - XWorkspaceReferencesProvider bool `json:"xworkspaceReferencesProvider,omitempty"` - - // XDefinitionProvider indicates the server provides support for - // textDocument/xdefinition. This is a Sourcegraph extension. - XDefinitionProvider bool `json:"xdefinitionProvider,omitempty"` - - // XWorkspaceSymbolByProperties indicates the server provides support for - // querying symbols by properties with WorkspaceSymbolParams.symbol. This - // is a Sourcegraph extension. - XWorkspaceSymbolByProperties bool `json:"xworkspaceSymbolByProperties,omitempty"` - - Experimental interface{} `json:"experimental,omitempty"` -} - -type CompletionOptions struct { - ResolveProvider bool `json:"resolveProvider,omitempty"` - TriggerCharacters []string `json:"triggerCharacters,omitempty"` -} - -type DocumentOnTypeFormattingOptions struct { - FirstTriggerCharacter string `json:"firstTriggerCharacter"` - MoreTriggerCharacter []string `json:"moreTriggerCharacter,omitempty"` -} - -type CodeLensOptions struct { - ResolveProvider bool `json:"resolveProvider,omitempty"` -} - -type SignatureHelpOptions struct { - TriggerCharacters []string `json:"triggerCharacters,omitempty"` -} - -type CodeActionOptions struct { - CodeActionKinds *[]CodeActionKind `json:"codeActionKinds,omitempty` - ResolveProvider *bool `json:"resolveProvider,omitempty` -} - -type BoolOrCodeActionOptions struct { - IsProvider *bool - Options *CodeActionOptions -} - -// MarshalJSON implements json.Marshaler. -func (v *BoolOrCodeActionOptions) MarshalJSON() ([]byte, error) { - if v.IsProvider != nil { - return json.Marshal(v.IsProvider) - } - if v.Options != nil { - return json.Marshal(v.Options) - } - return []byte("null"), nil -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *BoolOrCodeActionOptions) UnmarshalJSON(data []byte) error { - if bytes.Equal(data, []byte("null")) { - v.IsProvider = nil - v.Options = nil - return nil - } - var b bool - if err := json.Unmarshal(data, &b); err == nil { - v.IsProvider = &b - v.Options = nil - return nil - } - var opts CodeActionOptions - if err := json.Unmarshal(data, &opts); err != nil { - return err - } - v.IsProvider = nil - v.Options = &opts - return nil -} - -type ExecuteCommandOptions struct { - Commands []string `json:"commands"` -} - -type ExecuteCommandParams struct { - Command string `json:"command"` - Arguments []interface{} `json:"arguments,omitempty"` -} - -type RenameOptions struct { - PrepareProvider *bool `json:"prepareProvider,omitempty` -} - -type BoolOrRenameOptions struct { - IsProvider *bool - Options *RenameOptions -} - -// MarshalJSON implements json.Marshaler. -func (v *BoolOrRenameOptions) MarshalJSON() ([]byte, error) { - if v.IsProvider != nil { - return json.Marshal(v.IsProvider) - } - if v.Options != nil { - return json.Marshal(v.Options) - } - return []byte("null"), nil -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *BoolOrRenameOptions) UnmarshalJSON(data []byte) error { - if bytes.Equal(data, []byte("null")) { - v.IsProvider = nil - v.Options = nil - return nil - } - var b bool - if err := json.Unmarshal(data, &b); err == nil { - v.IsProvider = &b - v.Options = nil - return nil - } - var opts RenameOptions - if err := json.Unmarshal(data, &opts); err != nil { - return err - } - v.IsProvider = nil - v.Options = &opts - return nil -} - -type SemanticHighlightingOptions struct { - Scopes [][]string `json:"scopes,omitempty"` -} - -type CompletionItemKind int - -const ( - _ CompletionItemKind = iota - CIKText - CIKMethod - CIKFunction - CIKConstructor - CIKField - CIKVariable - CIKClass - CIKInterface - CIKModule - CIKProperty - CIKUnit - CIKValue - CIKEnum - CIKKeyword - CIKSnippet - CIKColor - CIKFile - CIKReference - CIKFolder - CIKEnumMember - CIKConstant - CIKStruct - CIKEvent - CIKOperator - CIKTypeParameter -) - -func (c CompletionItemKind) String() string { - return completionItemKindName[c] -} - -var completionItemKindName = map[CompletionItemKind]string{ - CIKText: "text", - CIKMethod: "method", - CIKFunction: "function", - CIKConstructor: "constructor", - CIKField: "field", - CIKVariable: "variable", - CIKClass: "class", - CIKInterface: "interface", - CIKModule: "module", - CIKProperty: "property", - CIKUnit: "unit", - CIKValue: "value", - CIKEnum: "enum", - CIKKeyword: "keyword", - CIKSnippet: "snippet", - CIKColor: "color", - CIKFile: "file", - CIKReference: "reference", - CIKFolder: "folder", - CIKEnumMember: "enumMember", - CIKConstant: "constant", - CIKStruct: "struct", - CIKEvent: "event", - CIKOperator: "operator", - CIKTypeParameter: "typeParameter", -} - -type CompletionItem struct { - Label string `json:"label"` - Kind CompletionItemKind `json:"kind,omitempty"` - Detail string `json:"detail,omitempty"` - Documentation *StringOrMarkupContent `json:"documentation,omitempty"` - SortText string `json:"sortText,omitempty"` - FilterText string `json:"filterText,omitempty"` - InsertText string `json:"insertText,omitempty"` - InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"` - TextEdit *TextEdit `json:"textEdit,omitempty"` - Data interface{} `json:"data,omitempty"` -} - -type StringOrMarkupContent struct { - String *string - MarkupContent *MarkupContent -} - -// MarshalJSON implements json.Marshaler. -func (v *StringOrMarkupContent) MarshalJSON() ([]byte, error) { - if v.String != nil { - return json.Marshal(v.String) - } - return json.Marshal(v.MarkupContent) -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *StringOrMarkupContent) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err == nil { - v.String = &s - v.MarkupContent = nil - return nil - } - v.String = nil - err := json.Unmarshal(data, &v.MarkupContent) - return err -} - -type CompletionList struct { - IsIncomplete bool `json:"isIncomplete"` - Items []CompletionItem `json:"items"` -} - -type CompletionTriggerKind int - -const ( - CTKInvoked CompletionTriggerKind = 1 - CTKTriggerCharacter = 2 -) - -type DocumentationFormat string - -const ( - DFPlainText DocumentationFormat = "plaintext" -) - -type CodeActionKind string - -const ( - CAKEmpty CodeActionKind = "" - CAKQuickFix CodeActionKind = "quickfix" - CAKRefactor CodeActionKind = "refactor" - CAKRefactorExtract CodeActionKind = "refactor.extract" - CAKRefactorInline CodeActionKind = "refactor.inline" - CAKRefactorRewrite CodeActionKind = "refactor.rewrite" - CAKSource CodeActionKind = "source" - CAKSourceOrganizeImports CodeActionKind = "source.organizeImports" -) - -type InsertTextFormat int - -const ( - ITFPlainText InsertTextFormat = 1 - ITFSnippet = 2 -) - -type CompletionContext struct { - TriggerKind CompletionTriggerKind `json:"triggerKind"` - TriggerCharacter string `json:"triggerCharacter,omitempty"` -} - -type CompletionParams struct { - TextDocumentPositionParams - Context CompletionContext `json:"context,omitempty"` -} - -// type Hover struct { -// Contents []MarkedString `json:"contents"` -// Range *Range `json:"range,omitempty"` -// } - -// type hover Hover - -// func (h Hover) MarshalJSON() ([]byte, error) { -// if h.Contents == nil { -// return json.Marshal(hover{ -// Contents: []MarkedString{}, -// Range: h.Range, -// }) -// } -// return json.Marshal(hover(h)) -// } - -type MarkedString markedString - -type markedString struct { - Language string `json:"language"` - Value string `json:"value"` - - isRawString bool -} - -func (m *MarkedString) UnmarshalJSON(data []byte) error { - if d := strings.TrimSpace(string(data)); len(d) > 0 && d[0] == '"' { - // Raw string - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - m.Value = s - m.isRawString = true - return nil - } - // Language string - ms := (*markedString)(m) - return json.Unmarshal(data, ms) -} - -func (m MarkedString) MarshalJSON() ([]byte, error) { - if m.isRawString { - return json.Marshal(m.Value) - } - return json.Marshal((markedString)(m)) -} - -// RawMarkedString returns a MarkedString consisting of only a raw -// string (i.e., "foo" instead of {"value":"foo", "language":"bar"}). -func RawMarkedString(s string) MarkedString { - return MarkedString{Value: s, isRawString: true} -} - -type SignatureHelp struct { - Signatures []SignatureInformation `json:"signatures"` - ActiveSignature int `json:"activeSignature"` - ActiveParameter int `json:"activeParameter"` -} - -type SignatureInformation struct { - Label string `json:"label"` - Documentation string `json:"documentation,omitempty"` - Parameters []ParameterInformation `json:"parameters,omitempty"` -} - -type ParameterInformation struct { - Label string `json:"label"` - Documentation string `json:"documentation,omitempty"` -} - -type ReferenceContext struct { - IncludeDeclaration bool `json:"includeDeclaration"` - - // Sourcegraph extension - XLimit int `json:"xlimit,omitempty"` -} - -type ReferenceParams struct { - TextDocumentPositionParams - Context ReferenceContext `json:"context"` -} - -type DocumentHighlightKind int - -const ( - Text DocumentHighlightKind = 1 - Read = 2 - Write = 3 -) - -type DocumentHighlight struct { - Range Range `json:"range"` - Kind int `json:"kind,omitempty"` -} - -type DocumentSymbolParams struct { - TextDocument TextDocumentIdentifier `json:"textDocument"` -} - -type SymbolKind int - -// The SymbolKind values are defined at https://microsoft.github.io/language-server-protocol/specification. -const ( - SKFile SymbolKind = 1 - SKModule SymbolKind = 2 - SKNamespace SymbolKind = 3 - SKPackage SymbolKind = 4 - SKClass SymbolKind = 5 - SKMethod SymbolKind = 6 - SKProperty SymbolKind = 7 - SKField SymbolKind = 8 - SKConstructor SymbolKind = 9 - SKEnum SymbolKind = 10 - SKInterface SymbolKind = 11 - SKFunction SymbolKind = 12 - SKVariable SymbolKind = 13 - SKConstant SymbolKind = 14 - SKString SymbolKind = 15 - SKNumber SymbolKind = 16 - SKBoolean SymbolKind = 17 - SKArray SymbolKind = 18 - SKObject SymbolKind = 19 - SKKey SymbolKind = 20 - SKNull SymbolKind = 21 - SKEnumMember SymbolKind = 22 - SKStruct SymbolKind = 23 - SKEvent SymbolKind = 24 - SKOperator SymbolKind = 25 - SKTypeParameter SymbolKind = 26 -) - -func (s SymbolKind) String() string { - return symbolKindName[s] -} - -var symbolKindName = map[SymbolKind]string{ - SKFile: "File", - SKModule: "Module", - SKNamespace: "Namespace", - SKPackage: "Package", - SKClass: "Class", - SKMethod: "Method", - SKProperty: "Property", - SKField: "Field", - SKConstructor: "Constructor", - SKEnum: "Enum", - SKInterface: "Interface", - SKFunction: "Function", - SKVariable: "Variable", - SKConstant: "Constant", - SKString: "String", - SKNumber: "Number", - SKBoolean: "Boolean", - SKArray: "Array", - SKObject: "Object", - SKKey: "Key", - SKNull: "Null", - SKEnumMember: "EnumMember", - SKStruct: "Struct", - SKEvent: "Event", - SKOperator: "Operator", - SKTypeParameter: "TypeParameter", -} - -type SymbolInformation struct { - Name string `json:"name"` - Kind SymbolKind `json:"kind"` - Location Location `json:"location"` - ContainerName string `json:"containerName,omitempty"` -} - -type WorkspaceSymbolParams struct { - Query string `json:"query"` - Limit int `json:"limit"` -} - -type ConfigurationParams struct { - Items []ConfigurationItem `json:"items"` -} - -type ConfigurationItem struct { - ScopeURI string `json:"scopeUri,omitempty"` - Section string `json:"section,omitempty"` -} - -type ConfigurationResult []interface{} - -type CodeActionContext struct { - Diagnostics []Diagnostic `json:"diagnostics"` -} - -type CodeActionParams struct { - TextDocument TextDocumentIdentifier `json:"textDocument"` - Range Range `json:"range"` - Context CodeActionContext `json:"context"` -} - -type CodeLensParams struct { - TextDocument TextDocumentIdentifier `json:"textDocument"` -} - -type CodeLens struct { - Range Range `json:"range"` - Command Command `json:"command,omitempty"` - Data interface{} `json:"data,omitempty"` -} - -type DocumentFormattingParams struct { - TextDocument TextDocumentIdentifier `json:"textDocument"` - Options FormattingOptions `json:"options"` -} - -type FormattingOptions struct { - TabSize int `json:"tabSize"` - InsertSpaces bool `json:"insertSpaces"` - Key string `json:"key"` -} - -type RenameParams struct { - TextDocument TextDocumentIdentifier `json:"textDocument"` - Position Position `json:"position"` - NewName string `json:"newName"` -} - -type DidOpenTextDocumentParams struct { - TextDocument TextDocumentItem `json:"textDocument"` -} - -type DidChangeTextDocumentParams struct { - TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` - ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"` -} - -type TextDocumentContentChangeEvent struct { - Range *Range `json:"range,omitempty"` - RangeLength uint `json:"rangeLength,omitempty"` - Text string `json:"text"` -} - -type DidCloseTextDocumentParams struct { - TextDocument TextDocumentIdentifier `json:"textDocument"` -} - -type DidSaveTextDocumentParams struct { - TextDocument TextDocumentIdentifier `json:"textDocument"` -} - -type MessageType int - -const ( - MTError MessageType = 1 - MTWarning = 2 - Info = 3 - Log = 4 -) - -type ShowMessageParams struct { - Type MessageType `json:"type"` - Message string `json:"message"` -} - -type MessageActionItem struct { - Title string `json:"title"` -} - -type ShowMessageRequestParams struct { - Type MessageType `json:"type"` - Message string `json:"message"` - Actions []MessageActionItem `json:"actions"` -} - -type LogMessageParams struct { - Type MessageType `json:"type"` - Message string `json:"message"` -} - -type DidChangeConfigurationParams struct { - Settings interface{} `json:"settings"` -} - -type FileChangeType int - -const ( - Created FileChangeType = 1 - Changed = 2 - Deleted = 3 -) - -type FileEvent struct { - URI DocumentURI `json:"uri"` - Type int `json:"type"` -} - -type DidChangeWatchedFilesParams struct { - Changes []FileEvent `json:"changes"` -} - -type PublishDiagnosticsParams struct { - URI DocumentURI `json:"uri"` - Diagnostics []Diagnostic `json:"diagnostics"` -} - -type DocumentRangeFormattingParams struct { - TextDocument TextDocumentIdentifier `json:"textDocument"` - Range Range `json:"range"` - Options FormattingOptions `json:"options"` -} - -type DocumentOnTypeFormattingParams struct { - TextDocument TextDocumentIdentifier `json:"textDocument"` - Position Position `json:"position"` - Ch string `json:"ch"` - Options FormattingOptions `json:"formattingOptions"` -} - -type CancelParams struct { - ID ID `json:"id"` -} - -type SemanticHighlightingParams struct { - TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` - Lines []SemanticHighlightingInformation `json:"lines"` -} - -// SemanticHighlightingInformation represents a semantic highlighting -// information that has to be applied on a specific line of the text -// document. -type SemanticHighlightingInformation struct { - // Line is the zero-based line position in the text document. - Line int `json:"line"` - - // Tokens is a base64 encoded string representing every single highlighted - // characters with its start position, length and the "lookup table" index of - // the semantic highlighting [TextMate scopes](https://manual.macromates.com/en/language_grammars). - // If the `tokens` is empty or not defined, then no highlighted positions are - // available for the line. - Tokens SemanticHighlightingTokens `json:"tokens,omitempty"` -} - -type semanticHighlightingInformation struct { - Line int `json:"line"` - Tokens *string `json:"tokens"` -} - -// MarshalJSON implements json.Marshaler. -func (v *SemanticHighlightingInformation) MarshalJSON() ([]byte, error) { - tokens := string(v.Tokens.Serialize()) - return json.Marshal(&semanticHighlightingInformation{ - Line: v.Line, - Tokens: &tokens, - }) -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *SemanticHighlightingInformation) UnmarshalJSON(data []byte) error { - var info semanticHighlightingInformation - err := json.Unmarshal(data, &info) - if err != nil { - return err - } - - if info.Tokens != nil { - v.Tokens, err = DeserializeSemanticHighlightingTokens([]byte(*info.Tokens)) - if err != nil { - return err - } - } - - v.Line = info.Line - return nil -} - -type SemanticHighlightingTokens []SemanticHighlightingToken - -func (v SemanticHighlightingTokens) Serialize() []byte { - var chunks [][]byte - - // Writes each token to `tokens` in the byte format specified by the LSP - // proposal. Described below: - // |<---- 4 bytes ---->|<-- 2 bytes -->|<--- 2 bytes -->| - // | character | length | index | - for _, token := range v { - chunk := make([]byte, 8) - binary.BigEndian.PutUint32(chunk[:4], token.Character) - binary.BigEndian.PutUint16(chunk[4:6], token.Length) - binary.BigEndian.PutUint16(chunk[6:], token.Scope) - chunks = append(chunks, chunk) - } - - src := make([]byte, len(chunks)*8) - for i, chunk := range chunks { - copy(src[i*8:i*8+8], chunk) - } - - dst := make([]byte, base64.StdEncoding.EncodedLen(len(src))) - base64.StdEncoding.Encode(dst, src) - return dst -} - -func DeserializeSemanticHighlightingTokens(src []byte) (SemanticHighlightingTokens, error) { - dst := make([]byte, base64.StdEncoding.DecodedLen(len(src))) - n, err := base64.StdEncoding.Decode(dst, src) - if err != nil { - return nil, err - } - - var chunks [][]byte - for i := 7; i < len(dst[:n]); i += 8 { - chunks = append(chunks, dst[i-7:i+1]) - } - - var tokens SemanticHighlightingTokens - for _, chunk := range chunks { - tokens = append(tokens, SemanticHighlightingToken{ - Character: binary.BigEndian.Uint32(chunk[:4]), - Length: binary.BigEndian.Uint16(chunk[4:6]), - Scope: binary.BigEndian.Uint16(chunk[6:]), - }) - } - - return tokens, nil -} - -type SemanticHighlightingToken struct { - Character uint32 - Length uint16 - Scope uint16 -} - -type ProgressParams struct { - Token string `json:"token"` - Value *json.RawMessage `json:"value"` -} - -type WorkDoneProgressCreateParams struct { - Token string `json:"token"` -} - -type WorkDoneProgressCreateResult struct{} - -// MarshalJSON implements json.Marshaler. -func (v *WorkDoneProgressCreateResult) MarshalJSON() ([]byte, error) { - return []byte("null"), nil -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *WorkDoneProgressCreateResult) UnmarshalJSON(data []byte) error { - if bytes.Equal(data, []byte("null")) { - return nil - } - return errors.New("expected null") -} - -type WorkDoneProgressBegin struct { - Title string `json:"title"` - Cancellable *bool `json:"cancellable,omitempty"` - Message *string `json:"message,omitempty"` - Percentage *int `json:"percentage,omitempty"` -} - -func Raw(v json.Marshaler) *json.RawMessage { - data, err := v.MarshalJSON() - if err != nil { - panic("error marshaling: " + err.Error()) - } - dataRaw := json.RawMessage(data) - return &dataRaw -} - -// MarshalJSON implements json.Marshaler. -func (v WorkDoneProgressBegin) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Title string `json:"title"` - Cancellable *bool `json:"cancellable,omitempty"` - Message *string `json:"message,omitempty"` - Percentage *int `json:"percentage,omitempty"` - Kind string `json:"kind"` - }{v.Title, v.Cancellable, v.Message, v.Percentage, "begin"}) -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *WorkDoneProgressBegin) UnmarshalJSON(data []byte) error { - type ProgressBegin struct { - Title string `json:"title"` - Cancellable *bool `json:"cancellable,omitempty"` - Message *string `json:"message,omitempty"` - Percentage *int `json:"percentage,omitempty"` - Kind string `json:"kind"` - } - var x ProgressBegin - if err := json.Unmarshal(data, &x); err != nil { - return err - } - if x.Kind != "begin" { - return errors.New(`expected kind == "begin"`) - } - (*v).Title = x.Title - (*v).Cancellable = x.Cancellable - (*v).Message = x.Message - (*v).Percentage = x.Percentage - return nil -} - -type WorkDoneProgressReport struct { - Cancellable *bool `json:"cancellable,omitempty"` - Message *string `json:"message,omitempty"` - Percentage *float64 `json:"percentage,omitempty"` -} - -// MarshalJSON implements json.Marshaler. -func (v WorkDoneProgressReport) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Cancellable *bool `json:"cancellable,omitempty"` - Message *string `json:"message,omitempty"` - Percentage *float64 `json:"percentage,omitempty"` - Kind string `json:"kind"` - }{v.Cancellable, v.Message, v.Percentage, "report"}) -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *WorkDoneProgressReport) UnmarshalJSON(data []byte) error { - type ProgressReport struct { - Cancellable *bool `json:"cancellable,omitempty"` - Message *string `json:"message,omitempty"` - Percentage *float64 `json:"percentage,omitempty"` - Kind string `json:"kind"` - } - var x ProgressReport - if err := json.Unmarshal(data, &x); err != nil { - return err - } - if x.Kind != "report" { - return errors.New(`expected kind == "report"`) - } - (*v).Cancellable = x.Cancellable - (*v).Message = x.Message - (*v).Percentage = x.Percentage - return nil -} - -type WorkDoneProgressEnd struct { - Message *string `json:"message,omitempty"` -} - -// MarshalJSON implements json.Marshaler. -func (v WorkDoneProgressEnd) MarshalJSON() ([]byte, error) { - return json.Marshal(struct { - Message *string `json:"message,omitempty"` - Kind string `json:"kind"` - }{v.Message, "end"}) -} - -// UnmarshalJSON implements json.Unmarshaler. -func (v *WorkDoneProgressEnd) UnmarshalJSON(data []byte) error { - type ProgressEnd struct { - Message *string `json:"message,omitempty"` - Kind string `json:"kind"` - } - var x ProgressEnd - if err := json.Unmarshal(data, &x); err != nil { - return err - } - if x.Kind != "end" { - return errors.New(`expected kind == "end"`) - } - (*v).Message = x.Message - return nil -} diff --git a/lsp/structures.go b/lsp/structures.go deleted file mode 100644 index be93da4..0000000 --- a/lsp/structures.go +++ /dev/null @@ -1,190 +0,0 @@ -package lsp - -import ( - "encoding/json" - "fmt" -) - -type Position struct { - /** - * Line position in a document (zero-based). - */ - Line int `json:"line"` - - /** - * Character offset on a line in a document (zero-based). - */ - Character int `json:"character"` -} - -func (p Position) String() string { - return fmt.Sprintf("%d:%d", p.Line, p.Character) -} - -// In returns true if the Position is within the Range r -func (p Position) In(r Range) bool { - return p.AfterOrEq(r.Start) && p.BeforeOrEq(r.End) -} - -// BeforeOrEq returns true if the Position is before or equal the give Position -func (p Position) BeforeOrEq(q Position) bool { - return p.Line < q.Line || (p.Line == q.Line && p.Character <= q.Character) -} - -// AfterOrEq returns true if the Position is after or equal the give Position -func (p Position) AfterOrEq(q Position) bool { - return p.Line > q.Line || (p.Line == q.Line && p.Character >= q.Character) -} - -type Range struct { - /** - * The range's start position. - */ - Start Position `json:"start"` - - /** - * The range's end position. - */ - End Position `json:"end"` -} - -var NilRange = Range{} - -func (r Range) String() string { - return fmt.Sprintf("%s-%s", r.Start, r.End) -} - -// Overlaps returns true if the Range overlaps with the given Range p -func (r Range) Overlaps(p Range) bool { - return r.Start.In(p) || r.End.In(p) || p.Start.In(r) || p.End.In(r) -} - -type Location struct { - URI DocumentURI `json:"uri"` - Range Range `json:"range"` -} - -type Diagnostic struct { - /** - * The range at which the message applies. - */ - Range Range `json:"range"` - - /** - * The diagnostic's severity. Can be omitted. If omitted it is up to the - * client to interpret diagnostics as error, warning, info or hint. - */ - Severity DiagnosticSeverity `json:"severity,omitempty"` - - /** - * The diagnostic's code. Can be omitted. - */ - Code string `json:"code,omitempty"` - - /** - * A human-readable string describing the source of this - * diagnostic, e.g. 'typescript' or 'super lint'. - */ - Source string `json:"source,omitempty"` - - /** - * The diagnostic's message. - */ - Message string `json:"message"` -} - -type DiagnosticSeverity int - -const ( - Error DiagnosticSeverity = 1 - Warning = 2 - Information = 3 - Hint = 4 -) - -type Command struct { - /** - * Title of the command, like `save`. - */ - Title string `json:"title"` - /** - * The identifier of the actual command handler. - */ - Command string `json:"command"` - /** - * Arguments that the command handler should be - * invoked with. - */ - Arguments []json.RawMessage `json:"arguments"` -} - -type TextEdit struct { - /** - * The range of the text document to be manipulated. To insert - * text into a document create a range where start === end. - */ - Range Range `json:"range"` - - /** - * The string to be inserted. For delete operations use an - * empty string. - */ - NewText string `json:"newText"` -} - -type WorkspaceEdit struct { - /** - * Holds changes to existing resources. - */ - Changes map[DocumentURI][]TextEdit `json:"changes"` -} - -type TextDocumentIdentifier struct { - /** - * The text document's URI. - */ - URI DocumentURI `json:"uri"` -} - -type TextDocumentItem struct { - /** - * The text document's URI. - */ - URI DocumentURI `json:"uri"` - - /** - * The text document's language identifier. - */ - LanguageID string `json:"languageId"` - - /** - * The version number of this document (it will strictly increase after each - * change, including undo/redo). - */ - Version int `json:"version"` - - /** - * The content of the opened text document. - */ - Text string `json:"text"` -} - -type VersionedTextDocumentIdentifier struct { - TextDocumentIdentifier - /** - * The version number of this document. - */ - Version int `json:"version"` -} - -type TextDocumentPositionParams struct { - /** - * The text document. - */ - TextDocument TextDocumentIdentifier `json:"textDocument"` - - /** - * The position inside the text document. - */ - Position Position `json:"position"` -} diff --git a/lsp/uri.go b/lsp/uri.go deleted file mode 100644 index d888e94..0000000 --- a/lsp/uri.go +++ /dev/null @@ -1,94 +0,0 @@ -package lsp - -import ( - "encoding/json" - "net/url" - "path/filepath" - "regexp" - - "github.com/arduino/go-paths-helper" - "github.com/pkg/errors" -) - -type DocumentURI struct { - url url.URL -} - -// NilURI is the empty DocumentURI -var NilURI = DocumentURI{} - -var expDriveID = regexp.MustCompile("^/[a-zA-Z]:") - -// AsPath convert the DocumentURI to a paths.Path -func (uri DocumentURI) AsPath() *paths.Path { - return paths.New(uri.unbox()).Canonical() -} - -// unbox convert the DocumentURI to a file path string -func (uri DocumentURI) unbox() string { - path := uri.url.Path - if expDriveID.MatchString(path) { - return path[1:] - } - return path -} - -func (uri DocumentURI) String() string { - return uri.url.String() -} - -// Ext returns the extension of the file pointed by the URI -func (uri DocumentURI) Ext() string { - return filepath.Ext(uri.unbox()) -} - -// NewDocumentURIFromPath create a DocumentURI from the given Path object -func NewDocumentURIFromPath(path *paths.Path) DocumentURI { - return NewDocumentURI(path.String()) -} - -var toSlash = filepath.ToSlash - -// NewDocumentURI create a DocumentURI from the given string path -func NewDocumentURI(path string) DocumentURI { - // tranform path into URI - path = toSlash(path) - if len(path) == 0 || path[0] != '/' { - path = "/" + path - } - uri, err := NewDocumentURIFromURL("file://" + path) - if err != nil { - panic(err) - } - return uri -} - -// NewDocumentURIFromURL converts an URL into a DocumentURI -func NewDocumentURIFromURL(inURL string) (DocumentURI, error) { - uri, err := url.Parse(inURL) - if err != nil { - return NilURI, err - } - return DocumentURI{url: *uri}, nil -} - -// UnmarshalJSON implements json.Unmarshaller interface -func (uri *DocumentURI) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return errors.WithMessage(err, "expoected JSON string for DocumentURI") - } - - newDocURI, err := NewDocumentURIFromURL(s) - if err != nil { - return errors.WithMessage(err, "parsing DocumentURI") - } - - *uri = newDocURI - return nil -} - -// MarshalJSON implements json.Marshaller interface -func (uri DocumentURI) MarshalJSON() ([]byte, error) { - return json.Marshal(uri.url.String()) -} diff --git a/lsp/uri_test.go b/lsp/uri_test.go deleted file mode 100644 index bebeedd..0000000 --- a/lsp/uri_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package lsp - -import ( - "encoding/json" - "fmt" - "strings" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestUriToPath(t *testing.T) { - d, err := NewDocumentURIFromURL("file:///C:/Users/test/Sketch.ino") - require.NoError(t, err) - require.Equal(t, "C:/Users/test/Sketch.ino", d.unbox()) - - d, err = NewDocumentURIFromURL("file:///c%3A/Users/test/Sketch.ino") - require.NoError(t, err) - require.Equal(t, "c:/Users/test/Sketch.ino", d.unbox()) - - d, err = NewDocumentURIFromURL("file:///Users/test/Sketch.ino") - require.NoError(t, err) - require.Equal(t, "/Users/test/Sketch.ino", d.unbox()) - - d, err = NewDocumentURIFromURL("file:///c%3A/Users/USERNA~1/AppData/Local/Temp/.arduinoProIDE-unsaved202108-10416-j28c17.lru6k/sketch_jan8a/sketch_jan8a.ino") - require.NoError(t, err) - require.Equal(t, "c:/Users/USERNA~1/AppData/Local/Temp/.arduinoProIDE-unsaved202108-10416-j28c17.lru6k/sketch_jan8a/sketch_jan8a.ino", d.unbox()) - - d, err = NewDocumentURIFromURL("file:///%F0%9F%98%9B") - require.NoError(t, err) - require.Equal(t, "/\U0001F61B", d.unbox()) -} - -func TestPathToUri(t *testing.T) { - toSlash = windowsToSlash // Emulate windows cases - - d := NewDocumentURI("C:\\Users\\test\\Sketch.ino") - require.Equal(t, "file:///C:/Users/test/Sketch.ino", d.String()) - d = NewDocumentURI("/Users/test/Sketch.ino") - require.Equal(t, "file:///Users/test/Sketch.ino", d.String()) - d = NewDocumentURI("\U0001F61B") - require.Equal(t, "file:///%F0%9F%98%9B", d.String()) -} - -func TestJSONMarshalUnmarshal(t *testing.T) { - toSlash = windowsToSlash // Emulate windows cases - - var d DocumentURI - err := json.Unmarshal([]byte(`"file:///Users/test/Sketch.ino"`), &d) - require.NoError(t, err) - require.Equal(t, "/Users/test/Sketch.ino", d.unbox()) - - err = json.Unmarshal([]byte(`"file:///%F0%9F%98%9B"`), &d) - require.NoError(t, err) - require.Equal(t, "/\U0001F61B", d.unbox()) - - d = NewDocumentURI("C:\\Users\\test\\Sketch.ino") - data, err := json.Marshal(d) - require.NoError(t, err) - require.Equal(t, `"file:///C:/Users/test/Sketch.ino"`, string(data)) - - d = NewDocumentURI("/Users/test/Sketch.ino") - data, err = json.Marshal(d) - require.NoError(t, err) - require.Equal(t, `"file:///Users/test/Sketch.ino"`, string(data)) - - d = NewDocumentURI("/User nàmé/test/Sketch.ino") - data, err = json.Marshal(d) - require.NoError(t, err) - require.Equal(t, `"file:///User%20n%C3%A0m%C3%A9/test/Sketch.ino"`, string(data)) - - d = NewDocumentURI("\U0001F61B") - data, err = json.Marshal(d) - require.NoError(t, err) - require.Equal(t, `"file:///%F0%9F%98%9B"`, string(data)) -} - -func TestNotInoFromSourceMapper(t *testing.T) { - d, err := NewDocumentURIFromURL("file:///not-ino") - require.NoError(t, err) - fmt.Println(d.String()) - fmt.Println(d.unbox()) -} - -func windowsToSlash(path string) string { - return strings.ReplaceAll(path, `\`, "/") -} - -func windowsFromSlash(path string) string { - return strings.ReplaceAll(path, "/", `\`) -} diff --git a/main.go b/main.go index f0b90da..e3622f4 100644 --- a/main.go +++ b/main.go @@ -4,64 +4,98 @@ import ( "flag" "io" "log" + "net/http" + _ "net/http/pprof" "os" "os/signal" - "github.com/arduino/arduino-language-server/handler" - "github.com/arduino/arduino-language-server/lsp" + "github.com/arduino/arduino-language-server/ls" "github.com/arduino/arduino-language-server/streams" "github.com/arduino/go-paths-helper" ) -var clangdPath string -var compileCommandsDir string -var cliPath string -var cliConfigPath string -var initialFqbn string -var initialBoardName string -var enableLogging bool -var loggingBasePath string -var formatFilePath string - func main() { - flag.StringVar(&clangdPath, "clangd", "clangd", "Path to clangd executable") - flag.StringVar(&compileCommandsDir, "compile-commands-dir", "", "Specify a path to look for compile_commands.json. If path is invalid, clangd will look in the current directory and parent paths of each source file. If not specified, the clangd process is started without the compilation database.") - flag.StringVar(&cliPath, "cli", "arduino-cli", "Path to arduino-cli executable") - flag.StringVar(&cliConfigPath, "cli-config", "", "Path to arduino-cli config file") - flag.StringVar(&initialFqbn, "fqbn", "arduino:avr:uno", "Fully qualified board name to use initially (can be changed via JSON-RPC)") - flag.StringVar(&initialBoardName, "board-name", "", "User-friendly board name to use initially (can be changed via JSON-RPC)") - flag.BoolVar(&enableLogging, "log", false, "Enable logging to files") - flag.StringVar(&loggingBasePath, "logpath", ".", "Location where to write logging files to when logging is enabled") - flag.StringVar(&formatFilePath, "format-conf-path", "", "Path to global clang-format configuration file") + clangdPath := flag.String( + "clangd", "", + "Path to clangd executable") + cliPath := flag.String( + "cli", "", + "Path to arduino-cli executable") + cliConfigPath := flag.String( + "cli-config", "", + "Path to arduino-cli config file") + fqbn := flag.String( + "fqbn", "", + "Fully qualified board name to use initially (can be changed via JSON-RPC)") + /* unused */ _ = flag.String( + "board-name", "", + "User-friendly board name to use initially (can be changed via JSON-RPC)") + enableLogging := flag.Bool( + "log", false, + "Enable logging to files") + loggingBasePath := flag.String( + "logpath", ".", + "Location where to write logging files to when logging is enabled") + formatFilePath := flag.String( + "format-conf-path", "", + "Path to global clang-format configuration file") + cliDaemonAddress := flag.String( + "cli-daemon-addr", "", + "TCP address and port of the Arduino CLI daemon (for example: localhost:50051)") + cliDaemonInstanceNumber := flag.Int( + "cli-daemon-instance", -1, + "Instance number of the Arduino CLI daemon") flag.Parse() - if loggingBasePath != "" { - streams.GlobalLogDirectory = paths.New(loggingBasePath) - } else if enableLogging { + if *loggingBasePath != "" { + streams.GlobalLogDirectory = paths.New(*loggingBasePath) + } else if *enableLogging { log.Fatalf("Please specify logpath") } - if enableLogging { + if *enableLogging { logfile := streams.OpenLogFileAs("inols-err.log") log.SetOutput(io.MultiWriter(logfile, os.Stderr)) defer streams.CatchAndLogPanic() + go func() { + log.Println(http.ListenAndServe("localhost:6060", nil)) + }() + log.Println("Language server launched with arguments:") + for i, arg := range os.Args { + log.Printf(" arg[%d] = %s", i, arg) + } } else { log.SetOutput(os.Stderr) } - if cliConfigPath == "" { - log.Fatal("Path to ArduinoCLI config file must be set.") + if *cliPath != "" { + if *cliConfigPath == "" { + log.Fatal("Path to ArduinoCLI config file must be set.") + } + } else if *cliDaemonAddress == "" || *cliDaemonInstanceNumber == -1 { + log.Fatal("ArduinoCLI daemon address and instance number must be set.") + } + if *clangdPath == "" { + log.Fatal("Path to Clangd must be set.") } - handler.Setup(cliPath, cliConfigPath, clangdPath, formatFilePath, enableLogging) - initialBoard := lsp.Board{Fqbn: initialFqbn, Name: initialBoardName} + config := &ls.Config{ + Fqbn: *fqbn, + ClangdPath: paths.New(*clangdPath), + EnableLogging: *enableLogging, + CliPath: paths.New(*cliPath), + CliConfigPath: paths.New(*cliConfigPath), + FormatterConf: paths.New(*formatFilePath), + CliDaemonAddress: *cliDaemonAddress, + CliInstanceNumber: *cliDaemonInstanceNumber, + } stdio := streams.NewReadWriteCloser(os.Stdin, os.Stdout) - if enableLogging { + if *enableLogging { stdio = streams.LogReadWriteCloserAs(stdio, "inols.log") } - inoHandler := handler.NewInoHandler(stdio, initialBoard) + inoHandler := ls.NewINOLanguageServer(stdio, stdio, config) // Intercept kill signal c := make(chan os.Signal, 2) diff --git a/handler/sourcemapper/ino.go b/sourcemapper/ino.go similarity index 63% rename from handler/sourcemapper/ino.go rename to sourcemapper/ino.go index 7511570..6911efd 100644 --- a/handler/sourcemapper/ino.go +++ b/sourcemapper/ino.go @@ -9,17 +9,17 @@ import ( "strconv" "strings" - "github.com/arduino/arduino-language-server/handler/textutils" - "github.com/arduino/arduino-language-server/lsp" "github.com/arduino/go-paths-helper" "github.com/pkg/errors" + "go.bug.st/lsp" + "go.bug.st/lsp/textedits" ) -// InoMapper is a mapping between the .ino sketch and the preprocessed .cpp file -type InoMapper struct { +// SketchMapper is a mapping between the .ino sketch and the preprocessed .cpp file +type SketchMapper struct { CppText *SourceRevision - toCpp map[InoLine]int // Converts File.ino:line -> line - toIno map[int]InoLine // Convers line -> File.ino:line + inoToCpp map[InoLine]int // Converts File.ino:line -> line + cppToIno map[int]InoLine // Convers line -> File.ino:line inoPreprocessed map[InoLine]int // map of the lines taken by the preprocessor: File.ino:line -> preprocessed line cppPreprocessed map[int]InoLine // map of the lines added by the preprocessor: preprocessed line -> File.ino:line } @@ -42,27 +42,27 @@ type InoLine struct { } // InoToCppLine converts a source (.ino) line into a target (.cpp) line -func (s *InoMapper) InoToCppLine(sourceURI lsp.DocumentURI, line int) int { - return s.toCpp[InoLine{sourceURI.AsPath().String(), line}] +func (s *SketchMapper) InoToCppLine(sourceURI lsp.DocumentURI, line int) int { + return s.inoToCpp[InoLine{sourceURI.AsPath().String(), line}] } // InoToCppLineOk converts a source (.ino) line into a target (.cpp) line -func (s *InoMapper) InoToCppLineOk(sourceURI lsp.DocumentURI, line int) (int, bool) { - res, ok := s.toCpp[InoLine{sourceURI.AsPath().String(), line}] +func (s *SketchMapper) InoToCppLineOk(sourceURI lsp.DocumentURI, line int) (int, bool) { + res, ok := s.inoToCpp[InoLine{sourceURI.AsPath().String(), line}] return res, ok } -// InoToCppLSPRange convert a lsp.Ranger reference to a .ino into a lsp.Range to .cpp -func (s *InoMapper) InoToCppLSPRange(sourceURI lsp.DocumentURI, r lsp.Range) lsp.Range { +// InoToCppLSPRange convert a lsp.Range reference to a .ino into a lsp.Range to .cpp +func (s *SketchMapper) InoToCppLSPRange(sourceURI lsp.DocumentURI, r lsp.Range) lsp.Range { res := r res.Start.Line = s.InoToCppLine(sourceURI, r.Start.Line) res.End.Line = s.InoToCppLine(sourceURI, r.End.Line) return res } -// InoToCppLSPRangeOk convert a lsp.Ranger reference to a .ino into a lsp.Range to .cpp and returns +// InoToCppLSPRangeOk convert a lsp.Range reference to a .ino into a lsp.Range to .cpp and returns // true if the conversion is successful or false if the conversion is invalid. -func (s *InoMapper) InoToCppLSPRangeOk(sourceURI lsp.DocumentURI, r lsp.Range) (lsp.Range, bool) { +func (s *SketchMapper) InoToCppLSPRangeOk(sourceURI lsp.DocumentURI, r lsp.Range) (lsp.Range, bool) { res := r if l, ok := s.InoToCppLineOk(sourceURI, r.Start.Line); ok { res.Start.Line = l @@ -78,14 +78,14 @@ func (s *InoMapper) InoToCppLSPRangeOk(sourceURI lsp.DocumentURI, r lsp.Range) ( } // CppToInoLine converts a target (.cpp) line into a source.ino:line -func (s *InoMapper) CppToInoLine(targetLine int) (string, int) { - res := s.toIno[targetLine] +func (s *SketchMapper) CppToInoLine(targetLine int) (string, int) { + res := s.cppToIno[targetLine] return res.File, res.Line } // CppToInoRange converts a target (.cpp) lsp.Range into a source.ino:lsp.Range. // It will panic if the range spans across multiple ino files. -func (s *InoMapper) CppToInoRange(cppRange lsp.Range) (string, lsp.Range) { +func (s *SketchMapper) CppToInoRange(cppRange lsp.Range) (string, lsp.Range) { inoFile, inoRange, err := s.CppToInoRangeOk(cppRange) if err != nil { panic(err.Error()) @@ -105,7 +105,7 @@ func (e AdjustedRangeErr) Error() string { // It returns an error if the range spans across multiple ino files. // If the range ends on the beginning of a new line in another .ino file, the range // is adjusted and AdjustedRangeErr is reported as err: the range may be still valid. -func (s *InoMapper) CppToInoRangeOk(cppRange lsp.Range) (string, lsp.Range, error) { +func (s *SketchMapper) CppToInoRangeOk(cppRange lsp.Range) (string, lsp.Range, error) { inoFile, startLine := s.CppToInoLine(cppRange.Start.Line) endInoFile, endLine := s.CppToInoLine(cppRange.End.Line) inoRange := cppRange @@ -131,36 +131,41 @@ func (s *InoMapper) CppToInoRangeOk(cppRange lsp.Range) (string, lsp.Range, erro // CppToInoLineOk converts a target (.cpp) line into a source (.ino) line and // returns true if the conversion is successful -func (s *InoMapper) CppToInoLineOk(targetLine int) (string, int, bool) { - res, ok := s.toIno[targetLine] +func (s *SketchMapper) CppToInoLineOk(targetLine int) (string, int, bool) { + res, ok := s.cppToIno[targetLine] return res.File, res.Line, ok } -// IsPreprocessedCppLine returns true if the give .cpp line is part of the +// IsPreprocessedCppLine returns true if the given .cpp line is part of the // section added by the arduino preprocessor. -func (s *InoMapper) IsPreprocessedCppLine(cppLine int) bool { +func (s *SketchMapper) IsPreprocessedCppLine(cppLine int) bool { _, preprocessed := s.cppPreprocessed[cppLine] - _, mapsToIno := s.toIno[cppLine] + _, mapsToIno := s.cppToIno[cppLine] return preprocessed || !mapsToIno } // CreateInoMapper create a InoMapper from the given target file -func CreateInoMapper(targetFile []byte) *InoMapper { - mapper := &InoMapper{ - toCpp: map[InoLine]int{}, - toIno: map[int]InoLine{}, - inoPreprocessed: map[InoLine]int{}, - cppPreprocessed: map[int]InoLine{}, +func CreateInoMapper(targetFile []byte) *SketchMapper { + mapper := &SketchMapper{ CppText: &SourceRevision{ Version: 1, Text: string(targetFile), }, } + mapper.regeneratehMapping() + return mapper +} + +func (s *SketchMapper) regeneratehMapping() { + s.inoToCpp = map[InoLine]int{} + s.cppToIno = map[int]InoLine{} + s.inoPreprocessed = map[InoLine]int{} + s.cppPreprocessed = map[int]InoLine{} sourceFile := "" sourceLine := -1 targetLine := 0 - scanner := bufio.NewScanner(bytes.NewReader(targetFile)) + scanner := bufio.NewScanner(bytes.NewReader([]byte(s.CppText.Text))) for scanner.Scan() { lineStr := scanner.Text() if strings.HasPrefix(lineStr, "#line") { @@ -170,27 +175,33 @@ func CreateInoMapper(targetFile []byte) *InoMapper { sourceLine = l - 1 } sourceFile = paths.New(unquoteCppString(tokens[2])).Canonical().String() - mapper.toIno[targetLine] = NotIno + s.cppToIno[targetLine] = NotIno } else if sourceFile != "" { - mapper.mapLine(sourceFile, sourceLine, targetLine) + // Sometimes the Arduino preprocessor fails to interpret correctly the code + // and may report a "#line 0" directive leading to a negative sourceLine. + // In this rare cases just interpret the source line as a NotIno line. + if sourceLine >= 0 { + s.mapLine(sourceFile, sourceLine, targetLine) + } else { + s.cppToIno[targetLine] = NotIno + } sourceLine++ } else { - mapper.toIno[targetLine] = NotIno + s.cppToIno[targetLine] = NotIno } targetLine++ } - mapper.mapLine(sourceFile, sourceLine, targetLine) - return mapper + s.mapLine(sourceFile, sourceLine, targetLine) } -func (s *InoMapper) mapLine(sourceFile string, sourceLine, targetLine int) { - inoLine := InoLine{sourceFile, sourceLine} - if line, ok := s.toCpp[inoLine]; ok { +func (s *SketchMapper) mapLine(inoSourceFile string, inoSourceLine, cppLine int) { + inoLine := InoLine{inoSourceFile, inoSourceLine} + if line, ok := s.inoToCpp[inoLine]; ok { s.cppPreprocessed[line] = inoLine s.inoPreprocessed[inoLine] = line } - s.toCpp[inoLine] = targetLine - s.toIno[targetLine] = inoLine + s.inoToCpp[inoLine] = cppLine + s.cppToIno[cppLine] = inoLine } func unquoteCppString(str string) string { @@ -205,20 +216,24 @@ func unquoteCppString(str string) string { // ApplyTextChange performs the text change and updates both .ino and .cpp files. // It returns true if the change is "dirty", this happens when the change alters preprocessed lines // and a new preprocessing may be probably required. -func (s *InoMapper) ApplyTextChange(inoURI lsp.DocumentURI, inoChange lsp.TextDocumentContentChangeEvent) (dirty bool) { +func (s *SketchMapper) ApplyTextChange(inoURI lsp.DocumentURI, inoChange lsp.TextDocumentContentChangeEvent) (dirty bool) { inoRange := *inoChange.Range - cppRange := s.InoToCppLSPRange(inoURI, inoRange) + cppRange, ok := s.InoToCppLSPRangeOk(inoURI, inoRange) + if !ok { + panic("Invalid sketch range " + inoURI.String() + ":" + inoRange.String()) + } + log.Print("Ino Range: ", inoRange, " -> Cpp Range:", cppRange) deletedLines := inoRange.End.Line - inoRange.Start.Line // Apply text changes - newText, err := textutils.ApplyTextChange(s.CppText.Text, cppRange, inoChange.Text) + newText, err := textedits.ApplyTextChange(s.CppText.Text, cppRange, inoChange.Text) if err != nil { panic("error replacing text: " + err.Error()) } s.CppText.Text = newText s.CppText.Version++ - if _, is := s.inoPreprocessed[s.toIno[cppRange.Start.Line]]; is { + if _, is := s.inoPreprocessed[s.cppToIno[cppRange.Start.Line]]; is { dirty = true } @@ -227,7 +242,7 @@ func (s *InoMapper) ApplyTextChange(inoURI lsp.DocumentURI, inoChange lsp.TextDo dirty = dirty || s.deleteCppLine(cppRange.Start.Line) deletedLines-- } - addedLines := strings.Count(inoChange.Text, "\n") - 1 + addedLines := strings.Count(inoChange.Text, "\n") for addedLines > 0 { dirty = dirty || s.addInoLine(cppRange.Start.Line) addedLines-- @@ -235,16 +250,16 @@ func (s *InoMapper) ApplyTextChange(inoURI lsp.DocumentURI, inoChange lsp.TextDo return } -func (s *InoMapper) addInoLine(cppLine int) (dirty bool) { +func (s *SketchMapper) addInoLine(cppLine int) (dirty bool) { preprocessToShiftCpp := map[InoLine]bool{} - addedInoLine := s.toIno[cppLine] - carry := s.toIno[cppLine] + addedInoLine := s.cppToIno[cppLine] + carry := s.cppToIno[cppLine] carry.Line++ for { - next, ok := s.toIno[cppLine+1] - s.toIno[cppLine+1] = carry - s.toCpp[carry] = cppLine + 1 + next, ok := s.cppToIno[cppLine+1] + s.cppToIno[cppLine+1] = carry + s.inoToCpp[carry] = cppLine + 1 if !ok { break } @@ -282,23 +297,23 @@ func (s *InoMapper) addInoLine(cppLine int) (dirty bool) { inoPre.Line++ s.inoPreprocessed[inoPre] = l s.cppPreprocessed[l] = inoPre - s.toIno[l] = inoPre + s.cppToIno[l] = inoPre } return } -func (s *InoMapper) deleteCppLine(line int) (dirty bool) { - removed := s.toIno[line] +func (s *SketchMapper) deleteCppLine(line int) (dirty bool) { + removed := s.cppToIno[line] for i := line + 1; ; i++ { - shifted, ok := s.toIno[i] + shifted, ok := s.cppToIno[i] if !ok { - delete(s.toIno, i-1) + delete(s.cppToIno, i-1) break } - s.toIno[i-1] = shifted + s.cppToIno[i-1] = shifted if shifted != NotIno { - s.toCpp[shifted] = i - 1 + s.inoToCpp[shifted] = i - 1 } } @@ -310,20 +325,20 @@ func (s *InoMapper) deleteCppLine(line int) (dirty bool) { next := curr next.Line++ - shifted, ok := s.toCpp[next] + shifted, ok := s.inoToCpp[next] if !ok { - delete(s.toCpp, curr) + delete(s.inoToCpp, curr) break } - s.toCpp[curr] = shifted - s.toIno[shifted] = curr + s.inoToCpp[curr] = shifted + s.cppToIno[shifted] = curr if l, ok := s.inoPreprocessed[next]; ok { s.inoPreprocessed[curr] = l s.cppPreprocessed[l] = curr delete(s.inoPreprocessed, next) - s.toIno[l] = curr + s.cppToIno[l] = curr } } return @@ -359,11 +374,29 @@ func dumpInoToCppMap(s map[InoLine]int) { } // DebugLogAll dumps the internal status of the mapper -func (s *InoMapper) DebugLogAll() { +func (s *SketchMapper) DebugLogAll() { + stripFile := func(s string) string { + l := strings.LastIndex(s, "/") + if l == -1 { + return s + } + return s[l:] + } cpp := strings.Split(s.CppText.Text, "\n") log.Printf(" > Current sketchmapper content:") for l, cppLine := range cpp { inoFile, inoLine := s.CppToInoLine(l) - log.Printf(" %3d: %-40s : %s:%d", l, cppLine, inoFile, inoLine) + cppLine = strings.Replace(cppLine, "\t", " ", -1) + if len(cppLine) > 60 { + cppLine = cppLine[:60] + } + + cppSource := fmt.Sprintf("%3d: %-60s", l, cppLine) + sketchFile := fmt.Sprintf("%s:%d", stripFile(inoFile), inoLine) + preprocLine := "" + if pr, ok := s.cppPreprocessed[l]; ok { + preprocLine = fmt.Sprintf("%s:%d", stripFile(pr.File), pr.Line) + } + log.Printf("%s | %-25s %-25s", cppSource, sketchFile, preprocLine) } } diff --git a/handler/sourcemapper/ino_test.go b/sourcemapper/ino_test.go similarity index 80% rename from handler/sourcemapper/ino_test.go rename to sourcemapper/ino_test.go index 5a89a77..a2e4da6 100644 --- a/handler/sourcemapper/ino_test.go +++ b/sourcemapper/ino_test.go @@ -6,30 +6,16 @@ import ( "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" + "go.bug.st/lsp" ) func TestCreateSourceMaps(t *testing.T) { - input := `#include -#line 1 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" -#line 1 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" - -#line 2 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" -void setup(); -#line 7 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" -void loop(); -#line 2 "/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino" -void setup() { - // put your setup code here, to run once: - -} + sketch := paths.New("testdata/sketch_july2a/sketch_july2a.ino").Canonical() + input, err := sketch.ReadFile() + require.NoError(t, err) -void loop() { - // put your main code here, to run repeatedly: - -} -` sourceMap := CreateInoMapper([]byte(input)) - sketchJuly2a := paths.New("/home/megabug/Workspace/arduino-language-server/handler/sourcemapper/sketch_july2a.ino").Canonical().String() + sketchJuly2a := sketch.String() require.EqualValues(t, map[InoLine]int{ {sketchJuly2a, 0}: 3, {sketchJuly2a, 1}: 9, @@ -42,7 +28,7 @@ void loop() { {sketchJuly2a, 8}: 16, {sketchJuly2a, 9}: 17, {sketchJuly2a, 10}: 18, - }, sourceMap.toCpp) + }, sourceMap.inoToCpp) require.EqualValues(t, map[int]InoLine{ 0: NotIno, 1: NotIno, @@ -63,23 +49,35 @@ void loop() { 16: {sketchJuly2a, 8}, 17: {sketchJuly2a, 9}, 18: {sketchJuly2a, 10}, - }, sourceMap.toIno) + }, sourceMap.cppToIno) require.EqualValues(t, map[int]InoLine{ 5: {sketchJuly2a, 1}, // setup 7: {sketchJuly2a, 6}, // loop }, sourceMap.cppPreprocessed) - dumpCppToInoMap(sourceMap.toIno) - dumpInoToCppMap(sourceMap.toCpp) - dumpCppToInoMap(sourceMap.cppPreprocessed) - dumpInoToCppMap(sourceMap.inoPreprocessed) + sourceMap.DebugLogAll() + + sourceMap.ApplyTextChange(lsp.NewDocumentURIFromPath(sketch), lsp.TextDocumentContentChangeEvent{ + Range: &lsp.Range{ + Start: lsp.Position{Line: 3, Character: 0}, + End: lsp.Position{Line: 3, Character: 0}, + }, + Text: "// Added line 1\n// Added line 2\n", + }) + sourceMap.DebugLogAll() + + // dumpCppToInoMap(sourceMap.cppToIno) + // dumpInoToCppMap(sourceMap.inoToCpp) + // dumpCppToInoMap(sourceMap.cppPreprocessed) + // dumpInoToCppMap(sourceMap.inoPreprocessed) //sourceMap.addInoLine(InoLine{"sketch_july2a.ino", 0}) - sourceMap.addInoLine(3) - fmt.Println("\nAdded line 13") - dumpCppToInoMap(sourceMap.toIno) - dumpInoToCppMap(sourceMap.toCpp) - dumpCppToInoMap(sourceMap.cppPreprocessed) - dumpInoToCppMap(sourceMap.inoPreprocessed) + // sourceMap.addInoLine(3) + // fmt.Println("\nAdded line 3") + // sourceMap.DebugLogAll() + // dumpCppToInoMap(sourceMap.cppToIno) + // dumpInoToCppMap(sourceMap.inoToCpp) + // dumpCppToInoMap(sourceMap.cppPreprocessed) + // dumpInoToCppMap(sourceMap.inoPreprocessed) } func TestCreateMultifileSourceMap(t *testing.T) { @@ -127,7 +125,7 @@ void secondFunction() { ProvaSpazio := paths.New("/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/Prova_Spazio.ino").Canonical().String() SecondTab := paths.New("/home/megabug/Workspace/sketchbook-cores-beta/Prova_Spazio/SecondTab.ino").Canonical().String() sourceMap := CreateInoMapper([]byte(input)) - require.EqualValues(t, sourceMap.toCpp, map[InoLine]int{ + require.EqualValues(t, sourceMap.inoToCpp, map[InoLine]int{ {ProvaSpazio, 0}: 2, {ProvaSpazio, 1}: 3, {ProvaSpazio, 2}: 4, @@ -159,7 +157,7 @@ void secondFunction() { {SecondTab, 3}: 40, {SecondTab, 4}: 41, }) - require.EqualValues(t, sourceMap.toIno, map[int]InoLine{ + require.EqualValues(t, sourceMap.cppToIno, map[int]InoLine{ 0: NotIno, 1: NotIno, 2: {ProvaSpazio, 0}, @@ -209,14 +207,14 @@ void secondFunction() { 10: {ProvaSpazio, 22}, // vino 12: {SecondTab, 1}, // secondFunction }, sourceMap.cppPreprocessed) - dumpCppToInoMap(sourceMap.toIno) - dumpInoToCppMap(sourceMap.toCpp) + dumpCppToInoMap(sourceMap.cppToIno) + dumpInoToCppMap(sourceMap.inoToCpp) dumpCppToInoMap(sourceMap.cppPreprocessed) dumpInoToCppMap(sourceMap.inoPreprocessed) sourceMap.deleteCppLine(21) fmt.Println("\nRemoved line 21") - dumpCppToInoMap(sourceMap.toIno) - dumpInoToCppMap(sourceMap.toCpp) + dumpCppToInoMap(sourceMap.cppToIno) + dumpInoToCppMap(sourceMap.inoToCpp) dumpCppToInoMap(sourceMap.cppPreprocessed) dumpInoToCppMap(sourceMap.inoPreprocessed) } diff --git a/sourcemapper/testdata/sketch_july2a/sketch_july2a.ino b/sourcemapper/testdata/sketch_july2a/sketch_july2a.ino new file mode 100644 index 0000000..3f11399 --- /dev/null +++ b/sourcemapper/testdata/sketch_july2a/sketch_july2a.ino @@ -0,0 +1,18 @@ +#include +#line 1 "testdata/sketch_july2a/sketch_july2a.ino" +#line 1 "testdata/sketch_july2a/sketch_july2a.ino" + +#line 2 "testdata/sketch_july2a/sketch_july2a.ino" +void setup(); +#line 7 "testdata/sketch_july2a/sketch_july2a.ino" +void loop(); +#line 2 "testdata/sketch_july2a/sketch_july2a.ino" +void setup() { + // put your setup code here, to run once: + +} + +void loop() { + // put your main code here, to run repeatedly: + +} \ No newline at end of file diff --git a/streams/jsonrpc2.go b/streams/jsonrpc2.go deleted file mode 100644 index 85244db..0000000 --- a/streams/jsonrpc2.go +++ /dev/null @@ -1,98 +0,0 @@ -package streams - -import ( - "encoding/json" - "fmt" - "log" - "runtime/debug" - - "github.com/arduino/arduino-language-server/lsp" - "github.com/fatih/color" - "github.com/sourcegraph/jsonrpc2" -) - -var green = color.New(color.FgHiGreen) -var red = color.New(color.FgHiRed) - -// JSONRPCConnLogOnRecv perform logging of the given req and resp -func JSONRPCConnLogOnRecv(prefix string) func(req *jsonrpc2.Request, resp *jsonrpc2.Response) { - return func(req *jsonrpc2.Request, resp *jsonrpc2.Response) { - jsonrpcLog(prefix, req, resp, false) - } -} - -// JSONRPCConnLogOnSend perform logging of the given req and resp -func JSONRPCConnLogOnSend(prefix string) func(req *jsonrpc2.Request, resp *jsonrpc2.Response) { - return func(req *jsonrpc2.Request, resp *jsonrpc2.Response) { - jsonrpcLog(prefix, req, resp, true) - } -} - -func jsonrpcLog(prefix string, req *jsonrpc2.Request, resp *jsonrpc2.Response, sending bool) { - color.NoColor = false - var c *color.Color - if sending { - c = red - } else { - c = green - } - if resp != nil { - dec := jsonrpcLogDecodeResp(resp) - if req != nil { - log.Print(c.Sprintf(prefix+" ANSWER %s %v (%v): %s", req.Method, req.ID, resp.ID, dec)) - } else { - log.Print(c.Sprintf(prefix+" ANSWER UNBOUND (%v): %s", resp.ID, dec)) - } - } else if req != nil { - dec := jsonrpcLogDecodeReq(req) - if !req.Notif { - log.Print(c.Sprintf(prefix+" REQUEST %s %v: %s", req.Method, req.ID, dec)) - } else { - log.Print(c.Sprintf(prefix+" NOTIFICATION %s: %s", req.Method, dec)) - } - } else { - log.Print(green.Sprintf(prefix + " NULL MESSAGE")) - log.Print(string(debug.Stack())) - } -} - -func jsonrpcLogDecodeReq(req *jsonrpc2.Request) string { - fmtString := func(s *string) string { - if s == nil { - return "" - } - return *s - } - fmtFloat := func(s *float64) float64 { - if s == nil { - return 0 - } - return *s - } - switch req.Method { - case "$/progress": - var v lsp.ProgressParams - if err := json.Unmarshal(*req.Params, &v); err != nil { - return err.Error() - } - var begin lsp.WorkDoneProgressBegin - if json.Unmarshal(*v.Value, &begin) == nil { - return fmt.Sprintf("TOKEN=%s BEGIN %v %v", v.Token, begin.Title, fmtString(begin.Message)) - } - var report lsp.WorkDoneProgressReport - if json.Unmarshal(*v.Value, &report) == nil { - return fmt.Sprintf("TOKEN=%s REPORT %v %v%%", v.Token, fmtString(report.Message), fmtFloat(report.Percentage)) - } - var end lsp.WorkDoneProgressEnd - if json.Unmarshal(*v.Value, &end) == nil { - return fmt.Sprintf("TOKEN=%s END %v", v.Token, fmtString(end.Message)) - } - return "UNKNOWN?" - default: - return "" - } -} - -func jsonrpcLogDecodeResp(resp *jsonrpc2.Response) string { - return "" -}