From 1942ec4735adc8e74e18ce7b6b4256b669f318f6 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 27 Oct 2021 18:22:50 +0200 Subject: [PATCH 01/76] Big refactoring to get rid of all legacy lsp/jsonrpc2 stuff --- go.mod | 8 +- go.sum | 44 +- handler/builder.go | 24 +- handler/handler.go | 1262 +++++++++++++++------------ handler/progress.go | 49 +- handler/sourcemapper/ino.go | 4 +- handler/textutils/textutils.go | 4 +- handler/textutils/textutils_test.go | 2 +- lsp/protocol.go | 18 +- main.go | 5 +- streams/jsonrpc2.go | 160 ++-- 11 files changed, 873 insertions(+), 707 deletions(-) diff --git a/go.mod b/go.mod index 07add5b..b749291 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,17 @@ module github.com/arduino/arduino-language-server go 1.12 +replace go.bug.st/lsp => ../lsp + +replace go.bug.st/json => ../go-json + require ( github.com/arduino/arduino-cli v0.0.0-20201215104024-6a177ebf56f2 - github.com/arduino/go-paths-helper v1.5.0 + github.com/arduino/go-paths-helper v1.6.1 github.com/fatih/color v1.7.0 github.com/pkg/errors v0.9.1 github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 github.com/stretchr/testify v1.6.1 + go.bug.st/json v1.0.0 + go.bug.st/lsp v0.0.0-00010101000000-000000000000 ) diff --git a/go.sum b/go.sum index 82a9597..a8c2161 100644 --- a/go.sum +++ b/go.sum @@ -10,18 +10,14 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF 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/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-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.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-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= @@ -32,11 +28,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce 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/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/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= @@ -45,31 +38,23 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc 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/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/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.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 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/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= @@ -80,7 +65,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 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/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= @@ -119,11 +103,9 @@ 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/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/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= @@ -142,7 +124,6 @@ github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0/go.mod h1:hpGvhGHPVbN 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/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -155,7 +136,6 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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= @@ -164,15 +144,12 @@ github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx 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/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/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -181,7 +158,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb 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/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= @@ -204,22 +180,15 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 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/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/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/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= @@ -245,13 +214,11 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn 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/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= @@ -262,7 +229,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 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= @@ -272,9 +238,7 @@ 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/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 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= @@ -285,7 +249,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf 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-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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -372,12 +335,9 @@ gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3M 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= diff --git a/handler/builder.go b/handler/builder.go index 1d7a260..ffb19b9 100644 --- a/handler/builder.go +++ b/handler/builder.go @@ -2,18 +2,17 @@ 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/fatih/color" "github.com/pkg/errors" + "go.bug.st/json" + "go.bug.st/lsp" ) func (handler *InoHandler) scheduleRebuildEnvironment() { @@ -25,6 +24,7 @@ func (handler *InoHandler) scheduleRebuildEnvironment() { func (handler *InoHandler) rebuildEnvironmentLoop() { defer streams.CatchAndLogPanic() + logger := streams.NewPrefixLogger(color.New(color.FgHiMagenta), "RBLD---") grabDeadline := func() *time.Time { handler.rebuildSketchDeadlineMutex.Lock() @@ -68,24 +68,24 @@ func (handler *InoHandler) rebuildEnvironmentLoop() { case <-time.After(time.Millisecond * 400): msg := "compiling" + dots[count%3] count++ - handler.progressHandler.Report("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressReport{Message: &msg}) + handler.progressHandler.Report("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressReport{Message: msg}) case <-done: msg := "done" - handler.progressHandler.End("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressEnd{Message: &msg}) + handler.progressHandler.End("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressEnd{Message: msg}) return } } }() - handler.dataLock("RBLD---") - handler.initializeWorkbench(context.Background(), nil) - handler.dataUnlock("RBLD---") + handler.writeLock(logger, false) + handler.initializeWorkbench(logger, nil) + handler.writeUnlock(logger) done <- true close(done) } } -func (handler *InoHandler) generateBuildEnvironment(buildPath *paths.Path) error { +func (handler *InoHandler) generateBuildEnvironment(logger streams.PrefixLogger, buildPath *paths.Path) error { sketchDir := handler.sketchRoot fqbn := handler.config.SelectedBoard.Fqbn @@ -130,7 +130,7 @@ func (handler *InoHandler) generateBuildEnvironment(buildPath *paths.Path) error cmdOutput := &bytes.Buffer{} cmd.RedirectStdoutTo(cmdOutput) cmd.SetDirFromPath(sketchDir) - log.Println("running: ", strings.Join(args, " ")) + logger("running: %s", strings.Join(args, " ")) if err := cmd.Run(); err != nil { return errors.Errorf("running %s: %s", strings.Join(args, " "), err) } @@ -150,7 +150,7 @@ func (handler *InoHandler) generateBuildEnvironment(buildPath *paths.Path) error if err := json.Unmarshal(cmdOutput.Bytes(), &res); err != nil { return errors.Errorf("parsing arduino-cli output: %s", err) } - log.Println("arduino-cli output:", cmdOutput) + logger("arduino-cli output: %s", cmdOutput) return nil } diff --git a/handler/handler.go b/handler/handler.go index 3a33155..c79628a 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -3,7 +3,6 @@ package handler import ( "bytes" "context" - "encoding/json" "fmt" "io" "log" @@ -13,19 +12,19 @@ import ( "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" + "go.bug.st/json" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" ) var globalCliPath string @@ -50,12 +49,12 @@ type CLangdStarter func() (stdin io.WriteCloser, stdout io.ReadCloser, stderr io // InoHandler is a JSON-RPC handler that delegates messages to clangd. type InoHandler struct { - StdioConn *jsonrpc2.Conn - ClangdConn *jsonrpc2.Conn + IDEConn *jsonrpc.Connection + ClangdConn *jsonrpc.Connection - stdioNotificationCount int64 - clangdNotificationCount int64 - progressHandler *ProgressProxyHandler + ideMessageCount int64 + clangdMessageCount int64 + progressHandler *ProgressProxyHandler closing chan bool clangdStarted *sync.Cond @@ -79,55 +78,86 @@ type InoHandler struct { docs map[string]*lsp.TextDocumentItem inoDocsWithDiagnostics map[lsp.DocumentURI]bool - config lsp.BoardConfig + config BoardConfig +} + +// 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"` } var yellow = color.New(color.FgHiYellow) -func (handler *InoHandler) dataLock(msg string) { +func (handler *InoHandler) writeLock(logger streams.PrefixLogger, requireClangd bool) { handler.dataMux.Lock() - log.Println(msg + yellow.Sprintf(" locked")) + logger(yellow.Sprintf("write-locked")) + if requireClangd { + handler.waitClangdStart(logger) + } } -func (handler *InoHandler) dataUnlock(msg string) { - log.Println(msg + yellow.Sprintf(" unlocked")) +func (handler *InoHandler) writeUnlock(logger streams.PrefixLogger) { + logger(yellow.Sprintf("write-unlocked")) handler.dataMux.Unlock() } -func (handler *InoHandler) dataRLock(msg string) { +func (handler *InoHandler) readLock(logger streams.PrefixLogger, requireClangd bool) { handler.dataMux.RLock() - log.Println(msg + yellow.Sprintf(" read-locked")) + logger(yellow.Sprintf("read-locked")) + + for requireClangd && handler.ClangdConn == 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(yellow.Sprintf("clang not started: read-unlocking...")) + handler.dataMux.RUnlock() + + handler.writeLock(logger, true) + handler.writeUnlock(logger) + + handler.dataMux.RLock() + logger(yellow.Sprintf("testing again if clang started: read-locked...")) + } } -func (handler *InoHandler) dataRUnlock(msg string) { - log.Println(msg + yellow.Sprintf(" read-unlocked")) +func (handler *InoHandler) readUnlock(logger streams.PrefixLogger) { + logger(yellow.Sprintf("read-unlocked")) handler.dataMux.RUnlock() } -func (handler *InoHandler) waitClangdStart(prefix string) error { +func (handler *InoHandler) waitClangdStart(logger streams.PrefixLogger) error { if handler.ClangdConn != nil { return nil } - log.Printf(prefix + "(throttled: waiting for clangd)") - log.Println(prefix + yellow.Sprintf(" unlocked (waiting clangd)")) + logger("(throttled: waiting for clangd)") + logger(yellow.Sprintf("unlocked (waiting clangd)")) handler.clangdStarted.Wait() - log.Println(prefix + yellow.Sprintf(" locked (waiting clangd)")) + logger(yellow.Sprintf("locked (waiting clangd)")) if handler.ClangdConn == nil { - log.Printf(prefix + "clangd startup failed: aborting call") + logger("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 { +func NewInoHandler(stdin io.Reader, stdout io.Writer, board Board) *InoHandler { + logger := streams.NewPrefixLogger(color.New(color.FgWhite), "LS: ") handler := &InoHandler{ docs: map[string]*lsp.TextDocumentItem{}, inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, closing: make(chan bool), - config: lsp.BoardConfig{ + config: BoardConfig{ SelectedBoard: board, }, } @@ -140,32 +170,46 @@ func NewInoHandler(stdio io.ReadWriteCloser, board lsp.Board) *InoHandler { 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:")), + logger("Initial board configuration: %s", board) + logger("Language server build path: %s", handler.buildPath) + logger("Language server build sketch root: %s", handler.buildSketchRoot) + } + + jsonrpcLogger := streams.NewJsonRPCLogger("IDE", "LS", false) + handler.IDEConn = jsonrpc.NewConnection(stdin, stdout, + func(ctx context.Context, method string, params json.RawMessage, respCallback func(result json.RawMessage, err *jsonrpc.ResponseError)) { + reqLogger, idx := jsonrpcLogger.LogClientRequest(method, params) + handler.HandleMessageFromIDE(ctx, reqLogger, method, params, func(result json.RawMessage, err *jsonrpc.ResponseError) { + jsonrpcLogger.LogServerResponse(idx, method, result, err) + respCallback(result, err) + }) + }, + func(ctx context.Context, method string, params json.RawMessage) { + notifLogger := jsonrpcLogger.LogClientNotification(method, params) + handler.HandleNotificationFromIDE(ctx, notifLogger, method, params) + }, + func(e error) {}, ) + go func() { + handler.IDEConn.Run() + logger("Lost connection with IDE!") + handler.Close() + }() - handler.progressHandler = NewProgressProxy(handler.StdioConn) + handler.progressHandler = NewProgressProxy(handler.IDEConn) 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 -} +// // 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() { @@ -187,185 +231,241 @@ func (handler *InoHandler) CloseNotify() <-chan bool { // 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) { +func (handler *InoHandler) HandleNotificationFromIDE(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { defer streams.CatchAndLogPanic() + // n := atomic.AddInt64(&handler.ideMessageCount, 1) + // prefix := fmt.Sprintf("IDE --> %s notif%d ", method, n) - 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) + params, err := lsp.DecodeNotificationParams(method, paramsRaw) if err != nil { - return nil, err + // TODO: log? + return } if params == nil { - params = req.Params + // TODO: log? + return } // 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", + switch method { + case "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) - } + // Write lock - clangd required + handler.writeLock(logger, true) + defer handler.writeUnlock(logger) + case "initialized": + // Read lock - NO clangd required + handler.readLock(logger, false) + defer handler.readUnlock(logger) + default: + // Read lock - clangd required + handler.readLock(logger, true) + defer handler.readUnlock(logger) } // Handle LSP methods: transform parameters and send to clangd - var inoURI, cppURI lsp.DocumentURI + var 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 + logger("notification is not propagated to clangd") + return // 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) + logger("(%s@%d as '%s')", p.TextDocument.URI, p.TextDocument.Version, p.TextDocument.LanguageID) - if res, e := handler.didOpen(p); e != nil { + if res, e := handler.didOpen(logger, 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 + logger("notification is not propagated to clangd") + return } else { - log.Printf(prefix+"to clang: didOpen(%s@%d as '%s')", res.TextDocument.URI, res.TextDocument.Version, res.TextDocument.LanguageID) + logger("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) + logger("--> didClose(%s)", p.TextDocument.URI) - if res, e := handler.didClose(p); e != nil { + if res, e := handler.didClose(logger, p); e != nil { } else if res == nil { - log.Println(" --X notification is not propagated to clangd") - return nil, nil // do not propagate to clangd + logger(" --X notification is not propagated to clangd") + return } else { - log.Printf(" --> didClose(%s)", res.TextDocument.URI) + logger(" --> 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) + logger("--> didChange(%s@%d)", p.TextDocument.URI, p.TextDocument.Version) for _, change := range p.ContentChanges { - log.Printf(" > %s -> %s", change.Range, strconv.Quote(change.Text)) + logger(" > %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 + if res, err := handler.didChange(logger, p); err != nil { + logger(" --E error: %s", err) + return } else if res == nil { - log.Println(" --X notification is not propagated to clangd") - return nil, err // do not propagate to clangd + logger(" --X notification is not propagated to clangd") + return } else { p = res } - log.Printf(" --> didChange(%s@%d)", p.TextDocument.URI, p.TextDocument.Version) + logger(" --> didChange(%s@%d)", p.TextDocument.URI, p.TextDocument.Version) for _, change := range p.ContentChanges { - log.Printf(" > %s -> %s", change.Range, strconv.Quote(change.Text)) + logger(" > %s -> %s", change.Range, strconv.Quote(change.Text)) } - err = handler.ClangdConn.Notify(ctx, req.Method, p) - return nil, err + if err := handler.ClangdConn.SendNotification(method, lsp.EncodeMessage(p)); err != nil { + // Exit the process and trigger a restart by the client in case of a severe error + logger("Connection error with clangd server:") + logger("%v", err) + logger("Please restart the language server.") + handler.Close() + } + return + + case *lsp.DidSaveTextDocumentParams: + // Method: "textDocument/didSave" + logger("--> %s(%s)", method, p.TextDocument.URI) + p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(logger, p.TextDocument) + cppURI = p.TextDocument.URI + if cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) { + logger(" --| didSave not forwarded to clangd") + return + } + logger(" --> %s(%s)", method, p.TextDocument.URI) + } + + if err != nil { + logger("Error: %s", err) + return + } + + logger("sending to Clang") + if err := handler.ClangdConn.SendNotification(method, lsp.EncodeMessage(params)); err != nil { + // Exit the process and trigger a restart by the client in case of a severe error + logger("Connection error with clangd server:") + logger("vs", err) + logger("Please restart the language server.") + handler.Close() + } + if handler.buildSketchSymbolsLoad { + handler.buildSketchSymbolsLoad = false + handler.buildSketchSymbolsCheck = false + logger("Queued resfreshing document symbols") + go handler.LoadCppDocumentSymbols() + } + if handler.buildSketchSymbolsCheck { + handler.buildSketchSymbolsCheck = false + logger("Queued check document symbols") + go handler.CheckCppDocumentSymbols() + } +} + +// HandleMessageFromIDE handles a message received from the IDE client (via stdio). +func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, logger streams.PrefixLogger, + method string, paramsRaw json.RawMessage, + returnCB func(result json.RawMessage, err *jsonrpc.ResponseError), +) { + defer streams.CatchAndLogPanic() + // n := atomic.AddInt64(&handler.ideMessageCount, 1) + // prefix := fmt.Sprintf("IDE --> %s %v ", method, n) + + params, err := lsp.DecodeRequestParams(method, paramsRaw) + if err != nil { + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInvalidParams, Message: err.Error()}) + return + } + if params == nil { + // TODO: log? + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInvalidParams}) + return + } + + // Set up RWLocks and wait for clangd startup + switch method { + case "initialize": + // Write lock - NO clangd required + handler.writeLock(logger, false) + defer handler.writeUnlock(logger) + default: + // Read lock - clangd required + handler.readLock(logger, true) + defer handler.readUnlock(logger) + } + + // 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() + logger := streams.NewPrefixLogger(color.New(color.FgCyan), "INIT --- ") + logger("initializing workbench") + + // Start clangd asynchronously + handler.writeLock(logger, false) // do not wait for clangd... we are starting it :-) + defer handler.writeUnlock(logger) + + handler.initializeWorkbench(logger, p) + + // clangd should be running now... + handler.clangdStarted.Broadcast() + + logger("initializing workbench (done)") + }() + + returnCB(lsp.EncodeMessage(&lsp.InitializeResult{ + Capabilities: lsp.ServerCapabilities{ + TextDocumentSync: &lsp.TextDocumentSyncOptions{}, //{Kind: &lsp.TDSKIncremental}, + HoverProvider: &lsp.HoverOptions{}, // true, + CompletionProvider: &lsp.CompletionOptions{ + TriggerCharacters: []string{".", "\u003e", ":"}, + }, + SignatureHelpProvider: &lsp.SignatureHelpOptions{ + TriggerCharacters: []string{"(", ","}, + }, + DefinitionProvider: &lsp.DefinitionOptions{}, // true, + // ReferencesProvider: &lsp.ReferenceOptions{}, // TODO: true + DocumentHighlightProvider: &lsp.DocumentHighlightOptions{}, //true, + DocumentSymbolProvider: &lsp.DocumentSymbolOptions{}, //true, + WorkspaceSymbolProvider: &lsp.WorkspaceSymbolOptions{}, //true, + CodeActionProvider: &lsp.CodeActionOptions{ResolveProvider: true}, + DocumentFormattingProvider: &lsp.DocumentFormattingOptions{}, //true, + DocumentRangeFormattingProvider: &lsp.DocumentRangeFormattingOptions{}, //true, + DocumentOnTypeFormattingProvider: &lsp.DocumentOnTypeFormattingOptions{ + FirstTriggerCharacter: "\n", + }, + RenameProvider: &lsp.RenameOptions{PrepareProvider: false}, // TODO: true + ExecuteCommandProvider: &lsp.ExecuteCommandOptions{ + Commands: []string{"clangd.applyFix", "clangd.applyTweak"}, + }, + }, + }), nil) + return case *lsp.CompletionParams: // method: "textDocument/completion" - log.Printf("--> completion(%s:%d:%d)\n", p.TextDocument.URI, p.Position.Line, p.Position.Character) + logger("--> 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) + if res, e := handler.ino2cppTextDocumentPositionParams(logger, p.TextDocumentPositionParams); e == nil { + p.TextDocumentPositionParams = res + logger(" --> completion(%s:%d:%d)\n", p.TextDocument.URI, p.Position.Line, p.Position.Character) } else { err = e } @@ -374,9 +474,9 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr case *lsp.CodeActionParams: // method "textDocument/codeAction" inoURI = p.TextDocument.URI - log.Printf("--> codeAction(%s:%s)", p.TextDocument.URI, p.Range.Start) + logger("--> codeAction(%s:%s)", p.TextDocument.URI, p.Range.Start) - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument) + p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(logger, p.TextDocument) if err != nil { break } @@ -387,16 +487,16 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr *r = handler.sketchMapper.InoToCppLSPRange(inoURI, *r) } } - log.Printf(" --> codeAction(%s:%s)", p.TextDocument.URI, p.Range.Start) + logger(" --> 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) + doc := p.TextDocumentPositionParams + logger("--> 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) + if res, e := handler.ino2cppTextDocumentPositionParams(logger, doc); e == nil { + p.TextDocumentPositionParams = res + logger(" --> hover(%s:%d:%d)\n", doc.TextDocument.URI, doc.Position.Line, doc.Position.Character) } else { err = e } @@ -405,19 +505,19 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr case *lsp.DocumentSymbolParams: // method "textDocument/documentSymbol" inoURI = p.TextDocument.URI - log.Printf("--> documentSymbol(%s)", p.TextDocument.URI) + logger("--> documentSymbol(%s)", p.TextDocument.URI) - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(p.TextDocument) - log.Printf(" --> documentSymbol(%s)", p.TextDocument.URI) + p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(logger, p.TextDocument) + logger(" --> 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) + logger("--> formatting(%s)", p.TextDocument.URI) + p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(logger, p.TextDocument) cppURI = p.TextDocument.URI - log.Printf(" --> formatting(%s)", p.TextDocument.URI) - if cleanup, e := handler.createClangdFormatterConfig(cppURI); e != nil { + logger(" --> formatting(%s)", p.TextDocument.URI) + if cleanup, e := handler.createClangdFormatterConfig(logger, cppURI); e != nil { err = e } else { defer cleanup() @@ -425,13 +525,13 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr case *lsp.DocumentRangeFormattingParams: // Method: "textDocument/rangeFormatting" - log.Printf("--> %s(%s:%s)", req.Method, p.TextDocument.URI, p.Range) + logger("--> %s(%s:%s)", method, p.TextDocument.URI, p.Range) inoURI = p.TextDocument.URI - if cppParams, e := handler.ino2cppDocumentRangeFormattingParams(p); e == nil { + if cppParams, e := handler.ino2cppDocumentRangeFormattingParams(logger, 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 { + logger(" --> %s(%s:%s)", method, cppParams.TextDocument.URI, cppParams.Range) + if cleanup, e := handler.createClangdFormatterConfig(logger, cppURI); e != nil { err = e } else { defer cleanup() @@ -440,117 +540,140 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, conn *jsonr err = e } - case *lsp.TextDocumentPositionParams: + case *lsp.SignatureHelpParams, + *lsp.DefinitionParams, + *lsp.TypeDefinitionParams, + *lsp.ImplementationParams, + *lsp.DocumentHighlightParams: + // it was *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 { + + tdp := p.(lsp.TextDocumentPositionParams) + + logger("--> %s(%s:%s)", method, tdp.TextDocument.URI, tdp.Position) + inoURI = tdp.TextDocument.URI + if res, e := handler.ino2cppTextDocumentPositionParams(logger, tdp); e == nil { cppURI = res.TextDocument.URI params = res - log.Printf(" --> %s(%s:%s)", req.Method, res.TextDocument.URI, res.Position) + logger(" --> %s(%s:%s)", method, res.TextDocument.URI, res.Position) } else { err = e } - case *lsp.DidSaveTextDocumentParams: - // Method: "textDocument/didSave" - log.Printf("--> %s(%s)", req.Method, p.TextDocument.URI) + case *lsp.ReferenceParams: + // "textDocument/references": + logger("--X " + method) + return 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) + _, err = handler.ino2cppTextDocumentPositionParams(logger, p.TextDocumentPositionParams) - 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 + case *lsp.DocumentOnTypeFormattingParams: + // "textDocument/onTypeFormatting": + logger("--X " + method) + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"}) + return inoURI = p.TextDocument.URI err = handler.ino2cppDocumentOnTypeFormattingParams(p) - case *lsp.RenameParams: // "textDocument/rename": - log.Printf("--X " + req.Method) - return nil, nil + + case *lsp.RenameParams: + // "textDocument/rename": + logger("--X " + method) + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"}) + return inoURI = p.TextDocument.URI err = handler.ino2cppRenameParams(p) - case *lsp.DidChangeWatchedFilesParams: // "workspace/didChangeWatchedFiles": - log.Printf("--X " + req.Method) - return nil, nil + + case *lsp.DidChangeWatchedFilesParams: + // "workspace/didChangeWatchedFiles": + logger("--X " + method) + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"}) + return err = handler.ino2cppDidChangeWatchedFilesParams(p) - case *lsp.ExecuteCommandParams: // "workspace/executeCommand": - log.Printf("--X " + req.Method) - return nil, nil + + case *lsp.ExecuteCommandParams: + // "workspace/executeCommand": + logger("--X " + method) + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"}) + return err = handler.ino2cppExecuteCommand(p) } if err != nil { - log.Printf(prefix+"Error: %s", err) - return nil, err + logger("Error: %s", err) + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) + return } - 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) + logger("sent to Clang") + + // var result interface{} + // result, err = lsp.SendRequest(ctx, handler.ClangdConn, method, params) + clangRawResp, clangErr, err := handler.ClangdConn.SendRequest(ctx, method, lsp.EncodeMessage(params)) + 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" { + logger("Timeout exceeded while waiting for a reply from clangd.") + logger("Please restart the language server.") + handler.Close() + } else if strings.Contains(err.Error(), "non-added document") || strings.Contains(err.Error(), "non-added file") { + logger("The clangd process has lost track of the open document.") + logger("%v", err) + logger("Please restart the language server.") + handler.Close() + } else { + logger("clangd error: %v", err) + } + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) + return + } + if clangErr != nil { + logger("clangd response error: %v", clangErr.AsError()) + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()}) + return + } + clangResp, err := lsp.DecodeResponseResult(method, clangRawResp) + if err != nil { + logger("Error decoding clang response: %v", err) + returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) + return } - if err == nil && handler.buildSketchSymbolsLoad { + + if handler.buildSketchSymbolsLoad { handler.buildSketchSymbolsLoad = false handler.buildSketchSymbolsCheck = false - log.Println(prefix + "Queued resfreshing document symbols") + logger("Queued resfreshing document symbols") go handler.LoadCppDocumentSymbols() } - if err == nil && handler.buildSketchSymbolsCheck { + if handler.buildSketchSymbolsCheck { handler.buildSketchSymbolsCheck = false - log.Println(prefix + "Queued check document symbols") + logger("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) + if clangResp != nil { + clangResp = handler.transformClangdResult(logger, method, inoURI, cppURI, clangResp) } - return result, err + returnCB(lsp.EncodeMessage(clangResp), nil) } -func (handler *InoHandler) initializeWorkbench(ctx context.Context, params *lsp.InitializeParams) error { +func (handler *InoHandler) initializeWorkbench(logger streams.PrefixLogger, params *lsp.InitializeParams) error { currCppTextVersion := 0 if params != nil { - log.Printf(" --> initialize(%s)\n", params.RootURI) + logger(" --> initialize(%s)", params.RootURI) handler.lspInitializeParams = params handler.sketchRoot = params.RootURI.AsPath() handler.sketchName = handler.sketchRoot.Base() } else { - log.Printf(" --> RE-initialize()\n") + logger(" --> RE-initialize()") currCppTextVersion = handler.sketchMapper.CppText.Version } - if err := handler.generateBuildEnvironment(handler.buildPath); err != nil { + if err := handler.generateBuildEnvironment(logger, handler.buildPath); err != nil { return err } handler.buildSketchCpp = handler.buildSketchRoot.Join(handler.sketchName + ".ino.cpp") @@ -582,17 +705,18 @@ func (handler *InoHandler) initializeWorkbench(ctx context.Context, params *lsp. }, } - if err := handler.ClangdConn.Notify(ctx, "textDocument/didChange", syncEvent); err != nil { - log.Println(" error reinitilizing clangd:", err) + if err := handler.ClangdConn.SendNotification("textDocument/didChange", lsp.EncodeMessage(syncEvent)); err != nil { + logger(" error reinitilizing clangd:", err) return err } } else { // Otherwise start clangd! - dataFolder, err := extractDataFolderFromArduinoCLI() + dataFolder, err := extractDataFolderFromArduinoCLI(logger) if err != nil { - log.Printf(" error: %s", err) + logger(" error: %s", err) } - clangdStdout, clangdStdin, clangdStderr := startClangd(handler.buildPath, handler.buildSketchCpp, dataFolder) + clangdStdout, clangdStdin, clangdStderr := startClangd(logger, handler.buildPath, handler.buildSketchCpp, dataFolder) + clangdStdio := streams.NewReadWriteCloser(clangdStdin, clangdStdout) if enableLogging { clangdStdio = streams.LogReadWriteCloserAs(clangdStdio, "inols-clangd.log") @@ -601,28 +725,48 @@ func (handler *InoHandler) initializeWorkbench(ctx context.Context, params *lsp. 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:"))) + rpcLogger := streams.NewJsonRPCLogger("IDE LS", "CL", true) + handler.ClangdConn = jsonrpc.NewConnection(clangdStdio, clangdStdio, + func(ctx context.Context, method string, params json.RawMessage, respCallback func(result json.RawMessage, err *jsonrpc.ResponseError)) { + logger, idx := rpcLogger.LogServerRequest(method, params) + handler.HandleRequestFromClangd(ctx, logger, method, params, func(result json.RawMessage, err *jsonrpc.ResponseError) { + rpcLogger.LogClientResponse(idx, method, result, err) + respCallback(result, err) + }) + }, + func(ctx context.Context, method string, params json.RawMessage) { + logger := rpcLogger.LogClientNotification(method, params) + handler.HandleNotificationFromClangd(ctx, logger, method, params) + }, + func(e error) { + logger("connection error with clangd! %s", e) + handler.Close() + }, + ) go func() { - <-handler.ClangdConn.DisconnectNotify() - log.Printf("Lost connection with clangd!") + handler.ClangdConn.Run() + logger("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) + if rawResp, clangErr, err := handler.ClangdConn.SendRequest(ctx, "initialize", lsp.EncodeMessage(handler.lspInitializeParams)); err != nil { + logger(" error initilizing clangd: %v", err) + return err + } else if clangErr != nil { + logger(" error initilizing clangd: %v", clangErr.AsError()) + return clangErr.AsError() + } else if resp, err := lsp.DecodeResponseResult("initialize", rawResp); err != nil { + logger(" error initilizing clangd: %v", err) return err + } else { + logger(" clangd successfully started: %v", resp) } - if err := handler.ClangdConn.Notify(ctx, "initialized", lsp.InitializedParams{}); err != nil { - log.Println(" error sending initialize to clangd:", err) + if err := handler.ClangdConn.SendNotification("initialized", lsp.EncodeMessage(lsp.InitializedParams{})); err != nil { + logger(" error sending initialized notification to clangd: %v", err) return err } } @@ -631,7 +775,7 @@ func (handler *InoHandler) initializeWorkbench(ctx context.Context, params *lsp. return nil } -func extractDataFolderFromArduinoCLI() (*paths.Path, error) { +func extractDataFolderFromArduinoCLI(logger streams.PrefixLogger) (*paths.Path, error) { // XXX: do this from IDE or via gRPC args := []string{globalCliPath, "--config-file", globalCliConfigPath, @@ -645,7 +789,7 @@ func extractDataFolderFromArduinoCLI() (*paths.Path, error) { } cmdOutput := &bytes.Buffer{} cmd.RedirectStdoutTo(cmdOutput) - log.Println("running: ", strings.Join(args, " ")) + logger("running: %s", strings.Join(args, " ")) if err := cmd.Run(); err != nil { return nil, errors.Errorf("running %s: %s", strings.Join(args, " "), err) } @@ -660,37 +804,45 @@ func extractDataFolderFromArduinoCLI() (*paths.Path, error) { return nil, errors.Errorf("parsing arduino-cli output: %s", err) } // Return only the build path - log.Println("Arduino Data Dir -> ", res.Directories.Data) + logger("Arduino Data Dir -> %s", res.Directories.Data) return paths.New(res.Directories.Data), nil } -func (handler *InoHandler) refreshCppDocumentSymbols(prefix string) error { +func (handler *InoHandler) refreshCppDocumentSymbols(logger streams.PrefixLogger) error { // Query source code symbols + handler.readUnlock(logger) 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) + logger("requesting documentSymbol for %s", cppURI) + respRaw, resErr, err := handler.ClangdConn.SendRequest(context.Background(), "textDocument/documentSymbol", + lsp.EncodeMessage(&lsp.DocumentSymbolParams{ + TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, + })) + handler.readLock(logger, true) if err != nil { - log.Printf(prefix+"error: %s", err) - return errors.WithMessage(err, "quering source code symbols") + logger("error: %s", err) + return fmt.Errorf("quering source code symbols: %w", err) + } + if resErr != nil { + logger("error: %s", resErr.AsError()) + return fmt.Errorf("quering source code symbols: %w", resErr.AsError()) + } + result, err := lsp.DecodeResponseResult("textDocument/documentSymbol", respRaw) + if err != nil { + logger("invalid response: %s", err) + return fmt.Errorf("quering source code symbols: invalid response: %w", err) } - 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, ok := result.([]lsp.DocumentSymbol) + if !ok { + logger("error: expected DocumenSymbol array but got %T", result) + return fmt.Errorf("expected DocumenSymbol but got %T", result) } - symbols := *symbolResult.DocumentSymbolArray // Filter non-functions symbols i := 0 for _, symbol := range symbols { - if symbol.Kind != lsp.SKFunction { + if symbol.Kind != lsp.SymbolKindFunction { continue } symbols[i] = symbol @@ -701,15 +853,15 @@ func (handler *InoHandler) refreshCppDocumentSymbols(prefix string) error { symbolsCanary := "" for _, symbol := range symbols { - log.Printf(prefix+" symbol: %s %s %s", symbol.Kind, symbol.Name, symbol.Range) + logger(" 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) + logger(" > invalid range: %s", err) symbolsCanary += "/" } else if end := strings.Index(symbolText, "{"); end != -1 { - log.Printf(prefix+" TRIMMED> %s", symbolText[:end]) + logger(" TRIMMED> %s", symbolText[:end]) symbolsCanary += symbolText[:end] } else { - log.Printf(prefix+" > %s", symbolText) + logger(" > %s", symbolText) symbolsCanary += symbolText } } @@ -718,34 +870,34 @@ func (handler *InoHandler) refreshCppDocumentSymbols(prefix string) error { } func (handler *InoHandler) LoadCppDocumentSymbols() error { - prefix := "SYLD--- " - defer log.Printf(prefix + "(done)") - handler.dataRLock(prefix) - defer handler.dataRUnlock(prefix) - return handler.refreshCppDocumentSymbols(prefix) + logger := streams.NewPrefixLogger(color.New(color.FgHiBlue), "SYLD --- ") + defer logger("(done)") + handler.readLock(logger, true) + defer handler.readUnlock(logger) + return handler.refreshCppDocumentSymbols(logger) } func (handler *InoHandler) CheckCppDocumentSymbols() error { - prefix := "SYCK--- " - defer log.Printf(prefix + "(done)") - handler.dataRLock(prefix) - defer handler.dataRUnlock(prefix) + logger := streams.NewPrefixLogger(color.New(color.FgHiBlue), "SYCK --- ") + defer logger("(done)") + handler.readLock(logger, true) + defer handler.readUnlock(logger) oldSymbols := handler.buildSketchSymbols canary := handler.buildSketchSymbolsCanary - if err := handler.refreshCppDocumentSymbols(prefix); err != nil { + if err := handler.refreshCppDocumentSymbols(logger); err != nil { return err } if len(oldSymbols) != len(handler.buildSketchSymbols) || canary != handler.buildSketchSymbolsCanary { - log.Println(prefix + "function symbols change detected, triggering sketch rebuild!") + logger("function symbols change detected, triggering sketch rebuild!") handler.scheduleRebuildEnvironment() } return nil } func (handler *InoHandler) CheckCppIncludesChanges() { - prefix := "INCK--- " - + logger := streams.NewPrefixLogger(color.New(color.FgHiBlue), "INCK --- ") + logger("check for Cpp Include Changes") includesCanary := "" for _, line := range strings.Split(handler.sketchMapper.CppText.Text, "\n") { if strings.Contains(line, "#include ") { @@ -755,7 +907,7 @@ func (handler *InoHandler) CheckCppIncludesChanges() { if includesCanary != handler.buildSketchIncludesCanary { handler.buildSketchIncludesCanary = includesCanary - log.Println(prefix + "#include change detected, triggering sketch rebuild!") + logger("#include change detected, triggering sketch rebuild!") handler.scheduleRebuildEnvironment() } } @@ -790,7 +942,7 @@ func canonicalizeCompileCommandsJSON(compileCommandsDir *paths.Path) map[string] return compilers } -func startClangd(compileCommandsDir, sketchCpp *paths.Path, dataFolder *paths.Path) (io.WriteCloser, io.ReadCloser, io.ReadCloser) { +func startClangd(logger streams.PrefixLogger, compileCommandsDir, sketchCpp *paths.Path, dataFolder *paths.Path) (io.WriteCloser, io.ReadCloser, io.ReadCloser) { // Start clangd args := []string{ globalClangdPath, @@ -801,7 +953,7 @@ func startClangd(compileCommandsDir, sketchCpp *paths.Path, dataFolder *paths.Pa args = append(args, fmt.Sprintf("-query-driver=%s", dataFolder.Join("packages", "**"))) } if enableLogging { - log.Println(" Starting clangd:", strings.Join(args, " ")) + logger(" Starting clangd: %s", strings.Join(args, " ")) } if clangdCmd, err := executils.NewProcess(args...); err != nil { panic("starting clangd: " + err.Error()) @@ -818,7 +970,7 @@ func startClangd(compileCommandsDir, sketchCpp *paths.Path, dataFolder *paths.Pa } } -func (handler *InoHandler) didOpen(inoDidOpen *lsp.DidOpenTextDocumentParams) (*lsp.DidOpenTextDocumentParams, error) { +func (handler *InoHandler) didOpen(logger streams.PrefixLogger, inoDidOpen *lsp.DidOpenTextDocumentParams) (*lsp.DidOpenTextDocumentParams, error) { // Add the TextDocumentItem in the tracked files list inoItem := inoDidOpen.TextDocument handler.docs[inoItem.URI.AsPath().String()] = &inoItem @@ -826,7 +978,7 @@ func (handler *InoHandler) didOpen(inoDidOpen *lsp.DidOpenTextDocumentParams) (* // If we are tracking a .ino... if inoItem.URI.Ext() == ".ino" { handler.sketchTrackedFilesCount++ - log.Printf(" increasing .ino tracked files count: %d", handler.sketchTrackedFilesCount) + logger(" increasing .ino tracked files count: %d", handler.sketchTrackedFilesCount) // notify clang that sketchCpp has been opened only once if handler.sketchTrackedFilesCount != 1 { @@ -834,25 +986,25 @@ func (handler *InoHandler) didOpen(inoDidOpen *lsp.DidOpenTextDocumentParams) (* } } - cppItem, err := handler.ino2cppTextDocumentItem(inoItem) + cppItem, err := handler.ino2cppTextDocumentItem(logger, inoItem) return &lsp.DidOpenTextDocumentParams{ TextDocument: cppItem, }, err } -func (handler *InoHandler) didClose(inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { +func (handler *InoHandler) didClose(logger streams.PrefixLogger, 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) + logger(" 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) + logger(" 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 { @@ -860,14 +1012,14 @@ func (handler *InoHandler) didClose(inoDidClose *lsp.DidCloseTextDocumentParams) } } - cppIdentifier, err := handler.ino2cppTextDocumentIdentifier(inoIdentifier) + cppIdentifier, err := handler.ino2cppTextDocumentIdentifier(logger, 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) +func (handler *InoHandler) ino2cppTextDocumentItem(logger streams.PrefixLogger, inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { + cppURI, err := handler.ino2cppDocumentURI(logger, inoItem.URI) if err != nil { return cppItem, err } @@ -887,7 +1039,7 @@ func (handler *InoHandler) ino2cppTextDocumentItem(inoItem lsp.TextDocumentItem) return cppItem, nil } -func (handler *InoHandler) didChange(ctx context.Context, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { +func (handler *InoHandler) didChange(logger streams.PrefixLogger, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { doc := req.TextDocument trackedDoc, ok := handler.docs[doc.URI.AsPath().String()] @@ -902,9 +1054,9 @@ func (handler *InoHandler) didChange(ctx context.Context, req *lsp.DidChangeText cppChanges := []lsp.TextDocumentContentChangeEvent{} for _, inoChange := range req.ContentChanges { - cppRange, ok := handler.sketchMapper.InoToCppLSPRangeOk(doc.URI, *inoChange.Range) + cppRange, ok := handler.sketchMapper.InoToCppLSPRangeOk(doc.URI, inoChange.Range) if !ok { - return nil, errors.Errorf("invalid change range %s:%s", doc.URI, *inoChange.Range) + return nil, errors.Errorf("invalid change range %s:%s", doc.URI, inoChange.Range) } // Detect changes in critical lines (for example function definitions) @@ -913,24 +1065,24 @@ func (handler *InoHandler) didChange(ctx context.Context, req *lsp.DidChangeText for _, sym := range handler.buildSketchSymbols { if sym.SelectionRange.Overlaps(cppRange) { dirty = true - log.Println("--! DIRTY CHANGE detected using symbol tables, force sketch rebuild!") + logger("--! 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!") + logger("--! 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("----------------------") + // logger("New version:----------") + // logger(handler.sketchMapper.CppText.Text) + // logger("----------------------") cppChange := lsp.TextDocumentContentChangeEvent{ - Range: &cppRange, + Range: cppRange, RangeLength: inoChange.RangeLength, Text: inoChange.Text, } @@ -954,7 +1106,7 @@ func (handler *InoHandler) didChange(ctx context.Context, req *lsp.DidChangeText } // If changes are applied to other files pass them by converting just the URI - cppDoc, err := handler.ino2cppVersionedTextDocumentIdentifier(req.TextDocument) + cppDoc, err := handler.ino2cppVersionedTextDocumentIdentifier(logger, req.TextDocument) if err != nil { return nil, err } @@ -996,25 +1148,25 @@ func (handler *InoHandler) handleError(ctx context.Context, err error) error { } else { message = "Could not start editor support.\n" + errorStr } - go handler.showMessage(ctx, lsp.MTError, message) + go handler.showMessage(ctx, lsp.MessageTypeError, message) return errors.New(message) } -func (handler *InoHandler) ino2cppVersionedTextDocumentIdentifier(doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { - cppURI, err := handler.ino2cppDocumentURI(doc.URI) +func (handler *InoHandler) ino2cppVersionedTextDocumentIdentifier(logger streams.PrefixLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { + cppURI, err := handler.ino2cppDocumentURI(logger, 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) +func (handler *InoHandler) ino2cppTextDocumentIdentifier(logger streams.PrefixLogger, doc lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { + cppURI, err := handler.ino2cppDocumentURI(logger, doc.URI) res := doc res.URI = cppURI return res, err } -func (handler *InoHandler) ino2cppDocumentURI(inoURI lsp.DocumentURI) (lsp.DocumentURI, error) { +func (handler *InoHandler) ino2cppDocumentURI(logger streams.PrefixLogger, 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) @@ -1028,35 +1180,35 @@ func (handler *InoHandler) ino2cppDocumentURI(inoURI lsp.DocumentURI) (lsp.Docum inside, err := inoPath.IsInsideDir(handler.sketchRoot) if err != nil { - log.Printf(" could not determine if '%s' is inside '%s'", inoPath, handler.sketchRoot) + logger(" 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) + logger(" '%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) + logger(" 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) + logger(" 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) { +func (handler *InoHandler) inoDocumentURIFromInoPath(logger streams.PrefixLogger, 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:") + logger(" !!! Unresolved .ino path: %s", inoPath) + logger(" !!! Known doc paths are:") for p := range handler.docs { - log.Printf(" !!! > %s", p) + logger(" !!! > %s", p) } uri := lsp.NewDocumentURI(inoPath) return uri, unknownURI(uri) @@ -1064,7 +1216,7 @@ func (handler *InoHandler) inoDocumentURIFromInoPath(inoPath string) (lsp.Docume return doc.URI, nil } -func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { +func (handler *InoHandler) cpp2inoDocumentURI(logger streams.PrefixLogger, 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] @@ -1081,51 +1233,52 @@ func (handler *InoHandler) cpp2inoDocumentURI(cppURI lsp.DocumentURI, cppRange l 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) + logger(" URI: is in preprocessed section") + logger(" converted %s to %s:%s", cppRange, inoPath, inoRange) } else { - log.Printf(" URI: converted %s to %s:%s", cppRange, inoPath, inoRange) + logger(" 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) + logger(" URI: converted %s to %s:%s (END LINE ADJUSTED)", cppRange, inoPath, inoRange) err = nil } else { - log.Printf(" URI: ERROR: %s", err) + logger(" URI: ERROR: %s", err) handler.sketchMapper.DebugLogAll() return lsp.NilURI, lsp.NilRange, err } - inoURI, err := handler.inoDocumentURIFromInoPath(inoPath) + inoURI, err := handler.inoDocumentURIFromInoPath(logger, 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) + logger(" 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) + logger(" '%s' is not inside '%s'", cppPath, handler.buildSketchRoot) + logger(" 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) + logger(" URI: '%s' -> '%s'", cppPath, inoPath) + inoURI, err := handler.inoDocumentURIFromInoPath(logger, inoPath) + logger(" as URI: '%s'", inoURI) return inoURI, cppRange, err } - log.Printf(" could not determine rel-path of '%s' in '%s': %s", cppPath, handler.buildSketchRoot, err) + logger(" 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) +func (handler *InoHandler) ino2cppTextDocumentPositionParams(logger streams.PrefixLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { + res := lsp.TextDocumentPositionParams{} + cppDoc, err := handler.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) if err != nil { - return nil, err + return res, err } cppPosition := inoParams.Position inoURI := inoParams.TextDocument.URI @@ -1133,18 +1286,17 @@ func (handler *InoHandler) ino2cppTextDocumentPositionParams(inoParams *lsp.Text 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) + logger(" invalid line requested: %s:%d", inoURI, inoParams.Position.Line) + return res, unknownURI(inoURI) } } - return &lsp.TextDocumentPositionParams{ - TextDocument: cppDoc, - Position: cppPosition, - }, nil + res.TextDocument = cppDoc + res.Position = cppPosition + return res, nil } -func (handler *InoHandler) ino2cppRange(inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { - cppURI, err := handler.ino2cppDocumentURI(inoURI) +func (handler *InoHandler) ino2cppRange(logger streams.PrefixLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { + cppURI, err := handler.ino2cppDocumentURI(logger, inoURI) if err != nil { return lsp.NilURI, lsp.Range{}, err } @@ -1155,13 +1307,13 @@ func (handler *InoHandler) ino2cppRange(inoURI lsp.DocumentURI, inoRange lsp.Ran return cppURI, inoRange, nil } -func (handler *InoHandler) ino2cppDocumentRangeFormattingParams(inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { - cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(inoParams.TextDocument) +func (handler *InoHandler) ino2cppDocumentRangeFormattingParams(logger streams.PrefixLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { + cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) if err != nil { return nil, err } - _, cppRange, err := handler.ino2cppRange(inoParams.TextDocument.URI, inoParams.Range) + _, cppRange, err := handler.ino2cppRange(logger, inoParams.TextDocument.URI, inoParams.Range) return &lsp.DocumentRangeFormattingParams{ TextDocument: cppTextDocument, Range: cppRange, @@ -1213,7 +1365,7 @@ func (handler *InoHandler) ino2cppExecuteCommand(executeCommand *lsp.ExecuteComm func (handler *InoHandler) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { panic("not implemented") - newEdit := lsp.WorkspaceEdit{Changes: make(map[lsp.DocumentURI][]lsp.TextEdit)} + newEdit := lsp.WorkspaceEdit{} // for uri, edit := range origEdit.Changes { // if data, ok := handler.data[lsp.DocumentURI(uri)]; ok { // newValue := make([]lsp.TextEdit, len(edit)) @@ -1231,7 +1383,7 @@ func (handler *InoHandler) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *ls return &newEdit } -func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI lsp.DocumentURI, result interface{}) interface{} { +func (handler *InoHandler) transformClangdResult(logger streams.PrefixLogger, method string, inoURI, cppURI lsp.DocumentURI, result interface{}) interface{} { cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp) switch r := result.(type) { @@ -1243,7 +1395,7 @@ func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI l if cppToIno { _, *r.Range = handler.sketchMapper.CppToInoRange(*r.Range) } - log.Printf("<-- hover(%s)", strconv.Quote(r.Contents.Value)) + logger("<-- hover(%s)", strconv.Quote(r.Contents.Value)) return r case *lsp.CompletionList: @@ -1259,53 +1411,45 @@ func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI l } } r.Items = newItems - log.Printf("<-- completion(%d items) cppToIno=%v", len(r.Items), cppToIno) + logger("<-- completion(%d items) cppToIno=%v", len(r.Items), cppToIno) return r - case *lsp.DocumentSymbolArrayOrSymbolInformationArray: + case []lsp.DocumentSymbol: // method "textDocument/documentSymbol" + logger(" <-- documentSymbol(%d document symbols)", len(r)) + return handler.cpp2inoDocumentSymbols(logger, r, inoURI) - 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.SymbolInformation: + // method "textDocument/documentSymbol" + logger(" <-- documentSymbol(%d symbol information)", len(r)) + return handler.cpp2inoSymbolInformation(r) - case *[]lsp.CommandOrCodeAction: + 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), + logger(" <-- codeAction(%d elements)", len(r)) + for i := range r { + switch item := r[i].Get().(type) { + case lsp.Command: + logger(" > Command: %s", item.Title) + r[i].Set(handler.Cpp2InoCommand(logger, item)) + case lsp.CodeAction: + logger(" > CodeAction: %s", item.Title) + r[i].Set(handler.cpp2inoCodeAction(logger, item, inoURI)) } } - log.Printf("<-- codeAction(%d elements)", len(*r)) + logger("<-- 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)) + logger(" <-- %s %s textEdit(%d elements)", method, cppURI, len(*r)) for _, edit := range *r { - log.Printf(" > %s -> %s", edit.Range, strconv.Quote(edit.NewText)) + logger(" > %s -> %s", edit.Range, strconv.Quote(edit.NewText)) } - sketchEdits, err := handler.cpp2inoTextEdits(cppURI, *r) + sketchEdits, err := handler.cpp2inoTextEdits(logger, cppURI, *r) if err != nil { - log.Printf("ERROR converting textEdits: %s", err) + logger("ERROR converting textEdits: %s", err) return nil } @@ -1313,9 +1457,9 @@ func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI l if !ok { inoEdits = []lsp.TextEdit{} } - log.Printf("<-- %s %s textEdit(%d elements)", method, inoURI, len(inoEdits)) + logger("<-- %s %s textEdit(%d elements)", method, inoURI, len(inoEdits)) for _, edit := range inoEdits { - log.Printf(" > %s -> %s", edit.Range, strconv.Quote(edit.NewText)) + logger(" > %s -> %s", edit.Range, strconv.Quote(edit.NewText)) } return &inoEdits @@ -1326,9 +1470,9 @@ func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI l // Method: "textDocument/references" inoLocations := []lsp.Location{} for _, cppLocation := range *r { - inoLocation, err := handler.cpp2inoLocation(cppLocation) + inoLocation, err := handler.cpp2inoLocation(logger, cppLocation) if err != nil { - log.Printf("ERROR converting location %s:%s: %s", cppLocation.URI, cppLocation.Range, err) + logger("ERROR converting location %s:%s: %s", cppLocation.URI, cppLocation.Range, err) return nil } inoLocations = append(inoLocations, inoLocation) @@ -1341,9 +1485,9 @@ func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI l inoSymbols := []lsp.SymbolInformation{} for _, cppSymbolInfo := range *r { cppLocation := cppSymbolInfo.Location - inoLocation, err := handler.cpp2inoLocation(cppLocation) + inoLocation, err := handler.cpp2inoLocation(logger, cppLocation) if err != nil { - log.Printf("ERROR converting location %s:%s: %s", cppLocation.URI, cppLocation.Range, err) + logger("ERROR converting location %s:%s: %s", cppLocation.URI, cppLocation.Range, err) return nil } inoSymbolInfo := cppSymbolInfo @@ -1356,9 +1500,9 @@ func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI l // Method: "textDocument/documentHighlight" res := []lsp.DocumentHighlight{} for _, cppHL := range *r { - inoHL, err := handler.cpp2inoDocumentHighlight(&cppHL, cppURI) + inoHL, err := handler.cpp2inoDocumentHighlight(logger, &cppHL, cppURI) if err != nil { - log.Printf("ERROR converting location %s:%s: %s", cppURI, cppHL.Range, err) + logger("ERROR converting location %s:%s: %s", cppURI, cppHL.Range, err) return nil } res = append(res, *inoHL) @@ -1366,21 +1510,21 @@ func (handler *InoHandler) transformClangdResult(method string, inoURI, cppURI l return &res case *lsp.WorkspaceEdit: // "textDocument/rename": - return handler.cpp2inoWorkspaceEdit(r) + return handler.cpp2inoWorkspaceEdit(logger, r) } return result } -func (handler *InoHandler) cpp2inoCodeAction(codeAction *lsp.CodeAction, uri lsp.DocumentURI) *lsp.CodeAction { - if codeAction == nil { - return nil - } - inoCodeAction := &lsp.CodeAction{ +func (handler *InoHandler) cpp2inoCodeAction(logger streams.PrefixLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { + inoCodeAction := lsp.CodeAction{ Title: codeAction.Title, Kind: codeAction.Kind, - Edit: handler.cpp2inoWorkspaceEdit(codeAction.Edit), + Edit: handler.cpp2inoWorkspaceEdit(logger, codeAction.Edit), Diagnostics: codeAction.Diagnostics, - Command: handler.Cpp2InoCommand(codeAction.Command), + } + if codeAction.Command != nil { + inoCommand := handler.Cpp2InoCommand(logger, *codeAction.Command) + inoCodeAction.Command = &inoCommand } if uri.Ext() == ".ino" { for i, diag := range inoCodeAction.Diagnostics { @@ -1390,11 +1534,8 @@ func (handler *InoHandler) cpp2inoCodeAction(codeAction *lsp.CodeAction, uri lsp return inoCodeAction } -func (handler *InoHandler) Cpp2InoCommand(command *lsp.Command) *lsp.Command { - if command == nil { - return nil - } - inoCommand := &lsp.Command{ +func (handler *InoHandler) Cpp2InoCommand(logger streams.PrefixLogger, command lsp.Command) lsp.Command { + inoCommand := lsp.Command{ Title: command.Title, Command: command.Command, Arguments: command.Arguments, @@ -1408,7 +1549,7 @@ func (handler *InoHandler) Cpp2InoCommand(command *lsp.Command) *lsp.Command { }{} if err := json.Unmarshal(command.Arguments[0], &v); err == nil { if v.TweakID == "ExtractVariable" { - log.Println(" > converted clangd ExtractVariable") + logger(" > converted clangd ExtractVariable") if v.File.AsPath().EquivalentTo(handler.buildSketchCpp) { inoFile, inoSelection := handler.sketchMapper.CppToInoRange(v.Selection) v.File = lsp.NewDocumentURI(inoFile) @@ -1427,7 +1568,7 @@ func (handler *InoHandler) Cpp2InoCommand(command *lsp.Command) *lsp.Command { return inoCommand } -func (handler *InoHandler) cpp2inoWorkspaceEdit(cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { +func (handler *InoHandler) cpp2inoWorkspaceEdit(logger streams.PrefixLogger, cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { if cppWorkspaceEdit == nil { return nil } @@ -1444,9 +1585,9 @@ func (handler *InoHandler) cpp2inoWorkspaceEdit(cppWorkspaceEdit *lsp.WorkspaceE // ...otherwise convert edits to the sketch.ino.cpp into multilpe .ino edits for _, edit := range edits { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(editURI, edit.Range) + inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, editURI, edit.Range) if err != nil { - log.Printf(" error converting edit %s:%s: %s", editURI, edit.Range, err) + logger(" error converting edit %s:%s: %s", editURI, edit.Range, err) continue } //inoFile, inoRange := handler.sketchMapper.CppToInoRange(edit.Range) @@ -1460,20 +1601,20 @@ func (handler *InoHandler) cpp2inoWorkspaceEdit(cppWorkspaceEdit *lsp.WorkspaceE }) } } - log.Printf(" done converting workspaceEdit") + logger(" done converting workspaceEdit") return inoWorkspaceEdit } -func (handler *InoHandler) cpp2inoLocation(cppLocation lsp.Location) (lsp.Location, error) { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(cppLocation.URI, cppLocation.Range) +func (handler *InoHandler) cpp2inoLocation(logger streams.PrefixLogger, cppLocation lsp.Location) (lsp.Location, error) { + inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, 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) +func (handler *InoHandler) cpp2inoDocumentHighlight(logger streams.PrefixLogger, cppHighlight *lsp.DocumentHighlight, cppURI lsp.DocumentURI) (*lsp.DocumentHighlight, error) { + _, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppHighlight.Range) if err != nil { return nil, err } @@ -1483,10 +1624,10 @@ func (handler *InoHandler) cpp2inoDocumentHighlight(cppHighlight *lsp.DocumentHi }, nil } -func (handler *InoHandler) cpp2inoTextEdits(cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { +func (handler *InoHandler) cpp2inoTextEdits(logger streams.PrefixLogger, 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) + inoURI, inoEdit, err := handler.cpp2inoTextEdit(logger, cppURI, cppEdit) if err != nil { return nil, err } @@ -1500,25 +1641,25 @@ func (handler *InoHandler) cpp2inoTextEdits(cppURI lsp.DocumentURI, cppEdits []l 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) +func (handler *InoHandler) cpp2inoTextEdit(logger streams.PrefixLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { + inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppEdit.Range) inoEdit := cppEdit inoEdit.Range = inoRange return inoURI, inoEdit, err } -func (handler *InoHandler) cpp2inoDocumentSymbols(cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { +func (handler *InoHandler) cpp2inoDocumentSymbols(logger streams.PrefixLogger, cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { inoRequested := inoRequestedURI.AsPath().String() - log.Printf(" filtering for requested ino file: %s", inoRequested) + logger(" 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) + logger(" > 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") + logger(" symbol is in the preprocessed section of the sketch.ino.cpp") continue } @@ -1526,14 +1667,14 @@ func (handler *InoHandler) cpp2inoDocumentSymbols(cppSymbols []lsp.DocumentSymbo 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) + logger(" ERROR: symbol range and selection belongs to different URI!") + logger(" symbol %s != selection %s", symbol.Range, symbol.SelectionRange) + logger(" %s:%s != %s:%s", inoFile, inoRange, inoSelectionURI, inoSelectionRange) continue } if inoFile != inoRequested { - log.Printf(" skipping symbol related to %s", inoFile) + logger(" skipping symbol related to %s", inoFile) continue } @@ -1544,7 +1685,7 @@ func (handler *InoHandler) cpp2inoDocumentSymbols(cppSymbols []lsp.DocumentSymbo Kind: symbol.Kind, Range: inoRange, SelectionRange: inoSelectionRange, - Children: handler.cpp2inoDocumentSymbols(symbol.Children, inoRequestedURI), + Children: handler.cpp2inoDocumentSymbols(logger, symbol.Children, inoRequestedURI), }) } @@ -1576,7 +1717,7 @@ func (handler *InoHandler) cpp2inoSymbolInformation(syms []lsp.SymbolInformation // return symbols } -func (handler *InoHandler) cpp2inoDiagnostics(cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { +func (handler *InoHandler) cpp2inoDiagnostics(logger streams.PrefixLogger, cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { inoDiagsParam := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} cppURI := cppDiags.URI @@ -1590,7 +1731,7 @@ func (handler *InoHandler) cpp2inoDiagnostics(cppDiags *lsp.PublishDiagnosticsPa } handler.inoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} } else { - inoURI, _, err := handler.cpp2inoDocumentURI(cppURI, lsp.NilRange) + inoURI, _, err := handler.cpp2inoDocumentURI(logger, cppURI, lsp.NilRange) if err != nil { return nil, err } @@ -1601,7 +1742,7 @@ func (handler *InoHandler) cpp2inoDiagnostics(cppDiags *lsp.PublishDiagnosticsPa } for _, cppDiag := range cppDiags.Diagnostics { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(cppURI, cppDiag.Range) + inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppDiag.Range) if err != nil { return nil, err } @@ -1628,11 +1769,14 @@ func (handler *InoHandler) cpp2inoDiagnostics(cppDiags *lsp.PublishDiagnosticsPa // 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 + var inoDiagCode string + if err := json.Unmarshal(inoDiag.Code, &inoDiagCode); err != nil { + if inoDiagCode == "undeclared_var_use_suggest" || + inoDiagCode == "undeclared_var_use" || + inoDiagCode == "ovl_no_viable_function_in_call" || + inoDiagCode == "pp_file_not_found" { + handler.buildSketchSymbolsCheck = true + } } } } @@ -1644,129 +1788,169 @@ func (handler *InoHandler) cpp2inoDiagnostics(cppDiags *lsp.PublishDiagnosticsPa 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) { +// HandleRequestFromClangd handles a notification message received from clangd. +func (handler *InoHandler) HandleNotificationFromClangd(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { 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) - } + // n := atomic.AddInt64(&handler.clangdMessageCount, 1) + // prefix := fmt.Sprintf("CLG <-- %s notif%d ", method, n) - 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 + params, err := lsp.DecodeNotificationParams(method, paramsRaw) + if err != nil { + logger("error parsing clang message:", err) + return } - - 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 + if params == nil { + // passthrough + logger("passing through message") + if err := handler.IDEConn.SendNotification(method, paramsRaw); err != nil { + logger("Error sending notification to IDE: " + err.Error()) } + return + } - 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 + // method: "$/progress" + if progress, ok := params.(lsp.ProgressParams); ok { + var token string + if err := json.Unmarshal(progress.Token, &token); err != nil { + logger("error decoding progess token: %s", err) + return } - - 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 + switch value := progress.TryToDecodeWellKnownValues().(type) { + case lsp.WorkDoneProgressBegin: + // logger("report %s %v", id, value) + handler.progressHandler.Begin(token, &value) + case lsp.WorkDoneProgressReport: + // logger("report %s %v", id, value) + handler.progressHandler.Report(token, &value) + case lsp.WorkDoneProgressEnd: + // logger("end %s %v", id, value) + handler.progressHandler.End(token, &value) + default: + logger("error unsupported $/progress: " + string(progress.Value)) } - - log.Printf(prefix + "error unsupported $/progress: " + string(*params.Value)) - return nil, errors.New("unsupported $/progress: " + string(*params.Value)) + return } // 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") - } + logger("(queued)") + handler.readLock(logger, false) + defer handler.readUnlock(logger) + logger("(running)") switch p := params.(type) { case *lsp.PublishDiagnosticsParams: // "textDocument/publishDiagnostics" - log.Printf(prefix+"publishDiagnostics(%s):", p.URI) + logger("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) + logger("> %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) + inoDiagnostics, err := handler.cpp2inoDiagnostics(logger, p) if err != nil { - return nil, err + logger(" Error converting diagnostics to .ino: %s", err) + return } // Push back to IDE the converted diagnostics for _, inoDiag := range inoDiagnostics { - - log.Printf(prefix+"to IDE: publishDiagnostics(%s):", inoDiag.URI) + logger("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) + logger("> %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 + if err := handler.IDEConn.SendNotification("textDocument/publishDiagnostics", lsp.EncodeMessage(inoDiag)); err != nil { + logger(" Error sending diagnostics to IDE: %s", err) + return } } - return nil, err + return + } + if err != nil { + logger("From clangd: Method:", method, "Error:", err) + return + } + logger("to IDE") + if err := handler.IDEConn.SendNotification(method, lsp.EncodeMessage(params)); err != nil { + logger("Error sending notification to IDE: " + err.Error()) + } +} + +// HandleRequestFromClangd handles a request message received from clangd. +func (handler *InoHandler) HandleRequestFromClangd(ctx context.Context, logger streams.PrefixLogger, + method string, paramsRaw json.RawMessage, + respCallback func(result json.RawMessage, err *jsonrpc.ResponseError), +) { + defer streams.CatchAndLogPanic() + + // n := atomic.AddInt64(&handler.clangdMessageCount, 1) + // prefix := fmt.Sprintf("CLG <-- %s %v ", method, n) + + params, err := lsp.DecodeRequestParams(method, paramsRaw) + if err != nil { + logger("Error parsing clang message: %v", err) + respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) + return + } + + if method == "window/workDoneProgress/create" { + // server initiated progress + var createReq lsp.WorkDoneProgressCreateParams + if err := json.Unmarshal(paramsRaw, &createReq); err != nil { + logger("error decoding window/workDoneProgress/create: %v", err) + respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) + return + } + + var token string + if err := json.Unmarshal(createReq.Token, &token); err != nil { + logger("error decoding progess token: %s", err) + respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) + return + } + handler.progressHandler.Create(token) + respCallback(lsp.EncodeMessage(struct{}{}), nil) + return + } + + // Default to read lock + logger("(queued)") + handler.readLock(logger, false) + defer handler.readUnlock(logger) + logger("(running)") + + switch p := params.(type) { case *lsp.ApplyWorkspaceEditParams: // "workspace/applyEdit" - p.Edit = *handler.cpp2inoWorkspaceEdit(&p.Edit) + p.Edit = *handler.cpp2inoWorkspaceEdit(logger, &p.Edit) } if err != nil { - log.Println("From clangd: Method:", req.Method, "Error:", err) - return nil, err + logger("From clangd: Method: %s, Error: %v", method, err) + respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) + return } + respRaw := lsp.EncodeMessage(params) if params == nil { // passthrough - log.Printf(prefix + "passing through message") - params = req.Params + logger("passing through message") + respRaw = paramsRaw } - 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) + logger("to IDE") + resp, respErr, err := handler.IDEConn.SendRequest(ctx, method, respRaw) + if err != nil { + logger("Error sending request to IDE:", err) + respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) + return } - return result, err + + respCallback(resp, respErr) } -func (handler *InoHandler) createClangdFormatterConfig(cppuri lsp.DocumentURI) (func(), error) { +func (handler *InoHandler) createClangdFormatterConfig(logger streams.PrefixLogger, 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 @@ -1921,9 +2105,9 @@ 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) + logger(" error reading custom formatter config file %s: %s", conf, err) } else { - log.Printf(" using custom formatter config file %s", conf) + logger(" using custom formatter config file %s", conf) config = string(c) } return true @@ -1944,9 +2128,9 @@ WhitespaceSensitiveMacros: [] targetFile = targetFile.Join(".clang-format") cleanup := func() { targetFile.Remove() - log.Printf(" formatter config cleaned") + logger(" formatter config cleaned") } - log.Printf(" writing formatter config in: %s", targetFile) + logger(" writing formatter config in: %s", targetFile) err := targetFile.WriteFile([]byte(config)) return cleanup, err } @@ -1958,7 +2142,9 @@ func (handler *InoHandler) showMessage(ctx context.Context, msgType lsp.MessageT Type: msgType, Message: message, } - handler.StdioConn.Notify(ctx, "window/showMessage", ¶ms) + if err := handler.IDEConn.SendNotification("window/showMessage", lsp.EncodeMessage(params)); err != nil { + // TODO: Log? + } } func unknownURI(uri lsp.DocumentURI) error { diff --git a/handler/progress.go b/handler/progress.go index ea2b997..f2041aa 100644 --- a/handler/progress.go +++ b/handler/progress.go @@ -5,13 +5,14 @@ import ( "log" "sync" - "github.com/arduino/arduino-language-server/lsp" "github.com/arduino/arduino-language-server/streams" - "github.com/sourcegraph/jsonrpc2" + "go.bug.st/json" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" ) type ProgressProxyHandler struct { - conn *jsonrpc2.Conn + conn *jsonrpc.Connection mux sync.Mutex actionRequiredCond *sync.Cond proxies map[string]*progressProxy @@ -35,7 +36,7 @@ type progressProxy struct { endReq *lsp.WorkDoneProgressEnd } -func NewProgressProxy(conn *jsonrpc2.Conn) *ProgressProxyHandler { +func NewProgressProxy(conn *jsonrpc.Connection) *ProgressProxyHandler { res := &ProgressProxyHandler{ conn: conn, proxies: map[string]*progressProxy{}, @@ -70,25 +71,30 @@ 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.SendRequest(context.Background(), "window/workDoneProgress/create", + lsp.EncodeMessage(&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), + notif, _ := json.Marshal(lsp.ProgressParams{ + Token: lsp.EncodeMessage(id), + Value: lsp.EncodeMessage(proxy.beginReq), }) + err := p.conn.SendNotification("$/progress", notif) proxy.beginReq = nil if err != nil { @@ -99,9 +105,11 @@ 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)}) + notif, _ := json.Marshal(&lsp.ProgressParams{ + Token: lsp.EncodeMessage(id), + Value: lsp.EncodeMessage(proxy.reportReq), + }) + err := p.conn.SendNotification("$/progress", notif) proxy.reportReq = nil if err != nil { @@ -111,10 +119,11 @@ 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), + notif, _ := json.Marshal(&lsp.ProgressParams{ + Token: lsp.EncodeMessage(id), + Value: lsp.EncodeMessage(proxy.endReq), }) + err := p.conn.SendNotification("$/progress", notif) proxy.endReq = nil if err != nil { diff --git a/handler/sourcemapper/ino.go b/handler/sourcemapper/ino.go index 7511570..c652c63 100644 --- a/handler/sourcemapper/ino.go +++ b/handler/sourcemapper/ino.go @@ -10,9 +10,9 @@ import ( "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" ) // InoMapper is a mapping between the .ino sketch and the preprocessed .cpp file @@ -206,7 +206,7 @@ func unquoteCppString(str string) string { // 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) { - inoRange := *inoChange.Range + inoRange := inoChange.Range cppRange := s.InoToCppLSPRange(inoURI, inoRange) deletedLines := inoRange.End.Line - inoRange.Start.Line diff --git a/handler/textutils/textutils.go b/handler/textutils/textutils.go index 22b0c91..fa46d0b 100644 --- a/handler/textutils/textutils.go +++ b/handler/textutils/textutils.go @@ -3,14 +3,14 @@ package textutils import ( "fmt" - "github.com/arduino/arduino-language-server/lsp" + "go.bug.st/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 { + if t, err := ApplyTextChange(newText, change.Range, change.Text); err == nil { newText = t } else { return err diff --git a/handler/textutils/textutils_test.go b/handler/textutils/textutils_test.go index c314760..24997b0 100644 --- a/handler/textutils/textutils_test.go +++ b/handler/textutils/textutils_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/arduino/arduino-language-server/lsp" + "go.bug.st/lsp" ) func TestApplyTextChange(t *testing.T) { diff --git a/lsp/protocol.go b/lsp/protocol.go index 7857db8..1b8ad2f 100644 --- a/lsp/protocol.go +++ b/lsp/protocol.go @@ -100,9 +100,9 @@ func ReadParams(method string, raw *json.RawMessage) (interface{}, error) { err := json.Unmarshal(*raw, params) return params, err case "arduino/selectedBoard": - params := new(BoardConfig) - err := json.Unmarshal(*raw, params) - return params, err + // params := new(BoardConfig) + // err := json.Unmarshal(*raw, params) + // return params, err } return nil, nil } @@ -316,15 +316,3 @@ 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/main.go b/main.go index f0b90da..d465b14 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( "os/signal" "github.com/arduino/arduino-language-server/handler" - "github.com/arduino/arduino-language-server/lsp" "github.com/arduino/arduino-language-server/streams" "github.com/arduino/go-paths-helper" ) @@ -54,14 +53,14 @@ func main() { } handler.Setup(cliPath, cliConfigPath, clangdPath, formatFilePath, enableLogging) - initialBoard := lsp.Board{Fqbn: initialFqbn, Name: initialBoardName} + initialBoard := handler.Board{Fqbn: initialFqbn, Name: initialBoardName} stdio := streams.NewReadWriteCloser(os.Stdin, os.Stdout) if enableLogging { stdio = streams.LogReadWriteCloserAs(stdio, "inols.log") } - inoHandler := handler.NewInoHandler(stdio, initialBoard) + inoHandler := handler.NewInoHandler(stdio, stdio, initialBoard) // Intercept kill signal c := make(chan os.Signal, 2) diff --git a/streams/jsonrpc2.go b/streams/jsonrpc2.go index 85244db..601e7c9 100644 --- a/streams/jsonrpc2.go +++ b/streams/jsonrpc2.go @@ -1,98 +1,116 @@ package streams import ( - "encoding/json" "fmt" "log" - "runtime/debug" + "sync/atomic" - "github.com/arduino/arduino-language-server/lsp" "github.com/fatih/color" - "github.com/sourcegraph/jsonrpc2" + "go.bug.st/json" + "go.bug.st/lsp/jsonrpc" ) -var green = color.New(color.FgHiGreen) -var red = color.New(color.FgHiRed) +var index int64 -// 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) +type PrefixLogger func(format string, a ...interface{}) + +func NewPrefixLogger(col *color.Color, prefix string) PrefixLogger { + return func(format string, a ...interface{}) { + log.Print(col.Sprintf(prefix+format, a...)) } } -// 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) - } +type JsonRPCLogger struct { + client string + server string + clColor *color.Color + srvColor *color.Color } -func jsonrpcLog(prefix string, req *jsonrpc2.Request, resp *jsonrpc2.Response, sending bool) { +func NewJsonRPCLogger(client, server string, weAreClient bool) *JsonRPCLogger { color.NoColor = false - var c *color.Color - if sending { - c = red - } else { - c = green + clColor := color.New(color.FgHiRed) + srvColor := color.New(color.FgHiGreen) + if !weAreClient { + clColor, srvColor = srvColor, clColor } - 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())) + return &JsonRPCLogger{ + client: client + " --> " + server + " ", + server: client + " <-- " + server + " ", + clColor: clColor, + srvColor: srvColor, } } -func jsonrpcLogDecodeReq(req *jsonrpc2.Request) string { - fmtString := func(s *string) string { - if s == nil { - return "" - } - return *s +func empty(s string) string { + return " "[:len(s)] +} + +func (l *JsonRPCLogger) LogClientRequest(method string, params json.RawMessage) (PrefixLogger, int64) { + id := atomic.AddInt64(&index, 1) + prefix := fmt.Sprintf("REQ %s %v: ", method, id) + dec := "" + log.Print(l.clColor.Sprintf(l.client+prefix+"%s", dec)) + return NewPrefixLogger(l.clColor, empty(l.client)+prefix), id +} + +func (l *JsonRPCLogger) LogClientResponse(id int64, method string, params json.RawMessage, err *jsonrpc.ResponseError) { + dec := "" + if err != nil { + dec += fmt.Sprintf("ERROR %v", err.AsError()) } - fmtFloat := func(s *float64) float64 { - if s == nil { - return 0 - } - return *s + log.Print(l.clColor.Sprintf(l.client+"RESP %s %v: %s", method, id, dec)) +} + +func (l *JsonRPCLogger) LogClientNotification(method string, params json.RawMessage) PrefixLogger { + prefix := fmt.Sprintf("NOTIF %s: ", method) + dec := "" + log.Print(l.clColor.Sprintf(l.client+prefix+"%s", dec)) + return NewPrefixLogger(l.clColor, empty(l.client)+prefix) +} + +func (l *JsonRPCLogger) LogServerRequest(method string, params json.RawMessage) (PrefixLogger, int64) { + id := atomic.AddInt64(&index, 1) + + prefix := fmt.Sprintf("REQ %s %v: ", method, id) + dec := "" + log.Print(l.srvColor.Sprintf(l.server+prefix+"%s", dec)) + return NewPrefixLogger(l.srvColor, empty(l.server)+prefix), id +} + +func (l *JsonRPCLogger) LogServerResponse(id int64, method string, params json.RawMessage, err *jsonrpc.ResponseError) { + dec := "" + if err != nil { + dec += fmt.Sprintf("ERROR %v", err.AsError()) } - switch req.Method { + log.Print(l.srvColor.Sprintf(l.server+"RESP %s %v: %s", method, id, dec)) +} + +func (l *JsonRPCLogger) LogServerNotification(method string, params json.RawMessage) PrefixLogger { + prefix := fmt.Sprintf("NOTIF %s: ", method) + dec := "" + log.Print(l.srvColor.Sprintf(l.server+prefix+"%s", dec)) + return NewPrefixLogger(l.srvColor, empty(l.server)+prefix) +} + +func decodeLspRequest(method string, req json.RawMessage) string { + switch 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?" + // var begin lsp.WorkDoneProgressBegin + // if json.Unmarshal(*v.Value, &begin) == nil { + // return fmt.Sprintf("TOKEN=%s BEGIN %v %v", v.Token, begin.Title, begin.Message) + // } + // var report lsp.WorkDoneProgressReport + // if json.Unmarshal(*v.Value, &report) == nil { + // return fmt.Sprintf("TOKEN=%s REPORT %v %v%%", v.Token, 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?" + return "SOME PROGRESS..." default: return "" } } - -func jsonrpcLogDecodeResp(resp *jsonrpc2.Response) string { - return "" -} From 99fa3a8c81c0cc0f7d8946c495790b7a8bd0c08f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 28 Oct 2021 15:32:52 +0200 Subject: [PATCH 02/76] removed old LSP lib --- handler/handler.go | 14 +- lsp/LICENSE | 19 - lsp/README.md | 10 - lsp/jsonrpc2.go | 52 -- lsp/protocol.go | 318 ----------- lsp/protocol_test.go | 172 ------ lsp/service.go | 1198 ------------------------------------------ lsp/structures.go | 190 ------- lsp/uri.go | 94 ---- lsp/uri_test.go | 91 ---- 10 files changed, 8 insertions(+), 2150 deletions(-) delete mode 100644 lsp/LICENSE delete mode 100644 lsp/README.md delete mode 100644 lsp/jsonrpc2.go delete mode 100644 lsp/protocol.go delete mode 100644 lsp/protocol_test.go delete mode 100644 lsp/service.go delete mode 100644 lsp/structures.go delete mode 100644 lsp/uri.go delete mode 100644 lsp/uri_test.go diff --git a/handler/handler.go b/handler/handler.go index c79628a..3d3cce6 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -432,8 +432,14 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, logger stre returnCB(lsp.EncodeMessage(&lsp.InitializeResult{ Capabilities: lsp.ServerCapabilities{ - TextDocumentSync: &lsp.TextDocumentSyncOptions{}, //{Kind: &lsp.TDSKIncremental}, - HoverProvider: &lsp.HoverOptions{}, // true, + TextDocumentSync: &lsp.TextDocumentSyncOptions{ + OpenClose: true, + Change: lsp.TextDocumentSyncKindIncremental, + Save: &lsp.SaveOptions{ + IncludeText: true, + }, + }, + HoverProvider: &lsp.HoverOptions{}, // true, CompletionProvider: &lsp.CompletionOptions{ TriggerCharacters: []string{".", "\u003e", ":"}, }, @@ -1833,10 +1839,8 @@ func (handler *InoHandler) HandleNotificationFromClangd(ctx context.Context, log } // Default to read lock - logger("(queued)") handler.readLock(logger, false) defer handler.readUnlock(logger) - logger("(running)") switch p := params.(type) { case *lsp.PublishDiagnosticsParams: @@ -1916,10 +1920,8 @@ func (handler *InoHandler) HandleRequestFromClangd(ctx context.Context, logger s } // Default to read lock - logger("(queued)") handler.readLock(logger, false) defer handler.readUnlock(logger) - logger("(running)") switch p := params.(type) { case *lsp.ApplyWorkspaceEditParams: 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 1b8ad2f..0000000 --- a/lsp/protocol.go +++ /dev/null @@ -1,318 +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"` -} 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, "/", `\`) -} From 2e2166a7c6b4511037342d24e0b649344c7c105f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 28 Oct 2021 17:03:57 +0200 Subject: [PATCH 03/76] Renamed INOHandler to INOLanguageServer --- handler/builder.go | 6 +-- handler/handler.go | 123 ++++++++++++++++++++------------------------- main.go | 2 +- 3 files changed, 58 insertions(+), 73 deletions(-) diff --git a/handler/builder.go b/handler/builder.go index ffb19b9..3cabd1d 100644 --- a/handler/builder.go +++ b/handler/builder.go @@ -15,14 +15,14 @@ import ( "go.bug.st/lsp" ) -func (handler *InoHandler) scheduleRebuildEnvironment() { +func (handler *INOLanguageServer) scheduleRebuildEnvironment() { handler.rebuildSketchDeadlineMutex.Lock() defer handler.rebuildSketchDeadlineMutex.Unlock() d := time.Now().Add(time.Second) handler.rebuildSketchDeadline = &d } -func (handler *InoHandler) rebuildEnvironmentLoop() { +func (handler *INOLanguageServer) rebuildEnvironmentLoop() { defer streams.CatchAndLogPanic() logger := streams.NewPrefixLogger(color.New(color.FgHiMagenta), "RBLD---") @@ -85,7 +85,7 @@ func (handler *InoHandler) rebuildEnvironmentLoop() { } } -func (handler *InoHandler) generateBuildEnvironment(logger streams.PrefixLogger, buildPath *paths.Path) error { +func (handler *INOLanguageServer) generateBuildEnvironment(logger streams.PrefixLogger, buildPath *paths.Path) error { sketchDir := handler.sketchRoot fqbn := handler.config.SelectedBoard.Fqbn diff --git a/handler/handler.go b/handler/handler.go index 3d3cce6..8842e70 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -47,14 +47,12 @@ func Setup(cliPath, cliConfigPath, clangdPath, formatFilePath string, _enableLog // 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 { +// INOLanguageServer is a JSON-RPC handler that delegates messages to clangd. +type INOLanguageServer struct { IDEConn *jsonrpc.Connection ClangdConn *jsonrpc.Connection - ideMessageCount int64 - clangdMessageCount int64 - progressHandler *ProgressProxyHandler + progressHandler *ProgressProxyHandler closing chan bool clangdStarted *sync.Cond @@ -95,7 +93,7 @@ type Board struct { var yellow = color.New(color.FgHiYellow) -func (handler *InoHandler) writeLock(logger streams.PrefixLogger, requireClangd bool) { +func (handler *INOLanguageServer) writeLock(logger streams.PrefixLogger, requireClangd bool) { handler.dataMux.Lock() logger(yellow.Sprintf("write-locked")) if requireClangd { @@ -103,12 +101,12 @@ func (handler *InoHandler) writeLock(logger streams.PrefixLogger, requireClangd } } -func (handler *InoHandler) writeUnlock(logger streams.PrefixLogger) { +func (handler *INOLanguageServer) writeUnlock(logger streams.PrefixLogger) { logger(yellow.Sprintf("write-unlocked")) handler.dataMux.Unlock() } -func (handler *InoHandler) readLock(logger streams.PrefixLogger, requireClangd bool) { +func (handler *INOLanguageServer) readLock(logger streams.PrefixLogger, requireClangd bool) { handler.dataMux.RLock() logger(yellow.Sprintf("read-locked")) @@ -128,12 +126,12 @@ func (handler *InoHandler) readLock(logger streams.PrefixLogger, requireClangd b } } -func (handler *InoHandler) readUnlock(logger streams.PrefixLogger) { +func (handler *INOLanguageServer) readUnlock(logger streams.PrefixLogger) { logger(yellow.Sprintf("read-unlocked")) handler.dataMux.RUnlock() } -func (handler *InoHandler) waitClangdStart(logger streams.PrefixLogger) error { +func (handler *INOLanguageServer) waitClangdStart(logger streams.PrefixLogger) error { if handler.ClangdConn != nil { return nil } @@ -150,10 +148,10 @@ func (handler *InoHandler) waitClangdStart(logger streams.PrefixLogger) error { return nil } -// NewInoHandler creates and configures an InoHandler. -func NewInoHandler(stdin io.Reader, stdout io.Writer, board Board) *InoHandler { +// NewINOLanguageServer creates and configures an Arduino Language Server. +func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLanguageServer { logger := streams.NewPrefixLogger(color.New(color.FgWhite), "LS: ") - handler := &InoHandler{ + handler := &INOLanguageServer{ docs: map[string]*lsp.TextDocumentItem{}, inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, closing: make(chan bool), @@ -202,17 +200,8 @@ func NewInoHandler(stdin io.Reader, stdout io.Writer, board Board) *InoHandler { 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() { +func (handler *INOLanguageServer) Close() { if handler.ClangdConn != nil { handler.ClangdConn.Close() handler.ClangdConn = nil @@ -224,22 +213,20 @@ func (handler *InoHandler) Close() { } // CloseNotify returns a channel that is closed when the InoHandler is closed -func (handler *InoHandler) CloseNotify() <-chan bool { +func (handler *INOLanguageServer) CloseNotify() <-chan bool { return handler.closing } // CleanUp performs cleanup of the workspace and temp files create by the language server -func (handler *InoHandler) CleanUp() { +func (handler *INOLanguageServer) CleanUp() { if handler.buildPath != nil { handler.buildPath.RemoveAll() handler.buildPath = nil } } -func (handler *InoHandler) HandleNotificationFromIDE(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { +func (handler *INOLanguageServer) HandleNotificationFromIDE(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { defer streams.CatchAndLogPanic() - // n := atomic.AddInt64(&handler.ideMessageCount, 1) - // prefix := fmt.Sprintf("IDE --> %s notif%d ", method, n) params, err := lsp.DecodeNotificationParams(method, paramsRaw) if err != nil { @@ -375,13 +362,11 @@ func (handler *InoHandler) HandleNotificationFromIDE(ctx context.Context, logger } // HandleMessageFromIDE handles a message received from the IDE client (via stdio). -func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, logger streams.PrefixLogger, +func (handler *INOLanguageServer) HandleMessageFromIDE(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage, returnCB func(result json.RawMessage, err *jsonrpc.ResponseError), ) { defer streams.CatchAndLogPanic() - // n := atomic.AddInt64(&handler.ideMessageCount, 1) - // prefix := fmt.Sprintf("IDE --> %s %v ", method, n) params, err := lsp.DecodeRequestParams(method, paramsRaw) if err != nil { @@ -667,7 +652,7 @@ func (handler *InoHandler) HandleMessageFromIDE(ctx context.Context, logger stre returnCB(lsp.EncodeMessage(clangResp), nil) } -func (handler *InoHandler) initializeWorkbench(logger streams.PrefixLogger, params *lsp.InitializeParams) error { +func (handler *INOLanguageServer) initializeWorkbench(logger streams.PrefixLogger, params *lsp.InitializeParams) error { currCppTextVersion := 0 if params != nil { logger(" --> initialize(%s)", params.RootURI) @@ -814,7 +799,7 @@ func extractDataFolderFromArduinoCLI(logger streams.PrefixLogger) (*paths.Path, return paths.New(res.Directories.Data), nil } -func (handler *InoHandler) refreshCppDocumentSymbols(logger streams.PrefixLogger) error { +func (handler *INOLanguageServer) refreshCppDocumentSymbols(logger streams.PrefixLogger) error { // Query source code symbols handler.readUnlock(logger) cppURI := lsp.NewDocumentURIFromPath(handler.buildSketchCpp) @@ -875,7 +860,7 @@ func (handler *InoHandler) refreshCppDocumentSymbols(logger streams.PrefixLogger return nil } -func (handler *InoHandler) LoadCppDocumentSymbols() error { +func (handler *INOLanguageServer) LoadCppDocumentSymbols() error { logger := streams.NewPrefixLogger(color.New(color.FgHiBlue), "SYLD --- ") defer logger("(done)") handler.readLock(logger, true) @@ -883,7 +868,7 @@ func (handler *InoHandler) LoadCppDocumentSymbols() error { return handler.refreshCppDocumentSymbols(logger) } -func (handler *InoHandler) CheckCppDocumentSymbols() error { +func (handler *INOLanguageServer) CheckCppDocumentSymbols() error { logger := streams.NewPrefixLogger(color.New(color.FgHiBlue), "SYCK --- ") defer logger("(done)") handler.readLock(logger, true) @@ -901,7 +886,7 @@ func (handler *InoHandler) CheckCppDocumentSymbols() error { return nil } -func (handler *InoHandler) CheckCppIncludesChanges() { +func (handler *INOLanguageServer) CheckCppIncludesChanges() { logger := streams.NewPrefixLogger(color.New(color.FgHiBlue), "INCK --- ") logger("check for Cpp Include Changes") includesCanary := "" @@ -976,7 +961,7 @@ func startClangd(logger streams.PrefixLogger, compileCommandsDir, sketchCpp *pat } } -func (handler *InoHandler) didOpen(logger streams.PrefixLogger, inoDidOpen *lsp.DidOpenTextDocumentParams) (*lsp.DidOpenTextDocumentParams, error) { +func (handler *INOLanguageServer) didOpen(logger streams.PrefixLogger, inoDidOpen *lsp.DidOpenTextDocumentParams) (*lsp.DidOpenTextDocumentParams, error) { // Add the TextDocumentItem in the tracked files list inoItem := inoDidOpen.TextDocument handler.docs[inoItem.URI.AsPath().String()] = &inoItem @@ -998,7 +983,7 @@ func (handler *InoHandler) didOpen(logger streams.PrefixLogger, inoDidOpen *lsp. }, err } -func (handler *InoHandler) didClose(logger streams.PrefixLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { +func (handler *INOLanguageServer) didClose(logger streams.PrefixLogger, 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()) @@ -1024,7 +1009,7 @@ func (handler *InoHandler) didClose(logger streams.PrefixLogger, inoDidClose *ls }, err } -func (handler *InoHandler) ino2cppTextDocumentItem(logger streams.PrefixLogger, inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { +func (handler *INOLanguageServer) ino2cppTextDocumentItem(logger streams.PrefixLogger, inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { cppURI, err := handler.ino2cppDocumentURI(logger, inoItem.URI) if err != nil { return cppItem, err @@ -1045,7 +1030,7 @@ func (handler *InoHandler) ino2cppTextDocumentItem(logger streams.PrefixLogger, return cppItem, nil } -func (handler *InoHandler) didChange(logger streams.PrefixLogger, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { +func (handler *INOLanguageServer) didChange(logger streams.PrefixLogger, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { doc := req.TextDocument trackedDoc, ok := handler.docs[doc.URI.AsPath().String()] @@ -1123,7 +1108,7 @@ func (handler *InoHandler) didChange(logger streams.PrefixLogger, req *lsp.DidCh return cppReq, err } -func (handler *InoHandler) handleError(ctx context.Context, err error) error { +func (handler *INOLanguageServer) handleError(ctx context.Context, err error) error { errorStr := err.Error() var message string if strings.Contains(errorStr, "#error") { @@ -1158,21 +1143,21 @@ func (handler *InoHandler) handleError(ctx context.Context, err error) error { return errors.New(message) } -func (handler *InoHandler) ino2cppVersionedTextDocumentIdentifier(logger streams.PrefixLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { +func (handler *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger streams.PrefixLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { cppURI, err := handler.ino2cppDocumentURI(logger, doc.URI) res := doc res.URI = cppURI return res, err } -func (handler *InoHandler) ino2cppTextDocumentIdentifier(logger streams.PrefixLogger, doc lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { +func (handler *INOLanguageServer) ino2cppTextDocumentIdentifier(logger streams.PrefixLogger, doc lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { cppURI, err := handler.ino2cppDocumentURI(logger, doc.URI) res := doc res.URI = cppURI return res, err } -func (handler *InoHandler) ino2cppDocumentURI(logger streams.PrefixLogger, inoURI lsp.DocumentURI) (lsp.DocumentURI, error) { +func (handler *INOLanguageServer) ino2cppDocumentURI(logger streams.PrefixLogger, 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) @@ -1205,7 +1190,7 @@ func (handler *InoHandler) ino2cppDocumentURI(logger streams.PrefixLogger, inoUR return lsp.NilURI, err } -func (handler *InoHandler) inoDocumentURIFromInoPath(logger streams.PrefixLogger, inoPath string) (lsp.DocumentURI, error) { +func (handler *INOLanguageServer) inoDocumentURIFromInoPath(logger streams.PrefixLogger, inoPath string) (lsp.DocumentURI, error) { if inoPath == sourcemapper.NotIno.File { return sourcemapper.NotInoURI, nil } @@ -1222,7 +1207,7 @@ func (handler *InoHandler) inoDocumentURIFromInoPath(logger streams.PrefixLogger return doc.URI, nil } -func (handler *InoHandler) cpp2inoDocumentURI(logger streams.PrefixLogger, cppURI lsp.DocumentURI, cppRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { +func (handler *INOLanguageServer) cpp2inoDocumentURI(logger streams.PrefixLogger, 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] @@ -1280,7 +1265,7 @@ func (handler *InoHandler) cpp2inoDocumentURI(logger streams.PrefixLogger, cppUR return lsp.NilURI, lsp.NilRange, err } -func (handler *InoHandler) ino2cppTextDocumentPositionParams(logger streams.PrefixLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { +func (handler *INOLanguageServer) ino2cppTextDocumentPositionParams(logger streams.PrefixLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { res := lsp.TextDocumentPositionParams{} cppDoc, err := handler.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) if err != nil { @@ -1301,7 +1286,7 @@ func (handler *InoHandler) ino2cppTextDocumentPositionParams(logger streams.Pref return res, nil } -func (handler *InoHandler) ino2cppRange(logger streams.PrefixLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { +func (handler *INOLanguageServer) ino2cppRange(logger streams.PrefixLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { cppURI, err := handler.ino2cppDocumentURI(logger, inoURI) if err != nil { return lsp.NilURI, lsp.Range{}, err @@ -1313,7 +1298,7 @@ func (handler *InoHandler) ino2cppRange(logger streams.PrefixLogger, inoURI lsp. return cppURI, inoRange, nil } -func (handler *InoHandler) ino2cppDocumentRangeFormattingParams(logger streams.PrefixLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { +func (handler *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger streams.PrefixLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) if err != nil { return nil, err @@ -1327,7 +1312,7 @@ func (handler *InoHandler) ino2cppDocumentRangeFormattingParams(logger streams.P }, err } -func (handler *InoHandler) ino2cppDocumentOnTypeFormattingParams(params *lsp.DocumentOnTypeFormattingParams) error { +func (handler *INOLanguageServer) ino2cppDocumentOnTypeFormattingParams(params *lsp.DocumentOnTypeFormattingParams) error { panic("not implemented") // handler.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) // if data, ok := handler.data[params.TextDocument.URI]; ok { @@ -1337,7 +1322,7 @@ func (handler *InoHandler) ino2cppDocumentOnTypeFormattingParams(params *lsp.Doc return unknownURI(params.TextDocument.URI) } -func (handler *InoHandler) ino2cppRenameParams(params *lsp.RenameParams) error { +func (handler *INOLanguageServer) ino2cppRenameParams(params *lsp.RenameParams) error { panic("not implemented") // handler.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) // if data, ok := handler.data[params.TextDocument.URI]; ok { @@ -1347,7 +1332,7 @@ func (handler *InoHandler) ino2cppRenameParams(params *lsp.RenameParams) error { return unknownURI(params.TextDocument.URI) } -func (handler *InoHandler) ino2cppDidChangeWatchedFilesParams(params *lsp.DidChangeWatchedFilesParams) error { +func (handler *INOLanguageServer) ino2cppDidChangeWatchedFilesParams(params *lsp.DidChangeWatchedFilesParams) error { panic("not implemented") // for index := range params.Changes { // fileEvent := ¶ms.Changes[index] @@ -1358,7 +1343,7 @@ func (handler *InoHandler) ino2cppDidChangeWatchedFilesParams(params *lsp.DidCha return nil } -func (handler *InoHandler) ino2cppExecuteCommand(executeCommand *lsp.ExecuteCommandParams) error { +func (handler *INOLanguageServer) ino2cppExecuteCommand(executeCommand *lsp.ExecuteCommandParams) error { panic("not implemented") // if len(executeCommand.Arguments) == 1 { // arg := handler.parseCommandArgument(executeCommand.Arguments[0]) @@ -1369,7 +1354,7 @@ func (handler *InoHandler) ino2cppExecuteCommand(executeCommand *lsp.ExecuteComm return nil } -func (handler *InoHandler) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { +func (handler *INOLanguageServer) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { panic("not implemented") newEdit := lsp.WorkspaceEdit{} // for uri, edit := range origEdit.Changes { @@ -1389,7 +1374,7 @@ func (handler *InoHandler) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *ls return &newEdit } -func (handler *InoHandler) transformClangdResult(logger streams.PrefixLogger, method string, inoURI, cppURI lsp.DocumentURI, result interface{}) interface{} { +func (handler *INOLanguageServer) transformClangdResult(logger streams.PrefixLogger, method string, inoURI, cppURI lsp.DocumentURI, result interface{}) interface{} { cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp) switch r := result.(type) { @@ -1521,7 +1506,7 @@ func (handler *InoHandler) transformClangdResult(logger streams.PrefixLogger, me return result } -func (handler *InoHandler) cpp2inoCodeAction(logger streams.PrefixLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { +func (handler *INOLanguageServer) cpp2inoCodeAction(logger streams.PrefixLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { inoCodeAction := lsp.CodeAction{ Title: codeAction.Title, Kind: codeAction.Kind, @@ -1540,7 +1525,7 @@ func (handler *InoHandler) cpp2inoCodeAction(logger streams.PrefixLogger, codeAc return inoCodeAction } -func (handler *InoHandler) Cpp2InoCommand(logger streams.PrefixLogger, command lsp.Command) lsp.Command { +func (handler *INOLanguageServer) Cpp2InoCommand(logger streams.PrefixLogger, command lsp.Command) lsp.Command { inoCommand := lsp.Command{ Title: command.Title, Command: command.Command, @@ -1574,7 +1559,7 @@ func (handler *InoHandler) Cpp2InoCommand(logger streams.PrefixLogger, command l return inoCommand } -func (handler *InoHandler) cpp2inoWorkspaceEdit(logger streams.PrefixLogger, cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { +func (handler *INOLanguageServer) cpp2inoWorkspaceEdit(logger streams.PrefixLogger, cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { if cppWorkspaceEdit == nil { return nil } @@ -1611,7 +1596,7 @@ func (handler *InoHandler) cpp2inoWorkspaceEdit(logger streams.PrefixLogger, cpp return inoWorkspaceEdit } -func (handler *InoHandler) cpp2inoLocation(logger streams.PrefixLogger, cppLocation lsp.Location) (lsp.Location, error) { +func (handler *INOLanguageServer) cpp2inoLocation(logger streams.PrefixLogger, cppLocation lsp.Location) (lsp.Location, error) { inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, cppLocation.URI, cppLocation.Range) return lsp.Location{ URI: inoURI, @@ -1619,7 +1604,7 @@ func (handler *InoHandler) cpp2inoLocation(logger streams.PrefixLogger, cppLocat }, err } -func (handler *InoHandler) cpp2inoDocumentHighlight(logger streams.PrefixLogger, cppHighlight *lsp.DocumentHighlight, cppURI lsp.DocumentURI) (*lsp.DocumentHighlight, error) { +func (handler *INOLanguageServer) cpp2inoDocumentHighlight(logger streams.PrefixLogger, cppHighlight *lsp.DocumentHighlight, cppURI lsp.DocumentURI) (*lsp.DocumentHighlight, error) { _, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppHighlight.Range) if err != nil { return nil, err @@ -1630,7 +1615,7 @@ func (handler *InoHandler) cpp2inoDocumentHighlight(logger streams.PrefixLogger, }, nil } -func (handler *InoHandler) cpp2inoTextEdits(logger streams.PrefixLogger, cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { +func (handler *INOLanguageServer) cpp2inoTextEdits(logger streams.PrefixLogger, 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(logger, cppURI, cppEdit) @@ -1647,14 +1632,14 @@ func (handler *InoHandler) cpp2inoTextEdits(logger streams.PrefixLogger, cppURI return res, nil } -func (handler *InoHandler) cpp2inoTextEdit(logger streams.PrefixLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { +func (handler *INOLanguageServer) cpp2inoTextEdit(logger streams.PrefixLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppEdit.Range) inoEdit := cppEdit inoEdit.Range = inoRange return inoURI, inoEdit, err } -func (handler *InoHandler) cpp2inoDocumentSymbols(logger streams.PrefixLogger, cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { +func (handler *INOLanguageServer) cpp2inoDocumentSymbols(logger streams.PrefixLogger, cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { inoRequested := inoRequestedURI.AsPath().String() logger(" filtering for requested ino file: %s", inoRequested) if inoRequestedURI.Ext() != ".ino" || len(cppSymbols) == 0 { @@ -1698,7 +1683,7 @@ func (handler *InoHandler) cpp2inoDocumentSymbols(logger streams.PrefixLogger, c return inoSymbols } -func (handler *InoHandler) cpp2inoSymbolInformation(syms []lsp.SymbolInformation) []lsp.SymbolInformation { +func (handler *INOLanguageServer) 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) @@ -1723,7 +1708,7 @@ func (handler *InoHandler) cpp2inoSymbolInformation(syms []lsp.SymbolInformation // return symbols } -func (handler *InoHandler) cpp2inoDiagnostics(logger streams.PrefixLogger, cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { +func (handler *INOLanguageServer) cpp2inoDiagnostics(logger streams.PrefixLogger, cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { inoDiagsParam := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} cppURI := cppDiags.URI @@ -1795,7 +1780,7 @@ func (handler *InoHandler) cpp2inoDiagnostics(logger streams.PrefixLogger, cppDi } // HandleRequestFromClangd handles a notification message received from clangd. -func (handler *InoHandler) HandleNotificationFromClangd(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { +func (handler *INOLanguageServer) HandleNotificationFromClangd(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { defer streams.CatchAndLogPanic() // n := atomic.AddInt64(&handler.clangdMessageCount, 1) @@ -1883,7 +1868,7 @@ func (handler *InoHandler) HandleNotificationFromClangd(ctx context.Context, log } // HandleRequestFromClangd handles a request message received from clangd. -func (handler *InoHandler) HandleRequestFromClangd(ctx context.Context, logger streams.PrefixLogger, +func (handler *INOLanguageServer) HandleRequestFromClangd(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage, respCallback func(result json.RawMessage, err *jsonrpc.ResponseError), ) { @@ -1952,7 +1937,7 @@ func (handler *InoHandler) HandleRequestFromClangd(ctx context.Context, logger s respCallback(resp, respErr) } -func (handler *InoHandler) createClangdFormatterConfig(logger streams.PrefixLogger, cppuri lsp.DocumentURI) (func(), error) { +func (handler *INOLanguageServer) createClangdFormatterConfig(logger streams.PrefixLogger, 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 @@ -2137,7 +2122,7 @@ WhitespaceSensitiveMacros: [] return cleanup, err } -func (handler *InoHandler) showMessage(ctx context.Context, msgType lsp.MessageType, message string) { +func (handler *INOLanguageServer) showMessage(ctx context.Context, msgType lsp.MessageType, message string) { defer streams.CatchAndLogPanic() params := lsp.ShowMessageParams{ diff --git a/main.go b/main.go index d465b14..200cadd 100644 --- a/main.go +++ b/main.go @@ -60,7 +60,7 @@ func main() { stdio = streams.LogReadWriteCloserAs(stdio, "inols.log") } - inoHandler := handler.NewInoHandler(stdio, stdio, initialBoard) + inoHandler := handler.NewINOLanguageServer(stdio, stdio, initialBoard) // Intercept kill signal c := make(chan os.Signal, 2) From 63c7ad1e76020480199aefa4f2b370cc84b70645 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 29 Oct 2021 15:26:03 +0200 Subject: [PATCH 04/76] Major improvement in logging --- handler/handler.go | 126 +++++++++++++++++++++++++++----------------- streams/jsonrpc2.go | 64 +++++++--------------- 2 files changed, 96 insertions(+), 94 deletions(-) diff --git a/handler/handler.go b/handler/handler.go index 8842e70..ee9e028 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -173,7 +173,7 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLa logger("Language server build sketch root: %s", handler.buildSketchRoot) } - jsonrpcLogger := streams.NewJsonRPCLogger("IDE", "LS", false) + jsonrpcLogger := streams.NewJsonRPCLogger("IDE", "LS") handler.IDEConn = jsonrpc.NewConnection(stdin, stdout, func(ctx context.Context, method string, params json.RawMessage, respCallback func(result json.RawMessage, err *jsonrpc.ResponseError)) { reqLogger, idx := jsonrpcLogger.LogClientRequest(method, params) @@ -228,7 +228,7 @@ func (handler *INOLanguageServer) CleanUp() { func (handler *INOLanguageServer) HandleNotificationFromIDE(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { defer streams.CatchAndLogPanic() - params, err := lsp.DecodeNotificationParams(method, paramsRaw) + params, err := lsp.DecodeClientNotificationParams(method, paramsRaw) if err != nil { // TODO: log? return @@ -314,10 +314,10 @@ func (handler *INOLanguageServer) HandleNotificationFromIDE(ctx context.Context, for _, change := range p.ContentChanges { logger(" > %s -> %s", change.Range, strconv.Quote(change.Text)) } + logger("LS --> CL NOTIF %s:", method) if err := handler.ClangdConn.SendNotification(method, lsp.EncodeMessage(p)); err != nil { // Exit the process and trigger a restart by the client in case of a severe error - logger("Connection error with clangd server:") - logger("%v", err) + logger("Connection error with clangd server: %v", err) logger("Please restart the language server.") handler.Close() } @@ -340,7 +340,7 @@ func (handler *INOLanguageServer) HandleNotificationFromIDE(ctx context.Context, return } - logger("sending to Clang") + logger("LS --> CL NOTIF %s:", method) if err := handler.ClangdConn.SendNotification(method, lsp.EncodeMessage(params)); err != nil { // Exit the process and trigger a restart by the client in case of a severe error logger("Connection error with clangd server:") @@ -368,7 +368,7 @@ func (handler *INOLanguageServer) HandleMessageFromIDE(ctx context.Context, logg ) { defer streams.CatchAndLogPanic() - params, err := lsp.DecodeRequestParams(method, paramsRaw) + params, err := lsp.DecodeClientRequestParams(method, paramsRaw) if err != nil { returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInvalidParams, Message: err.Error()}) return @@ -531,11 +531,21 @@ func (handler *INOLanguageServer) HandleMessageFromIDE(ctx context.Context, logg err = e } + case *lsp.DocumentHighlightParams: + tdp := p.TextDocumentPositionParams + + inoURI = tdp.TextDocument.URI + if res, e := handler.ino2cppTextDocumentPositionParams(logger, tdp); e == nil { + cppURI = res.TextDocument.URI + params = res + } else { + err = e + } + case *lsp.SignatureHelpParams, *lsp.DefinitionParams, *lsp.TypeDefinitionParams, - *lsp.ImplementationParams, - *lsp.DocumentHighlightParams: + *lsp.ImplementationParams: // it was *lsp.TextDocumentPositionParams: // Method: "textDocument/signatureHelp" @@ -599,10 +609,7 @@ func (handler *INOLanguageServer) HandleMessageFromIDE(ctx context.Context, logg return } - logger("sent to Clang") - - // var result interface{} - // result, err = lsp.SendRequest(ctx, handler.ClangdConn, method, params) + logger("LS --> CL REQ %s", method) clangRawResp, clangErr, err := handler.ClangdConn.SendRequest(ctx, method, lsp.EncodeMessage(params)) if err != nil { // Exit the process and trigger a restart by the client in case of a severe error @@ -626,7 +633,8 @@ func (handler *INOLanguageServer) HandleMessageFromIDE(ctx context.Context, logg returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()}) return } - clangResp, err := lsp.DecodeResponseResult(method, clangRawResp) + logger("LS <-- CL RESP %s", method) + clangResp, err := lsp.DecodeServerResponseResult(method, clangRawResp) if err != nil { logger("Error decoding clang response: %v", err) returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) @@ -696,7 +704,9 @@ func (handler *INOLanguageServer) initializeWorkbench(logger streams.PrefixLogge }, } - if err := handler.ClangdConn.SendNotification("textDocument/didChange", lsp.EncodeMessage(syncEvent)); err != nil { + method := "textDocument/didChange" + logger("LS --> CL NOTIF %s:", method) + if err := handler.ClangdConn.SendNotification(method, lsp.EncodeMessage(syncEvent)); err != nil { logger(" error reinitilizing clangd:", err) return err } @@ -716,7 +726,7 @@ func (handler *INOLanguageServer) initializeWorkbench(logger streams.PrefixLogge go io.Copy(os.Stderr, clangdStderr) } - rpcLogger := streams.NewJsonRPCLogger("IDE LS", "CL", true) + rpcLogger := streams.NewJsonRPCLogger("IDE LS", "CL") handler.ClangdConn = jsonrpc.NewConnection(clangdStdio, clangdStdio, func(ctx context.Context, method string, params json.RawMessage, respCallback func(result json.RawMessage, err *jsonrpc.ResponseError)) { logger, idx := rpcLogger.LogServerRequest(method, params) @@ -726,7 +736,7 @@ func (handler *INOLanguageServer) initializeWorkbench(logger streams.PrefixLogge }) }, func(ctx context.Context, method string, params json.RawMessage) { - logger := rpcLogger.LogClientNotification(method, params) + logger := rpcLogger.LogServerNotification(method, params) handler.HandleNotificationFromClangd(ctx, logger, method, params) }, func(e error) { @@ -743,20 +753,28 @@ func (handler *INOLanguageServer) initializeWorkbench(logger streams.PrefixLogge // Send initialization command to clangd ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - if rawResp, clangErr, err := handler.ClangdConn.SendRequest(ctx, "initialize", lsp.EncodeMessage(handler.lspInitializeParams)); err != nil { + method1 := "initialize" + logger("LS --> CL REQ %s", method1) + rawResp, clangErr, err := handler.ClangdConn.SendRequest( + ctx, method1, lsp.EncodeMessage(handler.lspInitializeParams)) + if err != nil { logger(" error initilizing clangd: %v", err) return err - } else if clangErr != nil { + } + logger("LS <-- CL RESP %s", method1) + if clangErr != nil { logger(" error initilizing clangd: %v", clangErr.AsError()) return clangErr.AsError() - } else if resp, err := lsp.DecodeResponseResult("initialize", rawResp); err != nil { + } else if resp, err := lsp.DecodeServerResponseResult("initialize", rawResp); err != nil { logger(" error initilizing clangd: %v", err) return err } else { - logger(" clangd successfully started: %v", resp) + logger("clangd successfully started: %v", resp) } - if err := handler.ClangdConn.SendNotification("initialized", lsp.EncodeMessage(lsp.InitializedParams{})); err != nil { + method2 := "initialized" + logger("LS --> CL NOTIF %s:", method2) + if err := handler.ClangdConn.SendNotification(method2, lsp.EncodeMessage(lsp.InitializedParams{})); err != nil { logger(" error sending initialized notification to clangd: %v", err) return err } @@ -804,7 +822,9 @@ func (handler *INOLanguageServer) refreshCppDocumentSymbols(logger streams.Prefi handler.readUnlock(logger) cppURI := lsp.NewDocumentURIFromPath(handler.buildSketchCpp) logger("requesting documentSymbol for %s", cppURI) - respRaw, resErr, err := handler.ClangdConn.SendRequest(context.Background(), "textDocument/documentSymbol", + method := "textDocument/documentSymbol" + logger("LS --> CL REQ %s", method) + respRaw, resErr, err := handler.ClangdConn.SendRequest(context.Background(), method, lsp.EncodeMessage(&lsp.DocumentSymbolParams{ TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, })) @@ -814,11 +834,12 @@ func (handler *INOLanguageServer) refreshCppDocumentSymbols(logger streams.Prefi logger("error: %s", err) return fmt.Errorf("quering source code symbols: %w", err) } + logger("LS <-- CL RESP %s", method) if resErr != nil { logger("error: %s", resErr.AsError()) return fmt.Errorf("quering source code symbols: %w", resErr.AsError()) } - result, err := lsp.DecodeResponseResult("textDocument/documentSymbol", respRaw) + result, err := lsp.DecodeServerResponseResult("textDocument/documentSymbol", respRaw) if err != nil { logger("invalid response: %s", err) return fmt.Errorf("quering source code symbols: invalid response: %w", err) @@ -1108,7 +1129,7 @@ func (handler *INOLanguageServer) didChange(logger streams.PrefixLogger, req *ls return cppReq, err } -func (handler *INOLanguageServer) handleError(ctx context.Context, err error) error { +func (handler *INOLanguageServer) handleError(logger streams.PrefixLogger, err error) error { errorStr := err.Error() var message string if strings.Contains(errorStr, "#error") { @@ -1139,7 +1160,7 @@ func (handler *INOLanguageServer) handleError(ctx context.Context, err error) er } else { message = "Could not start editor support.\n" + errorStr } - go handler.showMessage(ctx, lsp.MessageTypeError, message) + go handler.showMessage(logger, lsp.MessageTypeError, message) return errors.New(message) } @@ -1266,24 +1287,31 @@ func (handler *INOLanguageServer) cpp2inoDocumentURI(logger streams.PrefixLogger } func (handler *INOLanguageServer) ino2cppTextDocumentPositionParams(logger streams.PrefixLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { - res := lsp.TextDocumentPositionParams{} - cppDoc, err := handler.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) + inoTextDocument := inoParams.TextDocument + inoPosition := inoParams.Position + inoURI := inoTextDocument.URI + prefix := fmt.Sprintf("TextDocumentIdentifier %s", inoParams) + + cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoTextDocument) if err != nil { - return res, err + logger("%s -> invalid text document: %s", prefix, err) + return lsp.TextDocumentPositionParams{}, err } - cppPosition := inoParams.Position - inoURI := inoParams.TextDocument.URI + cppPosition := inoPosition if inoURI.Ext() == ".ino" { - if cppLine, ok := handler.sketchMapper.InoToCppLineOk(inoURI, inoParams.Position.Line); ok { + if cppLine, ok := handler.sketchMapper.InoToCppLineOk(inoURI, inoPosition.Line); ok { cppPosition.Line = cppLine } else { - logger(" invalid line requested: %s:%d", inoURI, inoParams.Position.Line) - return res, unknownURI(inoURI) + logger("%s -> invalid line requested: %s:%d", prefix, inoURI, inoPosition.Line) + return lsp.TextDocumentPositionParams{}, unknownURI(inoURI) } } - res.TextDocument = cppDoc - res.Position = cppPosition - return res, nil + cppParams := lsp.TextDocumentPositionParams{ + TextDocument: cppTextDocument, + Position: cppPosition, + } + logger("%s -> %s", prefix, cppParams) + return cppParams, nil } func (handler *INOLanguageServer) ino2cppRange(logger streams.PrefixLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { @@ -1783,17 +1811,14 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger streams.PrefixLogger func (handler *INOLanguageServer) HandleNotificationFromClangd(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { defer streams.CatchAndLogPanic() - // n := atomic.AddInt64(&handler.clangdMessageCount, 1) - // prefix := fmt.Sprintf("CLG <-- %s notif%d ", method, n) - - params, err := lsp.DecodeNotificationParams(method, paramsRaw) + params, err := lsp.DecodeServerNotificationParams(method, paramsRaw) if err != nil { logger("error parsing clang message:", err) return } if params == nil { // passthrough - logger("passing through message") + logger("IDE <-- LS NOTIF %s: passing through message", method) if err := handler.IDEConn.SendNotification(method, paramsRaw); err != nil { logger("Error sending notification to IDE: " + err.Error()) } @@ -1849,7 +1874,9 @@ func (handler *INOLanguageServer) HandleNotificationFromClangd(ctx context.Conte for _, diag := range inoDiag.Diagnostics { logger("> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) } - if err := handler.IDEConn.SendNotification("textDocument/publishDiagnostics", lsp.EncodeMessage(inoDiag)); err != nil { + method := "textDocument/publishDiagnostics" + logger("IDE <-- LS NOTIF %s:", method) + if err := handler.IDEConn.SendNotification(method, lsp.EncodeMessage(inoDiag)); err != nil { logger(" Error sending diagnostics to IDE: %s", err) return } @@ -1861,7 +1888,7 @@ func (handler *INOLanguageServer) HandleNotificationFromClangd(ctx context.Conte return } - logger("to IDE") + logger("IDE <-- LS NOTIF %s:", method) if err := handler.IDEConn.SendNotification(method, lsp.EncodeMessage(params)); err != nil { logger("Error sending notification to IDE: " + err.Error()) } @@ -1877,7 +1904,7 @@ func (handler *INOLanguageServer) HandleRequestFromClangd(ctx context.Context, l // n := atomic.AddInt64(&handler.clangdMessageCount, 1) // prefix := fmt.Sprintf("CLG <-- %s %v ", method, n) - params, err := lsp.DecodeRequestParams(method, paramsRaw) + params, err := lsp.DecodeServerRequestParams(method, paramsRaw) if err != nil { logger("Error parsing clang message: %v", err) respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) @@ -1926,14 +1953,14 @@ func (handler *INOLanguageServer) HandleRequestFromClangd(ctx context.Context, l respRaw = paramsRaw } - logger("to IDE") + logger("IDE <-- LS REQ %s", method) resp, respErr, err := handler.IDEConn.SendRequest(ctx, method, respRaw) if err != nil { logger("Error sending request to IDE:", err) respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) return } - + logger("IDE --> LS REQ %s", method) respCallback(resp, respErr) } @@ -2122,14 +2149,17 @@ WhitespaceSensitiveMacros: [] return cleanup, err } -func (handler *INOLanguageServer) showMessage(ctx context.Context, msgType lsp.MessageType, message string) { +func (handler *INOLanguageServer) showMessage(logger streams.PrefixLogger, msgType lsp.MessageType, message string) { defer streams.CatchAndLogPanic() params := lsp.ShowMessageParams{ Type: msgType, Message: message, } - if err := handler.IDEConn.SendNotification("window/showMessage", lsp.EncodeMessage(params)); err != nil { + + method := "window/showMessage" + logger("IDE <-- LS NOTIF %s:", method) + if err := handler.IDEConn.SendNotification(method, lsp.EncodeMessage(params)); err != nil { // TODO: Log? } } diff --git a/streams/jsonrpc2.go b/streams/jsonrpc2.go index 601e7c9..9aeda4a 100644 --- a/streams/jsonrpc2.go +++ b/streams/jsonrpc2.go @@ -21,24 +21,18 @@ func NewPrefixLogger(col *color.Color, prefix string) PrefixLogger { } type JsonRPCLogger struct { - client string - server string - clColor *color.Color - srvColor *color.Color + client string + server string } -func NewJsonRPCLogger(client, server string, weAreClient bool) *JsonRPCLogger { +var clColor = color.New(color.FgHiRed) +var srvColor = color.New(color.FgHiGreen) + +func NewJsonRPCLogger(client, server string) *JsonRPCLogger { color.NoColor = false - clColor := color.New(color.FgHiRed) - srvColor := color.New(color.FgHiGreen) - if !weAreClient { - clColor, srvColor = srvColor, clColor - } return &JsonRPCLogger{ - client: client + " --> " + server + " ", - server: client + " <-- " + server + " ", - clColor: clColor, - srvColor: srvColor, + client: client + " --> " + server + " ", + server: client + " <-- " + server + " ", } } @@ -50,8 +44,8 @@ func (l *JsonRPCLogger) LogClientRequest(method string, params json.RawMessage) id := atomic.AddInt64(&index, 1) prefix := fmt.Sprintf("REQ %s %v: ", method, id) dec := "" - log.Print(l.clColor.Sprintf(l.client+prefix+"%s", dec)) - return NewPrefixLogger(l.clColor, empty(l.client)+prefix), id + log.Print(clColor.Sprintf(l.client+prefix+"%s", dec)) + return NewPrefixLogger(clColor, empty(l.client)+prefix), id } func (l *JsonRPCLogger) LogClientResponse(id int64, method string, params json.RawMessage, err *jsonrpc.ResponseError) { @@ -59,14 +53,14 @@ func (l *JsonRPCLogger) LogClientResponse(id int64, method string, params json.R if err != nil { dec += fmt.Sprintf("ERROR %v", err.AsError()) } - log.Print(l.clColor.Sprintf(l.client+"RESP %s %v: %s", method, id, dec)) + log.Print(clColor.Sprintf(l.client+"RESP %s %v: %s", method, id, dec)) } func (l *JsonRPCLogger) LogClientNotification(method string, params json.RawMessage) PrefixLogger { prefix := fmt.Sprintf("NOTIF %s: ", method) dec := "" - log.Print(l.clColor.Sprintf(l.client+prefix+"%s", dec)) - return NewPrefixLogger(l.clColor, empty(l.client)+prefix) + log.Print(clColor.Sprintf(l.client+prefix+"%s", dec)) + return NewPrefixLogger(clColor, empty(l.client)+prefix) } func (l *JsonRPCLogger) LogServerRequest(method string, params json.RawMessage) (PrefixLogger, int64) { @@ -74,8 +68,8 @@ func (l *JsonRPCLogger) LogServerRequest(method string, params json.RawMessage) prefix := fmt.Sprintf("REQ %s %v: ", method, id) dec := "" - log.Print(l.srvColor.Sprintf(l.server+prefix+"%s", dec)) - return NewPrefixLogger(l.srvColor, empty(l.server)+prefix), id + log.Print(srvColor.Sprintf(l.server+prefix+"%s", dec)) + return NewPrefixLogger(srvColor, empty(l.server)+prefix), id } func (l *JsonRPCLogger) LogServerResponse(id int64, method string, params json.RawMessage, err *jsonrpc.ResponseError) { @@ -83,34 +77,12 @@ func (l *JsonRPCLogger) LogServerResponse(id int64, method string, params json.R if err != nil { dec += fmt.Sprintf("ERROR %v", err.AsError()) } - log.Print(l.srvColor.Sprintf(l.server+"RESP %s %v: %s", method, id, dec)) + log.Print(srvColor.Sprintf(l.server+"RESP %s %v: %s", method, id, dec)) } func (l *JsonRPCLogger) LogServerNotification(method string, params json.RawMessage) PrefixLogger { prefix := fmt.Sprintf("NOTIF %s: ", method) dec := "" - log.Print(l.srvColor.Sprintf(l.server+prefix+"%s", dec)) - return NewPrefixLogger(l.srvColor, empty(l.server)+prefix) -} - -func decodeLspRequest(method string, req json.RawMessage) string { - switch method { - case "$/progress": - // var begin lsp.WorkDoneProgressBegin - // if json.Unmarshal(*v.Value, &begin) == nil { - // return fmt.Sprintf("TOKEN=%s BEGIN %v %v", v.Token, begin.Title, begin.Message) - // } - // var report lsp.WorkDoneProgressReport - // if json.Unmarshal(*v.Value, &report) == nil { - // return fmt.Sprintf("TOKEN=%s REPORT %v %v%%", v.Token, 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?" - return "SOME PROGRESS..." - default: - return "" - } + log.Print(srvColor.Sprintf(l.server+prefix+"%s", dec)) + return NewPrefixLogger(srvColor, empty(l.server)+prefix) } From dec44e92d124198d3ba8ed16397748992774c77d Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 4 Nov 2021 12:02:22 +0100 Subject: [PATCH 05/76] Introduced lsp.Client/Server and big improvement in logging --- handler/builder.go | 10 +- handler/clangd_client.go | 141 +++ handler/handler.go | 2263 +++++++++++++++++++++----------------- handler/lsp_logger.go | 67 ++ handler/progress.go | 29 +- handler/syncer.go | 23 - streams/jsonrpc2.go | 88 -- 7 files changed, 1456 insertions(+), 1165 deletions(-) create mode 100644 handler/clangd_client.go create mode 100644 handler/lsp_logger.go delete mode 100644 handler/syncer.go delete mode 100644 streams/jsonrpc2.go diff --git a/handler/builder.go b/handler/builder.go index 3cabd1d..972b627 100644 --- a/handler/builder.go +++ b/handler/builder.go @@ -13,6 +13,7 @@ import ( "github.com/pkg/errors" "go.bug.st/json" "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" ) func (handler *INOLanguageServer) scheduleRebuildEnvironment() { @@ -23,8 +24,7 @@ func (handler *INOLanguageServer) scheduleRebuildEnvironment() { } func (handler *INOLanguageServer) rebuildEnvironmentLoop() { - defer streams.CatchAndLogPanic() - logger := streams.NewPrefixLogger(color.New(color.FgHiMagenta), "RBLD---") + logger := NewLSPFunctionLogger(color.HiMagentaString, "RBLD---") grabDeadline := func() *time.Time { handler.rebuildSketchDeadlineMutex.Lock() @@ -85,7 +85,7 @@ func (handler *INOLanguageServer) rebuildEnvironmentLoop() { } } -func (handler *INOLanguageServer) generateBuildEnvironment(logger streams.PrefixLogger, buildPath *paths.Path) error { +func (handler *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.FunctionLogger, buildPath *paths.Path) error { sketchDir := handler.sketchRoot fqbn := handler.config.SelectedBoard.Fqbn @@ -130,7 +130,7 @@ func (handler *INOLanguageServer) generateBuildEnvironment(logger streams.Prefix cmdOutput := &bytes.Buffer{} cmd.RedirectStdoutTo(cmdOutput) cmd.SetDirFromPath(sketchDir) - logger("running: %s", strings.Join(args, " ")) + logger.Logf("running: %s", strings.Join(args, " ")) if err := cmd.Run(); err != nil { return errors.Errorf("running %s: %s", strings.Join(args, " "), err) } @@ -150,7 +150,7 @@ func (handler *INOLanguageServer) generateBuildEnvironment(logger streams.Prefix if err := json.Unmarshal(cmdOutput.Bytes(), &res); err != nil { return errors.Errorf("parsing arduino-cli output: %s", err) } - logger("arduino-cli output: %s", cmdOutput) + logger.Logf("arduino-cli output: %s", cmdOutput) return nil } diff --git a/handler/clangd_client.go b/handler/clangd_client.go new file mode 100644 index 0000000..6002723 --- /dev/null +++ b/handler/clangd_client.go @@ -0,0 +1,141 @@ +package handler + +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" + "go.bug.st/json" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" +) + +type ClangdClient struct { + conn *lsp.Client + handler *INOLanguageServer +} + +func NewClangdClient(logger jsonrpc.FunctionLogger, + buildPath, buildSketchCpp, dataFolder *paths.Path, + connectionClosedCB func(), + inoLanguageServer *INOLanguageServer, +) *ClangdClient { + clangdStdout, clangdStdin, clangdStderr := startClangd(logger, buildPath, 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) + } + + client := &ClangdClient{ + handler: inoLanguageServer, + } + client.conn = lsp.NewClient(clangdStdio, clangdStdio, client) + client.conn.SetLogger(&LSPLogger{IncomingPrefix: "IDE LS <-- Clangd", OutgoingPrefix: "IDE LS --> Clangd"}) + go func() { + defer streams.CatchAndLogPanic() + client.conn.Run() + connectionClosedCB() + }() + + return client +} + +func startClangd(logger jsonrpc.FunctionLogger, compileCommandsDir, sketchCpp, 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", "**"))) + } + logger.Logf(" Starting clangd: %s", 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 (client *ClangdClient) Close() { + panic("unimplemented") +} + +// The following are events incoming from Clangd + +func (client *ClangdClient) WindowShowMessageRequest(context.Context, jsonrpc.FunctionLogger, *lsp.ShowMessageRequestParams) (*lsp.MessageActionItem, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdClient) WindowShowDocument(context.Context, jsonrpc.FunctionLogger, *lsp.ShowDocumentParams) (*lsp.ShowDocumentResult, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdClient) WindowWorkDoneProgressCreate(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCreateParams) *jsonrpc.ResponseError { + return client.handler.WindowWorkDoneProgressCreateFromClangd(ctx, logger, params) +} + +func (client *ClangdClient) ClientRegisterCapability(context.Context, jsonrpc.FunctionLogger, *lsp.RegistrationParams) *jsonrpc.ResponseError { + panic("unimplemented") +} + +func (client *ClangdClient) ClientUnregisterCapability(context.Context, jsonrpc.FunctionLogger, *lsp.UnregistrationParams) *jsonrpc.ResponseError { + panic("unimplemented") +} + +func (client *ClangdClient) WorkspaceWorkspaceFolders(context.Context, jsonrpc.FunctionLogger) ([]lsp.WorkspaceFolder, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdClient) WorkspaceConfiguration(context.Context, jsonrpc.FunctionLogger, *lsp.ConfigurationParams) ([]json.RawMessage, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdClient) WorkspaceApplyEdit(context.Context, jsonrpc.FunctionLogger, *lsp.ApplyWorkspaceEditParams) (*lsp.ApplyWorkspaceEditResult, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (client *ClangdClient) WorkspaceCodeLensRefresh(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { + panic("unimplemented") +} + +func (client *ClangdClient) Progress(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { + client.handler.ProgressFromClangd(logger, progress) +} + +func (client *ClangdClient) LogTrace(jsonrpc.FunctionLogger, *lsp.LogTraceParams) { + panic("unimplemented") +} + +func (client *ClangdClient) WindowShowMessage(jsonrpc.FunctionLogger, *lsp.ShowMessageParams) { + panic("unimplemented") +} + +func (client *ClangdClient) WindowLogMessage(jsonrpc.FunctionLogger, *lsp.LogMessageParams) { + panic("unimplemented") +} + +func (client *ClangdClient) TelemetryEvent(jsonrpc.FunctionLogger, json.RawMessage) { + panic("unimplemented") +} + +func (client *ClangdClient) TextDocumentPublishDiagnostics(logger jsonrpc.FunctionLogger, params *lsp.PublishDiagnosticsParams) { + client.handler.PublishDiagnosticsFromClangd(logger, params) +} diff --git a/handler/handler.go b/handler/handler.go index ee9e028..3fda7ba 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "log" - "os" "regexp" "runtime" "strconv" @@ -44,13 +43,10 @@ func Setup(cliPath, cliConfigPath, clangdPath, formatFilePath string, _enableLog enableLogging = _enableLogging } -// CLangdStarter starts clangd and returns its stdin/out/err -type CLangdStarter func() (stdin io.WriteCloser, stdout io.ReadCloser, stderr io.ReadCloser) - // INOLanguageServer is a JSON-RPC handler that delegates messages to clangd. type INOLanguageServer struct { - IDEConn *jsonrpc.Connection - ClangdConn *jsonrpc.Connection + IDEConn *lsp.Server + Clangd *ClangdClient progressHandler *ProgressProxyHandler @@ -65,15 +61,15 @@ type INOLanguageServer struct { buildSketchSymbols []lsp.DocumentSymbol buildSketchIncludesCanary string buildSketchSymbolsCanary string - buildSketchSymbolsLoad bool - buildSketchSymbolsCheck bool + buildSketchSymbolsLoad chan bool + buildSketchSymbolsCheck chan bool rebuildSketchDeadline *time.Time rebuildSketchDeadlineMutex sync.Mutex sketchRoot *paths.Path sketchName string sketchMapper *sourcemapper.InoMapper sketchTrackedFilesCount int - docs map[string]*lsp.TextDocumentItem + docs map[string]lsp.TextDocumentItem inoDocsWithDiagnostics map[lsp.DocumentURI]bool config BoardConfig @@ -93,56 +89,56 @@ type Board struct { var yellow = color.New(color.FgHiYellow) -func (handler *INOLanguageServer) writeLock(logger streams.PrefixLogger, requireClangd bool) { +func (handler *INOLanguageServer) writeLock(logger jsonrpc.FunctionLogger, requireClangd bool) { handler.dataMux.Lock() - logger(yellow.Sprintf("write-locked")) + logger.Logf(yellow.Sprintf("write-locked")) if requireClangd { handler.waitClangdStart(logger) } } -func (handler *INOLanguageServer) writeUnlock(logger streams.PrefixLogger) { - logger(yellow.Sprintf("write-unlocked")) +func (handler *INOLanguageServer) writeUnlock(logger jsonrpc.FunctionLogger) { + logger.Logf(yellow.Sprintf("write-unlocked")) handler.dataMux.Unlock() } -func (handler *INOLanguageServer) readLock(logger streams.PrefixLogger, requireClangd bool) { +func (handler *INOLanguageServer) readLock(logger jsonrpc.FunctionLogger, requireClangd bool) { handler.dataMux.RLock() - logger(yellow.Sprintf("read-locked")) + logger.Logf(yellow.Sprintf("read-locked")) - for requireClangd && handler.ClangdConn == nil { + for requireClangd && handler.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(yellow.Sprintf("clang not started: read-unlocking...")) + logger.Logf(yellow.Sprintf("clang not started: read-unlocking...")) handler.dataMux.RUnlock() handler.writeLock(logger, true) handler.writeUnlock(logger) handler.dataMux.RLock() - logger(yellow.Sprintf("testing again if clang started: read-locked...")) + logger.Logf(yellow.Sprintf("testing again if clang started: read-locked...")) } } -func (handler *INOLanguageServer) readUnlock(logger streams.PrefixLogger) { - logger(yellow.Sprintf("read-unlocked")) +func (handler *INOLanguageServer) readUnlock(logger jsonrpc.FunctionLogger) { + logger.Logf(yellow.Sprintf("read-unlocked")) handler.dataMux.RUnlock() } -func (handler *INOLanguageServer) waitClangdStart(logger streams.PrefixLogger) error { - if handler.ClangdConn != nil { +func (handler *INOLanguageServer) waitClangdStart(logger jsonrpc.FunctionLogger) error { + if handler.Clangd != nil { return nil } - logger("(throttled: waiting for clangd)") - logger(yellow.Sprintf("unlocked (waiting clangd)")) + logger.Logf("(throttled: waiting for clangd)") + logger.Logf(yellow.Sprintf("unlocked (waiting clangd)")) handler.clangdStarted.Wait() - logger(yellow.Sprintf("locked (waiting clangd)")) + logger.Logf(yellow.Sprintf("locked (waiting clangd)")) - if handler.ClangdConn == nil { - logger("clangd startup failed: aborting call") + if handler.Clangd == nil { + logger.Logf("clangd startup failed: aborting call") return errors.New("could not start clangd, aborted") } return nil @@ -150,11 +146,13 @@ func (handler *INOLanguageServer) waitClangdStart(logger streams.PrefixLogger) e // NewINOLanguageServer creates and configures an Arduino Language Server. func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLanguageServer { - logger := streams.NewPrefixLogger(color.New(color.FgWhite), "LS: ") + logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") handler := &INOLanguageServer{ - docs: map[string]*lsp.TextDocumentItem{}, - inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, - closing: make(chan bool), + docs: map[string]lsp.TextDocumentItem{}, + inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, + closing: make(chan bool), + buildSketchSymbolsLoad: make(chan bool, 1), + buildSketchSymbolsCheck: make(chan bool, 1), config: BoardConfig{ SelectedBoard: board, }, @@ -168,507 +166,1072 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLa handler.buildSketchRoot = handler.buildPath.Join("sketch") } if enableLogging { - logger("Initial board configuration: %s", board) - logger("Language server build path: %s", handler.buildPath) - logger("Language server build sketch root: %s", handler.buildSketchRoot) - } - - jsonrpcLogger := streams.NewJsonRPCLogger("IDE", "LS") - handler.IDEConn = jsonrpc.NewConnection(stdin, stdout, - func(ctx context.Context, method string, params json.RawMessage, respCallback func(result json.RawMessage, err *jsonrpc.ResponseError)) { - reqLogger, idx := jsonrpcLogger.LogClientRequest(method, params) - handler.HandleMessageFromIDE(ctx, reqLogger, method, params, func(result json.RawMessage, err *jsonrpc.ResponseError) { - jsonrpcLogger.LogServerResponse(idx, method, result, err) - respCallback(result, err) - }) - }, - func(ctx context.Context, method string, params json.RawMessage) { - notifLogger := jsonrpcLogger.LogClientNotification(method, params) - handler.HandleNotificationFromIDE(ctx, notifLogger, method, params) - }, - func(e error) {}, - ) + logger.Logf("Initial board configuration: %s", board) + logger.Logf("Language server build path: %s", handler.buildPath) + logger.Logf("Language server build sketch root: %s", handler.buildSketchRoot) + } + + handler.IDEConn = lsp.NewServer(stdin, stdout, handler) + handler.IDEConn.SetLogger(&LSPLogger{IncomingPrefix: "IDE --> LS", OutgoingPrefix: "IDE <-- LS"}) + handler.progressHandler = NewProgressProxy(handler.IDEConn) + go func() { + defer streams.CatchAndLogPanic() handler.IDEConn.Run() - logger("Lost connection with IDE!") + logger.Logf("Lost connection with IDE!") handler.Close() }() - handler.progressHandler = NewProgressProxy(handler.IDEConn) + go func() { + defer streams.CatchAndLogPanic() + for { + select { + case <-handler.buildSketchSymbolsLoad: + // ...also un-queue buildSketchSymbolsCheck + select { + case <-handler.buildSketchSymbolsCheck: + default: + } + handler.LoadCppDocumentSymbols() + + case <-handler.buildSketchSymbolsCheck: + handler.CheckCppDocumentSymbols() + + case <-handler.closing: + return + } + } + }() - go handler.rebuildEnvironmentLoop() + go func() { + defer streams.CatchAndLogPanic() + handler.rebuildEnvironmentLoop() + }() return handler } -// Close closes all the json-rpc connections. -func (handler *INOLanguageServer) Close() { - if handler.ClangdConn != nil { - handler.ClangdConn.Close() - handler.ClangdConn = nil +func (handler *INOLanguageServer) queueLoadCppDocumentSymbols() { + select { + case handler.buildSketchSymbolsLoad <- true: + default: } - if handler.closing != nil { - close(handler.closing) - handler.closing = nil +} + +func (handler *INOLanguageServer) queueCheckCppDocumentSymbols() { + select { + case handler.buildSketchSymbolsCheck <- true: + default: } } -// CloseNotify returns a channel that is closed when the InoHandler is closed -func (handler *INOLanguageServer) CloseNotify() <-chan bool { - return handler.closing +func (handler *INOLanguageServer) LoadCppDocumentSymbols() error { + logger := NewLSPFunctionLogger(color.HiBlueString, "SYLD --- ") + defer logger.Logf("(done)") + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + return handler.refreshCppDocumentSymbols(logger) } -// CleanUp performs cleanup of the workspace and temp files create by the language server -func (handler *INOLanguageServer) CleanUp() { - if handler.buildPath != nil { - handler.buildPath.RemoveAll() - handler.buildPath = nil +func (handler *INOLanguageServer) CheckCppDocumentSymbols() error { + logger := NewLSPFunctionLogger(color.HiBlueString, "SYCK --- ") + defer logger.Logf("(done)") + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + oldSymbols := handler.buildSketchSymbols + canary := handler.buildSketchSymbolsCanary + if err := handler.refreshCppDocumentSymbols(logger); err != nil { + return err } + if len(oldSymbols) != len(handler.buildSketchSymbols) || canary != handler.buildSketchSymbolsCanary { + logger.Logf("function symbols change detected, triggering sketch rebuild!") + handler.scheduleRebuildEnvironment() + } + return nil } -func (handler *INOLanguageServer) HandleNotificationFromIDE(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { - defer streams.CatchAndLogPanic() +func (handler *INOLanguageServer) startClangd(inoParams *lsp.InitializeParams) { + logger := NewLSPFunctionLogger(color.HiCyanString, "INIT --- ") + logger.Logf("initializing workbench") + + // Start clangd asynchronously + handler.writeLock(logger, false) // do not wait for clangd... we are starting it :-) + defer handler.writeUnlock(logger) + + // TODO: Inline this function + handler.initializeWorkbench(logger, inoParams) - params, err := lsp.DecodeClientNotificationParams(method, paramsRaw) + // signal that clangd is running now... + handler.clangdStarted.Broadcast() + + logger.Logf("Done initializing workbench") +} + +func (handler *INOLanguageServer) Initialize(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { + go func() { + defer streams.CatchAndLogPanic() + handler.startClangd(inoParams) + }() + + resp := &lsp.InitializeResult{ + Capabilities: lsp.ServerCapabilities{ + TextDocumentSync: &lsp.TextDocumentSyncOptions{ + OpenClose: true, + Change: lsp.TextDocumentSyncKindIncremental, + Save: &lsp.SaveOptions{ + IncludeText: true, + }, + }, + HoverProvider: &lsp.HoverOptions{}, // true, + CompletionProvider: &lsp.CompletionOptions{ + TriggerCharacters: []string{".", "\u003e", ":"}, + }, + SignatureHelpProvider: &lsp.SignatureHelpOptions{ + TriggerCharacters: []string{"(", ","}, + }, + DefinitionProvider: &lsp.DefinitionOptions{}, // true, + // ReferencesProvider: &lsp.ReferenceOptions{}, // TODO: true + DocumentHighlightProvider: &lsp.DocumentHighlightOptions{}, //true, + DocumentSymbolProvider: &lsp.DocumentSymbolOptions{}, //true, + WorkspaceSymbolProvider: &lsp.WorkspaceSymbolOptions{}, //true, + CodeActionProvider: &lsp.CodeActionOptions{ResolveProvider: true}, + DocumentFormattingProvider: &lsp.DocumentFormattingOptions{}, //true, + DocumentRangeFormattingProvider: &lsp.DocumentRangeFormattingOptions{}, //true, + DocumentOnTypeFormattingProvider: &lsp.DocumentOnTypeFormattingOptions{ + FirstTriggerCharacter: "\n", + }, + RenameProvider: &lsp.RenameOptions{PrepareProvider: false}, // TODO: true + ExecuteCommandProvider: &lsp.ExecuteCommandOptions{ + Commands: []string{"clangd.applyFix", "clangd.applyTweak"}, + }, + }, + } + logger.Logf("initialization parameters: %s", string(lsp.EncodeMessage(resp))) + return resp, nil +} + +func (handler *INOLanguageServer) Shutdown(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceSymbol(context.Context, jsonrpc.FunctionLogger, *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceExecuteCommand(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ExecuteCommandParams) (json.RawMessage, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + // return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"} + // err = handler.ino2cppExecuteCommand(inoParams) + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceWillCreateFiles(context.Context, jsonrpc.FunctionLogger, *lsp.CreateFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceWillRenameFiles(context.Context, jsonrpc.FunctionLogger, *lsp.RenameFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceWillDeleteFiles(context.Context, jsonrpc.FunctionLogger, *lsp.DeleteFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentWillSaveWaitUntil(context.Context, jsonrpc.FunctionLogger, *lsp.WillSaveTextDocumentParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentCompletion(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CompletionParams) (*lsp.CompletionList, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + logger.Logf("--> completion(%s)\n", inoParams.TextDocument) + cppTextDocPositionParams, err := handler.ino2cppTextDocumentPositionParams(logger, inoParams.TextDocumentPositionParams) if err != nil { - // TODO: log? - return + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - if params == nil { - // TODO: log? - return + + cppParams := inoParams + cppParams.TextDocumentPositionParams = cppTextDocPositionParams + logger.Logf(" --> completion(%s)\n", inoParams.TextDocument) + inoURI := inoParams.TextDocument.URI + + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - // Set up RWLocks and wait for clangd startup - switch method { - case "textDocument/didOpen", - "textDocument/didChange", - "textDocument/didClose": - // Write lock - clangd required - handler.writeLock(logger, true) - defer handler.writeUnlock(logger) - case "initialized": - // Read lock - NO clangd required - handler.readLock(logger, false) - defer handler.readUnlock(logger) - default: - // Read lock - clangd required - handler.readLock(logger, true) - defer handler.readUnlock(logger) + clangResp, clangErr, err := handler.Clangd.conn.TextDocumentCompletion(ctx, cppParams) + if err != nil { + logger.Logf("clangd connection error: %v", err) + handler.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()} } - // Handle LSP methods: transform parameters and send to clangd - var cppURI lsp.DocumentURI + cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp) - switch p := params.(type) { - case *lsp.InitializedParams: - // method "initialized" - logger("notification is not propagated to clangd") - return // Do not propagate to clangd + inoResp := *clangResp + inoItems := make([]lsp.CompletionItem, 0) + for _, item := range clangResp.Items { + if !strings.HasPrefix(item.InsertText, "_") { + if cppToIno && item.TextEdit != nil { + _, item.TextEdit.Range = handler.sketchMapper.CppToInoRange(item.TextEdit.Range) + } + inoItems = append(inoItems, item) + } + } + inoResp.Items = inoItems + logger.Logf("<-- completion(%d items) cppToIno=%v", len(inoResp.Items), cppToIno) + return &inoResp, nil +} - case *lsp.DidOpenTextDocumentParams: - // method "textDocument/didOpen" - logger("(%s@%d as '%s')", p.TextDocument.URI, p.TextDocument.Version, p.TextDocument.LanguageID) +func (handler *INOLanguageServer) CompletionItemResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CompletionItem) (*lsp.CompletionItem, *jsonrpc.ResponseError) { + panic("unimplemented") +} - if res, e := handler.didOpen(logger, p); e != nil { - params = nil - err = e - } else if res == nil { - logger("notification is not propagated to clangd") - return - } else { - logger("to clang: didOpen(%s@%d as '%s')", res.TextDocument.URI, res.TextDocument.Version, res.TextDocument.LanguageID) - params = res - } +func (handler *INOLanguageServer) TextDocumentHover(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.HoverParams) (*lsp.Hover, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) - case *lsp.DidCloseTextDocumentParams: - // Method: "textDocument/didClose" - logger("--> didClose(%s)", p.TextDocument.URI) + inoURI := inoParams.TextDocument.URI + inoTextDocPosition := inoParams.TextDocumentPositionParams + logger.Logf("--> hover(%s)\n", inoTextDocPosition) - if res, e := handler.didClose(logger, p); e != nil { - } else if res == nil { - logger(" --X notification is not propagated to clangd") - return - } else { - logger(" --> didClose(%s)", res.TextDocument.URI) - params = res - } + cppTextDocPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocPosition) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } - case *lsp.DidChangeTextDocumentParams: - // notification "textDocument/didChange" - logger("--> didChange(%s@%d)", p.TextDocument.URI, p.TextDocument.Version) - for _, change := range p.ContentChanges { - logger(" > %s -> %s", change.Range, strconv.Quote(change.Text)) - } + logger.Logf(" --> hover(%s)\n", cppTextDocPosition) + cppParams := &lsp.HoverParams{ + TextDocumentPositionParams: cppTextDocPosition, + } + clangResp, clangErr, err := handler.Clangd.conn.TextDocumentHover(ctx, cppParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) + handler.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 res, err := handler.didChange(logger, p); err != nil { - logger(" --E error: %s", err) - return - } else if res == nil { - logger(" --X notification is not propagated to clangd") - return - } else { - p = res + if clangResp == nil { + return nil, nil + } + + inoResp := *clangResp + // TODO: ???? + // if len(clangResp.Contents.Value) == 0 { + // return nil + // } + cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp) + if cppToIno { + _, inoRange := handler.sketchMapper.CppToInoRange(*clangResp.Range) + inoResp.Range = &inoRange + } + logger.Logf("<-- hover(%s)", strconv.Quote(inoResp.Contents.Value)) + return &inoResp, nil +} + +func (handler *INOLanguageServer) TextDocumentSignatureHelp(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.SignatureHelpParams) (*lsp.SignatureHelp, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + inoTextDocumentPosition := inoParams.TextDocumentPositionParams + + logger.Logf("%s", inoTextDocumentPosition) + cppTextDocumentPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + if err == nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + logger.Logf("-> %s", cppTextDocumentPosition) + cppParams := *inoParams + cppParams.TextDocumentPositionParams = cppTextDocumentPosition + cppSignatureHelp, cppErr, err := handler.Clangd.conn.TextDocumentSignatureHelp(ctx, inoParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) + handler.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if cppErr != nil { + logger.Logf("clangd response error: %v", cppErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().Error()} + } + + // No need to convert back to inoSignatureHelp + + return cppSignatureHelp, nil +} + +func (handler *INOLanguageServer) TextDocumentDeclaration(context.Context, jsonrpc.FunctionLogger, *lsp.DeclarationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, p *lsp.DefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + inoTextDocPosition := p.TextDocumentPositionParams + + logger.Logf("%s", inoTextDocPosition) + cppTextDocPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocPosition) + if err != nil { + logger.Logf("Error: %s", err) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + logger.Logf("-> %s", cppTextDocPosition) + cppParams := *p + cppParams.TextDocumentPositionParams = cppTextDocPosition + cppLocations, cppLocationLinks, cppErr, err := handler.Clangd.conn.TextDocumentDefinition(ctx, &cppParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) + handler.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if cppErr != nil { + logger.Logf("clangd response error: %v", cppErr.AsError()) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().Error()} + } + + var inoLocations []lsp.Location + if cppLocations != nil { + inoLocations, err = handler.cpp2inoLocationArray(logger, cppLocations) + if err != nil { + handler.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } + } + + var inoLocationLinks []lsp.LocationLink + if cppLocationLinks != nil { + panic("unimplemented") + } - logger(" --> didChange(%s@%d)", p.TextDocument.URI, p.TextDocument.Version) - for _, change := range p.ContentChanges { - logger(" > %s -> %s", change.Range, strconv.Quote(change.Text)) + return inoLocations, inoLocationLinks, nil +} + +func (handler *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, cppLocations []lsp.Location) ([]lsp.Location, error) { + inoLocations := []lsp.Location{} + for _, cppLocation := range cppLocations { + inoLocation, err := handler.cpp2inoLocation(logger, cppLocation) + if err != nil { + logger.Logf("ERROR converting location %s: %s", cppLocation, err) + return nil, err } - logger("LS --> CL NOTIF %s:", method) - if err := handler.ClangdConn.SendNotification(method, lsp.EncodeMessage(p)); err != nil { - // Exit the process and trigger a restart by the client in case of a severe error - logger("Connection error with clangd server: %v", err) - logger("Please restart the language server.") + inoLocations = append(inoLocations, inoLocation) + } + return inoLocations, nil +} + +func (handler *INOLanguageServer) TextDocumentTypeDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + inoTextDocumentPosition := inoParams.TextDocumentPositionParams + + logger.Logf("%s", inoTextDocumentPosition) + // inoURI := inoTextDocumentPosition.TextDocument.URI + cppTextDocumentPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + if err != nil { + logger.Logf("Error: %s", err) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + + // cppURI := cppTextDocumentPosition.TextDocument.URI + logger.Logf("-> %s", cppTextDocumentPosition) + + cppParams := *inoParams + cppParams.TextDocumentPositionParams = cppTextDocumentPosition + cppLocations, cppLocationLinks, cppErr, err := handler.Clangd.conn.TextDocumentTypeDefinition(ctx, &cppParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) + handler.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if cppErr != nil { + logger.Logf("clangd response error: %v", cppErr.AsError()) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().Error()} + } + + var inoLocations []lsp.Location + if cppLocations != nil { + inoLocations, err = handler.cpp2inoLocationArray(logger, cppLocations) + if err != nil { handler.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - return + } - case *lsp.DidSaveTextDocumentParams: - // Method: "textDocument/didSave" - logger("--> %s(%s)", method, p.TextDocument.URI) - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(logger, p.TextDocument) - cppURI = p.TextDocument.URI - if cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) { - logger(" --| didSave not forwarded to clangd") - return - } - logger(" --> %s(%s)", method, p.TextDocument.URI) + var inoLocationLinks []lsp.LocationLink + if cppLocationLinks != nil { + panic("unimplemented") } + return inoLocations, inoLocationLinks, nil +} + +func (handler *INOLanguageServer) TextDocumentImplementation(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ImplementationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + inoTextDocumentPosition := inoParams.TextDocumentPositionParams + logger.Logf("%s", inoTextDocumentPosition) + + cppTextDocumentPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { - logger("Error: %s", err) - return + logger.Logf("Error: %s", err) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - logger("LS --> CL NOTIF %s:", method) - if err := handler.ClangdConn.SendNotification(method, lsp.EncodeMessage(params)); err != nil { - // Exit the process and trigger a restart by the client in case of a severe error - logger("Connection error with clangd server:") - logger("vs", err) - logger("Please restart the language server.") + logger.Logf("-> %s", cppTextDocumentPosition) + cppParams := *inoParams + cppParams.TextDocumentPositionParams = cppTextDocumentPosition + cppLocations, cppLocationLinks, cppErr, err := handler.Clangd.conn.TextDocumentImplementation(ctx, &cppParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) handler.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - if handler.buildSketchSymbolsLoad { - handler.buildSketchSymbolsLoad = false - handler.buildSketchSymbolsCheck = false - logger("Queued resfreshing document symbols") - go handler.LoadCppDocumentSymbols() + if cppErr != nil { + logger.Logf("clangd response error: %v", cppErr.AsError()) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().Error()} } - if handler.buildSketchSymbolsCheck { - handler.buildSketchSymbolsCheck = false - logger("Queued check document symbols") - go handler.CheckCppDocumentSymbols() + + var inoLocations []lsp.Location + if cppLocations != nil { + inoLocations, err = handler.cpp2inoLocationArray(logger, cppLocations) + if err != nil { + handler.Close() + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + } + + var inoLocationLinks []lsp.LocationLink + if cppLocationLinks != nil { + panic("unimplemented") } + + return inoLocations, inoLocationLinks, nil } -// HandleMessageFromIDE handles a message received from the IDE client (via stdio). -func (handler *INOLanguageServer) HandleMessageFromIDE(ctx context.Context, logger streams.PrefixLogger, - method string, paramsRaw json.RawMessage, - returnCB func(result json.RawMessage, err *jsonrpc.ResponseError), -) { - defer streams.CatchAndLogPanic() +func (handler *INOLanguageServer) TextDocumentReferences(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ReferenceParams) ([]lsp.Location, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + panic("unimplemented") + // inoURI = p.TextDocument.URI + // _, err = handler.ino2cppTextDocumentPositionParams(logger, p.TextDocumentPositionParams) +} - params, err := lsp.DecodeClientRequestParams(method, paramsRaw) +func (handler *INOLanguageServer) TextDocumentDocumentHighlight(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + inoTextDocumentPosition := inoParams.TextDocumentPositionParams + cppTextDocumentPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInvalidParams, Message: err.Error()}) - return + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - if params == nil { - // TODO: log? - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInvalidParams}) - return + cppURI := cppTextDocumentPosition.TextDocument.URI + + cppParams := *inoParams + cppParams.TextDocumentPositionParams = cppTextDocumentPosition + cppHighlights, clangErr, err := handler.Clangd.conn.TextDocumentDocumentHighlight(ctx, &cppParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) + handler.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()} } - // Set up RWLocks and wait for clangd startup - switch method { - case "initialize": - // Write lock - NO clangd required - handler.writeLock(logger, false) - defer handler.writeUnlock(logger) - default: - // Read lock - clangd required - handler.readLock(logger, true) - defer handler.readUnlock(logger) + if cppHighlights != nil { + return nil, nil } - // Handle LSP methods: transform parameters and send to clangd - var inoURI, cppURI lsp.DocumentURI + inoHighlights := []lsp.DocumentHighlight{} + for _, cppHighlight := range cppHighlights { + inoHighlight, err := handler.cpp2inoDocumentHighlight(logger, cppHighlight, cppURI) + if err != nil { + logger.Logf("ERROR converting location %s:%s: %s", cppURI, cppHighlight.Range, err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} + } + inoHighlights = append(inoHighlights, inoHighlight) + } + return inoHighlights, nil +} - switch p := params.(type) { - case *lsp.InitializeParams: - // method "initialize" +func (handler *INOLanguageServer) TextDocumentDocumentSymbol(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, []lsp.SymbolInformation, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) - go func() { - defer streams.CatchAndLogPanic() - logger := streams.NewPrefixLogger(color.New(color.FgCyan), "INIT --- ") - logger("initializing workbench") + inoTextDocument := inoParams.TextDocument + inoURI := inoTextDocument.URI + logger.Logf("--> %s") - // Start clangd asynchronously - handler.writeLock(logger, false) // do not wait for clangd... we are starting it :-) - defer handler.writeUnlock(logger) + cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + if err != nil { + logger.Logf("Error: %s", err) + return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } - handler.initializeWorkbench(logger, p) + cppParams := *inoParams + cppParams.TextDocument = cppTextDocument + logger.Logf(" --> documentSymbol(%s)", cppTextDocument) + cppDocSymbols, cppSymbolInformation, clangErr, err := handler.Clangd.conn.TextDocumentDocumentSymbol(ctx, &cppParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) + handler.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()} + } - // clangd should be running now... - handler.clangdStarted.Broadcast() + var inoDocSymbols []lsp.DocumentSymbol + if cppDocSymbols != nil { + logger.Logf(" <-- documentSymbol(%d document symbols)", len(cppDocSymbols)) + inoDocSymbols = handler.cpp2inoDocumentSymbols(logger, cppDocSymbols, inoURI) + } + var inoSymbolInformation []lsp.SymbolInformation + if cppSymbolInformation != nil { + logger.Logf(" <-- documentSymbol(%d symbol information)", len(cppSymbolInformation)) + inoSymbolInformation = handler.cpp2inoSymbolInformation(cppSymbolInformation) + } + return inoDocSymbols, inoSymbolInformation, nil +} - logger("initializing workbench (done)") - }() +func (handler *INOLanguageServer) TextDocumentCodeAction(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CodeActionParams) ([]lsp.CommandOrCodeAction, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) - returnCB(lsp.EncodeMessage(&lsp.InitializeResult{ - Capabilities: lsp.ServerCapabilities{ - TextDocumentSync: &lsp.TextDocumentSyncOptions{ - OpenClose: true, - Change: lsp.TextDocumentSyncKindIncremental, - Save: &lsp.SaveOptions{ - IncludeText: true, - }, - }, - HoverProvider: &lsp.HoverOptions{}, // true, - CompletionProvider: &lsp.CompletionOptions{ - TriggerCharacters: []string{".", "\u003e", ":"}, - }, - SignatureHelpProvider: &lsp.SignatureHelpOptions{ - TriggerCharacters: []string{"(", ","}, - }, - DefinitionProvider: &lsp.DefinitionOptions{}, // true, - // ReferencesProvider: &lsp.ReferenceOptions{}, // TODO: true - DocumentHighlightProvider: &lsp.DocumentHighlightOptions{}, //true, - DocumentSymbolProvider: &lsp.DocumentSymbolOptions{}, //true, - WorkspaceSymbolProvider: &lsp.WorkspaceSymbolOptions{}, //true, - CodeActionProvider: &lsp.CodeActionOptions{ResolveProvider: true}, - DocumentFormattingProvider: &lsp.DocumentFormattingOptions{}, //true, - DocumentRangeFormattingProvider: &lsp.DocumentRangeFormattingOptions{}, //true, - DocumentOnTypeFormattingProvider: &lsp.DocumentOnTypeFormattingOptions{ - FirstTriggerCharacter: "\n", - }, - RenameProvider: &lsp.RenameOptions{PrepareProvider: false}, // TODO: true - ExecuteCommandProvider: &lsp.ExecuteCommandOptions{ - Commands: []string{"clangd.applyFix", "clangd.applyTweak"}, - }, - }, - }), nil) - return + inoTextDocument := inoParams.TextDocument + inoURI := inoTextDocument.URI + logger.Logf("--> codeAction(%s:%s)", inoTextDocument, inoParams.Range.Start) - case *lsp.CompletionParams: - // method: "textDocument/completion" - logger("--> completion(%s:%d:%d)\n", p.TextDocument.URI, p.Position.Line, p.Position.Character) + cppParams := *inoParams + cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + cppParams.TextDocument = cppTextDocument - if res, e := handler.ino2cppTextDocumentPositionParams(logger, p.TextDocumentPositionParams); e == nil { - p.TextDocumentPositionParams = res - logger(" --> completion(%s:%d:%d)\n", p.TextDocument.URI, p.Position.Line, p.Position.Character) - } else { - err = e + if cppTextDocument.URI.AsPath().EquivalentTo(handler.buildSketchCpp) { + cppParams.Range = handler.sketchMapper.InoToCppLSPRange(inoURI, inoParams.Range) + for i, inoDiag := range inoParams.Context.Diagnostics { + cppParams.Context.Diagnostics[i].Range = handler.sketchMapper.InoToCppLSPRange(inoURI, inoDiag.Range) } - inoURI = p.TextDocument.URI + } + logger.Logf(" --> codeAction(%s:%s)", cppParams.TextDocument, inoParams.Range.Start) - case *lsp.CodeActionParams: - // method "textDocument/codeAction" - inoURI = p.TextDocument.URI - logger("--> codeAction(%s:%s)", p.TextDocument.URI, p.Range.Start) + cppResp, cppErr, err := handler.Clangd.conn.TextDocumentCodeAction(ctx, &cppParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) + handler.Close() + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if cppErr != nil { + logger.Logf("clangd response error: %v", cppErr.AsError()) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().Error()} + } - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(logger, 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) + // TODO: Create a function for this one? + inoResp := []lsp.CommandOrCodeAction{} + if cppResp != nil { + logger.Logf(" <-- codeAction(%d elements)", len(cppResp)) + for _, cppItem := range cppResp { + inoItem := lsp.CommandOrCodeAction{} + switch i := cppItem.Get().(type) { + case lsp.Command: + logger.Logf(" > Command: %s", i.Title) + inoItem.Set(handler.cpp2inoCommand(logger, i)) + case lsp.CodeAction: + logger.Logf(" > CodeAction: %s", i.Title) + inoItem.Set(handler.cpp2inoCodeAction(logger, i, inoURI)) } + inoResp = append(inoResp, inoItem) } - logger(" --> codeAction(%s:%s)", p.TextDocument.URI, p.Range.Start) + logger.Logf("<-- codeAction(%d elements)", len(inoResp)) + } + return inoResp, nil +} - case *lsp.HoverParams: - // method: "textDocument/hover" - doc := p.TextDocumentPositionParams - logger("--> hover(%s:%d:%d)\n", doc.TextDocument.URI, doc.Position.Line, doc.Position.Character) +func (handler *INOLanguageServer) CodeActionResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CodeAction) (*lsp.CodeAction, *jsonrpc.ResponseError) { + panic("unimplemented") +} - if res, e := handler.ino2cppTextDocumentPositionParams(logger, doc); e == nil { - p.TextDocumentPositionParams = res - logger(" --> hover(%s:%d:%d)\n", doc.TextDocument.URI, doc.Position.Line, doc.Position.Character) - } else { - err = e +func (handler *INOLanguageServer) TextDocumentCodeLens(context.Context, jsonrpc.FunctionLogger, *lsp.CodeLensParams) ([]lsp.CodeLens, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) CodeLensResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CodeLens) (*lsp.CodeLens, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentDocumentLink(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentLinkParams) ([]lsp.DocumentLink, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) DocumentLinkResolve(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentLink) (*lsp.DocumentLink, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentDocumentColor(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentColorParams) ([]lsp.ColorInformation, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentColorPresentation(context.Context, jsonrpc.FunctionLogger, *lsp.ColorPresentationParams) ([]lsp.ColorPresentation, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + inoTextDocument := inoParams.TextDocument + inoURI := inoTextDocument.URI + logger.Logf("--> formatting(%s)", inoTextDocument) + + cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + cppURI := cppTextDocument.URI + + logger.Logf(" --> formatting(%s)", cppTextDocument) + + if cleanup, e := handler.createClangdFormatterConfig(logger, cppURI); e != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } else { + defer cleanup() + } + + cppParams := *inoParams + cppParams.TextDocument = cppTextDocument + cppEdits, clangErr, err := handler.Clangd.conn.TextDocumentFormatting(ctx, &cppParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) + handler.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 cppEdits == nil { + return nil, nil + } + + sketchEdits, err := handler.cpp2inoTextEdits(logger, cppURI, cppEdits) + if err != nil { + logger.Logf("ERROR converting textEdits: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if inoEdits, ok := sketchEdits[inoURI]; !ok { + return []lsp.TextEdit{}, nil + } else { + return inoEdits, nil + } +} + +func (handler *INOLanguageServer) TextDocumentRangeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + + // Method: "textDocument/rangeFormatting" + logger.Logf("%s", inoParams.TextDocument) + inoURI := inoParams.TextDocument.URI + cppParams, err := handler.ino2cppDocumentRangeFormattingParams(logger, inoParams) + if err != nil { + logger.Logf("Error: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + cppURI := cppParams.TextDocument.URI + logger.Logf("-> %s", cppParams.TextDocument) + if cleanup, e := handler.createClangdFormatterConfig(logger, cppURI); e != nil { + logger.Logf("cannot create formatter config file: %v", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } else { + defer cleanup() + } + + cppEdits, clangErr, err := handler.Clangd.conn.TextDocumentRangeFormatting(ctx, cppParams) + if err != nil { + logger.Logf("clangd connectiono error: %v", err) + handler.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()} + } + + // Transform and return the result + if cppEdits != nil { + return nil, nil + } + + sketchEdits, err := handler.cpp2inoTextEdits(logger, cppURI, cppEdits) + if err != nil { + logger.Logf("ERROR converting textEdits: %s", err) + return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + } + if inoEdits, ok := sketchEdits[inoURI]; !ok { + return []lsp.TextEdit{}, nil + } else { + return inoEdits, nil + } +} + +func (handler *INOLanguageServer) TextDocumentOnTypeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentOnTypeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + // return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"} + // inoURI = p.TextDocument.URI + // err = handler.ino2cppDocumentOnTypeFormattingParams(p) + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentRename(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.RenameParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + // return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"} + // inoURI = p.TextDocument.URI + // err = handler.ino2cppRenameParams(p) + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentFoldingRange(context.Context, jsonrpc.FunctionLogger, *lsp.FoldingRangeParams) ([]lsp.FoldingRange, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentSelectionRange(context.Context, jsonrpc.FunctionLogger, *lsp.SelectionRangeParams) ([]lsp.SelectionRange, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentPrepareCallHierarchy(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyPrepareParams) ([]lsp.CallHierarchyItem, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) CallHierarchyIncomingCalls(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyIncomingCallsParams) ([]lsp.CallHierarchyIncomingCall, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) CallHierarchyOutgoingCalls(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyOutgoingCallsParams) ([]lsp.CallHierarchyOutgoingCall, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentSemanticTokensFull(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentSemanticTokensFullDelta(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensDeltaParams) (*lsp.SemanticTokens, *lsp.SemanticTokensDelta, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentSemanticTokensRange(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensRangeParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceSemanticTokensRefresh(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentLinkedEditingRange(context.Context, jsonrpc.FunctionLogger, *lsp.LinkedEditingRangeParams) (*lsp.LinkedEditingRanges, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) TextDocumentMoniker(context.Context, jsonrpc.FunctionLogger, *lsp.MonikerParams) ([]lsp.Moniker, *jsonrpc.ResponseError) { + panic("unimplemented") +} + +// Notifications from IDE -> + +func (handler *INOLanguageServer) Progress(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) Initialized(logger jsonrpc.FunctionLogger, params *lsp.InitializedParams) { + logger.Logf("Notification is not propagated to clangd") +} + +func (handler *INOLanguageServer) Exit(jsonrpc.FunctionLogger) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) SetTrace(jsonrpc.FunctionLogger, *lsp.SetTraceParams) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WindowWorkDoneProgressCancel(jsonrpc.FunctionLogger, *lsp.WorkDoneProgressCancelParams) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceDidChangeWorkspaceFolders(jsonrpc.FunctionLogger, *lsp.DidChangeWorkspaceFoldersParams) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceDidChangeConfiguration(jsonrpc.FunctionLogger, *lsp.DidChangeConfigurationParams) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceDidChangeWatchedFiles(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeWatchedFilesParams) { + handler.readLock(logger, true) + defer handler.readUnlock(logger) + // return + // err = handler.ino2cppDidChangeWatchedFilesParams(p) + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceDidCreateFiles(jsonrpc.FunctionLogger, *lsp.CreateFilesParams) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceDidRenameFiles(jsonrpc.FunctionLogger, *lsp.RenameFilesParams) { + panic("unimplemented") +} + +func (handler *INOLanguageServer) WorkspaceDidDeleteFiles(jsonrpc.FunctionLogger, *lsp.DeleteFilesParams) { + panic("unimplemented") +} + +// Notifications from Clangd <- + +func (handler *INOLanguageServer) PublishDiagnosticsFromClangd(logger jsonrpc.FunctionLogger, cppParams *lsp.PublishDiagnosticsParams) { + // Default to read lock + handler.readLock(logger, false) + defer handler.readUnlock(logger) + + logger.Logf("publishDiagnostics(%s):", cppParams.URI) + for _, diag := range cppParams.Diagnostics { + logger.Logf("> %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... + allInoParams, err := handler.cpp2inoDiagnostics(logger, cppParams) + if err != nil { + logger.Logf(" Error converting diagnostics to .ino: %s", err) + return + } + + // Push back to IDE the converted diagnostics + for _, inoParams := range allInoParams { + logger.Logf("to IDE: publishDiagnostics(%s):", inoParams.URI) + for _, diag := range inoParams.Diagnostics { + logger.Logf("> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) } - inoURI = p.TextDocument.URI - - case *lsp.DocumentSymbolParams: - // method "textDocument/documentSymbol" - inoURI = p.TextDocument.URI - logger("--> documentSymbol(%s)", p.TextDocument.URI) - - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(logger, p.TextDocument) - logger(" --> documentSymbol(%s)", p.TextDocument.URI) - - case *lsp.DocumentFormattingParams: - // method "textDocument/formatting" - inoURI = p.TextDocument.URI - logger("--> formatting(%s)", p.TextDocument.URI) - p.TextDocument, err = handler.ino2cppTextDocumentIdentifier(logger, p.TextDocument) - cppURI = p.TextDocument.URI - logger(" --> formatting(%s)", p.TextDocument.URI) - if cleanup, e := handler.createClangdFormatterConfig(logger, cppURI); e != nil { - err = e - } else { - defer cleanup() + logger.Logf("IDE <-- LS NOTIF textDocument/publishDiagnostics:") + + if err := handler.IDEConn.TextDocumentPublishDiagnostics(inoParams); err != nil { + logger.Logf(" Error sending diagnostics to IDE: %s", err) + return } + } +} - case *lsp.DocumentRangeFormattingParams: - // Method: "textDocument/rangeFormatting" - logger("--> %s(%s:%s)", method, p.TextDocument.URI, p.Range) - inoURI = p.TextDocument.URI - if cppParams, e := handler.ino2cppDocumentRangeFormattingParams(logger, p); e == nil { - params = cppParams - cppURI = cppParams.TextDocument.URI - logger(" --> %s(%s:%s)", method, cppParams.TextDocument.URI, cppParams.Range) - if cleanup, e := handler.createClangdFormatterConfig(logger, cppURI); e != nil { - err = e - } else { - defer cleanup() - } - } else { - err = e +func (handler *INOLanguageServer) ProgressFromClangd(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("begin %s %v", token, value) + handler.progressHandler.Begin(token, &value) + case lsp.WorkDoneProgressReport: + logger.Logf("report %s %v", token, value) + handler.progressHandler.Report(token, &value) + case lsp.WorkDoneProgressEnd: + logger.Logf("end %s %v", token, value) + handler.progressHandler.End(token, &value) + default: + logger.Logf("error unsupported $/progress: " + string(progress.Value)) + } +} + +// Requests from IDE <-> + +func (handler *INOLanguageServer) TextDocumentDidOpen(logger jsonrpc.FunctionLogger, inoParam *lsp.DidOpenTextDocumentParams) { + handler.writeLock(logger, true) + defer handler.writeUnlock(logger) + + // Add the TextDocumentItem in the tracked files list + inoTextDocItem := inoParam.TextDocument + handler.docs[inoTextDocItem.URI.AsPath().String()] = inoTextDocItem + + // If we are tracking a .ino... + if inoTextDocItem.URI.Ext() == ".ino" { + handler.sketchTrackedFilesCount++ + logger.Logf("Increasing .ino tracked files count to %d", handler.sketchTrackedFilesCount) + + // Notify clangd that sketchCpp has been opened only once + if handler.sketchTrackedFilesCount != 1 { + logger.Logf("Clang already notified, do not notify it anymore") + return } + } + + if cppItem, err := handler.ino2cppTextDocumentItem(logger, inoTextDocItem); err != nil { + logger.Logf("Error: %s", err) + } else if err := handler.Clangd.conn.TextDocumentDidOpen(&lsp.DidOpenTextDocumentParams{ + TextDocument: cppItem, + }); 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.") + handler.Close() + } +} + +func (handler *INOLanguageServer) TextDocumentDidChange(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeTextDocumentParams) { + handler.writeLock(logger, true) + defer handler.writeUnlock(logger) - case *lsp.DocumentHighlightParams: - tdp := p.TextDocumentPositionParams + logger.Logf("didChange(%s)", inoParams.TextDocument) + for _, change := range inoParams.ContentChanges { + logger.Logf("> %s", change) + } - inoURI = tdp.TextDocument.URI - if res, e := handler.ino2cppTextDocumentPositionParams(logger, tdp); e == nil { - cppURI = res.TextDocument.URI - params = res - } else { - err = e + if cppParams, err := handler.didChange(logger, inoParams); err != nil { + logger.Logf("--E Error: %s", err) + } else if cppParams == nil { + logger.Logf("--X Notification is not propagated to clangd") + } else { + logger.Logf("LS --> CL NOTIF didChange(%s@%d)", cppParams.TextDocument) + for _, change := range cppParams.ContentChanges { + logger.Logf(" > %s", change) } - - case *lsp.SignatureHelpParams, - *lsp.DefinitionParams, - *lsp.TypeDefinitionParams, - *lsp.ImplementationParams: - // it was *lsp.TextDocumentPositionParams: - - // Method: "textDocument/signatureHelp" - // Method: "textDocument/definition" - // Method: "textDocument/typeDefinition" - // Method: "textDocument/implementation" - // Method: "textDocument/documentHighlight" - - tdp := p.(lsp.TextDocumentPositionParams) - - logger("--> %s(%s:%s)", method, tdp.TextDocument.URI, tdp.Position) - inoURI = tdp.TextDocument.URI - if res, e := handler.ino2cppTextDocumentPositionParams(logger, tdp); e == nil { - cppURI = res.TextDocument.URI - params = res - logger(" --> %s(%s:%s)", method, res.TextDocument.URI, res.Position) - } else { - err = e + if err := handler.Clangd.conn.TextDocumentDidChange(cppParams); err != nil { + // Exit the process and trigger a restart by the client in case of a severe error + logger.Logf("Connection error with clangd server: %v", err) + logger.Logf("Please restart the language server.") + handler.Close() } + } +} - case *lsp.ReferenceParams: - // "textDocument/references": - logger("--X " + method) - return - inoURI = p.TextDocument.URI - _, err = handler.ino2cppTextDocumentPositionParams(logger, p.TextDocumentPositionParams) +func (handler *INOLanguageServer) TextDocumentWillSave(jsonrpc.FunctionLogger, *lsp.WillSaveTextDocumentParams) { + panic("unimplemented") +} - case *lsp.DocumentOnTypeFormattingParams: - // "textDocument/onTypeFormatting": - logger("--X " + method) - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"}) - return - inoURI = p.TextDocument.URI - err = handler.ino2cppDocumentOnTypeFormattingParams(p) +func (handler *INOLanguageServer) TextDocumentDidSave(logger jsonrpc.FunctionLogger, inoParams *lsp.DidSaveTextDocumentParams) { + handler.writeLock(logger, true) + defer handler.writeUnlock(logger) - case *lsp.RenameParams: - // "textDocument/rename": - logger("--X " + method) - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"}) - return - inoURI = p.TextDocument.URI - err = handler.ino2cppRenameParams(p) + logger.Logf("didSave(%s)", inoParams.TextDocument) + if cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument); err != nil { + logger.Logf("--E Error: %s", err) + } else if cppTextDocument.URI.AsPath().EquivalentTo(handler.buildSketchCpp) { + logger.Logf(" didSave(%s) equals %s", cppTextDocument, handler.buildSketchCpp) + logger.Logf("--| didSave not forwarded to clangd") + } else { + logger.Logf("LS --> CL NOTIF didSave(%s)", cppTextDocument) + if err := handler.Clangd.conn.TextDocumentDidSave(&lsp.DidSaveTextDocumentParams{ + TextDocument: cppTextDocument, + Text: inoParams.Text, + }); err != nil { + // Exit the process and trigger a restart by the client in case of a severe error + logger.Logf("Connection error with clangd server: %v", err) + logger.Logf("Please restart the language server.") + handler.Close() + } + } +} - case *lsp.DidChangeWatchedFilesParams: - // "workspace/didChangeWatchedFiles": - logger("--X " + method) - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"}) - return - err = handler.ino2cppDidChangeWatchedFilesParams(p) +func (handler *INOLanguageServer) TextDocumentDidClose(logger jsonrpc.FunctionLogger, inoParams *lsp.DidCloseTextDocumentParams) { + handler.writeLock(logger, true) + defer handler.writeUnlock(logger) - case *lsp.ExecuteCommandParams: - // "workspace/executeCommand": - logger("--X " + method) - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"}) - return - err = handler.ino2cppExecuteCommand(p) - } - if err != nil { - logger("Error: %s", err) - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) - return - } + logger.Logf("didClose(%s)", inoParams.TextDocument) - logger("LS --> CL REQ %s", method) - clangRawResp, clangErr, err := handler.ClangdConn.SendRequest(ctx, method, lsp.EncodeMessage(params)) - 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" { - logger("Timeout exceeded while waiting for a reply from clangd.") - logger("Please restart the language server.") - handler.Close() - } else if strings.Contains(err.Error(), "non-added document") || strings.Contains(err.Error(), "non-added file") { - logger("The clangd process has lost track of the open document.") - logger("%v", err) - logger("Please restart the language server.") + if cppParams, err := handler.didClose(logger, inoParams); err != nil { + logger.Logf("--E Error: %s", err) + } else if cppParams == nil { + logger.Logf("--X Notification is not propagated to clangd") + } else { + logger.Logf("--> CL NOTIF didClose(%s)", cppParams.TextDocument) + if err := handler.Clangd.conn.TextDocumentDidClose(cppParams); 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.") handler.Close() - } else { - logger("clangd error: %v", err) } - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) - return } - if clangErr != nil { - logger("clangd response error: %v", clangErr.AsError()) - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()}) - return - } - logger("LS <-- CL RESP %s", method) - clangResp, err := lsp.DecodeServerResponseResult(method, clangRawResp) - if err != nil { - logger("Error decoding clang response: %v", err) - returnCB(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) - return +} + +// Requests from Clangd <-> + +func (handler *INOLanguageServer) WindowWorkDoneProgressCreateFromClangd(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()} } + handler.progressHandler.Create(token) + return nil +} - if handler.buildSketchSymbolsLoad { - handler.buildSketchSymbolsLoad = false - handler.buildSketchSymbolsCheck = false - logger("Queued resfreshing document symbols") - go handler.LoadCppDocumentSymbols() +// Close closes all the json-rpc connections. +func (handler *INOLanguageServer) Close() { + if handler.Clangd != nil { + handler.Clangd.Close() + handler.Clangd = nil } - if handler.buildSketchSymbolsCheck { - handler.buildSketchSymbolsCheck = false - logger("Queued check document symbols") - go handler.CheckCppDocumentSymbols() + if handler.closing != nil { + close(handler.closing) + handler.closing = nil } +} - // Transform and return the result - if clangResp != nil { - clangResp = handler.transformClangdResult(logger, method, inoURI, cppURI, clangResp) +// CloseNotify returns a channel that is closed when the InoHandler is closed +func (handler *INOLanguageServer) CloseNotify() <-chan bool { + return handler.closing +} + +// CleanUp performs cleanup of the workspace and temp files create by the language server +func (handler *INOLanguageServer) CleanUp() { + if handler.buildPath != nil { + handler.buildPath.RemoveAll() + handler.buildPath = nil } - returnCB(lsp.EncodeMessage(clangResp), nil) } -func (handler *INOLanguageServer) initializeWorkbench(logger streams.PrefixLogger, params *lsp.InitializeParams) error { +func (handler *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, params *lsp.InitializeParams) error { + // TODO: This function must be split into two + // -> start clang (when params != nil) + // -> reser clang status (when params == nil) + // the two flows shares very little + currCppTextVersion := 0 if params != nil { - logger(" --> initialize(%s)", params.RootURI) + logger.Logf(" --> initialize(%s)", params.RootURI) handler.lspInitializeParams = params handler.sketchRoot = params.RootURI.AsPath() handler.sketchName = handler.sketchRoot.Base() } else { - logger(" --> RE-initialize()") + logger.Logf(" --> RE-initialize()") currCppTextVersion = handler.sketchMapper.CppText.Version } @@ -704,87 +1267,51 @@ func (handler *INOLanguageServer) initializeWorkbench(logger streams.PrefixLogge }, } - method := "textDocument/didChange" - logger("LS --> CL NOTIF %s:", method) - if err := handler.ClangdConn.SendNotification(method, lsp.EncodeMessage(syncEvent)); err != nil { - logger(" error reinitilizing clangd:", err) + logger.Logf("LS --> CL NOTIF textDocument/didChange:") + if err := handler.Clangd.conn.TextDocumentDidChange(syncEvent); err != nil { + logger.Logf(" error reinitilizing clangd:", err) return err } } else { // Otherwise start clangd! dataFolder, err := extractDataFolderFromArduinoCLI(logger) if err != nil { - logger(" error: %s", err) + logger.Logf(" error: %s", err) } - clangdStdout, clangdStdin, clangdStderr := startClangd(logger, 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) - } - - rpcLogger := streams.NewJsonRPCLogger("IDE LS", "CL") - handler.ClangdConn = jsonrpc.NewConnection(clangdStdio, clangdStdio, - func(ctx context.Context, method string, params json.RawMessage, respCallback func(result json.RawMessage, err *jsonrpc.ResponseError)) { - logger, idx := rpcLogger.LogServerRequest(method, params) - handler.HandleRequestFromClangd(ctx, logger, method, params, func(result json.RawMessage, err *jsonrpc.ResponseError) { - rpcLogger.LogClientResponse(idx, method, result, err) - respCallback(result, err) - }) - }, - func(ctx context.Context, method string, params json.RawMessage) { - logger := rpcLogger.LogServerNotification(method, params) - handler.HandleNotificationFromClangd(ctx, logger, method, params) - }, - func(e error) { - logger("connection error with clangd! %s", e) + handler.Clangd = NewClangdClient( + logger, handler.buildPath, handler.buildSketchCpp, dataFolder, + func() { + logger.Logf("Lost connection with clangd!") handler.Close() - }, - ) - go func() { - handler.ClangdConn.Run() - logger("Lost connection with clangd!") - handler.Close() - }() + }, handler) // Send initialization command to clangd ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - method1 := "initialize" - logger("LS --> CL REQ %s", method1) - rawResp, clangErr, err := handler.ClangdConn.SendRequest( - ctx, method1, lsp.EncodeMessage(handler.lspInitializeParams)) + initRes, clangErr, err := handler.Clangd.conn.Initialize(ctx, handler.lspInitializeParams) if err != nil { - logger(" error initilizing clangd: %v", err) + logger.Logf(" error initilizing clangd: %v", err) return err } - logger("LS <-- CL RESP %s", method1) if clangErr != nil { - logger(" error initilizing clangd: %v", clangErr.AsError()) + logger.Logf(" error initilizing clangd: %v", clangErr.AsError()) return clangErr.AsError() - } else if resp, err := lsp.DecodeServerResponseResult("initialize", rawResp); err != nil { - logger(" error initilizing clangd: %v", err) - return err } else { - logger("clangd successfully started: %v", resp) + logger.Logf("clangd successfully started: %s", string(lsp.EncodeMessage(initRes))) } - method2 := "initialized" - logger("LS --> CL NOTIF %s:", method2) - if err := handler.ClangdConn.SendNotification(method2, lsp.EncodeMessage(lsp.InitializedParams{})); err != nil { - logger(" error sending initialized notification to clangd: %v", err) + if err := handler.Clangd.conn.Initialized(&lsp.InitializedParams{}); err != nil { + logger.Logf(" error sending initialized notification to clangd: %v", err) return err } } - handler.buildSketchSymbolsLoad = true + handler.queueLoadCppDocumentSymbols() return nil } -func extractDataFolderFromArduinoCLI(logger streams.PrefixLogger) (*paths.Path, error) { +func extractDataFolderFromArduinoCLI(logger jsonrpc.FunctionLogger) (*paths.Path, error) { // XXX: do this from IDE or via gRPC args := []string{globalCliPath, "--config-file", globalCliConfigPath, @@ -798,7 +1325,7 @@ func extractDataFolderFromArduinoCLI(logger streams.PrefixLogger) (*paths.Path, } cmdOutput := &bytes.Buffer{} cmd.RedirectStdoutTo(cmdOutput) - logger("running: %s", strings.Join(args, " ")) + logger.Logf("running: %s", strings.Join(args, " ")) if err := cmd.Run(); err != nil { return nil, errors.Errorf("running %s: %s", strings.Join(args, " "), err) } @@ -813,67 +1340,59 @@ func extractDataFolderFromArduinoCLI(logger streams.PrefixLogger) (*paths.Path, return nil, errors.Errorf("parsing arduino-cli output: %s", err) } // Return only the build path - logger("Arduino Data Dir -> %s", res.Directories.Data) + logger.Logf("Arduino Data Dir -> %s", res.Directories.Data) return paths.New(res.Directories.Data), nil } -func (handler *INOLanguageServer) refreshCppDocumentSymbols(logger streams.PrefixLogger) error { +func (handler *INOLanguageServer) refreshCppDocumentSymbols(logger jsonrpc.FunctionLogger) error { // Query source code symbols handler.readUnlock(logger) cppURI := lsp.NewDocumentURIFromPath(handler.buildSketchCpp) - logger("requesting documentSymbol for %s", cppURI) - method := "textDocument/documentSymbol" - logger("LS --> CL REQ %s", method) - respRaw, resErr, err := handler.ClangdConn.SendRequest(context.Background(), method, - lsp.EncodeMessage(&lsp.DocumentSymbolParams{ - TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, - })) - handler.readLock(logger, true) + logger.Logf("requesting documentSymbol for %s", cppURI) + cppParams := &lsp.DocumentSymbolParams{ + TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, + } + cppDocumentSymbols, _ /* cppSymbolInformation */, cppErr, err := handler.Clangd.conn.TextDocumentDocumentSymbol(context.Background(), cppParams) + handler.readLock(logger, true) if err != nil { - logger("error: %s", err) + logger.Logf("error: %s", err) return fmt.Errorf("quering source code symbols: %w", err) } - logger("LS <-- CL RESP %s", method) - if resErr != nil { - logger("error: %s", resErr.AsError()) - return fmt.Errorf("quering source code symbols: %w", resErr.AsError()) - } - result, err := lsp.DecodeServerResponseResult("textDocument/documentSymbol", respRaw) - if err != nil { - logger("invalid response: %s", err) - return fmt.Errorf("quering source code symbols: invalid response: %w", err) + if cppErr != nil { + logger.Logf("error: %s", cppErr.AsError()) + return fmt.Errorf("quering source code symbols: %w", cppErr.AsError()) } - symbols, ok := result.([]lsp.DocumentSymbol) - if !ok { - logger("error: expected DocumenSymbol array but got %T", result) - return fmt.Errorf("expected DocumenSymbol but got %T", result) + if cppDocumentSymbols == nil { + err := errors.New("expected DocumenSymbol array but got SymbolInformation instead") + logger.Logf("error: %s", err) + return err } // Filter non-functions symbols i := 0 - for _, symbol := range symbols { + for _, symbol := range cppDocumentSymbols { if symbol.Kind != lsp.SymbolKindFunction { continue } - symbols[i] = symbol + cppDocumentSymbols[i] = symbol i++ } - symbols = symbols[:i] - handler.buildSketchSymbols = symbols + cppDocumentSymbols = cppDocumentSymbols[:i] + handler.buildSketchSymbols = cppDocumentSymbols symbolsCanary := "" - for _, symbol := range symbols { - logger(" symbol: %s %s %s", symbol.Kind, symbol.Name, symbol.Range) + for _, symbol := range cppDocumentSymbols { + logger.Logf(" symbol: %s %s %s", symbol.Kind, symbol.Name, symbol.Range) if symbolText, err := textutils.ExtractRange(handler.sketchMapper.CppText.Text, symbol.Range); err != nil { - logger(" > invalid range: %s", err) + logger.Logf(" > invalid range: %s", err) symbolsCanary += "/" } else if end := strings.Index(symbolText, "{"); end != -1 { - logger(" TRIMMED> %s", symbolText[:end]) + logger.Logf(" TRIMMED> %s", symbolText[:end]) symbolsCanary += symbolText[:end] } else { - logger(" > %s", symbolText) + logger.Logf(" > %s", symbolText) symbolsCanary += symbolText } } @@ -881,35 +1400,9 @@ func (handler *INOLanguageServer) refreshCppDocumentSymbols(logger streams.Prefi return nil } -func (handler *INOLanguageServer) LoadCppDocumentSymbols() error { - logger := streams.NewPrefixLogger(color.New(color.FgHiBlue), "SYLD --- ") - defer logger("(done)") - handler.readLock(logger, true) - defer handler.readUnlock(logger) - return handler.refreshCppDocumentSymbols(logger) -} - -func (handler *INOLanguageServer) CheckCppDocumentSymbols() error { - logger := streams.NewPrefixLogger(color.New(color.FgHiBlue), "SYCK --- ") - defer logger("(done)") - handler.readLock(logger, true) - defer handler.readUnlock(logger) - - oldSymbols := handler.buildSketchSymbols - canary := handler.buildSketchSymbolsCanary - if err := handler.refreshCppDocumentSymbols(logger); err != nil { - return err - } - if len(oldSymbols) != len(handler.buildSketchSymbols) || canary != handler.buildSketchSymbolsCanary { - logger("function symbols change detected, triggering sketch rebuild!") - handler.scheduleRebuildEnvironment() - } - return nil -} - func (handler *INOLanguageServer) CheckCppIncludesChanges() { - logger := streams.NewPrefixLogger(color.New(color.FgHiBlue), "INCK --- ") - logger("check for Cpp Include Changes") + logger := NewLSPFunctionLogger(color.HiBlueString, "INCK --- ") + logger.Logf("check for Cpp Include Changes") includesCanary := "" for _, line := range strings.Split(handler.sketchMapper.CppText.Text, "\n") { if strings.Contains(line, "#include ") { @@ -919,7 +1412,7 @@ func (handler *INOLanguageServer) CheckCppIncludesChanges() { if includesCanary != handler.buildSketchIncludesCanary { handler.buildSketchIncludesCanary = includesCanary - logger("#include change detected, triggering sketch rebuild!") + logger.Logf("#include change detected, triggering sketch rebuild!") handler.scheduleRebuildEnvironment() } } @@ -954,69 +1447,19 @@ func canonicalizeCompileCommandsJSON(compileCommandsDir *paths.Path) map[string] return compilers } -func startClangd(logger streams.PrefixLogger, 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 { - logger(" Starting clangd: %s", 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 *INOLanguageServer) didOpen(logger streams.PrefixLogger, 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++ - logger(" 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(logger, inoItem) - return &lsp.DidOpenTextDocumentParams{ - TextDocument: cppItem, - }, err -} - -func (handler *INOLanguageServer) didClose(logger streams.PrefixLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { +func (handler *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, 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 { - logger(" didClose of untracked document: %s", inoIdentifier.URI) + logger.Logf(" 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-- - logger(" decreasing .ino tracked files count: %d", handler.sketchTrackedFilesCount) + logger.Logf(" 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 { @@ -1030,7 +1473,7 @@ func (handler *INOLanguageServer) didClose(logger streams.PrefixLogger, inoDidCl }, err } -func (handler *INOLanguageServer) ino2cppTextDocumentItem(logger streams.PrefixLogger, inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { +func (handler *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogger, inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { cppURI, err := handler.ino2cppDocumentURI(logger, inoItem.URI) if err != nil { return cppItem, err @@ -1051,14 +1494,14 @@ func (handler *INOLanguageServer) ino2cppTextDocumentItem(logger streams.PrefixL return cppItem, nil } -func (handler *INOLanguageServer) didChange(logger streams.PrefixLogger, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { +func (handler *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, 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) + 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. @@ -1077,21 +1520,21 @@ func (handler *INOLanguageServer) didChange(logger streams.PrefixLogger, req *ls for _, sym := range handler.buildSketchSymbols { if sym.SelectionRange.Overlaps(cppRange) { dirty = true - logger("--! DIRTY CHANGE detected using symbol tables, force sketch rebuild!") + logger.Logf("--! DIRTY CHANGE detected using symbol tables, force sketch rebuild!") break } } if handler.sketchMapper.ApplyTextChange(doc.URI, inoChange) { dirty = true - logger("--! DIRTY CHANGE detected with sketch mapper, force sketch rebuild!") + logger.Logf("--! DIRTY CHANGE detected with sketch mapper, force sketch rebuild!") } if dirty { handler.scheduleRebuildEnvironment() } - // logger("New version:----------") - // logger(handler.sketchMapper.CppText.Text) - // logger("----------------------") + // logger.Logf("New version:----------") + // logger.Logf(handler.sketchMapper.CppText.Text) + // logger.Logf("----------------------") cppChange := lsp.TextDocumentContentChangeEvent{ Range: cppRange, @@ -1129,7 +1572,7 @@ func (handler *INOLanguageServer) didChange(logger streams.PrefixLogger, req *ls return cppReq, err } -func (handler *INOLanguageServer) handleError(logger streams.PrefixLogger, err error) error { +func (handler *INOLanguageServer) handleError(logger jsonrpc.FunctionLogger, err error) error { errorStr := err.Error() var message string if strings.Contains(errorStr, "#error") { @@ -1150,7 +1593,7 @@ func (handler *INOLanguageServer) handleError(logger streams.PrefixLogger, err e return err } } else if strings.Contains(errorStr, "No such file or directory") { - exp, regexpErr := regexp.Compile("([\\w\\.\\-]+): No such file or directory") + exp, regexpErr := regexp.Compile(`([\w\.\-]+): No such file or directory`) if regexpErr != nil { panic(regexpErr) } @@ -1160,25 +1603,28 @@ func (handler *INOLanguageServer) handleError(logger streams.PrefixLogger, err e } else { message = "Could not start editor support.\n" + errorStr } - go handler.showMessage(logger, lsp.MessageTypeError, message) + go func() { + defer streams.CatchAndLogPanic() + handler.showMessage(logger, lsp.MessageTypeError, message) + }() return errors.New(message) } -func (handler *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger streams.PrefixLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { +func (handler *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { cppURI, err := handler.ino2cppDocumentURI(logger, doc.URI) res := doc res.URI = cppURI return res, err } -func (handler *INOLanguageServer) ino2cppTextDocumentIdentifier(logger streams.PrefixLogger, doc lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { +func (handler *INOLanguageServer) ino2cppTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { cppURI, err := handler.ino2cppDocumentURI(logger, doc.URI) res := doc res.URI = cppURI return res, err } -func (handler *INOLanguageServer) ino2cppDocumentURI(logger streams.PrefixLogger, inoURI lsp.DocumentURI) (lsp.DocumentURI, error) { +func (handler *INOLanguageServer) ino2cppDocumentURI(logger jsonrpc.FunctionLogger, 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) @@ -1192,35 +1638,35 @@ func (handler *INOLanguageServer) ino2cppDocumentURI(logger streams.PrefixLogger inside, err := inoPath.IsInsideDir(handler.sketchRoot) if err != nil { - logger(" could not determine if '%s' is inside '%s'", inoPath, handler.sketchRoot) + logger.Logf(" could not determine if '%s' is inside '%s'", inoPath, handler.sketchRoot) return lsp.NilURI, unknownURI(inoURI) } if !inside { - logger(" '%s' not inside sketchroot '%s', passing doc identifier to as-is", handler.sketchRoot, inoPath) + logger.Logf(" '%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) - logger(" URI: '%s' -> '%s'", inoPath, cppPath) + logger.Logf(" URI: '%s' -> '%s'", inoPath, cppPath) return lsp.NewDocumentURIFromPath(cppPath), nil } - logger(" could not determine rel-path of '%s' in '%s': %s", inoPath, handler.sketchRoot, err) + logger.Logf(" could not determine rel-path of '%s' in '%s': %s", inoPath, handler.sketchRoot, err) return lsp.NilURI, err } -func (handler *INOLanguageServer) inoDocumentURIFromInoPath(logger streams.PrefixLogger, inoPath string) (lsp.DocumentURI, error) { +func (handler *INOLanguageServer) inoDocumentURIFromInoPath(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { if inoPath == sourcemapper.NotIno.File { return sourcemapper.NotInoURI, nil } doc, ok := handler.docs[inoPath] if !ok { - logger(" !!! Unresolved .ino path: %s", inoPath) - logger(" !!! Known doc paths are:") + logger.Logf(" !!! Unresolved .ino path: %s", inoPath) + logger.Logf(" !!! Known doc paths are:") for p := range handler.docs { - logger(" !!! > %s", p) + logger.Logf(" !!! > %s", p) } uri := lsp.NewDocumentURI(inoPath) return uri, unknownURI(uri) @@ -1228,7 +1674,7 @@ func (handler *INOLanguageServer) inoDocumentURIFromInoPath(logger streams.Prefi return doc.URI, nil } -func (handler *INOLanguageServer) cpp2inoDocumentURI(logger streams.PrefixLogger, cppURI lsp.DocumentURI, cppRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { +func (handler *INOLanguageServer) cpp2inoDocumentURI(logger jsonrpc.FunctionLogger, 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] @@ -1245,16 +1691,16 @@ func (handler *INOLanguageServer) cpp2inoDocumentURI(logger streams.PrefixLogger if err == nil { if handler.sketchMapper.IsPreprocessedCppLine(cppRange.Start.Line) { inoPath = sourcemapper.NotIno.File - logger(" URI: is in preprocessed section") - logger(" converted %s to %s:%s", cppRange, inoPath, inoRange) + logger.Logf(" URI: is in preprocessed section") + logger.Logf(" converted %s to %s:%s", cppRange, inoPath, inoRange) } else { - logger(" URI: converted %s to %s:%s", cppRange, inoPath, inoRange) + logger.Logf(" URI: converted %s to %s:%s", cppRange, inoPath, inoRange) } } else if _, ok := err.(sourcemapper.AdjustedRangeErr); ok { - logger(" URI: converted %s to %s:%s (END LINE ADJUSTED)", cppRange, inoPath, inoRange) + logger.Logf(" URI: converted %s to %s:%s (END LINE ADJUSTED)", cppRange, inoPath, inoRange) err = nil } else { - logger(" URI: ERROR: %s", err) + logger.Logf(" URI: ERROR: %s", err) handler.sketchMapper.DebugLogAll() return lsp.NilURI, lsp.NilRange, err } @@ -1264,29 +1710,29 @@ func (handler *INOLanguageServer) cpp2inoDocumentURI(logger streams.PrefixLogger inside, err := cppPath.IsInsideDir(handler.buildSketchRoot) if err != nil { - logger(" could not determine if '%s' is inside '%s'", cppPath, handler.buildSketchRoot) + logger.Logf(" could not determine if '%s' is inside '%s'", cppPath, handler.buildSketchRoot) return lsp.NilURI, lsp.NilRange, err } if !inside { - logger(" '%s' is not inside '%s'", cppPath, handler.buildSketchRoot) - logger(" keep doc identifier to '%s' as-is", cppPath) + logger.Logf(" '%s' is not inside '%s'", cppPath, handler.buildSketchRoot) + logger.Logf(" 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() - logger(" URI: '%s' -> '%s'", cppPath, inoPath) + logger.Logf(" URI: '%s' -> '%s'", cppPath, inoPath) inoURI, err := handler.inoDocumentURIFromInoPath(logger, inoPath) - logger(" as URI: '%s'", inoURI) + logger.Logf(" as URI: '%s'", inoURI) return inoURI, cppRange, err } - logger(" could not determine rel-path of '%s' in '%s': %s", cppPath, handler.buildSketchRoot, err) + logger.Logf(" could not determine rel-path of '%s' in '%s': %s", cppPath, handler.buildSketchRoot, err) return lsp.NilURI, lsp.NilRange, err } -func (handler *INOLanguageServer) ino2cppTextDocumentPositionParams(logger streams.PrefixLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { +func (handler *INOLanguageServer) ino2cppTextDocumentPositionParams(logger jsonrpc.FunctionLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { inoTextDocument := inoParams.TextDocument inoPosition := inoParams.Position inoURI := inoTextDocument.URI @@ -1294,7 +1740,7 @@ func (handler *INOLanguageServer) ino2cppTextDocumentPositionParams(logger strea cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoTextDocument) if err != nil { - logger("%s -> invalid text document: %s", prefix, err) + logger.Logf("%s -> invalid text document: %s", prefix, err) return lsp.TextDocumentPositionParams{}, err } cppPosition := inoPosition @@ -1302,7 +1748,7 @@ func (handler *INOLanguageServer) ino2cppTextDocumentPositionParams(logger strea if cppLine, ok := handler.sketchMapper.InoToCppLineOk(inoURI, inoPosition.Line); ok { cppPosition.Line = cppLine } else { - logger("%s -> invalid line requested: %s:%d", prefix, inoURI, inoPosition.Line) + logger.Logf("%s -> invalid line requested: %s:%d", prefix, inoURI, inoPosition.Line) return lsp.TextDocumentPositionParams{}, unknownURI(inoURI) } } @@ -1310,11 +1756,11 @@ func (handler *INOLanguageServer) ino2cppTextDocumentPositionParams(logger strea TextDocument: cppTextDocument, Position: cppPosition, } - logger("%s -> %s", prefix, cppParams) + logger.Logf("%s -> %s", prefix, cppParams) return cppParams, nil } -func (handler *INOLanguageServer) ino2cppRange(logger streams.PrefixLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { +func (handler *INOLanguageServer) ino2cppRange(logger jsonrpc.FunctionLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { cppURI, err := handler.ino2cppDocumentURI(logger, inoURI) if err != nil { return lsp.NilURI, lsp.Range{}, err @@ -1326,7 +1772,7 @@ func (handler *INOLanguageServer) ino2cppRange(logger streams.PrefixLogger, inoU return cppURI, inoRange, nil } -func (handler *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger streams.PrefixLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { +func (handler *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) if err != nil { return nil, err @@ -1340,201 +1786,65 @@ func (handler *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger st }, err } -func (handler *INOLanguageServer) 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 *INOLanguageServer) 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 *INOLanguageServer) 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 *INOLanguageServer) 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 *INOLanguageServer) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { - panic("not implemented") - newEdit := lsp.WorkspaceEdit{} - // 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 *INOLanguageServer) transformClangdResult(logger streams.PrefixLogger, 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) - } - logger("<-- 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 - logger("<-- completion(%d items) cppToIno=%v", len(r.Items), cppToIno) - return r - - case []lsp.DocumentSymbol: - // method "textDocument/documentSymbol" - logger(" <-- documentSymbol(%d document symbols)", len(r)) - return handler.cpp2inoDocumentSymbols(logger, r, inoURI) - - case []lsp.SymbolInformation: - // method "textDocument/documentSymbol" - logger(" <-- documentSymbol(%d symbol information)", len(r)) - return handler.cpp2inoSymbolInformation(r) - - case []lsp.CommandOrCodeAction: - // method "textDocument/codeAction" - logger(" <-- codeAction(%d elements)", len(r)) - for i := range r { - switch item := r[i].Get().(type) { - case lsp.Command: - logger(" > Command: %s", item.Title) - r[i].Set(handler.Cpp2InoCommand(logger, item)) - case lsp.CodeAction: - logger(" > CodeAction: %s", item.Title) - r[i].Set(handler.cpp2inoCodeAction(logger, item, inoURI)) - } - } - logger("<-- codeAction(%d elements)", len(r)) - - case *[]lsp.TextEdit: - // Method: "textDocument/rangeFormatting" - // Method: "textDocument/onTypeFormatting" - // Method: "textDocument/formatting" - logger(" <-- %s %s textEdit(%d elements)", method, cppURI, len(*r)) - for _, edit := range *r { - logger(" > %s -> %s", edit.Range, strconv.Quote(edit.NewText)) - } - sketchEdits, err := handler.cpp2inoTextEdits(logger, cppURI, *r) - if err != nil { - logger("ERROR converting textEdits: %s", err) - return nil - } - - inoEdits, ok := sketchEdits[inoURI] - if !ok { - inoEdits = []lsp.TextEdit{} - } - logger("<-- %s %s textEdit(%d elements)", method, inoURI, len(inoEdits)) - for _, edit := range inoEdits { - logger(" > %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(logger, cppLocation) - if err != nil { - logger("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(logger, cppLocation) - if err != nil { - logger("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(logger, &cppHL, cppURI) - if err != nil { - logger("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(logger, r) - } - return result -} - -func (handler *INOLanguageServer) cpp2inoCodeAction(logger streams.PrefixLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { +// func (handler *INOLanguageServer) 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 *INOLanguageServer) ino2cppRenameParams(params *lsp.RenameParams) error { +// 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 *INOLanguageServer) ino2cppDidChangeWatchedFilesParams(params *lsp.DidChangeWatchedFilesParams) error { +// 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 *INOLanguageServer) ino2cppExecuteCommand(executeCommand *lsp.ExecuteCommandParams) error { +// 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 *INOLanguageServer) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { +// newEdit := lsp.WorkspaceEdit{} +// 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 *INOLanguageServer) cpp2inoCodeAction(logger jsonrpc.FunctionLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { inoCodeAction := lsp.CodeAction{ Title: codeAction.Title, Kind: codeAction.Kind, @@ -1542,7 +1852,7 @@ func (handler *INOLanguageServer) cpp2inoCodeAction(logger streams.PrefixLogger, Diagnostics: codeAction.Diagnostics, } if codeAction.Command != nil { - inoCommand := handler.Cpp2InoCommand(logger, *codeAction.Command) + inoCommand := handler.cpp2inoCommand(logger, *codeAction.Command) inoCodeAction.Command = &inoCommand } if uri.Ext() == ".ino" { @@ -1553,7 +1863,7 @@ func (handler *INOLanguageServer) cpp2inoCodeAction(logger streams.PrefixLogger, return inoCodeAction } -func (handler *INOLanguageServer) Cpp2InoCommand(logger streams.PrefixLogger, command lsp.Command) lsp.Command { +func (handler *INOLanguageServer) cpp2inoCommand(logger jsonrpc.FunctionLogger, command lsp.Command) lsp.Command { inoCommand := lsp.Command{ Title: command.Title, Command: command.Command, @@ -1568,7 +1878,7 @@ func (handler *INOLanguageServer) Cpp2InoCommand(logger streams.PrefixLogger, co }{} if err := json.Unmarshal(command.Arguments[0], &v); err == nil { if v.TweakID == "ExtractVariable" { - logger(" > converted clangd ExtractVariable") + logger.Logf(" > converted clangd ExtractVariable") if v.File.AsPath().EquivalentTo(handler.buildSketchCpp) { inoFile, inoSelection := handler.sketchMapper.CppToInoRange(v.Selection) v.File = lsp.NewDocumentURI(inoFile) @@ -1587,7 +1897,7 @@ func (handler *INOLanguageServer) Cpp2InoCommand(logger streams.PrefixLogger, co return inoCommand } -func (handler *INOLanguageServer) cpp2inoWorkspaceEdit(logger streams.PrefixLogger, cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { +func (handler *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { if cppWorkspaceEdit == nil { return nil } @@ -1606,7 +1916,7 @@ func (handler *INOLanguageServer) cpp2inoWorkspaceEdit(logger streams.PrefixLogg for _, edit := range edits { inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, editURI, edit.Range) if err != nil { - logger(" error converting edit %s:%s: %s", editURI, edit.Range, err) + logger.Logf(" error converting edit %s:%s: %s", editURI, edit.Range, err) continue } //inoFile, inoRange := handler.sketchMapper.CppToInoRange(edit.Range) @@ -1620,11 +1930,11 @@ func (handler *INOLanguageServer) cpp2inoWorkspaceEdit(logger streams.PrefixLogg }) } } - logger(" done converting workspaceEdit") + logger.Logf(" done converting workspaceEdit") return inoWorkspaceEdit } -func (handler *INOLanguageServer) cpp2inoLocation(logger streams.PrefixLogger, cppLocation lsp.Location) (lsp.Location, error) { +func (handler *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppLocation lsp.Location) (lsp.Location, error) { inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, cppLocation.URI, cppLocation.Range) return lsp.Location{ URI: inoURI, @@ -1632,53 +1942,59 @@ func (handler *INOLanguageServer) cpp2inoLocation(logger streams.PrefixLogger, c }, err } -func (handler *INOLanguageServer) cpp2inoDocumentHighlight(logger streams.PrefixLogger, cppHighlight *lsp.DocumentHighlight, cppURI lsp.DocumentURI) (*lsp.DocumentHighlight, error) { +func (handler *INOLanguageServer) cpp2inoDocumentHighlight(logger jsonrpc.FunctionLogger, cppHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, error) { _, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppHighlight.Range) if err != nil { - return nil, err + return lsp.DocumentHighlight{}, err } - return &lsp.DocumentHighlight{ + return lsp.DocumentHighlight{ Kind: cppHighlight.Kind, Range: inoRange, }, nil } -func (handler *INOLanguageServer) cpp2inoTextEdits(logger streams.PrefixLogger, cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { - res := map[lsp.DocumentURI][]lsp.TextEdit{} +func (handler *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { + logger.Logf("%s cpp/textEdit (%d elements)", cppURI, len(cppEdits)) + allInoEdits := map[lsp.DocumentURI][]lsp.TextEdit{} for _, cppEdit := range cppEdits { + logger.Logf(" > %s -> %s", cppEdit.Range, strconv.Quote(cppEdit.NewText)) inoURI, inoEdit, err := handler.cpp2inoTextEdit(logger, cppURI, cppEdit) if err != nil { return nil, err } - inoEdits, ok := res[inoURI] - if !ok { - inoEdits = []lsp.TextEdit{} + allInoEdits[inoURI] = append(allInoEdits[inoURI], inoEdit) + } + + logger.Logf("converted to:") + + for inoURI, inoEdits := range allInoEdits { + logger.Logf("-> %s ino/textEdit (%d elements)", inoURI, len(inoEdits)) + for _, inoEdit := range inoEdits { + logger.Logf(" > %s -> %s", inoEdit.Range, strconv.Quote(inoEdit.NewText)) } - inoEdits = append(inoEdits, inoEdit) - res[inoURI] = inoEdits } - return res, nil + return allInoEdits, nil } -func (handler *INOLanguageServer) cpp2inoTextEdit(logger streams.PrefixLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { +func (handler *INOLanguageServer) cpp2inoTextEdit(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppEdit.Range) inoEdit := cppEdit inoEdit.Range = inoRange return inoURI, inoEdit, err } -func (handler *INOLanguageServer) cpp2inoDocumentSymbols(logger streams.PrefixLogger, cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { +func (handler *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.FunctionLogger, cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { inoRequested := inoRequestedURI.AsPath().String() - logger(" filtering for requested ino file: %s", inoRequested) + logger.Logf(" filtering for requested ino file: %s", inoRequested) if inoRequestedURI.Ext() != ".ino" || len(cppSymbols) == 0 { return cppSymbols } inoSymbols := []lsp.DocumentSymbol{} for _, symbol := range cppSymbols { - logger(" > convert %s %s", symbol.Kind, symbol.Range) + logger.Logf(" > convert %s %s", symbol.Kind, symbol.Range) if handler.sketchMapper.IsPreprocessedCppLine(symbol.Range.Start.Line) { - logger(" symbol is in the preprocessed section of the sketch.ino.cpp") + logger.Logf(" symbol is in the preprocessed section of the sketch.ino.cpp") continue } @@ -1686,14 +2002,14 @@ func (handler *INOLanguageServer) cpp2inoDocumentSymbols(logger streams.PrefixLo inoSelectionURI, inoSelectionRange := handler.sketchMapper.CppToInoRange(symbol.SelectionRange) if inoFile != inoSelectionURI { - logger(" ERROR: symbol range and selection belongs to different URI!") - logger(" symbol %s != selection %s", symbol.Range, symbol.SelectionRange) - logger(" %s:%s != %s:%s", inoFile, inoRange, inoSelectionURI, inoSelectionRange) + logger.Logf(" ERROR: symbol range and selection belongs to different URI!") + logger.Logf(" symbol %s != selection %s", symbol.Range, symbol.SelectionRange) + logger.Logf(" %s:%s != %s:%s", inoFile, inoRange, inoSelectionURI, inoSelectionRange) continue } if inoFile != inoRequested { - logger(" skipping symbol related to %s", inoFile) + logger.Logf(" skipping symbol related to %s", inoFile) continue } @@ -1736,7 +2052,7 @@ func (handler *INOLanguageServer) cpp2inoSymbolInformation(syms []lsp.SymbolInfo // return symbols } -func (handler *INOLanguageServer) cpp2inoDiagnostics(logger streams.PrefixLogger, cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { +func (handler *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { inoDiagsParam := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} cppURI := cppDiags.URI @@ -1794,7 +2110,7 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger streams.PrefixLogger inoDiagCode == "undeclared_var_use" || inoDiagCode == "ovl_no_viable_function_in_call" || inoDiagCode == "pp_file_not_found" { - handler.buildSketchSymbolsCheck = true + handler.queueCheckCppDocumentSymbols() } } } @@ -1807,164 +2123,52 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger streams.PrefixLogger return inoDiagParams, nil } -// HandleRequestFromClangd handles a notification message received from clangd. -func (handler *INOLanguageServer) HandleNotificationFromClangd(ctx context.Context, logger streams.PrefixLogger, method string, paramsRaw json.RawMessage) { - defer streams.CatchAndLogPanic() - - params, err := lsp.DecodeServerNotificationParams(method, paramsRaw) - if err != nil { - logger("error parsing clang message:", err) - return - } - if params == nil { - // passthrough - logger("IDE <-- LS NOTIF %s: passing through message", method) - if err := handler.IDEConn.SendNotification(method, paramsRaw); err != nil { - logger("Error sending notification to IDE: " + err.Error()) - } - return - } - - // method: "$/progress" - if progress, ok := params.(lsp.ProgressParams); ok { - var token string - if err := json.Unmarshal(progress.Token, &token); err != nil { - logger("error decoding progess token: %s", err) - return - } - switch value := progress.TryToDecodeWellKnownValues().(type) { - case lsp.WorkDoneProgressBegin: - // logger("report %s %v", id, value) - handler.progressHandler.Begin(token, &value) - case lsp.WorkDoneProgressReport: - // logger("report %s %v", id, value) - handler.progressHandler.Report(token, &value) - case lsp.WorkDoneProgressEnd: - // logger("end %s %v", id, value) - handler.progressHandler.End(token, &value) - default: - logger("error unsupported $/progress: " + string(progress.Value)) - } - return - } - - // Default to read lock - handler.readLock(logger, false) - defer handler.readUnlock(logger) - - switch p := params.(type) { - case *lsp.PublishDiagnosticsParams: - // "textDocument/publishDiagnostics" - logger("publishDiagnostics(%s):", p.URI) - for _, diag := range p.Diagnostics { - logger("> %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(logger, p) - if err != nil { - logger(" Error converting diagnostics to .ino: %s", err) - return - } - - // Push back to IDE the converted diagnostics - for _, inoDiag := range inoDiagnostics { - logger("to IDE: publishDiagnostics(%s):", inoDiag.URI) - for _, diag := range inoDiag.Diagnostics { - logger("> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) - } - method := "textDocument/publishDiagnostics" - logger("IDE <-- LS NOTIF %s:", method) - if err := handler.IDEConn.SendNotification(method, lsp.EncodeMessage(inoDiag)); err != nil { - logger(" Error sending diagnostics to IDE: %s", err) - return - } - } - return - } - if err != nil { - logger("From clangd: Method:", method, "Error:", err) - return - } - - logger("IDE <-- LS NOTIF %s:", method) - if err := handler.IDEConn.SendNotification(method, lsp.EncodeMessage(params)); err != nil { - logger("Error sending notification to IDE: " + err.Error()) - } -} - -// HandleRequestFromClangd handles a request message received from clangd. -func (handler *INOLanguageServer) HandleRequestFromClangd(ctx context.Context, logger streams.PrefixLogger, - method string, paramsRaw json.RawMessage, - respCallback func(result json.RawMessage, err *jsonrpc.ResponseError), -) { - defer streams.CatchAndLogPanic() - - // n := atomic.AddInt64(&handler.clangdMessageCount, 1) - // prefix := fmt.Sprintf("CLG <-- %s %v ", method, n) - - params, err := lsp.DecodeServerRequestParams(method, paramsRaw) - if err != nil { - logger("Error parsing clang message: %v", err) - respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) - return - } - - if method == "window/workDoneProgress/create" { - // server initiated progress - var createReq lsp.WorkDoneProgressCreateParams - if err := json.Unmarshal(paramsRaw, &createReq); err != nil { - logger("error decoding window/workDoneProgress/create: %v", err) - respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) - return - } - - var token string - if err := json.Unmarshal(createReq.Token, &token); err != nil { - logger("error decoding progess token: %s", err) - respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) - return - } - handler.progressHandler.Create(token) - respCallback(lsp.EncodeMessage(struct{}{}), nil) - return - } - - // Default to read lock - handler.readLock(logger, false) - defer handler.readUnlock(logger) - - switch p := params.(type) { - case *lsp.ApplyWorkspaceEditParams: - // "workspace/applyEdit" - p.Edit = *handler.cpp2inoWorkspaceEdit(logger, &p.Edit) - } - if err != nil { - logger("From clangd: Method: %s, Error: %v", method, err) - respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) - return - } - - respRaw := lsp.EncodeMessage(params) - if params == nil { - // passthrough - logger("passing through message") - respRaw = paramsRaw - } - - logger("IDE <-- LS REQ %s", method) - resp, respErr, err := handler.IDEConn.SendRequest(ctx, method, respRaw) - if err != nil { - logger("Error sending request to IDE:", err) - respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) - return - } - logger("IDE --> LS REQ %s", method) - respCallback(resp, respErr) -} - -func (handler *INOLanguageServer) createClangdFormatterConfig(logger streams.PrefixLogger, cppuri lsp.DocumentURI) (func(), error) { +// // HandleRequestFromClangd handles a request message received from clangd. +// func (handler *INOLanguageServer) HandleRequestFromClangd(ctx context.Context, logger jsonrpc.FunctionLogger, +// method string, paramsRaw json.RawMessage, +// respCallback func(result json.RawMessage, err *jsonrpc.ResponseError), +// ) { +// // n := atomic.AddInt64(&handler.clangdMessageCount, 1) +// // prefix := fmt.Sprintf("CLG <-- %s %v ", method, n) +// params, err := lsp.DecodeServerRequestParams(method, paramsRaw) +// if err != nil { +// logger.Logf("Error parsing clang message: %v", err) +// respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) +// return +// } +// panic("unimplemented") +// // Default to read lock +// handler.readLock(logger, false) +// defer handler.readUnlock(logger) +// switch p := params.(type) { +// case *lsp.ApplyWorkspaceEditParams: +// // "workspace/applyEdit" +// p.Edit = *handler.cpp2inoWorkspaceEdit(logger, &p.Edit) +// } +// if err != nil { +// logger.Logf("From clangd: Method: %s, Error: %v", method, err) +// respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) +// return +// } +// // respRaw := lsp.EncodeMessage(params) +// // if params == nil { +// // // passthrough +// // logger.Logf("passing through message") +// // respRaw = paramsRaw +// // } + +// // logger.Logf("IDE <-- LS REQ %s", method) +// // resp, respErr, err := handler.IDEConn.SendRequest(ctx, method, respRaw) +// // if err != nil { +// // logger.Logf("Error sending request to IDE:", err) +// // respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) +// // return +// // } +// // logger.Logf("IDE --> LS REQ %s", method) +// // respCallback(resp, respErr) +// } + +func (handler *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 @@ -2119,9 +2323,9 @@ WhitespaceSensitiveMacros: [] try := func(conf *paths.Path) bool { if c, err := conf.ReadFile(); err != nil { - logger(" error reading custom formatter config file %s: %s", conf, err) + logger.Logf(" error reading custom formatter config file %s: %s", conf, err) } else { - logger(" using custom formatter config file %s", conf) + logger.Logf(" using custom formatter config file %s", conf) config = string(c) } return true @@ -2142,25 +2346,20 @@ WhitespaceSensitiveMacros: [] targetFile = targetFile.Join(".clang-format") cleanup := func() { targetFile.Remove() - logger(" formatter config cleaned") + logger.Logf(" formatter config cleaned") } - logger(" writing formatter config in: %s", targetFile) + logger.Logf(" writing formatter config in: %s", targetFile) err := targetFile.WriteFile([]byte(config)) return cleanup, err } -func (handler *INOLanguageServer) showMessage(logger streams.PrefixLogger, msgType lsp.MessageType, message string) { - defer streams.CatchAndLogPanic() - +func (handler *INOLanguageServer) showMessage(logger jsonrpc.FunctionLogger, msgType lsp.MessageType, message string) { params := lsp.ShowMessageParams{ Type: msgType, Message: message, } - - method := "window/showMessage" - logger("IDE <-- LS NOTIF %s:", method) - if err := handler.IDEConn.SendNotification(method, lsp.EncodeMessage(params)); err != nil { - // TODO: Log? + if err := handler.IDEConn.WindowShowMessage(¶ms); err != nil { + logger.Logf("error sending showMessage notification: %s", err) } } diff --git a/handler/lsp_logger.go b/handler/lsp_logger.go new file mode 100644 index 0000000..661eaf0 --- /dev/null +++ b/handler/lsp_logger.go @@ -0,0 +1,67 @@ +package handler + +import ( + "fmt" + "log" + + "github.com/fatih/color" + "go.bug.st/json" + "go.bug.st/lsp/jsonrpc" +) + +type LSPLogger struct { + IncomingPrefix, OutgoingPrefix string +} + +func (l *LSPLogger) LogOutgoingRequest(id string, method string, params json.RawMessage) { + log.Print(color.HiGreenString("%s REQU %s %s", l.OutgoingPrefix, method, id)) +} +func (l *LSPLogger) LogOutgoingCancelRequest(id string) { + log.Print(color.GreenString("%s CANCEL %s", l.OutgoingPrefix, id)) +} +func (l *LSPLogger) LogIncomingResponse(id string, method string, resp json.RawMessage, respErr *jsonrpc.ResponseError) { + log.Print(color.GreenString("%s RESP %s %s", l.IncomingPrefix, method, id)) +} +func (l *LSPLogger) LogOutgoingNotification(method string, params json.RawMessage) { + log.Print(color.HiGreenString("%s NOTIF %s", l.OutgoingPrefix, method)) +} + +func (l *LSPLogger) LogIncomingRequest(id string, method string, params json.RawMessage) jsonrpc.FunctionLogger { + spaces := " " + log.Print(color.HiRedString(fmt.Sprintf("%s REQU %s %s", l.IncomingPrefix, method, id))) + return &LSPFunctionLogger{ + colorFunc: color.HiRedString, + prefix: fmt.Sprintf("%s %s %s", spaces[:len(l.IncomingPrefix)], method, id), + } +} +func (l *LSPLogger) LogIncomingCancelRequest(id string) { + log.Print(color.RedString("%s CANCEL %s", l.IncomingPrefix, id)) +} +func (l *LSPLogger) LogOutgoingResponse(id string, method string, resp json.RawMessage, respErr *jsonrpc.ResponseError) { + log.Print(color.RedString("%s RESP %s %s", l.OutgoingPrefix, method, id)) +} +func (l *LSPLogger) LogIncomingNotification(method string, params json.RawMessage) jsonrpc.FunctionLogger { + spaces := " " + log.Print(color.HiRedString(fmt.Sprintf("%s NOTIF %s", l.IncomingPrefix, method))) + return &LSPFunctionLogger{ + colorFunc: color.HiRedString, + 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/handler/progress.go b/handler/progress.go index f2041aa..869ce2b 100644 --- a/handler/progress.go +++ b/handler/progress.go @@ -6,13 +6,11 @@ import ( "sync" "github.com/arduino/arduino-language-server/streams" - "go.bug.st/json" "go.bug.st/lsp" - "go.bug.st/lsp/jsonrpc" ) type ProgressProxyHandler struct { - conn *jsonrpc.Connection + conn *lsp.Server mux sync.Mutex actionRequiredCond *sync.Cond proxies map[string]*progressProxy @@ -36,19 +34,20 @@ type progressProxy struct { endReq *lsp.WorkDoneProgressEnd } -func NewProgressProxy(conn *jsonrpc.Connection) *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() @@ -74,10 +73,9 @@ func (p *ProgressProxyHandler) handleProxy(id string, proxy *progressProxy) { switch proxy.currentStatus { case progressProxyNew: p.mux.Unlock() - _, respErr, err := p.conn.SendRequest(context.Background(), "window/workDoneProgress/create", - lsp.EncodeMessage(&lsp.WorkDoneProgressCreateParams{ - Token: lsp.EncodeMessage(id), - })) + 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) break @@ -90,11 +88,10 @@ func (p *ProgressProxyHandler) handleProxy(id string, proxy *progressProxy) { proxy.currentStatus = progressProxyCreated case progressProxyCreated: - notif, _ := json.Marshal(lsp.ProgressParams{ + err := p.conn.Progress(&lsp.ProgressParams{ Token: lsp.EncodeMessage(id), Value: lsp.EncodeMessage(proxy.beginReq), }) - err := p.conn.SendNotification("$/progress", notif) proxy.beginReq = nil if err != nil { @@ -105,11 +102,10 @@ func (p *ProgressProxyHandler) handleProxy(id string, proxy *progressProxy) { case progressProxyBegin: if proxy.requiredStatus == progressProxyReport { - notif, _ := json.Marshal(&lsp.ProgressParams{ + err := p.conn.Progress(&lsp.ProgressParams{ Token: lsp.EncodeMessage(id), Value: lsp.EncodeMessage(proxy.reportReq), }) - err := p.conn.SendNotification("$/progress", notif) proxy.reportReq = nil if err != nil { @@ -119,11 +115,10 @@ func (p *ProgressProxyHandler) handleProxy(id string, proxy *progressProxy) { } } else if proxy.requiredStatus == progressProxyEnd { - notif, _ := json.Marshal(&lsp.ProgressParams{ + err := p.conn.Progress(&lsp.ProgressParams{ Token: lsp.EncodeMessage(id), Value: lsp.EncodeMessage(proxy.endReq), }) - err := p.conn.SendNotification("$/progress", notif) proxy.endReq = nil if err != nil { 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/streams/jsonrpc2.go b/streams/jsonrpc2.go deleted file mode 100644 index 9aeda4a..0000000 --- a/streams/jsonrpc2.go +++ /dev/null @@ -1,88 +0,0 @@ -package streams - -import ( - "fmt" - "log" - "sync/atomic" - - "github.com/fatih/color" - "go.bug.st/json" - "go.bug.st/lsp/jsonrpc" -) - -var index int64 - -type PrefixLogger func(format string, a ...interface{}) - -func NewPrefixLogger(col *color.Color, prefix string) PrefixLogger { - return func(format string, a ...interface{}) { - log.Print(col.Sprintf(prefix+format, a...)) - } -} - -type JsonRPCLogger struct { - client string - server string -} - -var clColor = color.New(color.FgHiRed) -var srvColor = color.New(color.FgHiGreen) - -func NewJsonRPCLogger(client, server string) *JsonRPCLogger { - color.NoColor = false - return &JsonRPCLogger{ - client: client + " --> " + server + " ", - server: client + " <-- " + server + " ", - } -} - -func empty(s string) string { - return " "[:len(s)] -} - -func (l *JsonRPCLogger) LogClientRequest(method string, params json.RawMessage) (PrefixLogger, int64) { - id := atomic.AddInt64(&index, 1) - prefix := fmt.Sprintf("REQ %s %v: ", method, id) - dec := "" - log.Print(clColor.Sprintf(l.client+prefix+"%s", dec)) - return NewPrefixLogger(clColor, empty(l.client)+prefix), id -} - -func (l *JsonRPCLogger) LogClientResponse(id int64, method string, params json.RawMessage, err *jsonrpc.ResponseError) { - dec := "" - if err != nil { - dec += fmt.Sprintf("ERROR %v", err.AsError()) - } - log.Print(clColor.Sprintf(l.client+"RESP %s %v: %s", method, id, dec)) -} - -func (l *JsonRPCLogger) LogClientNotification(method string, params json.RawMessage) PrefixLogger { - prefix := fmt.Sprintf("NOTIF %s: ", method) - dec := "" - log.Print(clColor.Sprintf(l.client+prefix+"%s", dec)) - return NewPrefixLogger(clColor, empty(l.client)+prefix) -} - -func (l *JsonRPCLogger) LogServerRequest(method string, params json.RawMessage) (PrefixLogger, int64) { - id := atomic.AddInt64(&index, 1) - - prefix := fmt.Sprintf("REQ %s %v: ", method, id) - dec := "" - log.Print(srvColor.Sprintf(l.server+prefix+"%s", dec)) - return NewPrefixLogger(srvColor, empty(l.server)+prefix), id -} - -func (l *JsonRPCLogger) LogServerResponse(id int64, method string, params json.RawMessage, err *jsonrpc.ResponseError) { - dec := "" - if err != nil { - dec += fmt.Sprintf("ERROR %v", err.AsError()) - } - log.Print(srvColor.Sprintf(l.server+"RESP %s %v: %s", method, id, dec)) -} - -func (l *JsonRPCLogger) LogServerNotification(method string, params json.RawMessage) PrefixLogger { - prefix := fmt.Sprintf("NOTIF %s: ", method) - dec := "" - log.Print(srvColor.Sprintf(l.server+prefix+"%s", dec)) - return NewPrefixLogger(srvColor, empty(l.server)+prefix) -} From 07011ac600f34066c3e587640e45c466dd047a2f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 4 Nov 2021 12:15:58 +0100 Subject: [PATCH 06/76] Reorganized repository --- {handler => ls}/builder.go | 2 +- handler/handler.go => ls/ls.go | 774 +++++++++--------- .../lsp_client_clangd.go | 54 +- {handler => ls}/lsp_logger.go | 2 +- {handler => ls}/progress.go | 2 +- main.go | 8 +- {handler/sourcemapper => sourcemapper}/ino.go | 2 +- .../sourcemapper => sourcemapper}/ino_test.go | 0 {handler/textutils => textutils}/textutils.go | 0 .../textutils => textutils}/textutils_test.go | 0 10 files changed, 422 insertions(+), 422 deletions(-) rename {handler => ls}/builder.go (99%) rename handler/handler.go => ls/ls.go (64%) rename handler/clangd_client.go => ls/lsp_client_clangd.go (50%) rename {handler => ls}/lsp_logger.go (99%) rename {handler => ls}/progress.go (99%) rename {handler/sourcemapper => sourcemapper}/ino.go (99%) rename {handler/sourcemapper => sourcemapper}/ino_test.go (100%) rename {handler/textutils => textutils}/textutils.go (100%) rename {handler/textutils => textutils}/textutils_test.go (100%) diff --git a/handler/builder.go b/ls/builder.go similarity index 99% rename from handler/builder.go rename to ls/builder.go index 972b627..0a5d8fe 100644 --- a/handler/builder.go +++ b/ls/builder.go @@ -1,4 +1,4 @@ -package handler +package ls import ( "bytes" diff --git a/handler/handler.go b/ls/ls.go similarity index 64% rename from handler/handler.go rename to ls/ls.go index 3fda7ba..283805f 100644 --- a/handler/handler.go +++ b/ls/ls.go @@ -1,4 +1,4 @@ -package handler +package ls import ( "bytes" @@ -15,9 +15,9 @@ import ( "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/sourcemapper" "github.com/arduino/arduino-language-server/streams" + "github.com/arduino/arduino-language-server/textutils" "github.com/arduino/go-paths-helper" "github.com/fatih/color" "github.com/pkg/errors" @@ -46,7 +46,7 @@ func Setup(cliPath, cliConfigPath, clangdPath, formatFilePath string, _enableLog // INOLanguageServer is a JSON-RPC handler that delegates messages to clangd. type INOLanguageServer struct { IDEConn *lsp.Server - Clangd *ClangdClient + Clangd *ClangdLSPClient progressHandler *ProgressProxyHandler @@ -89,55 +89,55 @@ type Board struct { var yellow = color.New(color.FgHiYellow) -func (handler *INOLanguageServer) writeLock(logger jsonrpc.FunctionLogger, requireClangd bool) { - handler.dataMux.Lock() +func (ls *INOLanguageServer) writeLock(logger jsonrpc.FunctionLogger, requireClangd bool) { + ls.dataMux.Lock() logger.Logf(yellow.Sprintf("write-locked")) if requireClangd { - handler.waitClangdStart(logger) + ls.waitClangdStart(logger) } } -func (handler *INOLanguageServer) writeUnlock(logger jsonrpc.FunctionLogger) { +func (ls *INOLanguageServer) writeUnlock(logger jsonrpc.FunctionLogger) { logger.Logf(yellow.Sprintf("write-unlocked")) - handler.dataMux.Unlock() + ls.dataMux.Unlock() } -func (handler *INOLanguageServer) readLock(logger jsonrpc.FunctionLogger, requireClangd bool) { - handler.dataMux.RLock() +func (ls *INOLanguageServer) readLock(logger jsonrpc.FunctionLogger, requireClangd bool) { + ls.dataMux.RLock() logger.Logf(yellow.Sprintf("read-locked")) - for requireClangd && handler.Clangd == nil { + 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...")) - handler.dataMux.RUnlock() + ls.dataMux.RUnlock() - handler.writeLock(logger, true) - handler.writeUnlock(logger) + ls.writeLock(logger, true) + ls.writeUnlock(logger) - handler.dataMux.RLock() + ls.dataMux.RLock() logger.Logf(yellow.Sprintf("testing again if clang started: read-locked...")) } } -func (handler *INOLanguageServer) readUnlock(logger jsonrpc.FunctionLogger) { +func (ls *INOLanguageServer) readUnlock(logger jsonrpc.FunctionLogger) { logger.Logf(yellow.Sprintf("read-unlocked")) - handler.dataMux.RUnlock() + ls.dataMux.RUnlock() } -func (handler *INOLanguageServer) waitClangdStart(logger jsonrpc.FunctionLogger) error { - if handler.Clangd != nil { +func (ls *INOLanguageServer) waitClangdStart(logger jsonrpc.FunctionLogger) error { + if ls.Clangd != nil { return nil } logger.Logf("(throttled: waiting for clangd)") logger.Logf(yellow.Sprintf("unlocked (waiting clangd)")) - handler.clangdStarted.Wait() + ls.clangdStarted.Wait() logger.Logf(yellow.Sprintf("locked (waiting clangd)")) - if handler.Clangd == nil { + if ls.Clangd == nil { logger.Logf("clangd startup failed: aborting call") return errors.New("could not start clangd, aborted") } @@ -210,9 +210,9 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLa return handler } -func (handler *INOLanguageServer) queueLoadCppDocumentSymbols() { +func (ls *INOLanguageServer) queueLoadCppDocumentSymbols() { select { - case handler.buildSketchSymbolsLoad <- true: + case ls.buildSketchSymbolsLoad <- true: default: } } @@ -311,44 +311,44 @@ func (handler *INOLanguageServer) Initialize(ctx context.Context, logger jsonrpc return resp, nil } -func (handler *INOLanguageServer) Shutdown(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { +func (ls *INOLanguageServer) Shutdown(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceSymbol(context.Context, jsonrpc.FunctionLogger, *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) WorkspaceSymbol(context.Context, jsonrpc.FunctionLogger, *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceExecuteCommand(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ExecuteCommandParams) (json.RawMessage, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) WorkspaceExecuteCommand(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ExecuteCommandParams) (json.RawMessage, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) // return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"} - // err = handler.ino2cppExecuteCommand(inoParams) + // err = ls.ino2cppExecuteCommand(inoParams) panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceWillCreateFiles(context.Context, jsonrpc.FunctionLogger, *lsp.CreateFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) WorkspaceWillCreateFiles(context.Context, jsonrpc.FunctionLogger, *lsp.CreateFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceWillRenameFiles(context.Context, jsonrpc.FunctionLogger, *lsp.RenameFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) WorkspaceWillRenameFiles(context.Context, jsonrpc.FunctionLogger, *lsp.RenameFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceWillDeleteFiles(context.Context, jsonrpc.FunctionLogger, *lsp.DeleteFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) WorkspaceWillDeleteFiles(context.Context, jsonrpc.FunctionLogger, *lsp.DeleteFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentWillSaveWaitUntil(context.Context, jsonrpc.FunctionLogger, *lsp.WillSaveTextDocumentParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentWillSaveWaitUntil(context.Context, jsonrpc.FunctionLogger, *lsp.WillSaveTextDocumentParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentCompletion(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CompletionParams) (*lsp.CompletionList, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentCompletion(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CompletionParams) (*lsp.CompletionList, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) logger.Logf("--> completion(%s)\n", inoParams.TextDocument) - cppTextDocPositionParams, err := handler.ino2cppTextDocumentPositionParams(logger, inoParams.TextDocumentPositionParams) + cppTextDocPositionParams, err := ls.ino2cppTextDocumentPositionParams(logger, inoParams.TextDocumentPositionParams) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -364,10 +364,10 @@ func (handler *INOLanguageServer) TextDocumentCompletion(ctx context.Context, lo return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - clangResp, clangErr, err := handler.Clangd.conn.TextDocumentCompletion(ctx, cppParams) + clangResp, clangErr, err := ls.Clangd.conn.TextDocumentCompletion(ctx, cppParams) if err != nil { logger.Logf("clangd connection error: %v", err) - handler.Close() + ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if clangErr != nil { @@ -375,14 +375,14 @@ func (handler *INOLanguageServer) TextDocumentCompletion(ctx context.Context, lo return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} } - cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp) + cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(ls.buildSketchCpp) inoResp := *clangResp inoItems := make([]lsp.CompletionItem, 0) for _, item := range clangResp.Items { if !strings.HasPrefix(item.InsertText, "_") { if cppToIno && item.TextEdit != nil { - _, item.TextEdit.Range = handler.sketchMapper.CppToInoRange(item.TextEdit.Range) + _, item.TextEdit.Range = ls.sketchMapper.CppToInoRange(item.TextEdit.Range) } inoItems = append(inoItems, item) } @@ -392,19 +392,19 @@ func (handler *INOLanguageServer) TextDocumentCompletion(ctx context.Context, lo return &inoResp, nil } -func (handler *INOLanguageServer) CompletionItemResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CompletionItem) (*lsp.CompletionItem, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) CompletionItemResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CompletionItem) (*lsp.CompletionItem, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentHover(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.HoverParams) (*lsp.Hover, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentHover(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.HoverParams) (*lsp.Hover, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) inoURI := inoParams.TextDocument.URI inoTextDocPosition := inoParams.TextDocumentPositionParams logger.Logf("--> hover(%s)\n", inoTextDocPosition) - cppTextDocPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocPosition) + cppTextDocPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocPosition) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -414,10 +414,10 @@ func (handler *INOLanguageServer) TextDocumentHover(ctx context.Context, logger cppParams := &lsp.HoverParams{ TextDocumentPositionParams: cppTextDocPosition, } - clangResp, clangErr, err := handler.Clangd.conn.TextDocumentHover(ctx, cppParams) + clangResp, clangErr, err := ls.Clangd.conn.TextDocumentHover(ctx, cppParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if clangErr != nil { @@ -434,23 +434,23 @@ func (handler *INOLanguageServer) TextDocumentHover(ctx context.Context, logger // if len(clangResp.Contents.Value) == 0 { // return nil // } - cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(handler.buildSketchCpp) + cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(ls.buildSketchCpp) if cppToIno { - _, inoRange := handler.sketchMapper.CppToInoRange(*clangResp.Range) + _, inoRange := ls.sketchMapper.CppToInoRange(*clangResp.Range) inoResp.Range = &inoRange } logger.Logf("<-- hover(%s)", strconv.Quote(inoResp.Contents.Value)) return &inoResp, nil } -func (handler *INOLanguageServer) TextDocumentSignatureHelp(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.SignatureHelpParams) (*lsp.SignatureHelp, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentSignatureHelp(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.SignatureHelpParams) (*lsp.SignatureHelp, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) inoTextDocumentPosition := inoParams.TextDocumentPositionParams logger.Logf("%s", inoTextDocumentPosition) - cppTextDocumentPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) if err == nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -459,10 +459,10 @@ func (handler *INOLanguageServer) TextDocumentSignatureHelp(ctx context.Context, logger.Logf("-> %s", cppTextDocumentPosition) cppParams := *inoParams cppParams.TextDocumentPositionParams = cppTextDocumentPosition - cppSignatureHelp, cppErr, err := handler.Clangd.conn.TextDocumentSignatureHelp(ctx, inoParams) + cppSignatureHelp, cppErr, err := ls.Clangd.conn.TextDocumentSignatureHelp(ctx, inoParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if cppErr != nil { @@ -475,18 +475,18 @@ func (handler *INOLanguageServer) TextDocumentSignatureHelp(ctx context.Context, return cppSignatureHelp, nil } -func (handler *INOLanguageServer) TextDocumentDeclaration(context.Context, jsonrpc.FunctionLogger, *lsp.DeclarationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentDeclaration(context.Context, jsonrpc.FunctionLogger, *lsp.DeclarationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, p *lsp.DefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, p *lsp.DefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) inoTextDocPosition := p.TextDocumentPositionParams logger.Logf("%s", inoTextDocPosition) - cppTextDocPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocPosition) + cppTextDocPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -495,10 +495,10 @@ func (handler *INOLanguageServer) TextDocumentDefinition(ctx context.Context, lo logger.Logf("-> %s", cppTextDocPosition) cppParams := *p cppParams.TextDocumentPositionParams = cppTextDocPosition - cppLocations, cppLocationLinks, cppErr, err := handler.Clangd.conn.TextDocumentDefinition(ctx, &cppParams) + cppLocations, cppLocationLinks, cppErr, err := ls.Clangd.conn.TextDocumentDefinition(ctx, &cppParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if cppErr != nil { @@ -508,9 +508,9 @@ func (handler *INOLanguageServer) TextDocumentDefinition(ctx context.Context, lo var inoLocations []lsp.Location if cppLocations != nil { - inoLocations, err = handler.cpp2inoLocationArray(logger, cppLocations) + inoLocations, err = ls.cpp2inoLocationArray(logger, cppLocations) if err != nil { - handler.Close() + ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } } @@ -523,10 +523,10 @@ func (handler *INOLanguageServer) TextDocumentDefinition(ctx context.Context, lo return inoLocations, inoLocationLinks, nil } -func (handler *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, cppLocations []lsp.Location) ([]lsp.Location, error) { +func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, cppLocations []lsp.Location) ([]lsp.Location, error) { inoLocations := []lsp.Location{} for _, cppLocation := range cppLocations { - inoLocation, err := handler.cpp2inoLocation(logger, cppLocation) + inoLocation, err := ls.cpp2inoLocation(logger, cppLocation) if err != nil { logger.Logf("ERROR converting location %s: %s", cppLocation, err) return nil, err @@ -536,15 +536,15 @@ func (handler *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLo return inoLocations, nil } -func (handler *INOLanguageServer) TextDocumentTypeDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentTypeDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) inoTextDocumentPosition := inoParams.TextDocumentPositionParams logger.Logf("%s", inoTextDocumentPosition) // inoURI := inoTextDocumentPosition.TextDocument.URI - cppTextDocumentPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -555,10 +555,10 @@ func (handler *INOLanguageServer) TextDocumentTypeDefinition(ctx context.Context cppParams := *inoParams cppParams.TextDocumentPositionParams = cppTextDocumentPosition - cppLocations, cppLocationLinks, cppErr, err := handler.Clangd.conn.TextDocumentTypeDefinition(ctx, &cppParams) + cppLocations, cppLocationLinks, cppErr, err := ls.Clangd.conn.TextDocumentTypeDefinition(ctx, &cppParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if cppErr != nil { @@ -568,9 +568,9 @@ func (handler *INOLanguageServer) TextDocumentTypeDefinition(ctx context.Context var inoLocations []lsp.Location if cppLocations != nil { - inoLocations, err = handler.cpp2inoLocationArray(logger, cppLocations) + inoLocations, err = ls.cpp2inoLocationArray(logger, cppLocations) if err != nil { - handler.Close() + ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } } @@ -583,14 +583,14 @@ func (handler *INOLanguageServer) TextDocumentTypeDefinition(ctx context.Context return inoLocations, inoLocationLinks, nil } -func (handler *INOLanguageServer) TextDocumentImplementation(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ImplementationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentImplementation(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ImplementationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) inoTextDocumentPosition := inoParams.TextDocumentPositionParams logger.Logf("%s", inoTextDocumentPosition) - cppTextDocumentPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -599,10 +599,10 @@ func (handler *INOLanguageServer) TextDocumentImplementation(ctx context.Context logger.Logf("-> %s", cppTextDocumentPosition) cppParams := *inoParams cppParams.TextDocumentPositionParams = cppTextDocumentPosition - cppLocations, cppLocationLinks, cppErr, err := handler.Clangd.conn.TextDocumentImplementation(ctx, &cppParams) + cppLocations, cppLocationLinks, cppErr, err := ls.Clangd.conn.TextDocumentImplementation(ctx, &cppParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if cppErr != nil { @@ -612,9 +612,9 @@ func (handler *INOLanguageServer) TextDocumentImplementation(ctx context.Context var inoLocations []lsp.Location if cppLocations != nil { - inoLocations, err = handler.cpp2inoLocationArray(logger, cppLocations) + inoLocations, err = ls.cpp2inoLocationArray(logger, cppLocations) if err != nil { - handler.Close() + ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } } @@ -627,20 +627,20 @@ func (handler *INOLanguageServer) TextDocumentImplementation(ctx context.Context return inoLocations, inoLocationLinks, nil } -func (handler *INOLanguageServer) TextDocumentReferences(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ReferenceParams) ([]lsp.Location, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentReferences(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ReferenceParams) ([]lsp.Location, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) panic("unimplemented") // inoURI = p.TextDocument.URI - // _, err = handler.ino2cppTextDocumentPositionParams(logger, p.TextDocumentPositionParams) + // _, err = ls.ino2cppTextDocumentPositionParams(logger, p.TextDocumentPositionParams) } -func (handler *INOLanguageServer) TextDocumentDocumentHighlight(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentDocumentHighlight(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) inoTextDocumentPosition := inoParams.TextDocumentPositionParams - cppTextDocumentPosition, err := handler.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -649,10 +649,10 @@ func (handler *INOLanguageServer) TextDocumentDocumentHighlight(ctx context.Cont cppParams := *inoParams cppParams.TextDocumentPositionParams = cppTextDocumentPosition - cppHighlights, clangErr, err := handler.Clangd.conn.TextDocumentDocumentHighlight(ctx, &cppParams) + cppHighlights, clangErr, err := ls.Clangd.conn.TextDocumentDocumentHighlight(ctx, &cppParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if clangErr != nil { @@ -666,7 +666,7 @@ func (handler *INOLanguageServer) TextDocumentDocumentHighlight(ctx context.Cont inoHighlights := []lsp.DocumentHighlight{} for _, cppHighlight := range cppHighlights { - inoHighlight, err := handler.cpp2inoDocumentHighlight(logger, cppHighlight, cppURI) + inoHighlight, err := ls.cpp2inoDocumentHighlight(logger, cppHighlight, cppURI) if err != nil { logger.Logf("ERROR converting location %s:%s: %s", cppURI, cppHighlight.Range, err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} @@ -676,15 +676,15 @@ func (handler *INOLanguageServer) TextDocumentDocumentHighlight(ctx context.Cont return inoHighlights, nil } -func (handler *INOLanguageServer) TextDocumentDocumentSymbol(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, []lsp.SymbolInformation, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentDocumentSymbol(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, []lsp.SymbolInformation, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) inoTextDocument := inoParams.TextDocument inoURI := inoTextDocument.URI logger.Logf("--> %s") - cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -693,10 +693,10 @@ func (handler *INOLanguageServer) TextDocumentDocumentSymbol(ctx context.Context cppParams := *inoParams cppParams.TextDocument = cppTextDocument logger.Logf(" --> documentSymbol(%s)", cppTextDocument) - cppDocSymbols, cppSymbolInformation, clangErr, err := handler.Clangd.conn.TextDocumentDocumentSymbol(ctx, &cppParams) + cppDocSymbols, cppSymbolInformation, clangErr, err := ls.Clangd.conn.TextDocumentDocumentSymbol(ctx, &cppParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if clangErr != nil { @@ -707,44 +707,44 @@ func (handler *INOLanguageServer) TextDocumentDocumentSymbol(ctx context.Context var inoDocSymbols []lsp.DocumentSymbol if cppDocSymbols != nil { logger.Logf(" <-- documentSymbol(%d document symbols)", len(cppDocSymbols)) - inoDocSymbols = handler.cpp2inoDocumentSymbols(logger, cppDocSymbols, inoURI) + inoDocSymbols = ls.cpp2inoDocumentSymbols(logger, cppDocSymbols, inoURI) } var inoSymbolInformation []lsp.SymbolInformation if cppSymbolInformation != nil { logger.Logf(" <-- documentSymbol(%d symbol information)", len(cppSymbolInformation)) - inoSymbolInformation = handler.cpp2inoSymbolInformation(cppSymbolInformation) + inoSymbolInformation = ls.cpp2inoSymbolInformation(cppSymbolInformation) } return inoDocSymbols, inoSymbolInformation, nil } -func (handler *INOLanguageServer) TextDocumentCodeAction(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CodeActionParams) ([]lsp.CommandOrCodeAction, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentCodeAction(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CodeActionParams) ([]lsp.CommandOrCodeAction, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) inoTextDocument := inoParams.TextDocument inoURI := inoTextDocument.URI logger.Logf("--> codeAction(%s:%s)", inoTextDocument, inoParams.Range.Start) cppParams := *inoParams - cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } cppParams.TextDocument = cppTextDocument - if cppTextDocument.URI.AsPath().EquivalentTo(handler.buildSketchCpp) { - cppParams.Range = handler.sketchMapper.InoToCppLSPRange(inoURI, inoParams.Range) + if cppTextDocument.URI.AsPath().EquivalentTo(ls.buildSketchCpp) { + cppParams.Range = ls.sketchMapper.InoToCppLSPRange(inoURI, inoParams.Range) for i, inoDiag := range inoParams.Context.Diagnostics { - cppParams.Context.Diagnostics[i].Range = handler.sketchMapper.InoToCppLSPRange(inoURI, inoDiag.Range) + cppParams.Context.Diagnostics[i].Range = ls.sketchMapper.InoToCppLSPRange(inoURI, inoDiag.Range) } } logger.Logf(" --> codeAction(%s:%s)", cppParams.TextDocument, inoParams.Range.Start) - cppResp, cppErr, err := handler.Clangd.conn.TextDocumentCodeAction(ctx, &cppParams) + cppResp, cppErr, err := ls.Clangd.conn.TextDocumentCodeAction(ctx, &cppParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if cppErr != nil { @@ -761,10 +761,10 @@ func (handler *INOLanguageServer) TextDocumentCodeAction(ctx context.Context, lo switch i := cppItem.Get().(type) { case lsp.Command: logger.Logf(" > Command: %s", i.Title) - inoItem.Set(handler.cpp2inoCommand(logger, i)) + inoItem.Set(ls.cpp2inoCommand(logger, i)) case lsp.CodeAction: logger.Logf(" > CodeAction: %s", i.Title) - inoItem.Set(handler.cpp2inoCodeAction(logger, i, inoURI)) + inoItem.Set(ls.cpp2inoCodeAction(logger, i, inoURI)) } inoResp = append(inoResp, inoItem) } @@ -773,43 +773,43 @@ func (handler *INOLanguageServer) TextDocumentCodeAction(ctx context.Context, lo return inoResp, nil } -func (handler *INOLanguageServer) CodeActionResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CodeAction) (*lsp.CodeAction, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) CodeActionResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CodeAction) (*lsp.CodeAction, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentCodeLens(context.Context, jsonrpc.FunctionLogger, *lsp.CodeLensParams) ([]lsp.CodeLens, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentCodeLens(context.Context, jsonrpc.FunctionLogger, *lsp.CodeLensParams) ([]lsp.CodeLens, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) CodeLensResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CodeLens) (*lsp.CodeLens, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) CodeLensResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CodeLens) (*lsp.CodeLens, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentDocumentLink(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentLinkParams) ([]lsp.DocumentLink, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentDocumentLink(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentLinkParams) ([]lsp.DocumentLink, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) DocumentLinkResolve(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentLink) (*lsp.DocumentLink, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) DocumentLinkResolve(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentLink) (*lsp.DocumentLink, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentDocumentColor(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentColorParams) ([]lsp.ColorInformation, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentDocumentColor(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentColorParams) ([]lsp.ColorInformation, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentColorPresentation(context.Context, jsonrpc.FunctionLogger, *lsp.ColorPresentationParams) ([]lsp.ColorPresentation, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentColorPresentation(context.Context, jsonrpc.FunctionLogger, *lsp.ColorPresentationParams) ([]lsp.ColorPresentation, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) inoTextDocument := inoParams.TextDocument inoURI := inoTextDocument.URI logger.Logf("--> formatting(%s)", inoTextDocument) - cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -818,7 +818,7 @@ func (handler *INOLanguageServer) TextDocumentFormatting(ctx context.Context, lo logger.Logf(" --> formatting(%s)", cppTextDocument) - if cleanup, e := handler.createClangdFormatterConfig(logger, cppURI); e != nil { + if cleanup, e := ls.createClangdFormatterConfig(logger, cppURI); e != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } else { @@ -827,10 +827,10 @@ func (handler *INOLanguageServer) TextDocumentFormatting(ctx context.Context, lo cppParams := *inoParams cppParams.TextDocument = cppTextDocument - cppEdits, clangErr, err := handler.Clangd.conn.TextDocumentFormatting(ctx, &cppParams) + cppEdits, clangErr, err := ls.Clangd.conn.TextDocumentFormatting(ctx, &cppParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if clangErr != nil { @@ -842,7 +842,7 @@ func (handler *INOLanguageServer) TextDocumentFormatting(ctx context.Context, lo return nil, nil } - sketchEdits, err := handler.cpp2inoTextEdits(logger, cppURI, cppEdits) + sketchEdits, err := ls.cpp2inoTextEdits(logger, cppURI, cppEdits) if err != nil { logger.Logf("ERROR converting textEdits: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -854,31 +854,31 @@ func (handler *INOLanguageServer) TextDocumentFormatting(ctx context.Context, lo } } -func (handler *INOLanguageServer) TextDocumentRangeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentRangeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) // Method: "textDocument/rangeFormatting" logger.Logf("%s", inoParams.TextDocument) inoURI := inoParams.TextDocument.URI - cppParams, err := handler.ino2cppDocumentRangeFormattingParams(logger, inoParams) + cppParams, err := ls.ino2cppDocumentRangeFormattingParams(logger, inoParams) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } cppURI := cppParams.TextDocument.URI logger.Logf("-> %s", cppParams.TextDocument) - if cleanup, e := handler.createClangdFormatterConfig(logger, cppURI); e != nil { + if cleanup, e := ls.createClangdFormatterConfig(logger, cppURI); e != nil { logger.Logf("cannot create formatter config file: %v", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } else { defer cleanup() } - cppEdits, clangErr, err := handler.Clangd.conn.TextDocumentRangeFormatting(ctx, cppParams) + cppEdits, clangErr, err := ls.Clangd.conn.TextDocumentRangeFormatting(ctx, cppParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) - handler.Close() + ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } if clangErr != nil { @@ -891,7 +891,7 @@ func (handler *INOLanguageServer) TextDocumentRangeFormatting(ctx context.Contex return nil, nil } - sketchEdits, err := handler.cpp2inoTextEdits(logger, cppURI, cppEdits) + sketchEdits, err := ls.cpp2inoTextEdits(logger, cppURI, cppEdits) if err != nil { logger.Logf("ERROR converting textEdits: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -903,124 +903,124 @@ func (handler *INOLanguageServer) TextDocumentRangeFormatting(ctx context.Contex } } -func (handler *INOLanguageServer) TextDocumentOnTypeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentOnTypeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentOnTypeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentOnTypeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) // return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"} // inoURI = p.TextDocument.URI - // err = handler.ino2cppDocumentOnTypeFormattingParams(p) + // err = ls.ino2cppDocumentOnTypeFormattingParams(p) panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentRename(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.RenameParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) TextDocumentRename(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.RenameParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) // return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"} // inoURI = p.TextDocument.URI - // err = handler.ino2cppRenameParams(p) + // err = ls.ino2cppRenameParams(p) panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentFoldingRange(context.Context, jsonrpc.FunctionLogger, *lsp.FoldingRangeParams) ([]lsp.FoldingRange, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentFoldingRange(context.Context, jsonrpc.FunctionLogger, *lsp.FoldingRangeParams) ([]lsp.FoldingRange, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentSelectionRange(context.Context, jsonrpc.FunctionLogger, *lsp.SelectionRangeParams) ([]lsp.SelectionRange, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentSelectionRange(context.Context, jsonrpc.FunctionLogger, *lsp.SelectionRangeParams) ([]lsp.SelectionRange, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentPrepareCallHierarchy(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyPrepareParams) ([]lsp.CallHierarchyItem, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentPrepareCallHierarchy(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyPrepareParams) ([]lsp.CallHierarchyItem, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) CallHierarchyIncomingCalls(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyIncomingCallsParams) ([]lsp.CallHierarchyIncomingCall, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) CallHierarchyIncomingCalls(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyIncomingCallsParams) ([]lsp.CallHierarchyIncomingCall, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) CallHierarchyOutgoingCalls(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyOutgoingCallsParams) ([]lsp.CallHierarchyOutgoingCall, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) CallHierarchyOutgoingCalls(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyOutgoingCallsParams) ([]lsp.CallHierarchyOutgoingCall, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentSemanticTokensFull(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentSemanticTokensFull(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentSemanticTokensFullDelta(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensDeltaParams) (*lsp.SemanticTokens, *lsp.SemanticTokensDelta, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentSemanticTokensFullDelta(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensDeltaParams) (*lsp.SemanticTokens, *lsp.SemanticTokensDelta, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentSemanticTokensRange(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensRangeParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentSemanticTokensRange(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensRangeParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceSemanticTokensRefresh(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { +func (ls *INOLanguageServer) WorkspaceSemanticTokensRefresh(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentLinkedEditingRange(context.Context, jsonrpc.FunctionLogger, *lsp.LinkedEditingRangeParams) (*lsp.LinkedEditingRanges, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentLinkedEditingRange(context.Context, jsonrpc.FunctionLogger, *lsp.LinkedEditingRangeParams) (*lsp.LinkedEditingRanges, *jsonrpc.ResponseError) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentMoniker(context.Context, jsonrpc.FunctionLogger, *lsp.MonikerParams) ([]lsp.Moniker, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentMoniker(context.Context, jsonrpc.FunctionLogger, *lsp.MonikerParams) ([]lsp.Moniker, *jsonrpc.ResponseError) { panic("unimplemented") } // Notifications from IDE -> -func (handler *INOLanguageServer) Progress(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { +func (ls *INOLanguageServer) Progress(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { panic("unimplemented") } -func (handler *INOLanguageServer) Initialized(logger jsonrpc.FunctionLogger, params *lsp.InitializedParams) { +func (ls *INOLanguageServer) Initialized(logger jsonrpc.FunctionLogger, params *lsp.InitializedParams) { logger.Logf("Notification is not propagated to clangd") } -func (handler *INOLanguageServer) Exit(jsonrpc.FunctionLogger) { +func (ls *INOLanguageServer) Exit(jsonrpc.FunctionLogger) { panic("unimplemented") } -func (handler *INOLanguageServer) SetTrace(jsonrpc.FunctionLogger, *lsp.SetTraceParams) { +func (ls *INOLanguageServer) SetTrace(jsonrpc.FunctionLogger, *lsp.SetTraceParams) { panic("unimplemented") } -func (handler *INOLanguageServer) WindowWorkDoneProgressCancel(jsonrpc.FunctionLogger, *lsp.WorkDoneProgressCancelParams) { +func (ls *INOLanguageServer) WindowWorkDoneProgressCancel(jsonrpc.FunctionLogger, *lsp.WorkDoneProgressCancelParams) { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceDidChangeWorkspaceFolders(jsonrpc.FunctionLogger, *lsp.DidChangeWorkspaceFoldersParams) { +func (ls *INOLanguageServer) WorkspaceDidChangeWorkspaceFolders(jsonrpc.FunctionLogger, *lsp.DidChangeWorkspaceFoldersParams) { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceDidChangeConfiguration(jsonrpc.FunctionLogger, *lsp.DidChangeConfigurationParams) { +func (ls *INOLanguageServer) WorkspaceDidChangeConfiguration(jsonrpc.FunctionLogger, *lsp.DidChangeConfigurationParams) { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceDidChangeWatchedFiles(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeWatchedFilesParams) { - handler.readLock(logger, true) - defer handler.readUnlock(logger) +func (ls *INOLanguageServer) WorkspaceDidChangeWatchedFiles(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeWatchedFilesParams) { + ls.readLock(logger, true) + defer ls.readUnlock(logger) // return - // err = handler.ino2cppDidChangeWatchedFilesParams(p) + // err = ls.ino2cppDidChangeWatchedFilesParams(p) panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceDidCreateFiles(jsonrpc.FunctionLogger, *lsp.CreateFilesParams) { +func (ls *INOLanguageServer) WorkspaceDidCreateFiles(jsonrpc.FunctionLogger, *lsp.CreateFilesParams) { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceDidRenameFiles(jsonrpc.FunctionLogger, *lsp.RenameFilesParams) { +func (ls *INOLanguageServer) WorkspaceDidRenameFiles(jsonrpc.FunctionLogger, *lsp.RenameFilesParams) { panic("unimplemented") } -func (handler *INOLanguageServer) WorkspaceDidDeleteFiles(jsonrpc.FunctionLogger, *lsp.DeleteFilesParams) { +func (ls *INOLanguageServer) WorkspaceDidDeleteFiles(jsonrpc.FunctionLogger, *lsp.DeleteFilesParams) { panic("unimplemented") } // Notifications from Clangd <- -func (handler *INOLanguageServer) PublishDiagnosticsFromClangd(logger jsonrpc.FunctionLogger, cppParams *lsp.PublishDiagnosticsParams) { +func (ls *INOLanguageServer) PublishDiagnosticsFromClangd(logger jsonrpc.FunctionLogger, cppParams *lsp.PublishDiagnosticsParams) { // Default to read lock - handler.readLock(logger, false) - defer handler.readUnlock(logger) + ls.readLock(logger, false) + defer ls.readUnlock(logger) logger.Logf("publishDiagnostics(%s):", cppParams.URI) for _, diag := range cppParams.Diagnostics { @@ -1029,7 +1029,7 @@ func (handler *INOLanguageServer) PublishDiagnosticsFromClangd(logger jsonrpc.Fu // the diagnostics on sketch.cpp.ino once mapped into their // .ino counter parts may span over multiple .ino files... - allInoParams, err := handler.cpp2inoDiagnostics(logger, cppParams) + allInoParams, err := ls.cpp2inoDiagnostics(logger, cppParams) if err != nil { logger.Logf(" Error converting diagnostics to .ino: %s", err) return @@ -1043,14 +1043,14 @@ func (handler *INOLanguageServer) PublishDiagnosticsFromClangd(logger jsonrpc.Fu } logger.Logf("IDE <-- LS NOTIF textDocument/publishDiagnostics:") - if err := handler.IDEConn.TextDocumentPublishDiagnostics(inoParams); err != nil { + if err := ls.IDEConn.TextDocumentPublishDiagnostics(inoParams); err != nil { logger.Logf(" Error sending diagnostics to IDE: %s", err) return } } } -func (handler *INOLanguageServer) ProgressFromClangd(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { +func (ls *INOLanguageServer) ProgressFromClangd(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) @@ -1059,13 +1059,13 @@ func (handler *INOLanguageServer) ProgressFromClangd(logger jsonrpc.FunctionLogg switch value := progress.TryToDecodeWellKnownValues().(type) { case lsp.WorkDoneProgressBegin: logger.Logf("begin %s %v", token, value) - handler.progressHandler.Begin(token, &value) + ls.progressHandler.Begin(token, &value) case lsp.WorkDoneProgressReport: logger.Logf("report %s %v", token, value) - handler.progressHandler.Report(token, &value) + ls.progressHandler.Report(token, &value) case lsp.WorkDoneProgressEnd: logger.Logf("end %s %v", token, value) - handler.progressHandler.End(token, &value) + ls.progressHandler.End(token, &value) default: logger.Logf("error unsupported $/progress: " + string(progress.Value)) } @@ -1073,48 +1073,48 @@ func (handler *INOLanguageServer) ProgressFromClangd(logger jsonrpc.FunctionLogg // Requests from IDE <-> -func (handler *INOLanguageServer) TextDocumentDidOpen(logger jsonrpc.FunctionLogger, inoParam *lsp.DidOpenTextDocumentParams) { - handler.writeLock(logger, true) - defer handler.writeUnlock(logger) +func (ls *INOLanguageServer) TextDocumentDidOpen(logger jsonrpc.FunctionLogger, inoParam *lsp.DidOpenTextDocumentParams) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) // Add the TextDocumentItem in the tracked files list inoTextDocItem := inoParam.TextDocument - handler.docs[inoTextDocItem.URI.AsPath().String()] = inoTextDocItem + ls.docs[inoTextDocItem.URI.AsPath().String()] = inoTextDocItem // If we are tracking a .ino... if inoTextDocItem.URI.Ext() == ".ino" { - handler.sketchTrackedFilesCount++ - logger.Logf("Increasing .ino tracked files count to %d", handler.sketchTrackedFilesCount) + ls.sketchTrackedFilesCount++ + logger.Logf("Increasing .ino tracked files count to %d", ls.sketchTrackedFilesCount) // Notify clangd that sketchCpp has been opened only once - if handler.sketchTrackedFilesCount != 1 { + if ls.sketchTrackedFilesCount != 1 { logger.Logf("Clang already notified, do not notify it anymore") return } } - if cppItem, err := handler.ino2cppTextDocumentItem(logger, inoTextDocItem); err != nil { + if cppItem, err := ls.ino2cppTextDocumentItem(logger, inoTextDocItem); err != nil { logger.Logf("Error: %s", err) - } else if err := handler.Clangd.conn.TextDocumentDidOpen(&lsp.DidOpenTextDocumentParams{ + } else if err := ls.Clangd.conn.TextDocumentDidOpen(&lsp.DidOpenTextDocumentParams{ TextDocument: cppItem, }); 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.") - handler.Close() + ls.Close() } } -func (handler *INOLanguageServer) TextDocumentDidChange(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeTextDocumentParams) { - handler.writeLock(logger, true) - defer handler.writeUnlock(logger) +func (ls *INOLanguageServer) TextDocumentDidChange(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeTextDocumentParams) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) logger.Logf("didChange(%s)", inoParams.TextDocument) for _, change := range inoParams.ContentChanges { logger.Logf("> %s", change) } - if cppParams, err := handler.didChange(logger, inoParams); err != nil { + if cppParams, err := ls.didChange(logger, inoParams); err != nil { logger.Logf("--E Error: %s", err) } else if cppParams == nil { logger.Logf("--X Notification is not propagated to clangd") @@ -1123,102 +1123,102 @@ func (handler *INOLanguageServer) TextDocumentDidChange(logger jsonrpc.FunctionL for _, change := range cppParams.ContentChanges { logger.Logf(" > %s", change) } - if err := handler.Clangd.conn.TextDocumentDidChange(cppParams); err != nil { + if err := ls.Clangd.conn.TextDocumentDidChange(cppParams); err != nil { // Exit the process and trigger a restart by the client in case of a severe error logger.Logf("Connection error with clangd server: %v", err) logger.Logf("Please restart the language server.") - handler.Close() + ls.Close() } } } -func (handler *INOLanguageServer) TextDocumentWillSave(jsonrpc.FunctionLogger, *lsp.WillSaveTextDocumentParams) { +func (ls *INOLanguageServer) TextDocumentWillSave(jsonrpc.FunctionLogger, *lsp.WillSaveTextDocumentParams) { panic("unimplemented") } -func (handler *INOLanguageServer) TextDocumentDidSave(logger jsonrpc.FunctionLogger, inoParams *lsp.DidSaveTextDocumentParams) { - handler.writeLock(logger, true) - defer handler.writeUnlock(logger) +func (ls *INOLanguageServer) TextDocumentDidSave(logger jsonrpc.FunctionLogger, inoParams *lsp.DidSaveTextDocumentParams) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) logger.Logf("didSave(%s)", inoParams.TextDocument) - if cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument); err != nil { + if cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument); err != nil { logger.Logf("--E Error: %s", err) - } else if cppTextDocument.URI.AsPath().EquivalentTo(handler.buildSketchCpp) { - logger.Logf(" didSave(%s) equals %s", cppTextDocument, handler.buildSketchCpp) + } else if cppTextDocument.URI.AsPath().EquivalentTo(ls.buildSketchCpp) { + logger.Logf(" didSave(%s) equals %s", cppTextDocument, ls.buildSketchCpp) logger.Logf("--| didSave not forwarded to clangd") } else { logger.Logf("LS --> CL NOTIF didSave(%s)", cppTextDocument) - if err := handler.Clangd.conn.TextDocumentDidSave(&lsp.DidSaveTextDocumentParams{ + if err := ls.Clangd.conn.TextDocumentDidSave(&lsp.DidSaveTextDocumentParams{ TextDocument: cppTextDocument, Text: inoParams.Text, }); err != nil { // Exit the process and trigger a restart by the client in case of a severe error logger.Logf("Connection error with clangd server: %v", err) logger.Logf("Please restart the language server.") - handler.Close() + ls.Close() } } } -func (handler *INOLanguageServer) TextDocumentDidClose(logger jsonrpc.FunctionLogger, inoParams *lsp.DidCloseTextDocumentParams) { - handler.writeLock(logger, true) - defer handler.writeUnlock(logger) +func (ls *INOLanguageServer) TextDocumentDidClose(logger jsonrpc.FunctionLogger, inoParams *lsp.DidCloseTextDocumentParams) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) logger.Logf("didClose(%s)", inoParams.TextDocument) - if cppParams, err := handler.didClose(logger, inoParams); err != nil { + if cppParams, err := ls.didClose(logger, inoParams); err != nil { logger.Logf("--E Error: %s", err) } else if cppParams == nil { logger.Logf("--X Notification is not propagated to clangd") } else { logger.Logf("--> CL NOTIF didClose(%s)", cppParams.TextDocument) - if err := handler.Clangd.conn.TextDocumentDidClose(cppParams); err != nil { + if err := ls.Clangd.conn.TextDocumentDidClose(cppParams); 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.") - handler.Close() + ls.Close() } } } // Requests from Clangd <-> -func (handler *INOLanguageServer) WindowWorkDoneProgressCreateFromClangd(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCreateParams) *jsonrpc.ResponseError { +func (ls *INOLanguageServer) WindowWorkDoneProgressCreateFromClangd(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()} } - handler.progressHandler.Create(token) + ls.progressHandler.Create(token) return nil } // Close closes all the json-rpc connections. -func (handler *INOLanguageServer) Close() { - if handler.Clangd != nil { - handler.Clangd.Close() - handler.Clangd = nil +func (ls *INOLanguageServer) Close() { + if ls.Clangd != nil { + ls.Clangd.Close() + ls.Clangd = nil } - if handler.closing != nil { - close(handler.closing) - handler.closing = nil + if ls.closing != nil { + close(ls.closing) + ls.closing = nil } } // CloseNotify returns a channel that is closed when the InoHandler is closed -func (handler *INOLanguageServer) CloseNotify() <-chan bool { - return handler.closing +func (ls *INOLanguageServer) CloseNotify() <-chan bool { + return ls.closing } // CleanUp performs cleanup of the workspace and temp files create by the language server -func (handler *INOLanguageServer) CleanUp() { - if handler.buildPath != nil { - handler.buildPath.RemoveAll() - handler.buildPath = nil +func (ls *INOLanguageServer) CleanUp() { + if ls.buildPath != nil { + ls.buildPath.RemoveAll() + ls.buildPath = nil } } -func (handler *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, params *lsp.InitializeParams) error { +func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, params *lsp.InitializeParams) error { // TODO: This function must be split into two // -> start clang (when params != nil) // -> reser clang status (when params == nil) @@ -1227,48 +1227,48 @@ func (handler *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLog currCppTextVersion := 0 if params != nil { logger.Logf(" --> initialize(%s)", params.RootURI) - handler.lspInitializeParams = params - handler.sketchRoot = params.RootURI.AsPath() - handler.sketchName = handler.sketchRoot.Base() + ls.lspInitializeParams = params + ls.sketchRoot = params.RootURI.AsPath() + ls.sketchName = ls.sketchRoot.Base() } else { logger.Logf(" --> RE-initialize()") - currCppTextVersion = handler.sketchMapper.CppText.Version + currCppTextVersion = ls.sketchMapper.CppText.Version } - if err := handler.generateBuildEnvironment(logger, handler.buildPath); err != nil { + if err := ls.generateBuildEnvironment(logger, ls.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) + ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") + ls.buildSketchCppVersion = 1 + ls.lspInitializeParams.RootPath = ls.buildSketchRoot.String() + ls.lspInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) - if cppContent, err := handler.buildSketchCpp.ReadFile(); err == nil { - handler.sketchMapper = sourcemapper.CreateInoMapper(cppContent) - handler.sketchMapper.CppText.Version = currCppTextVersion + 1 + if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { + ls.sketchMapper = sourcemapper.CreateInoMapper(cppContent) + ls.sketchMapper.CppText.Version = currCppTextVersion + 1 } else { return errors.WithMessage(err, "reading generated cpp file from sketch") } - canonicalizeCompileCommandsJSON(handler.buildPath) + canonicalizeCompileCommandsJSON(ls.buildPath) if params == nil { // If we are restarting re-synchronize clangd - cppURI := lsp.NewDocumentURIFromPath(handler.buildSketchCpp) + cppURI := lsp.NewDocumentURIFromPath(ls.buildSketchCpp) cppTextDocumentIdentifier := lsp.TextDocumentIdentifier{URI: cppURI} syncEvent := &lsp.DidChangeTextDocumentParams{ TextDocument: lsp.VersionedTextDocumentIdentifier{ TextDocumentIdentifier: cppTextDocumentIdentifier, - Version: handler.sketchMapper.CppText.Version, + Version: ls.sketchMapper.CppText.Version, }, ContentChanges: []lsp.TextDocumentContentChangeEvent{ - {Text: handler.sketchMapper.CppText.Text}, // Full text change + {Text: ls.sketchMapper.CppText.Text}, // Full text change }, } logger.Logf("LS --> CL NOTIF textDocument/didChange:") - if err := handler.Clangd.conn.TextDocumentDidChange(syncEvent); err != nil { + if err := ls.Clangd.conn.TextDocumentDidChange(syncEvent); err != nil { logger.Logf(" error reinitilizing clangd:", err) return err } @@ -1279,17 +1279,17 @@ func (handler *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLog logger.Logf(" error: %s", err) } - handler.Clangd = NewClangdClient( - logger, handler.buildPath, handler.buildSketchCpp, dataFolder, + ls.Clangd = NewClangdLSPClient( + logger, ls.buildPath, ls.buildSketchCpp, dataFolder, func() { logger.Logf("Lost connection with clangd!") - handler.Close() - }, handler) + ls.Close() + }, ls) // Send initialization command to clangd ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - initRes, clangErr, err := handler.Clangd.conn.Initialize(ctx, handler.lspInitializeParams) + initRes, clangErr, err := ls.Clangd.conn.Initialize(ctx, ls.lspInitializeParams) if err != nil { logger.Logf(" error initilizing clangd: %v", err) return err @@ -1301,13 +1301,13 @@ func (handler *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLog logger.Logf("clangd successfully started: %s", string(lsp.EncodeMessage(initRes))) } - if err := handler.Clangd.conn.Initialized(&lsp.InitializedParams{}); err != nil { + if err := ls.Clangd.conn.Initialized(&lsp.InitializedParams{}); err != nil { logger.Logf(" error sending initialized notification to clangd: %v", err) return err } } - handler.queueLoadCppDocumentSymbols() + ls.queueLoadCppDocumentSymbols() return nil } @@ -1344,17 +1344,17 @@ func extractDataFolderFromArduinoCLI(logger jsonrpc.FunctionLogger) (*paths.Path return paths.New(res.Directories.Data), nil } -func (handler *INOLanguageServer) refreshCppDocumentSymbols(logger jsonrpc.FunctionLogger) error { +func (ls *INOLanguageServer) refreshCppDocumentSymbols(logger jsonrpc.FunctionLogger) error { // Query source code symbols - handler.readUnlock(logger) - cppURI := lsp.NewDocumentURIFromPath(handler.buildSketchCpp) + ls.readUnlock(logger) + cppURI := lsp.NewDocumentURIFromPath(ls.buildSketchCpp) logger.Logf("requesting documentSymbol for %s", cppURI) cppParams := &lsp.DocumentSymbolParams{ TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, } - cppDocumentSymbols, _ /* cppSymbolInformation */, cppErr, err := handler.Clangd.conn.TextDocumentDocumentSymbol(context.Background(), cppParams) - handler.readLock(logger, true) + cppDocumentSymbols, _ /* cppSymbolInformation */, cppErr, err := ls.Clangd.conn.TextDocumentDocumentSymbol(context.Background(), cppParams) + ls.readLock(logger, true) if err != nil { logger.Logf("error: %s", err) return fmt.Errorf("quering source code symbols: %w", err) @@ -1380,12 +1380,12 @@ func (handler *INOLanguageServer) refreshCppDocumentSymbols(logger jsonrpc.Funct i++ } cppDocumentSymbols = cppDocumentSymbols[:i] - handler.buildSketchSymbols = cppDocumentSymbols + ls.buildSketchSymbols = cppDocumentSymbols symbolsCanary := "" for _, symbol := range cppDocumentSymbols { logger.Logf(" symbol: %s %s %s", symbol.Kind, symbol.Name, symbol.Range) - if symbolText, err := textutils.ExtractRange(handler.sketchMapper.CppText.Text, symbol.Range); err != nil { + if symbolText, err := textutils.ExtractRange(ls.sketchMapper.CppText.Text, symbol.Range); err != nil { logger.Logf(" > invalid range: %s", err) symbolsCanary += "/" } else if end := strings.Index(symbolText, "{"); end != -1 { @@ -1396,24 +1396,24 @@ func (handler *INOLanguageServer) refreshCppDocumentSymbols(logger jsonrpc.Funct symbolsCanary += symbolText } } - handler.buildSketchSymbolsCanary = symbolsCanary + ls.buildSketchSymbolsCanary = symbolsCanary return nil } -func (handler *INOLanguageServer) CheckCppIncludesChanges() { +func (ls *INOLanguageServer) CheckCppIncludesChanges() { logger := NewLSPFunctionLogger(color.HiBlueString, "INCK --- ") logger.Logf("check for Cpp Include Changes") includesCanary := "" - for _, line := range strings.Split(handler.sketchMapper.CppText.Text, "\n") { + for _, line := range strings.Split(ls.sketchMapper.CppText.Text, "\n") { if strings.Contains(line, "#include ") { includesCanary += line } } - if includesCanary != handler.buildSketchIncludesCanary { - handler.buildSketchIncludesCanary = includesCanary + if includesCanary != ls.buildSketchIncludesCanary { + ls.buildSketchIncludesCanary = includesCanary logger.Logf("#include change detected, triggering sketch rebuild!") - handler.scheduleRebuildEnvironment() + ls.scheduleRebuildEnvironment() } } @@ -1447,10 +1447,10 @@ func canonicalizeCompileCommandsJSON(compileCommandsDir *paths.Path) map[string] return compilers } -func (handler *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { +func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, 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()) + if _, exist := ls.docs[inoIdentifier.URI.AsPath().String()]; exist { + delete(ls.docs, inoIdentifier.URI.AsPath().String()) } else { logger.Logf(" didClose of untracked document: %s", inoIdentifier.URI) return nil, unknownURI(inoIdentifier.URI) @@ -1458,46 +1458,46 @@ func (handler *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDid // If we are tracking a .ino... if inoIdentifier.URI.Ext() == ".ino" { - handler.sketchTrackedFilesCount-- - logger.Logf(" decreasing .ino tracked files count: %d", handler.sketchTrackedFilesCount) + ls.sketchTrackedFilesCount-- + logger.Logf(" decreasing .ino tracked files count: %d", ls.sketchTrackedFilesCount) // notify clang that sketchCpp has been close only once all .ino are closed - if handler.sketchTrackedFilesCount != 0 { + if ls.sketchTrackedFilesCount != 0 { return nil, nil } } - cppIdentifier, err := handler.ino2cppTextDocumentIdentifier(logger, inoIdentifier) + cppIdentifier, err := ls.ino2cppTextDocumentIdentifier(logger, inoIdentifier) return &lsp.DidCloseTextDocumentParams{ TextDocument: cppIdentifier, }, err } -func (handler *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogger, inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { - cppURI, err := handler.ino2cppDocumentURI(logger, inoItem.URI) +func (ls *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogger, inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { + cppURI, err := ls.ino2cppDocumentURI(logger, inoItem.URI) if err != nil { return cppItem, err } cppItem.URI = cppURI - if cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) { + if cppURI.AsPath().EquivalentTo(ls.buildSketchCpp) { cppItem.LanguageID = "cpp" - cppItem.Text = handler.sketchMapper.CppText.Text - cppItem.Version = handler.sketchMapper.CppText.Version + cppItem.Text = ls.sketchMapper.CppText.Text + cppItem.Version = ls.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 + cppItem.Text = ls.docs[inoPath].Text + cppItem.Version = ls.docs[inoPath].Version } return cppItem, nil } -func (handler *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { +func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { doc := req.TextDocument - trackedDoc, ok := handler.docs[doc.URI.AsPath().String()] + trackedDoc, ok := ls.docs[doc.URI.AsPath().String()] if !ok { return nil, unknownURI(doc.URI) } @@ -1509,7 +1509,7 @@ func (handler *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, req * cppChanges := []lsp.TextDocumentContentChangeEvent{} for _, inoChange := range req.ContentChanges { - cppRange, ok := handler.sketchMapper.InoToCppLSPRangeOk(doc.URI, inoChange.Range) + cppRange, ok := ls.sketchMapper.InoToCppLSPRangeOk(doc.URI, inoChange.Range) if !ok { return nil, errors.Errorf("invalid change range %s:%s", doc.URI, inoChange.Range) } @@ -1517,23 +1517,23 @@ func (handler *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, req * // Detect changes in critical lines (for example function definitions) // and trigger arduino-preprocessing + clangd restart. dirty := false - for _, sym := range handler.buildSketchSymbols { + for _, sym := range ls.buildSketchSymbols { if sym.SelectionRange.Overlaps(cppRange) { dirty = true logger.Logf("--! DIRTY CHANGE detected using symbol tables, force sketch rebuild!") break } } - if handler.sketchMapper.ApplyTextChange(doc.URI, inoChange) { + if ls.sketchMapper.ApplyTextChange(doc.URI, inoChange) { dirty = true logger.Logf("--! DIRTY CHANGE detected with sketch mapper, force sketch rebuild!") } if dirty { - handler.scheduleRebuildEnvironment() + ls.scheduleRebuildEnvironment() } // logger.Logf("New version:----------") - // logger.Logf(handler.sketchMapper.CppText.Text) + // logger.Logf(ls.sketchMapper.CppText.Text) // logger.Logf("----------------------") cppChange := lsp.TextDocumentContentChangeEvent{ @@ -1544,16 +1544,16 @@ func (handler *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, req * cppChanges = append(cppChanges, cppChange) } - handler.CheckCppIncludesChanges() + ls.CheckCppIncludesChanges() // build a cpp equivalent didChange request cppReq := &lsp.DidChangeTextDocumentParams{ ContentChanges: cppChanges, TextDocument: lsp.VersionedTextDocumentIdentifier{ TextDocumentIdentifier: lsp.TextDocumentIdentifier{ - URI: lsp.NewDocumentURIFromPath(handler.buildSketchCpp), + URI: lsp.NewDocumentURIFromPath(ls.buildSketchCpp), }, - Version: handler.sketchMapper.CppText.Version, + Version: ls.sketchMapper.CppText.Version, }, } @@ -1561,7 +1561,7 @@ func (handler *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, req * } // If changes are applied to other files pass them by converting just the URI - cppDoc, err := handler.ino2cppVersionedTextDocumentIdentifier(logger, req.TextDocument) + cppDoc, err := ls.ino2cppVersionedTextDocumentIdentifier(logger, req.TextDocument) if err != nil { return nil, err } @@ -1572,7 +1572,7 @@ func (handler *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, req * return cppReq, err } -func (handler *INOLanguageServer) handleError(logger jsonrpc.FunctionLogger, err error) error { +func (ls *INOLanguageServer) handleError(logger jsonrpc.FunctionLogger, err error) error { errorStr := err.Error() var message string if strings.Contains(errorStr, "#error") { @@ -1583,8 +1583,8 @@ func (handler *INOLanguageServer) handleError(logger jsonrpc.FunctionLogger, err 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 + if len(ls.config.SelectedBoard.Name) > 0 { + board := ls.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 { @@ -1605,26 +1605,26 @@ func (handler *INOLanguageServer) handleError(logger jsonrpc.FunctionLogger, err } go func() { defer streams.CatchAndLogPanic() - handler.showMessage(logger, lsp.MessageTypeError, message) + ls.showMessage(logger, lsp.MessageTypeError, message) }() return errors.New(message) } -func (handler *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { - cppURI, err := handler.ino2cppDocumentURI(logger, doc.URI) +func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { + cppURI, err := ls.ino2cppDocumentURI(logger, doc.URI) res := doc res.URI = cppURI return res, err } -func (handler *INOLanguageServer) ino2cppTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { - cppURI, err := handler.ino2cppDocumentURI(logger, doc.URI) +func (ls *INOLanguageServer) ino2cppTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { + cppURI, err := ls.ino2cppDocumentURI(logger, doc.URI) res := doc res.URI = cppURI return res, err } -func (handler *INOLanguageServer) ino2cppDocumentURI(logger jsonrpc.FunctionLogger, inoURI lsp.DocumentURI) (lsp.DocumentURI, error) { +func (ls *INOLanguageServer) ino2cppDocumentURI(logger jsonrpc.FunctionLogger, 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) @@ -1633,39 +1633,39 @@ func (handler *INOLanguageServer) ino2cppDocumentURI(logger jsonrpc.FunctionLogg // Convert sketch path to build path inoPath := inoURI.AsPath() if inoPath.Ext() == ".ino" { - return lsp.NewDocumentURIFromPath(handler.buildSketchCpp), nil + return lsp.NewDocumentURIFromPath(ls.buildSketchCpp), nil } - inside, err := inoPath.IsInsideDir(handler.sketchRoot) + inside, err := inoPath.IsInsideDir(ls.sketchRoot) if err != nil { - logger.Logf(" could not determine if '%s' is inside '%s'", inoPath, handler.sketchRoot) + logger.Logf(" could not determine if '%s' is inside '%s'", inoPath, ls.sketchRoot) return lsp.NilURI, unknownURI(inoURI) } if !inside { - logger.Logf(" '%s' not inside sketchroot '%s', passing doc identifier to as-is", handler.sketchRoot, inoPath) + logger.Logf(" '%s' not inside sketchroot '%s', passing doc identifier to as-is", ls.sketchRoot, inoPath) return inoURI, nil } - rel, err := handler.sketchRoot.RelTo(inoPath) + rel, err := ls.sketchRoot.RelTo(inoPath) if err == nil { - cppPath := handler.buildSketchRoot.JoinPath(rel) + cppPath := ls.buildSketchRoot.JoinPath(rel) logger.Logf(" URI: '%s' -> '%s'", inoPath, cppPath) return lsp.NewDocumentURIFromPath(cppPath), nil } - logger.Logf(" could not determine rel-path of '%s' in '%s': %s", inoPath, handler.sketchRoot, err) + logger.Logf(" could not determine rel-path of '%s' in '%s': %s", inoPath, ls.sketchRoot, err) return lsp.NilURI, err } -func (handler *INOLanguageServer) inoDocumentURIFromInoPath(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { +func (ls *INOLanguageServer) inoDocumentURIFromInoPath(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { if inoPath == sourcemapper.NotIno.File { return sourcemapper.NotInoURI, nil } - doc, ok := handler.docs[inoPath] + doc, ok := ls.docs[inoPath] if !ok { logger.Logf(" !!! Unresolved .ino path: %s", inoPath) logger.Logf(" !!! Known doc paths are:") - for p := range handler.docs { + for p := range ls.docs { logger.Logf(" !!! > %s", p) } uri := lsp.NewDocumentURI(inoPath) @@ -1674,7 +1674,7 @@ func (handler *INOLanguageServer) inoDocumentURIFromInoPath(logger jsonrpc.Funct return doc.URI, nil } -func (handler *INOLanguageServer) cpp2inoDocumentURI(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { +func (ls *INOLanguageServer) cpp2inoDocumentURI(logger jsonrpc.FunctionLogger, 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] @@ -1686,10 +1686,10 @@ func (handler *INOLanguageServer) cpp2inoDocumentURI(logger jsonrpc.FunctionLogg // Convert build path to sketch path cppPath := cppURI.AsPath() - if cppPath.EquivalentTo(handler.buildSketchCpp) { - inoPath, inoRange, err := handler.sketchMapper.CppToInoRangeOk(cppRange) + if cppPath.EquivalentTo(ls.buildSketchCpp) { + inoPath, inoRange, err := ls.sketchMapper.CppToInoRangeOk(cppRange) if err == nil { - if handler.sketchMapper.IsPreprocessedCppLine(cppRange.Start.Line) { + if ls.sketchMapper.IsPreprocessedCppLine(cppRange.Start.Line) { inoPath = sourcemapper.NotIno.File logger.Logf(" URI: is in preprocessed section") logger.Logf(" converted %s to %s:%s", cppRange, inoPath, inoRange) @@ -1701,51 +1701,51 @@ func (handler *INOLanguageServer) cpp2inoDocumentURI(logger jsonrpc.FunctionLogg err = nil } else { logger.Logf(" URI: ERROR: %s", err) - handler.sketchMapper.DebugLogAll() + ls.sketchMapper.DebugLogAll() return lsp.NilURI, lsp.NilRange, err } - inoURI, err := handler.inoDocumentURIFromInoPath(logger, inoPath) + inoURI, err := ls.inoDocumentURIFromInoPath(logger, inoPath) return inoURI, inoRange, err } - inside, err := cppPath.IsInsideDir(handler.buildSketchRoot) + inside, err := cppPath.IsInsideDir(ls.buildSketchRoot) if err != nil { - logger.Logf(" could not determine if '%s' is inside '%s'", cppPath, handler.buildSketchRoot) + logger.Logf(" could not determine if '%s' is inside '%s'", cppPath, ls.buildSketchRoot) return lsp.NilURI, lsp.NilRange, err } if !inside { - logger.Logf(" '%s' is not inside '%s'", cppPath, handler.buildSketchRoot) + logger.Logf(" '%s' is not inside '%s'", cppPath, ls.buildSketchRoot) logger.Logf(" keep doc identifier to '%s' as-is", cppPath) return cppURI, cppRange, nil } - rel, err := handler.buildSketchRoot.RelTo(cppPath) + rel, err := ls.buildSketchRoot.RelTo(cppPath) if err == nil { - inoPath := handler.sketchRoot.JoinPath(rel).String() + inoPath := ls.sketchRoot.JoinPath(rel).String() logger.Logf(" URI: '%s' -> '%s'", cppPath, inoPath) - inoURI, err := handler.inoDocumentURIFromInoPath(logger, inoPath) + inoURI, err := ls.inoDocumentURIFromInoPath(logger, inoPath) logger.Logf(" as URI: '%s'", inoURI) return inoURI, cppRange, err } - logger.Logf(" could not determine rel-path of '%s' in '%s': %s", cppPath, handler.buildSketchRoot, err) + logger.Logf(" could not determine rel-path of '%s' in '%s': %s", cppPath, ls.buildSketchRoot, err) return lsp.NilURI, lsp.NilRange, err } -func (handler *INOLanguageServer) ino2cppTextDocumentPositionParams(logger jsonrpc.FunctionLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { +func (ls *INOLanguageServer) ino2cppTextDocumentPositionParams(logger jsonrpc.FunctionLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { inoTextDocument := inoParams.TextDocument inoPosition := inoParams.Position inoURI := inoTextDocument.URI prefix := fmt.Sprintf("TextDocumentIdentifier %s", inoParams) - cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoTextDocument) if err != nil { logger.Logf("%s -> invalid text document: %s", prefix, err) return lsp.TextDocumentPositionParams{}, err } cppPosition := inoPosition if inoURI.Ext() == ".ino" { - if cppLine, ok := handler.sketchMapper.InoToCppLineOk(inoURI, inoPosition.Line); ok { + if cppLine, ok := ls.sketchMapper.InoToCppLineOk(inoURI, inoPosition.Line); ok { cppPosition.Line = cppLine } else { logger.Logf("%s -> invalid line requested: %s:%d", prefix, inoURI, inoPosition.Line) @@ -1760,25 +1760,25 @@ func (handler *INOLanguageServer) ino2cppTextDocumentPositionParams(logger jsonr return cppParams, nil } -func (handler *INOLanguageServer) ino2cppRange(logger jsonrpc.FunctionLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { - cppURI, err := handler.ino2cppDocumentURI(logger, inoURI) +func (ls *INOLanguageServer) ino2cppRange(logger jsonrpc.FunctionLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { + cppURI, err := ls.ino2cppDocumentURI(logger, inoURI) if err != nil { return lsp.NilURI, lsp.Range{}, err } - if cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) { - cppRange := handler.sketchMapper.InoToCppLSPRange(inoURI, inoRange) + if cppURI.AsPath().EquivalentTo(ls.buildSketchCpp) { + cppRange := ls.sketchMapper.InoToCppLSPRange(inoURI, inoRange) return cppURI, cppRange, nil } return cppURI, inoRange, nil } -func (handler *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { - cppTextDocument, err := handler.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) +func (ls *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { + cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) if err != nil { return nil, err } - _, cppRange, err := handler.ino2cppRange(logger, inoParams.TextDocument.URI, inoParams.Range) + _, cppRange, err := ls.ino2cppRange(logger, inoParams.TextDocument.URI, inoParams.Range) return &lsp.DocumentRangeFormattingParams{ TextDocument: cppTextDocument, Range: cppRange, @@ -1786,49 +1786,49 @@ func (handler *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger js }, err } -// func (handler *INOLanguageServer) ino2cppDocumentOnTypeFormattingParams(params *lsp.DocumentOnTypeFormattingParams) error { +// func (ls *INOLanguageServer) ino2cppDocumentOnTypeFormattingParams(params *lsp.DocumentOnTypeFormattingParams) error { // panic("not implemented") -// handler.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) -// if data, ok := handler.data[params.TextDocument.URI]; ok { +// ls.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) +// if data, ok := ls.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 *INOLanguageServer) ino2cppRenameParams(params *lsp.RenameParams) error { -// handler.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) -// if data, ok := handler.data[params.TextDocument.URI]; ok { +// func (ls *INOLanguageServer) ino2cppRenameParams(params *lsp.RenameParams) error { +// ls.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) +// if data, ok := ls.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 *INOLanguageServer) ino2cppDidChangeWatchedFilesParams(params *lsp.DidChangeWatchedFilesParams) error { +// func (ls *INOLanguageServer) ino2cppDidChangeWatchedFilesParams(params *lsp.DidChangeWatchedFilesParams) error { // for index := range params.Changes { // fileEvent := ¶ms.Changes[index] -// if data, ok := handler.data[fileEvent.URI]; ok { +// if data, ok := ls.data[fileEvent.URI]; ok { // fileEvent.URI = data.targetURI // } // } // return nil // } -// func (handler *INOLanguageServer) ino2cppExecuteCommand(executeCommand *lsp.ExecuteCommandParams) error { +// func (ls *INOLanguageServer) ino2cppExecuteCommand(executeCommand *lsp.ExecuteCommandParams) error { // if len(executeCommand.Arguments) == 1 { -// arg := handler.parseCommandArgument(executeCommand.Arguments[0]) +// arg := ls.parseCommandArgument(executeCommand.Arguments[0]) // if workspaceEdit, ok := arg.(*lsp.WorkspaceEdit); ok { -// executeCommand.Arguments[0] = handler.ino2cppWorkspaceEdit(workspaceEdit) +// executeCommand.Arguments[0] = ls.ino2cppWorkspaceEdit(workspaceEdit) // } // } // return nil // } -// func (handler *INOLanguageServer) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { +// func (ls *INOLanguageServer) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { // newEdit := lsp.WorkspaceEdit{} // for uri, edit := range origEdit.Changes { -// if data, ok := handler.data[lsp.DocumentURI(uri)]; ok { +// if data, ok := ls.data[lsp.DocumentURI(uri)]; ok { // newValue := make([]lsp.TextEdit, len(edit)) // for index := range edit { // newValue[index] = lsp.TextEdit{ @@ -1844,26 +1844,26 @@ func (handler *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger js // return &newEdit // } -func (handler *INOLanguageServer) cpp2inoCodeAction(logger jsonrpc.FunctionLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { +func (ls *INOLanguageServer) cpp2inoCodeAction(logger jsonrpc.FunctionLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { inoCodeAction := lsp.CodeAction{ Title: codeAction.Title, Kind: codeAction.Kind, - Edit: handler.cpp2inoWorkspaceEdit(logger, codeAction.Edit), + Edit: ls.cpp2inoWorkspaceEdit(logger, codeAction.Edit), Diagnostics: codeAction.Diagnostics, } if codeAction.Command != nil { - inoCommand := handler.cpp2inoCommand(logger, *codeAction.Command) + inoCommand := ls.cpp2inoCommand(logger, *codeAction.Command) inoCodeAction.Command = &inoCommand } if uri.Ext() == ".ino" { for i, diag := range inoCodeAction.Diagnostics { - _, inoCodeAction.Diagnostics[i].Range = handler.sketchMapper.CppToInoRange(diag.Range) + _, inoCodeAction.Diagnostics[i].Range = ls.sketchMapper.CppToInoRange(diag.Range) } } return inoCodeAction } -func (handler *INOLanguageServer) cpp2inoCommand(logger jsonrpc.FunctionLogger, command lsp.Command) lsp.Command { +func (ls *INOLanguageServer) cpp2inoCommand(logger jsonrpc.FunctionLogger, command lsp.Command) lsp.Command { inoCommand := lsp.Command{ Title: command.Title, Command: command.Command, @@ -1879,8 +1879,8 @@ func (handler *INOLanguageServer) cpp2inoCommand(logger jsonrpc.FunctionLogger, if err := json.Unmarshal(command.Arguments[0], &v); err == nil { if v.TweakID == "ExtractVariable" { logger.Logf(" > converted clangd ExtractVariable") - if v.File.AsPath().EquivalentTo(handler.buildSketchCpp) { - inoFile, inoSelection := handler.sketchMapper.CppToInoRange(v.Selection) + if v.File.AsPath().EquivalentTo(ls.buildSketchCpp) { + inoFile, inoSelection := ls.sketchMapper.CppToInoRange(v.Selection) v.File = lsp.NewDocumentURI(inoFile) v.Selection = inoSelection } @@ -1897,7 +1897,7 @@ func (handler *INOLanguageServer) cpp2inoCommand(logger jsonrpc.FunctionLogger, return inoCommand } -func (handler *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { +func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { if cppWorkspaceEdit == nil { return nil } @@ -1906,7 +1906,7 @@ func (handler *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLo } for editURI, edits := range cppWorkspaceEdit.Changes { // if the edits are not relative to sketch file... - if !editURI.AsPath().EquivalentTo(handler.buildSketchCpp) { + if !editURI.AsPath().EquivalentTo(ls.buildSketchCpp) { // ...pass them through... inoWorkspaceEdit.Changes[editURI] = edits continue @@ -1914,12 +1914,12 @@ func (handler *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLo // ...otherwise convert edits to the sketch.ino.cpp into multilpe .ino edits for _, edit := range edits { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, editURI, edit.Range) + inoURI, inoRange, err := ls.cpp2inoDocumentURI(logger, editURI, edit.Range) if err != nil { logger.Logf(" error converting edit %s:%s: %s", editURI, edit.Range, err) continue } - //inoFile, inoRange := handler.sketchMapper.CppToInoRange(edit.Range) + //inoFile, inoRange := ls.sketchMapper.CppToInoRange(edit.Range) //inoURI := lsp.NewDocumentURI(inoFile) if _, have := inoWorkspaceEdit.Changes[inoURI]; !have { inoWorkspaceEdit.Changes[inoURI] = []lsp.TextEdit{} @@ -1934,16 +1934,16 @@ func (handler *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLo return inoWorkspaceEdit } -func (handler *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppLocation lsp.Location) (lsp.Location, error) { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, cppLocation.URI, cppLocation.Range) +func (ls *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppLocation lsp.Location) (lsp.Location, error) { + inoURI, inoRange, err := ls.cpp2inoDocumentURI(logger, cppLocation.URI, cppLocation.Range) return lsp.Location{ URI: inoURI, Range: inoRange, }, err } -func (handler *INOLanguageServer) cpp2inoDocumentHighlight(logger jsonrpc.FunctionLogger, cppHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, error) { - _, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppHighlight.Range) +func (ls *INOLanguageServer) cpp2inoDocumentHighlight(logger jsonrpc.FunctionLogger, cppHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, error) { + _, inoRange, err := ls.cpp2inoDocumentURI(logger, cppURI, cppHighlight.Range) if err != nil { return lsp.DocumentHighlight{}, err } @@ -1953,12 +1953,12 @@ func (handler *INOLanguageServer) cpp2inoDocumentHighlight(logger jsonrpc.Functi }, nil } -func (handler *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { +func (ls *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { logger.Logf("%s cpp/textEdit (%d elements)", cppURI, len(cppEdits)) allInoEdits := map[lsp.DocumentURI][]lsp.TextEdit{} for _, cppEdit := range cppEdits { logger.Logf(" > %s -> %s", cppEdit.Range, strconv.Quote(cppEdit.NewText)) - inoURI, inoEdit, err := handler.cpp2inoTextEdit(logger, cppURI, cppEdit) + inoURI, inoEdit, err := ls.cpp2inoTextEdit(logger, cppURI, cppEdit) if err != nil { return nil, err } @@ -1976,14 +1976,14 @@ func (handler *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger return allInoEdits, nil } -func (handler *INOLanguageServer) cpp2inoTextEdit(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppEdit.Range) +func (ls *INOLanguageServer) cpp2inoTextEdit(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { + inoURI, inoRange, err := ls.cpp2inoDocumentURI(logger, cppURI, cppEdit.Range) inoEdit := cppEdit inoEdit.Range = inoRange return inoURI, inoEdit, err } -func (handler *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.FunctionLogger, cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { +func (ls *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.FunctionLogger, cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { inoRequested := inoRequestedURI.AsPath().String() logger.Logf(" filtering for requested ino file: %s", inoRequested) if inoRequestedURI.Ext() != ".ino" || len(cppSymbols) == 0 { @@ -1993,13 +1993,13 @@ func (handler *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.Function inoSymbols := []lsp.DocumentSymbol{} for _, symbol := range cppSymbols { logger.Logf(" > convert %s %s", symbol.Kind, symbol.Range) - if handler.sketchMapper.IsPreprocessedCppLine(symbol.Range.Start.Line) { + if ls.sketchMapper.IsPreprocessedCppLine(symbol.Range.Start.Line) { logger.Logf(" 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) + inoFile, inoRange := ls.sketchMapper.CppToInoRange(symbol.Range) + inoSelectionURI, inoSelectionRange := ls.sketchMapper.CppToInoRange(symbol.SelectionRange) if inoFile != inoSelectionURI { logger.Logf(" ERROR: symbol range and selection belongs to different URI!") @@ -2020,19 +2020,19 @@ func (handler *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.Function Kind: symbol.Kind, Range: inoRange, SelectionRange: inoSelectionRange, - Children: handler.cpp2inoDocumentSymbols(logger, symbol.Children, inoRequestedURI), + Children: ls.cpp2inoDocumentSymbols(logger, symbol.Children, inoRequestedURI), }) } return inoSymbols } -func (handler *INOLanguageServer) cpp2inoSymbolInformation(syms []lsp.SymbolInformation) []lsp.SymbolInformation { +func (ls *INOLanguageServer) 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) + // ls.cpp2inoLocation(&sym.Location) // nme := fmt.Sprintf("%s::%s", sym.ContainerName, sym.Name) // other, duplicate := idx[nme] @@ -2052,21 +2052,21 @@ func (handler *INOLanguageServer) cpp2inoSymbolInformation(syms []lsp.SymbolInfo // return symbols } -func (handler *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { +func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { inoDiagsParam := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} cppURI := cppDiags.URI - isSketch := cppURI.AsPath().EquivalentTo(handler.buildSketchCpp) + isSketch := cppURI.AsPath().EquivalentTo(ls.buildSketchCpp) if isSketch { - for inoURI := range handler.inoDocsWithDiagnostics { + for inoURI := range ls.inoDocsWithDiagnostics { inoDiagsParam[inoURI] = &lsp.PublishDiagnosticsParams{ URI: inoURI, Diagnostics: []lsp.Diagnostic{}, } } - handler.inoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} + ls.inoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} } else { - inoURI, _, err := handler.cpp2inoDocumentURI(logger, cppURI, lsp.NilRange) + inoURI, _, err := ls.cpp2inoDocumentURI(logger, cppURI, lsp.NilRange) if err != nil { return nil, err } @@ -2077,7 +2077,7 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogg } for _, cppDiag := range cppDiags.Diagnostics { - inoURI, inoRange, err := handler.cpp2inoDocumentURI(logger, cppURI, cppDiag.Range) + inoURI, inoRange, err := ls.cpp2inoDocumentURI(logger, cppURI, cppDiag.Range) if err != nil { return nil, err } @@ -2099,7 +2099,7 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogg inoDiagParam.Diagnostics = append(inoDiagParam.Diagnostics, inoDiag) if isSketch { - handler.inoDocsWithDiagnostics[inoURI] = true + ls.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 @@ -2110,7 +2110,7 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogg inoDiagCode == "undeclared_var_use" || inoDiagCode == "ovl_no_viable_function_in_call" || inoDiagCode == "pp_file_not_found" { - handler.queueCheckCppDocumentSymbols() + ls.queueCheckCppDocumentSymbols() } } } @@ -2124,11 +2124,11 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogg } // // HandleRequestFromClangd handles a request message received from clangd. -// func (handler *INOLanguageServer) HandleRequestFromClangd(ctx context.Context, logger jsonrpc.FunctionLogger, +// func (ls *INOLanguageServer) HandleRequestFromClangd(ctx context.Context, logger jsonrpc.FunctionLogger, // method string, paramsRaw json.RawMessage, // respCallback func(result json.RawMessage, err *jsonrpc.ResponseError), // ) { -// // n := atomic.AddInt64(&handler.clangdMessageCount, 1) +// // n := atomic.AddInt64(&ls.clangdMessageCount, 1) // // prefix := fmt.Sprintf("CLG <-- %s %v ", method, n) // params, err := lsp.DecodeServerRequestParams(method, paramsRaw) // if err != nil { @@ -2138,12 +2138,12 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogg // } // panic("unimplemented") // // Default to read lock -// handler.readLock(logger, false) -// defer handler.readUnlock(logger) +// ls.readLock(logger, false) +// defer ls.readUnlock(logger) // switch p := params.(type) { // case *lsp.ApplyWorkspaceEditParams: // // "workspace/applyEdit" -// p.Edit = *handler.cpp2inoWorkspaceEdit(logger, &p.Edit) +// p.Edit = *ls.cpp2inoWorkspaceEdit(logger, &p.Edit) // } // if err != nil { // logger.Logf("From clangd: Method: %s, Error: %v", method, err) @@ -2158,7 +2158,7 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogg // // } // // logger.Logf("IDE <-- LS REQ %s", method) -// // resp, respErr, err := handler.IDEConn.SendRequest(ctx, method, respRaw) +// // resp, respErr, err := ls.IDEConn.SendRequest(ctx, method, respRaw) // // if err != nil { // // logger.Logf("Error sending request to IDE:", err) // // respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) @@ -2168,7 +2168,7 @@ func (handler *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogg // // respCallback(resp, respErr) // } -func (handler *INOLanguageServer) createClangdFormatterConfig(logger jsonrpc.FunctionLogger, cppuri lsp.DocumentURI) (func(), error) { +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 @@ -2331,7 +2331,7 @@ WhitespaceSensitiveMacros: [] return true } - if sketchFormatterConf := handler.sketchRoot.Join(".clang-format"); sketchFormatterConf.Exist() { + 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 globalFormatterConf != nil && globalFormatterConf.Exist() { @@ -2353,12 +2353,12 @@ WhitespaceSensitiveMacros: [] return cleanup, err } -func (handler *INOLanguageServer) showMessage(logger jsonrpc.FunctionLogger, msgType lsp.MessageType, message string) { +func (ls *INOLanguageServer) showMessage(logger jsonrpc.FunctionLogger, msgType lsp.MessageType, message string) { params := lsp.ShowMessageParams{ Type: msgType, Message: message, } - if err := handler.IDEConn.WindowShowMessage(¶ms); err != nil { + if err := ls.IDEConn.WindowShowMessage(¶ms); err != nil { logger.Logf("error sending showMessage notification: %s", err) } } diff --git a/handler/clangd_client.go b/ls/lsp_client_clangd.go similarity index 50% rename from handler/clangd_client.go rename to ls/lsp_client_clangd.go index 6002723..dc51337 100644 --- a/handler/clangd_client.go +++ b/ls/lsp_client_clangd.go @@ -1,4 +1,4 @@ -package handler +package ls import ( "context" @@ -15,16 +15,16 @@ import ( "go.bug.st/lsp/jsonrpc" ) -type ClangdClient struct { - conn *lsp.Client - handler *INOLanguageServer +type ClangdLSPClient struct { + conn *lsp.Client + ls *INOLanguageServer } -func NewClangdClient(logger jsonrpc.FunctionLogger, +func NewClangdLSPClient(logger jsonrpc.FunctionLogger, buildPath, buildSketchCpp, dataFolder *paths.Path, connectionClosedCB func(), inoLanguageServer *INOLanguageServer, -) *ClangdClient { +) *ClangdLSPClient { clangdStdout, clangdStdin, clangdStderr := startClangd(logger, buildPath, buildSketchCpp, dataFolder) clangdStdio := streams.NewReadWriteCloser(clangdStdin, clangdStdout) if enableLogging { @@ -34,8 +34,8 @@ func NewClangdClient(logger jsonrpc.FunctionLogger, go io.Copy(os.Stderr, clangdStderr) } - client := &ClangdClient{ - handler: inoLanguageServer, + client := &ClangdLSPClient{ + ls: inoLanguageServer, } client.conn = lsp.NewClient(clangdStdio, clangdStdio, client) client.conn.SetLogger(&LSPLogger{IncomingPrefix: "IDE LS <-- Clangd", OutgoingPrefix: "IDE LS --> Clangd"}) @@ -74,68 +74,68 @@ func startClangd(logger jsonrpc.FunctionLogger, compileCommandsDir, sketchCpp, d } } -func (client *ClangdClient) Close() { +func (client *ClangdLSPClient) Close() { panic("unimplemented") } // The following are events incoming from Clangd -func (client *ClangdClient) WindowShowMessageRequest(context.Context, jsonrpc.FunctionLogger, *lsp.ShowMessageRequestParams) (*lsp.MessageActionItem, *jsonrpc.ResponseError) { +func (client *ClangdLSPClient) WindowShowMessageRequest(context.Context, jsonrpc.FunctionLogger, *lsp.ShowMessageRequestParams) (*lsp.MessageActionItem, *jsonrpc.ResponseError) { panic("unimplemented") } -func (client *ClangdClient) WindowShowDocument(context.Context, jsonrpc.FunctionLogger, *lsp.ShowDocumentParams) (*lsp.ShowDocumentResult, *jsonrpc.ResponseError) { +func (client *ClangdLSPClient) WindowShowDocument(context.Context, jsonrpc.FunctionLogger, *lsp.ShowDocumentParams) (*lsp.ShowDocumentResult, *jsonrpc.ResponseError) { panic("unimplemented") } -func (client *ClangdClient) WindowWorkDoneProgressCreate(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCreateParams) *jsonrpc.ResponseError { - return client.handler.WindowWorkDoneProgressCreateFromClangd(ctx, logger, params) +func (client *ClangdLSPClient) WindowWorkDoneProgressCreate(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCreateParams) *jsonrpc.ResponseError { + return client.ls.WindowWorkDoneProgressCreateFromClangd(ctx, logger, params) } -func (client *ClangdClient) ClientRegisterCapability(context.Context, jsonrpc.FunctionLogger, *lsp.RegistrationParams) *jsonrpc.ResponseError { +func (client *ClangdLSPClient) ClientRegisterCapability(context.Context, jsonrpc.FunctionLogger, *lsp.RegistrationParams) *jsonrpc.ResponseError { panic("unimplemented") } -func (client *ClangdClient) ClientUnregisterCapability(context.Context, jsonrpc.FunctionLogger, *lsp.UnregistrationParams) *jsonrpc.ResponseError { +func (client *ClangdLSPClient) ClientUnregisterCapability(context.Context, jsonrpc.FunctionLogger, *lsp.UnregistrationParams) *jsonrpc.ResponseError { panic("unimplemented") } -func (client *ClangdClient) WorkspaceWorkspaceFolders(context.Context, jsonrpc.FunctionLogger) ([]lsp.WorkspaceFolder, *jsonrpc.ResponseError) { +func (client *ClangdLSPClient) WorkspaceWorkspaceFolders(context.Context, jsonrpc.FunctionLogger) ([]lsp.WorkspaceFolder, *jsonrpc.ResponseError) { panic("unimplemented") } -func (client *ClangdClient) WorkspaceConfiguration(context.Context, jsonrpc.FunctionLogger, *lsp.ConfigurationParams) ([]json.RawMessage, *jsonrpc.ResponseError) { +func (client *ClangdLSPClient) WorkspaceConfiguration(context.Context, jsonrpc.FunctionLogger, *lsp.ConfigurationParams) ([]json.RawMessage, *jsonrpc.ResponseError) { panic("unimplemented") } -func (client *ClangdClient) WorkspaceApplyEdit(context.Context, jsonrpc.FunctionLogger, *lsp.ApplyWorkspaceEditParams) (*lsp.ApplyWorkspaceEditResult, *jsonrpc.ResponseError) { +func (client *ClangdLSPClient) WorkspaceApplyEdit(context.Context, jsonrpc.FunctionLogger, *lsp.ApplyWorkspaceEditParams) (*lsp.ApplyWorkspaceEditResult, *jsonrpc.ResponseError) { panic("unimplemented") } -func (client *ClangdClient) WorkspaceCodeLensRefresh(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { +func (client *ClangdLSPClient) WorkspaceCodeLensRefresh(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { panic("unimplemented") } -func (client *ClangdClient) Progress(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { - client.handler.ProgressFromClangd(logger, progress) +func (client *ClangdLSPClient) Progress(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { + client.ls.ProgressFromClangd(logger, progress) } -func (client *ClangdClient) LogTrace(jsonrpc.FunctionLogger, *lsp.LogTraceParams) { +func (client *ClangdLSPClient) LogTrace(jsonrpc.FunctionLogger, *lsp.LogTraceParams) { panic("unimplemented") } -func (client *ClangdClient) WindowShowMessage(jsonrpc.FunctionLogger, *lsp.ShowMessageParams) { +func (client *ClangdLSPClient) WindowShowMessage(jsonrpc.FunctionLogger, *lsp.ShowMessageParams) { panic("unimplemented") } -func (client *ClangdClient) WindowLogMessage(jsonrpc.FunctionLogger, *lsp.LogMessageParams) { +func (client *ClangdLSPClient) WindowLogMessage(jsonrpc.FunctionLogger, *lsp.LogMessageParams) { panic("unimplemented") } -func (client *ClangdClient) TelemetryEvent(jsonrpc.FunctionLogger, json.RawMessage) { +func (client *ClangdLSPClient) TelemetryEvent(jsonrpc.FunctionLogger, json.RawMessage) { panic("unimplemented") } -func (client *ClangdClient) TextDocumentPublishDiagnostics(logger jsonrpc.FunctionLogger, params *lsp.PublishDiagnosticsParams) { - client.handler.PublishDiagnosticsFromClangd(logger, params) +func (client *ClangdLSPClient) TextDocumentPublishDiagnostics(logger jsonrpc.FunctionLogger, params *lsp.PublishDiagnosticsParams) { + client.ls.PublishDiagnosticsFromClangd(logger, params) } diff --git a/handler/lsp_logger.go b/ls/lsp_logger.go similarity index 99% rename from handler/lsp_logger.go rename to ls/lsp_logger.go index 661eaf0..1f73a2d 100644 --- a/handler/lsp_logger.go +++ b/ls/lsp_logger.go @@ -1,4 +1,4 @@ -package handler +package ls import ( "fmt" diff --git a/handler/progress.go b/ls/progress.go similarity index 99% rename from handler/progress.go rename to ls/progress.go index 869ce2b..8df523c 100644 --- a/handler/progress.go +++ b/ls/progress.go @@ -1,4 +1,4 @@ -package handler +package ls import ( "context" diff --git a/main.go b/main.go index 200cadd..f30381b 100644 --- a/main.go +++ b/main.go @@ -7,7 +7,7 @@ import ( "os" "os/signal" - "github.com/arduino/arduino-language-server/handler" + "github.com/arduino/arduino-language-server/ls" "github.com/arduino/arduino-language-server/streams" "github.com/arduino/go-paths-helper" ) @@ -52,15 +52,15 @@ func main() { log.Fatal("Path to ArduinoCLI config file must be set.") } - handler.Setup(cliPath, cliConfigPath, clangdPath, formatFilePath, enableLogging) - initialBoard := handler.Board{Fqbn: initialFqbn, Name: initialBoardName} + ls.Setup(cliPath, cliConfigPath, clangdPath, formatFilePath, enableLogging) + initialBoard := ls.Board{Fqbn: initialFqbn, Name: initialBoardName} stdio := streams.NewReadWriteCloser(os.Stdin, os.Stdout) if enableLogging { stdio = streams.LogReadWriteCloserAs(stdio, "inols.log") } - inoHandler := handler.NewINOLanguageServer(stdio, stdio, initialBoard) + inoHandler := ls.NewINOLanguageServer(stdio, stdio, initialBoard) // Intercept kill signal c := make(chan os.Signal, 2) diff --git a/handler/sourcemapper/ino.go b/sourcemapper/ino.go similarity index 99% rename from handler/sourcemapper/ino.go rename to sourcemapper/ino.go index c652c63..b3a1d9e 100644 --- a/handler/sourcemapper/ino.go +++ b/sourcemapper/ino.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - "github.com/arduino/arduino-language-server/handler/textutils" + "github.com/arduino/arduino-language-server/textutils" "github.com/arduino/go-paths-helper" "github.com/pkg/errors" "go.bug.st/lsp" diff --git a/handler/sourcemapper/ino_test.go b/sourcemapper/ino_test.go similarity index 100% rename from handler/sourcemapper/ino_test.go rename to sourcemapper/ino_test.go diff --git a/handler/textutils/textutils.go b/textutils/textutils.go similarity index 100% rename from handler/textutils/textutils.go rename to textutils/textutils.go diff --git a/handler/textutils/textutils_test.go b/textutils/textutils_test.go similarity index 100% rename from handler/textutils/textutils_test.go rename to textutils/textutils_test.go From 6cceaacf66c7b68ddfc3ab45a73811a3882a1a74 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 4 Nov 2021 15:40:12 +0100 Subject: [PATCH 07/76] Better isolation of LSPClient/LSPServer --- ls/ls.go | 592 ++++++++-------------------------------- ls/lsp_client_clangd.go | 20 +- ls/lsp_server_ide.go | 262 ++++++++++++++++++ ls/unused.go | 59 ++++ 4 files changed, 442 insertions(+), 491 deletions(-) create mode 100644 ls/lsp_server_ide.go create mode 100644 ls/unused.go diff --git a/ls/ls.go b/ls/ls.go index 283805f..696a9f4 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "log" - "regexp" "runtime" "strconv" "strings" @@ -45,8 +44,8 @@ func Setup(cliPath, cliConfigPath, clangdPath, formatFilePath string, _enableLog // INOLanguageServer is a JSON-RPC handler that delegates messages to clangd. type INOLanguageServer struct { - IDEConn *lsp.Server - Clangd *ClangdLSPClient + IDE *IDELSPServer + Clangd *ClangdLSPClient progressHandler *ProgressProxyHandler @@ -147,7 +146,7 @@ func (ls *INOLanguageServer) waitClangdStart(logger jsonrpc.FunctionLogger) erro // NewINOLanguageServer creates and configures an Arduino Language Server. func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLanguageServer { logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") - handler := &INOLanguageServer{ + ls := &INOLanguageServer{ docs: map[string]lsp.TextDocumentItem{}, inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, closing: make(chan bool), @@ -157,47 +156,45 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLa SelectedBoard: board, }, } - handler.clangdStarted = sync.NewCond(&handler.dataMux) + ls.clangdStarted = sync.NewCond(&ls.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") + ls.buildPath = buildPath.Canonical() + ls.buildSketchRoot = ls.buildPath.Join("sketch") } if enableLogging { logger.Logf("Initial board configuration: %s", board) - logger.Logf("Language server build path: %s", handler.buildPath) - logger.Logf("Language server build sketch root: %s", handler.buildSketchRoot) + logger.Logf("Language server build path: %s", ls.buildPath) + logger.Logf("Language server build sketch root: %s", ls.buildSketchRoot) } - handler.IDEConn = lsp.NewServer(stdin, stdout, handler) - handler.IDEConn.SetLogger(&LSPLogger{IncomingPrefix: "IDE --> LS", OutgoingPrefix: "IDE <-- LS"}) - handler.progressHandler = NewProgressProxy(handler.IDEConn) - + ls.IDE = NewIDELSPServer(logger, stdin, stdout, ls) + ls.progressHandler = NewProgressProxy(ls.IDE.conn) go func() { defer streams.CatchAndLogPanic() - handler.IDEConn.Run() + ls.IDE.Run() logger.Logf("Lost connection with IDE!") - handler.Close() + ls.Close() }() go func() { defer streams.CatchAndLogPanic() for { select { - case <-handler.buildSketchSymbolsLoad: + case <-ls.buildSketchSymbolsLoad: // ...also un-queue buildSketchSymbolsCheck select { - case <-handler.buildSketchSymbolsCheck: + case <-ls.buildSketchSymbolsCheck: default: } - handler.LoadCppDocumentSymbols() + ls.LoadCppDocumentSymbols() - case <-handler.buildSketchSymbolsCheck: - handler.CheckCppDocumentSymbols() + case <-ls.buildSketchSymbolsCheck: + ls.CheckCppDocumentSymbols() - case <-handler.closing: + case <-ls.closing: return } } @@ -205,9 +202,9 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLa go func() { defer streams.CatchAndLogPanic() - handler.rebuildEnvironmentLoop() + ls.rebuildEnvironmentLoop() }() - return handler + return ls } func (ls *INOLanguageServer) queueLoadCppDocumentSymbols() { @@ -268,7 +265,7 @@ func (handler *INOLanguageServer) startClangd(inoParams *lsp.InitializeParams) { logger.Logf("Done initializing workbench") } -func (handler *INOLanguageServer) Initialize(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { +func (handler *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { go func() { defer streams.CatchAndLogPanic() handler.startClangd(inoParams) @@ -311,39 +308,7 @@ func (handler *INOLanguageServer) Initialize(ctx context.Context, logger jsonrpc return resp, nil } -func (ls *INOLanguageServer) Shutdown(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceSymbol(context.Context, jsonrpc.FunctionLogger, *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceExecuteCommand(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ExecuteCommandParams) (json.RawMessage, *jsonrpc.ResponseError) { - ls.readLock(logger, true) - defer ls.readUnlock(logger) - // return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"} - // err = ls.ino2cppExecuteCommand(inoParams) - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceWillCreateFiles(context.Context, jsonrpc.FunctionLogger, *lsp.CreateFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceWillRenameFiles(context.Context, jsonrpc.FunctionLogger, *lsp.RenameFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceWillDeleteFiles(context.Context, jsonrpc.FunctionLogger, *lsp.DeleteFilesParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentWillSaveWaitUntil(context.Context, jsonrpc.FunctionLogger, *lsp.WillSaveTextDocumentParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentCompletion(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CompletionParams) (*lsp.CompletionList, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CompletionParams) (*lsp.CompletionList, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -392,11 +357,7 @@ func (ls *INOLanguageServer) TextDocumentCompletion(ctx context.Context, logger return &inoResp, nil } -func (ls *INOLanguageServer) CompletionItemResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CompletionItem) (*lsp.CompletionItem, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentHover(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.HoverParams) (*lsp.Hover, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.HoverParams) (*lsp.Hover, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -443,7 +404,7 @@ func (ls *INOLanguageServer) TextDocumentHover(ctx context.Context, logger jsonr return &inoResp, nil } -func (ls *INOLanguageServer) TextDocumentSignatureHelp(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.SignatureHelpParams) (*lsp.SignatureHelp, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentSignatureHelpReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.SignatureHelpParams) (*lsp.SignatureHelp, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -475,11 +436,7 @@ func (ls *INOLanguageServer) TextDocumentSignatureHelp(ctx context.Context, logg return cppSignatureHelp, nil } -func (ls *INOLanguageServer) TextDocumentDeclaration(context.Context, jsonrpc.FunctionLogger, *lsp.DeclarationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, p *lsp.DefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, p *lsp.DefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -523,20 +480,7 @@ func (ls *INOLanguageServer) TextDocumentDefinition(ctx context.Context, logger return inoLocations, inoLocationLinks, nil } -func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, cppLocations []lsp.Location) ([]lsp.Location, error) { - inoLocations := []lsp.Location{} - for _, cppLocation := range cppLocations { - inoLocation, err := ls.cpp2inoLocation(logger, cppLocation) - if err != nil { - logger.Logf("ERROR converting location %s: %s", cppLocation, err) - return nil, err - } - inoLocations = append(inoLocations, inoLocation) - } - return inoLocations, nil -} - -func (ls *INOLanguageServer) TextDocumentTypeDefinition(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentTypeDefinitionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -583,7 +527,7 @@ func (ls *INOLanguageServer) TextDocumentTypeDefinition(ctx context.Context, log return inoLocations, inoLocationLinks, nil } -func (ls *INOLanguageServer) TextDocumentImplementation(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ImplementationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentImplementationReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ImplementationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -627,15 +571,7 @@ func (ls *INOLanguageServer) TextDocumentImplementation(ctx context.Context, log return inoLocations, inoLocationLinks, nil } -func (ls *INOLanguageServer) TextDocumentReferences(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ReferenceParams) ([]lsp.Location, *jsonrpc.ResponseError) { - ls.readLock(logger, true) - defer ls.readUnlock(logger) - panic("unimplemented") - // inoURI = p.TextDocument.URI - // _, err = ls.ino2cppTextDocumentPositionParams(logger, p.TextDocumentPositionParams) -} - -func (ls *INOLanguageServer) TextDocumentDocumentHighlight(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -676,7 +612,7 @@ func (ls *INOLanguageServer) TextDocumentDocumentHighlight(ctx context.Context, return inoHighlights, nil } -func (ls *INOLanguageServer) TextDocumentDocumentSymbol(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, []lsp.SymbolInformation, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, []lsp.SymbolInformation, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -717,7 +653,7 @@ func (ls *INOLanguageServer) TextDocumentDocumentSymbol(ctx context.Context, log return inoDocSymbols, inoSymbolInformation, nil } -func (ls *INOLanguageServer) TextDocumentCodeAction(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CodeActionParams) ([]lsp.CommandOrCodeAction, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentCodeActionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CodeActionParams) ([]lsp.CommandOrCodeAction, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -773,35 +709,7 @@ func (ls *INOLanguageServer) TextDocumentCodeAction(ctx context.Context, logger return inoResp, nil } -func (ls *INOLanguageServer) CodeActionResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CodeAction) (*lsp.CodeAction, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentCodeLens(context.Context, jsonrpc.FunctionLogger, *lsp.CodeLensParams) ([]lsp.CodeLens, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) CodeLensResolve(context.Context, jsonrpc.FunctionLogger, *lsp.CodeLens) (*lsp.CodeLens, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentDocumentLink(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentLinkParams) ([]lsp.DocumentLink, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) DocumentLinkResolve(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentLink) (*lsp.DocumentLink, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentDocumentColor(context.Context, jsonrpc.FunctionLogger, *lsp.DocumentColorParams) ([]lsp.ColorInformation, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentColorPresentation(context.Context, jsonrpc.FunctionLogger, *lsp.ColorPresentationParams) ([]lsp.ColorPresentation, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentFormattingReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -854,7 +762,7 @@ func (ls *INOLanguageServer) TextDocumentFormatting(ctx context.Context, logger } } -func (ls *INOLanguageServer) TextDocumentRangeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentRangeFormattingReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -903,177 +811,11 @@ func (ls *INOLanguageServer) TextDocumentRangeFormatting(ctx context.Context, lo } } -func (ls *INOLanguageServer) TextDocumentOnTypeFormatting(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentOnTypeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { - ls.readLock(logger, true) - defer ls.readUnlock(logger) - // return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"} - // inoURI = p.TextDocument.URI - // err = ls.ino2cppDocumentOnTypeFormattingParams(p) - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentRename(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.RenameParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { - ls.readLock(logger, true) - defer ls.readUnlock(logger) - // return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesMethodNotFound, Message: "Unimplemented"} - // inoURI = p.TextDocument.URI - // err = ls.ino2cppRenameParams(p) - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentFoldingRange(context.Context, jsonrpc.FunctionLogger, *lsp.FoldingRangeParams) ([]lsp.FoldingRange, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentSelectionRange(context.Context, jsonrpc.FunctionLogger, *lsp.SelectionRangeParams) ([]lsp.SelectionRange, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentPrepareCallHierarchy(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyPrepareParams) ([]lsp.CallHierarchyItem, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) CallHierarchyIncomingCalls(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyIncomingCallsParams) ([]lsp.CallHierarchyIncomingCall, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) CallHierarchyOutgoingCalls(context.Context, jsonrpc.FunctionLogger, *lsp.CallHierarchyOutgoingCallsParams) ([]lsp.CallHierarchyOutgoingCall, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentSemanticTokensFull(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentSemanticTokensFullDelta(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensDeltaParams) (*lsp.SemanticTokens, *lsp.SemanticTokensDelta, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentSemanticTokensRange(context.Context, jsonrpc.FunctionLogger, *lsp.SemanticTokensRangeParams) (*lsp.SemanticTokens, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceSemanticTokensRefresh(context.Context, jsonrpc.FunctionLogger) *jsonrpc.ResponseError { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentLinkedEditingRange(context.Context, jsonrpc.FunctionLogger, *lsp.LinkedEditingRangeParams) (*lsp.LinkedEditingRanges, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentMoniker(context.Context, jsonrpc.FunctionLogger, *lsp.MonikerParams) ([]lsp.Moniker, *jsonrpc.ResponseError) { - panic("unimplemented") -} - -// Notifications from IDE -> - -func (ls *INOLanguageServer) Progress(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) Initialized(logger jsonrpc.FunctionLogger, params *lsp.InitializedParams) { +func (ls *INOLanguageServer) InitializedNotifFromIDE(logger jsonrpc.FunctionLogger, params *lsp.InitializedParams) { logger.Logf("Notification is not propagated to clangd") } -func (ls *INOLanguageServer) Exit(jsonrpc.FunctionLogger) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) SetTrace(jsonrpc.FunctionLogger, *lsp.SetTraceParams) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WindowWorkDoneProgressCancel(jsonrpc.FunctionLogger, *lsp.WorkDoneProgressCancelParams) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceDidChangeWorkspaceFolders(jsonrpc.FunctionLogger, *lsp.DidChangeWorkspaceFoldersParams) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceDidChangeConfiguration(jsonrpc.FunctionLogger, *lsp.DidChangeConfigurationParams) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceDidChangeWatchedFiles(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeWatchedFilesParams) { - ls.readLock(logger, true) - defer ls.readUnlock(logger) - // return - // err = ls.ino2cppDidChangeWatchedFilesParams(p) - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceDidCreateFiles(jsonrpc.FunctionLogger, *lsp.CreateFilesParams) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceDidRenameFiles(jsonrpc.FunctionLogger, *lsp.RenameFilesParams) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) WorkspaceDidDeleteFiles(jsonrpc.FunctionLogger, *lsp.DeleteFilesParams) { - panic("unimplemented") -} - -// Notifications from Clangd <- - -func (ls *INOLanguageServer) PublishDiagnosticsFromClangd(logger jsonrpc.FunctionLogger, cppParams *lsp.PublishDiagnosticsParams) { - // Default to read lock - ls.readLock(logger, false) - defer ls.readUnlock(logger) - - logger.Logf("publishDiagnostics(%s):", cppParams.URI) - for _, diag := range cppParams.Diagnostics { - logger.Logf("> %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... - allInoParams, err := ls.cpp2inoDiagnostics(logger, cppParams) - if err != nil { - logger.Logf(" Error converting diagnostics to .ino: %s", err) - return - } - - // Push back to IDE the converted diagnostics - for _, inoParams := range allInoParams { - logger.Logf("to IDE: publishDiagnostics(%s):", inoParams.URI) - for _, diag := range inoParams.Diagnostics { - logger.Logf("> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) - } - logger.Logf("IDE <-- LS NOTIF textDocument/publishDiagnostics:") - - if err := ls.IDEConn.TextDocumentPublishDiagnostics(inoParams); err != nil { - logger.Logf(" Error sending diagnostics to IDE: %s", err) - return - } - } -} - -func (ls *INOLanguageServer) ProgressFromClangd(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("begin %s %v", token, value) - ls.progressHandler.Begin(token, &value) - case lsp.WorkDoneProgressReport: - logger.Logf("report %s %v", token, value) - ls.progressHandler.Report(token, &value) - case lsp.WorkDoneProgressEnd: - logger.Logf("end %s %v", token, value) - ls.progressHandler.End(token, &value) - default: - logger.Logf("error unsupported $/progress: " + string(progress.Value)) - } -} - -// Requests from IDE <-> - -func (ls *INOLanguageServer) TextDocumentDidOpen(logger jsonrpc.FunctionLogger, inoParam *lsp.DidOpenTextDocumentParams) { +func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.FunctionLogger, inoParam *lsp.DidOpenTextDocumentParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) @@ -1105,7 +847,7 @@ func (ls *INOLanguageServer) TextDocumentDidOpen(logger jsonrpc.FunctionLogger, } } -func (ls *INOLanguageServer) TextDocumentDidChange(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeTextDocumentParams) { +func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeTextDocumentParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) @@ -1132,11 +874,7 @@ func (ls *INOLanguageServer) TextDocumentDidChange(logger jsonrpc.FunctionLogger } } -func (ls *INOLanguageServer) TextDocumentWillSave(jsonrpc.FunctionLogger, *lsp.WillSaveTextDocumentParams) { - panic("unimplemented") -} - -func (ls *INOLanguageServer) TextDocumentDidSave(logger jsonrpc.FunctionLogger, inoParams *lsp.DidSaveTextDocumentParams) { +func (ls *INOLanguageServer) TextDocumentDidSaveNotifFromIDE(logger jsonrpc.FunctionLogger, inoParams *lsp.DidSaveTextDocumentParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) @@ -1160,7 +898,7 @@ func (ls *INOLanguageServer) TextDocumentDidSave(logger jsonrpc.FunctionLogger, } } -func (ls *INOLanguageServer) TextDocumentDidClose(logger jsonrpc.FunctionLogger, inoParams *lsp.DidCloseTextDocumentParams) { +func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.FunctionLogger, inoParams *lsp.DidCloseTextDocumentParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) @@ -1181,9 +919,61 @@ func (ls *INOLanguageServer) TextDocumentDidClose(logger jsonrpc.FunctionLogger, } } -// Requests from Clangd <-> +func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.FunctionLogger, cppParams *lsp.PublishDiagnosticsParams) { + // Default to read lock + ls.readLock(logger, false) + defer ls.readUnlock(logger) + + logger.Logf("publishDiagnostics(%s):", cppParams.URI) + for _, diag := range cppParams.Diagnostics { + logger.Logf("> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) + } -func (ls *INOLanguageServer) WindowWorkDoneProgressCreateFromClangd(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCreateParams) *jsonrpc.ResponseError { + // the diagnostics on sketch.cpp.ino once mapped into their + // .ino counter parts may span over multiple .ino files... + allInoParams, err := ls.cpp2inoDiagnostics(logger, cppParams) + if err != nil { + logger.Logf(" Error converting diagnostics to .ino: %s", err) + return + } + + // Push back to IDE the converted diagnostics + for _, inoParams := range allInoParams { + logger.Logf("to IDE: publishDiagnostics(%s):", inoParams.URI) + for _, diag := range inoParams.Diagnostics { + logger.Logf("> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) + } + logger.Logf("IDE <-- LS NOTIF textDocument/publishDiagnostics:") + + if err := ls.IDE.conn.TextDocumentPublishDiagnostics(inoParams); err != nil { + logger.Logf(" Error sending diagnostics to IDE: %s", err) + return + } + } +} + +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("begin %s %v", token, value) + ls.progressHandler.Begin(token, &value) + case lsp.WorkDoneProgressReport: + logger.Logf("report %s %v", token, value) + ls.progressHandler.Report(token, &value) + case lsp.WorkDoneProgressEnd: + logger.Logf("end %s %v", 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) @@ -1279,12 +1069,14 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, logger.Logf(" error: %s", err) } - ls.Clangd = NewClangdLSPClient( - logger, ls.buildPath, ls.buildSketchCpp, dataFolder, - func() { - logger.Logf("Lost connection with clangd!") - ls.Close() - }, ls) + // Start clangd + ls.Clangd = NewClangdLSPClient(logger, ls.buildPath, ls.buildSketchCpp, dataFolder, ls) + go func() { + defer streams.CatchAndLogPanic() + ls.Clangd.Run() + logger.Logf("Lost connection with clangd!") + ls.Close() + }() // Send initialization command to clangd ctx, cancel := context.WithTimeout(context.Background(), time.Second) @@ -1572,44 +1364,6 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, req *lsp.D return cppReq, err } -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 len(ls.config.SelectedBoard.Name) > 0 { - board := ls.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 func() { - defer streams.CatchAndLogPanic() - ls.showMessage(logger, lsp.MessageTypeError, message) - }() - return errors.New(message) -} - func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { cppURI, err := ls.ino2cppDocumentURI(logger, doc.URI) res := doc @@ -1772,6 +1526,19 @@ func (ls *INOLanguageServer) ino2cppRange(logger jsonrpc.FunctionLogger, inoURI return cppURI, inoRange, nil } +func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, cppLocations []lsp.Location) ([]lsp.Location, error) { + inoLocations := []lsp.Location{} + for _, cppLocation := range cppLocations { + inoLocation, err := ls.cpp2inoLocation(logger, cppLocation) + if err != nil { + logger.Logf("ERROR converting location %s: %s", cppLocation, err) + return nil, err + } + inoLocations = append(inoLocations, inoLocation) + } + return inoLocations, nil +} + func (ls *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) if err != nil { @@ -1786,64 +1553,6 @@ func (ls *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger jsonrpc }, err } -// func (ls *INOLanguageServer) ino2cppDocumentOnTypeFormattingParams(params *lsp.DocumentOnTypeFormattingParams) error { -// panic("not implemented") -// ls.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) -// if data, ok := ls.data[params.TextDocument.URI]; ok { -// params.Position.Line = data.sourceMap.InoToCppLine(data.sourceURI, params.Position.Line) -// return nil -// } -// return unknownURI(params.TextDocument.URI) -// } - -// func (ls *INOLanguageServer) ino2cppRenameParams(params *lsp.RenameParams) error { -// ls.sketchToBuildPathTextDocumentIdentifier(¶ms.TextDocument) -// if data, ok := ls.data[params.TextDocument.URI]; ok { -// params.Position.Line = data.sourceMap.InoToCppLine(data.sourceURI, params.Position.Line) -// return nil -// } -// return unknownURI(params.TextDocument.URI) -// } - -// func (ls *INOLanguageServer) ino2cppDidChangeWatchedFilesParams(params *lsp.DidChangeWatchedFilesParams) error { -// for index := range params.Changes { -// fileEvent := ¶ms.Changes[index] -// if data, ok := ls.data[fileEvent.URI]; ok { -// fileEvent.URI = data.targetURI -// } -// } -// return nil -// } - -// func (ls *INOLanguageServer) ino2cppExecuteCommand(executeCommand *lsp.ExecuteCommandParams) error { -// if len(executeCommand.Arguments) == 1 { -// arg := ls.parseCommandArgument(executeCommand.Arguments[0]) -// if workspaceEdit, ok := arg.(*lsp.WorkspaceEdit); ok { -// executeCommand.Arguments[0] = ls.ino2cppWorkspaceEdit(workspaceEdit) -// } -// } -// return nil -// } - -// func (ls *INOLanguageServer) ino2cppWorkspaceEdit(origEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { -// newEdit := lsp.WorkspaceEdit{} -// for uri, edit := range origEdit.Changes { -// if data, ok := ls.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 (ls *INOLanguageServer) cpp2inoCodeAction(logger jsonrpc.FunctionLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { inoCodeAction := lsp.CodeAction{ Title: codeAction.Title, @@ -2029,27 +1738,6 @@ func (ls *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.FunctionLogge func (ls *INOLanguageServer) 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 { - // ls.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 (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { @@ -2123,51 +1811,6 @@ func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, c return inoDiagParams, nil } -// // HandleRequestFromClangd handles a request message received from clangd. -// func (ls *INOLanguageServer) HandleRequestFromClangd(ctx context.Context, logger jsonrpc.FunctionLogger, -// method string, paramsRaw json.RawMessage, -// respCallback func(result json.RawMessage, err *jsonrpc.ResponseError), -// ) { -// // n := atomic.AddInt64(&ls.clangdMessageCount, 1) -// // prefix := fmt.Sprintf("CLG <-- %s %v ", method, n) -// params, err := lsp.DecodeServerRequestParams(method, paramsRaw) -// if err != nil { -// logger.Logf("Error parsing clang message: %v", err) -// respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) -// return -// } -// panic("unimplemented") -// // Default to read lock -// ls.readLock(logger, false) -// defer ls.readUnlock(logger) -// switch p := params.(type) { -// case *lsp.ApplyWorkspaceEditParams: -// // "workspace/applyEdit" -// p.Edit = *ls.cpp2inoWorkspaceEdit(logger, &p.Edit) -// } -// if err != nil { -// logger.Logf("From clangd: Method: %s, Error: %v", method, err) -// respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) -// return -// } -// // respRaw := lsp.EncodeMessage(params) -// // if params == nil { -// // // passthrough -// // logger.Logf("passing through message") -// // respRaw = paramsRaw -// // } - -// // logger.Logf("IDE <-- LS REQ %s", method) -// // resp, respErr, err := ls.IDEConn.SendRequest(ctx, method, respRaw) -// // if err != nil { -// // logger.Logf("Error sending request to IDE:", err) -// // respCallback(nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()}) -// // return -// // } -// // logger.Logf("IDE --> LS REQ %s", method) -// // respCallback(resp, respErr) -// } - 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. @@ -2320,7 +1963,6 @@ 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) @@ -2353,16 +1995,6 @@ WhitespaceSensitiveMacros: [] return cleanup, err } -func (ls *INOLanguageServer) showMessage(logger jsonrpc.FunctionLogger, msgType lsp.MessageType, message string) { - params := lsp.ShowMessageParams{ - Type: msgType, - Message: message, - } - if err := ls.IDEConn.WindowShowMessage(¶ms); err != nil { - logger.Logf("error sending showMessage notification: %s", err) - } -} - func unknownURI(uri lsp.DocumentURI) error { return errors.New("Document is not available: " + uri.String()) } diff --git a/ls/lsp_client_clangd.go b/ls/lsp_client_clangd.go index dc51337..2c0e062 100644 --- a/ls/lsp_client_clangd.go +++ b/ls/lsp_client_clangd.go @@ -20,9 +20,9 @@ type ClangdLSPClient struct { ls *INOLanguageServer } -func NewClangdLSPClient(logger jsonrpc.FunctionLogger, +func NewClangdLSPClient( + logger jsonrpc.FunctionLogger, buildPath, buildSketchCpp, dataFolder *paths.Path, - connectionClosedCB func(), inoLanguageServer *INOLanguageServer, ) *ClangdLSPClient { clangdStdout, clangdStdin, clangdStderr := startClangd(logger, buildPath, buildSketchCpp, dataFolder) @@ -39,12 +39,6 @@ func NewClangdLSPClient(logger jsonrpc.FunctionLogger, } client.conn = lsp.NewClient(clangdStdio, clangdStdio, client) client.conn.SetLogger(&LSPLogger{IncomingPrefix: "IDE LS <-- Clangd", OutgoingPrefix: "IDE LS --> Clangd"}) - go func() { - defer streams.CatchAndLogPanic() - client.conn.Run() - connectionClosedCB() - }() - return client } @@ -74,6 +68,10 @@ func startClangd(logger jsonrpc.FunctionLogger, compileCommandsDir, sketchCpp, d } } +func (client *ClangdLSPClient) Run() { + client.conn.Run() +} + func (client *ClangdLSPClient) Close() { panic("unimplemented") } @@ -89,7 +87,7 @@ func (client *ClangdLSPClient) WindowShowDocument(context.Context, jsonrpc.Funct } func (client *ClangdLSPClient) WindowWorkDoneProgressCreate(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCreateParams) *jsonrpc.ResponseError { - return client.ls.WindowWorkDoneProgressCreateFromClangd(ctx, logger, params) + return client.ls.WindowWorkDoneProgressCreateReqFromClangd(ctx, logger, params) } func (client *ClangdLSPClient) ClientRegisterCapability(context.Context, jsonrpc.FunctionLogger, *lsp.RegistrationParams) *jsonrpc.ResponseError { @@ -117,7 +115,7 @@ func (client *ClangdLSPClient) WorkspaceCodeLensRefresh(context.Context, jsonrpc } func (client *ClangdLSPClient) Progress(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { - client.ls.ProgressFromClangd(logger, progress) + client.ls.ProgressNotifFromClangd(logger, progress) } func (client *ClangdLSPClient) LogTrace(jsonrpc.FunctionLogger, *lsp.LogTraceParams) { @@ -137,5 +135,5 @@ func (client *ClangdLSPClient) TelemetryEvent(jsonrpc.FunctionLogger, json.RawMe } func (client *ClangdLSPClient) TextDocumentPublishDiagnostics(logger jsonrpc.FunctionLogger, params *lsp.PublishDiagnosticsParams) { - client.ls.PublishDiagnosticsFromClangd(logger, params) + client.ls.PublishDiagnosticsNotifFromClangd(logger, params) } diff --git a/ls/lsp_server_ide.go b/ls/lsp_server_ide.go new file mode 100644 index 0000000..25e3905 --- /dev/null +++ b/ls/lsp_server_ide.go @@ -0,0 +1,262 @@ +package ls + +import ( + "context" + "io" + + "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"}) + 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 { + panic("unimplemented") +} + +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) { + panic("unimplemented") +} + +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) { + panic("unimplemented") +} + +func (server *IDELSPServer) SetTrace(logger jsonrpc.FunctionLogger, params *lsp.SetTraceParams) { + panic("unimplemented") +} + +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/ls/unused.go b/ls/unused.go new file mode 100644 index 0000000..e1c90f3 --- /dev/null +++ b/ls/unused.go @@ -0,0 +1,59 @@ +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 len(ls.config.SelectedBoard.Name) > 0 { + board := ls.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 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) + } +} From be08b32ad6e6fc9d1f7c76e83176ef7316cbd94c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 4 Nov 2021 15:58:59 +0100 Subject: [PATCH 08/76] Handled events 'shutdown' and 'exit' --- ls/ls.go | 12 ++++++++++++ ls/lsp_client_clangd.go | 3 ++- ls/lsp_server_ide.go | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 696a9f4..f733999 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -308,6 +308,14 @@ func (handler *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logg return resp, nil } +func (ls *INOLanguageServer) ShutdownReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger) *jsonrpc.ResponseError { + logger.Logf("Sending shutdown notification to clangd...") + ls.Clangd.conn.Shutdown(context.Background()) + logger.Logf("Arduino Language Server is shutting down.") + ls.Close() + return nil +} + func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CompletionParams) (*lsp.CompletionList, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -815,6 +823,10 @@ func (ls *INOLanguageServer) InitializedNotifFromIDE(logger jsonrpc.FunctionLogg logger.Logf("Notification is not propagated to clangd") } +func (ls *INOLanguageServer) ExitNotifFromIDE(logger jsonrpc.FunctionLogger) { + logger.Logf("Notification is not propagated to clangd") +} + func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.FunctionLogger, inoParam *lsp.DidOpenTextDocumentParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) diff --git a/ls/lsp_client_clangd.go b/ls/lsp_client_clangd.go index 2c0e062..6ee0e23 100644 --- a/ls/lsp_client_clangd.go +++ b/ls/lsp_client_clangd.go @@ -73,7 +73,8 @@ func (client *ClangdLSPClient) Run() { } func (client *ClangdLSPClient) Close() { - panic("unimplemented") + client.conn.Exit() // send "exit" notification to Clangd + // TODO: kill client.conn } // The following are events incoming from Clangd diff --git a/ls/lsp_server_ide.go b/ls/lsp_server_ide.go index 25e3905..c92823c 100644 --- a/ls/lsp_server_ide.go +++ b/ls/lsp_server_ide.go @@ -32,7 +32,7 @@ func (server *IDELSPServer) Initialize(ctx context.Context, logger jsonrpc.Funct } func (server *IDELSPServer) Shutdown(ctx context.Context, logger jsonrpc.FunctionLogger) *jsonrpc.ResponseError { - panic("unimplemented") + return server.ls.ShutdownReqFromIDE(ctx, logger) } func (server *IDELSPServer) WorkspaceSymbol(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, *jsonrpc.ResponseError) { @@ -206,7 +206,7 @@ func (server *IDELSPServer) Initialized(logger jsonrpc.FunctionLogger, params *l } func (server *IDELSPServer) Exit(logger jsonrpc.FunctionLogger) { - panic("unimplemented") + server.ls.ExitNotifFromIDE(logger) } func (server *IDELSPServer) SetTrace(logger jsonrpc.FunctionLogger, params *lsp.SetTraceParams) { From 270c49c095bebd823d326436b1fa3231811bf95e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 4 Nov 2021 16:03:49 +0100 Subject: [PATCH 09/76] Made logging color more coherent --- ls/lsp_client_clangd.go | 7 ++++++- ls/lsp_logger.go | 21 +++++++++++---------- ls/lsp_server_ide.go | 7 ++++++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/ls/lsp_client_clangd.go b/ls/lsp_client_clangd.go index 6ee0e23..9c7c8fa 100644 --- a/ls/lsp_client_clangd.go +++ b/ls/lsp_client_clangd.go @@ -10,6 +10,7 @@ import ( "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" @@ -38,7 +39,11 @@ func NewClangdLSPClient( ls: inoLanguageServer, } client.conn = lsp.NewClient(clangdStdio, clangdStdio, client) - client.conn.SetLogger(&LSPLogger{IncomingPrefix: "IDE LS <-- Clangd", OutgoingPrefix: "IDE LS --> Clangd"}) + client.conn.SetLogger(&LSPLogger{ + IncomingPrefix: "IDE LS <-- Clangd", + OutgoingPrefix: "IDE LS --> Clangd", + HiColor: color.HiRedString, + LoColor: color.RedString}) return client } diff --git a/ls/lsp_logger.go b/ls/lsp_logger.go index 1f73a2d..65ca457 100644 --- a/ls/lsp_logger.go +++ b/ls/lsp_logger.go @@ -11,40 +11,41 @@ import ( type LSPLogger struct { IncomingPrefix, OutgoingPrefix string + HiColor, LoColor func(format string, a ...interface{}) string } func (l *LSPLogger) LogOutgoingRequest(id string, method string, params json.RawMessage) { - log.Print(color.HiGreenString("%s REQU %s %s", l.OutgoingPrefix, method, id)) + log.Print(l.HiColor("%s REQU %s %s", l.OutgoingPrefix, method, id)) } func (l *LSPLogger) LogOutgoingCancelRequest(id string) { - log.Print(color.GreenString("%s CANCEL %s", l.OutgoingPrefix, id)) + log.Print(l.LoColor("%s CANCEL %s", l.OutgoingPrefix, id)) } func (l *LSPLogger) LogIncomingResponse(id string, method string, resp json.RawMessage, respErr *jsonrpc.ResponseError) { - log.Print(color.GreenString("%s RESP %s %s", l.IncomingPrefix, method, id)) + log.Print(l.LoColor("%s RESP %s %s", l.IncomingPrefix, method, id)) } func (l *LSPLogger) LogOutgoingNotification(method string, params json.RawMessage) { - log.Print(color.HiGreenString("%s NOTIF %s", l.OutgoingPrefix, method)) + 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(color.HiRedString(fmt.Sprintf("%s REQU %s %s", l.IncomingPrefix, method, id))) + log.Print(l.HiColor(fmt.Sprintf("%s REQU %s %s", l.IncomingPrefix, method, id))) return &LSPFunctionLogger{ - colorFunc: color.HiRedString, + colorFunc: l.HiColor, prefix: fmt.Sprintf("%s %s %s", spaces[:len(l.IncomingPrefix)], method, id), } } func (l *LSPLogger) LogIncomingCancelRequest(id string) { - log.Print(color.RedString("%s CANCEL %s", l.IncomingPrefix, id)) + log.Print(l.LoColor("%s CANCEL %s", l.IncomingPrefix, id)) } func (l *LSPLogger) LogOutgoingResponse(id string, method string, resp json.RawMessage, respErr *jsonrpc.ResponseError) { - log.Print(color.RedString("%s RESP %s %s", l.OutgoingPrefix, method, id)) + log.Print(l.LoColor("%s RESP %s %s", l.OutgoingPrefix, method, id)) } func (l *LSPLogger) LogIncomingNotification(method string, params json.RawMessage) jsonrpc.FunctionLogger { spaces := " " - log.Print(color.HiRedString(fmt.Sprintf("%s NOTIF %s", l.IncomingPrefix, method))) + log.Print(l.HiColor(fmt.Sprintf("%s NOTIF %s", l.IncomingPrefix, method))) return &LSPFunctionLogger{ - colorFunc: color.HiRedString, + colorFunc: l.HiColor, prefix: fmt.Sprintf("%s %s", spaces[:len(l.IncomingPrefix)], method), } } diff --git a/ls/lsp_server_ide.go b/ls/lsp_server_ide.go index c92823c..eae71b9 100644 --- a/ls/lsp_server_ide.go +++ b/ls/lsp_server_ide.go @@ -4,6 +4,7 @@ import ( "context" "io" + "github.com/fatih/color" "go.bug.st/json" "go.bug.st/lsp" "go.bug.st/lsp/jsonrpc" @@ -19,7 +20,11 @@ func NewIDELSPServer(logger jsonrpc.FunctionLogger, in io.Reader, out io.Writer, ls: ls, } server.conn = lsp.NewServer(in, out, server) - server.conn.SetLogger(&LSPLogger{IncomingPrefix: "IDE --> LS", OutgoingPrefix: "IDE <-- LS"}) + server.conn.SetLogger(&LSPLogger{ + IncomingPrefix: "IDE --> LS", + OutgoingPrefix: "IDE <-- LS", + HiColor: color.HiGreenString, + LoColor: color.GreenString}) return server } From cfd57dd6657d8148243eb7e6f8a173b53f093167 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 4 Nov 2021 16:44:11 +0100 Subject: [PATCH 10/76] Improved progress logging --- ls/ls.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index f733999..9cd138b 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -972,13 +972,13 @@ func (ls *INOLanguageServer) ProgressNotifFromClangd(logger jsonrpc.FunctionLogg } switch value := progress.TryToDecodeWellKnownValues().(type) { case lsp.WorkDoneProgressBegin: - logger.Logf("begin %s %v", token, value) + logger.Logf("%s %s", token, value) ls.progressHandler.Begin(token, &value) case lsp.WorkDoneProgressReport: - logger.Logf("report %s %v", token, value) + logger.Logf("%s %s", token, value) ls.progressHandler.Report(token, &value) case lsp.WorkDoneProgressEnd: - logger.Logf("end %s %v", token, value) + logger.Logf("%s %s", token, value) ls.progressHandler.End(token, &value) default: logger.Logf("error unsupported $/progress: " + string(progress.Value)) From fca69e3e4985cb3d813bc27f5c1126f7e68a40fa Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 5 Nov 2021 10:00:29 +0100 Subject: [PATCH 11/76] removed dependency --- go.mod | 1 - go.sum | 4 ---- 2 files changed, 5 deletions(-) diff --git a/go.mod b/go.mod index b749291..f86e8be 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/arduino/go-paths-helper v1.6.1 github.com/fatih/color v1.7.0 github.com/pkg/errors v0.9.1 - github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 github.com/stretchr/testify v1.6.1 go.bug.st/json v1.0.0 go.bug.st/lsp v0.0.0-00010101000000-000000000000 diff --git a/go.sum b/go.sum index a8c2161..ba1bf0c 100644 --- a/go.sum +++ b/go.sum @@ -93,8 +93,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 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= @@ -198,8 +196,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 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= From 83860ca0ef479b566cd4fa09d058b51230997899 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 5 Nov 2021 10:00:56 +0100 Subject: [PATCH 12/76] Load CPP symbols only after adding the file --- ls/ls.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ls/ls.go b/ls/ls.go index 9cd138b..ea4fea2 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -845,6 +845,9 @@ func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.Func logger.Logf("Clang already notified, do not notify it anymore") return } + + // Queue a load of ino.cpp document symbols + ls.queueLoadCppDocumentSymbols() } if cppItem, err := ls.ino2cppTextDocumentItem(logger, inoTextDocItem); err != nil { @@ -1111,7 +1114,6 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, } } - ls.queueLoadCppDocumentSymbols() return nil } From a82b525cb1dcbc8d315fdcc1610ff23d1ced136f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 8 Nov 2021 11:22:17 +0100 Subject: [PATCH 13/76] Renamed fields and improved didChange handling --- ls/builder.go | 3 +- ls/ls.go | 143 +++++++++++++++++++++++++------------------------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 0a5d8fe..5f3aafc 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -94,7 +94,7 @@ func (handler *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.Functi Overrides map[string]string `json:"overrides"` } data := overridesFile{Overrides: map[string]string{}} - for uri, trackedFile := range handler.docs { + for uri, trackedFile := range handler.trackedInoDocs { rel, err := paths.New(uri).RelFrom(handler.sketchRoot) if err != nil { return errors.WithMessage(err, "dumping tracked files") @@ -107,6 +107,7 @@ func (handler *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.Functi } else if tmpFile, err := paths.WriteToTempFile(jsonBytes, nil, ""); err != nil { return errors.WithMessage(err, "dumping tracked files") } else { + // logger.Logf("Dumped overrides: %s", string(jsonBytes)) overridesJSON = tmpFile defer tmpFile.Remove() } diff --git a/ls/ls.go b/ls/ls.go index ea4fea2..91d45d3 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -56,7 +56,6 @@ type INOLanguageServer struct { buildPath *paths.Path buildSketchRoot *paths.Path buildSketchCpp *paths.Path - buildSketchCppVersion int buildSketchSymbols []lsp.DocumentSymbol buildSketchIncludesCanary string buildSketchSymbolsCanary string @@ -68,7 +67,7 @@ type INOLanguageServer struct { sketchName string sketchMapper *sourcemapper.InoMapper sketchTrackedFilesCount int - docs map[string]lsp.TextDocumentItem + trackedInoDocs map[string]lsp.TextDocumentItem inoDocsWithDiagnostics map[lsp.DocumentURI]bool config BoardConfig @@ -147,7 +146,7 @@ func (ls *INOLanguageServer) waitClangdStart(logger jsonrpc.FunctionLogger) erro func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLanguageServer { logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") ls := &INOLanguageServer{ - docs: map[string]lsp.TextDocumentItem{}, + trackedInoDocs: map[string]lsp.TextDocumentItem{}, inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, closing: make(chan bool), buildSketchSymbolsLoad: make(chan bool, 1), @@ -833,7 +832,7 @@ func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.Func // Add the TextDocumentItem in the tracked files list inoTextDocItem := inoParam.TextDocument - ls.docs[inoTextDocItem.URI.AsPath().String()] = inoTextDocItem + ls.trackedInoDocs[inoTextDocItem.URI.AsPath().String()] = inoTextDocItem // If we are tracking a .ino... if inoTextDocItem.URI.Ext() == ".ino" { @@ -1044,7 +1043,6 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, return err } ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") - ls.buildSketchCppVersion = 1 ls.lspInitializeParams.RootPath = ls.buildSketchRoot.String() ls.lspInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) @@ -1255,8 +1253,8 @@ func canonicalizeCompileCommandsJSON(compileCommandsDir *paths.Path) map[string] func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { inoIdentifier := inoDidClose.TextDocument - if _, exist := ls.docs[inoIdentifier.URI.AsPath().String()]; exist { - delete(ls.docs, inoIdentifier.URI.AsPath().String()) + if _, exist := ls.trackedInoDocs[inoIdentifier.URI.AsPath().String()]; exist { + delete(ls.trackedInoDocs, inoIdentifier.URI.AsPath().String()) } else { logger.Logf(" didClose of untracked document: %s", inoIdentifier.URI) return nil, unknownURI(inoIdentifier.URI) @@ -1293,89 +1291,90 @@ func (ls *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogg } else { cppItem.LanguageID = inoItem.LanguageID inoPath := inoItem.URI.AsPath().String() - cppItem.Text = ls.docs[inoPath].Text - cppItem.Version = ls.docs[inoPath].Version + cppItem.Text = ls.trackedInoDocs[inoPath].Text + cppItem.Version = ls.trackedInoDocs[inoPath].Version } return cppItem, nil } -func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, req *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { - doc := req.TextDocument +func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChangeParams *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { + inoDoc := inoDidChangeParams.TextDocument - trackedDoc, ok := ls.docs[doc.URI.AsPath().String()] + // Apply the change to the tracked sketch file. + trackedInoID := inoDoc.URI.AsPath().String() + trackedInoDoc, ok := ls.trackedInoDocs[trackedInoID] if !ok { - return nil, unknownURI(doc.URI) + return nil, unknownURI(inoDoc.URI) + } + if updatedTrackedInoDoc, err := textutils.ApplyLSPTextDocumentContentChangeEvent(trackedInoDoc, inoDidChangeParams.ContentChanges, inoDoc.Version); err != nil { + return nil, err + } else { + ls.trackedInoDocs[trackedInoID] = updatedTrackedInoDoc + } + + logger.Logf("Tracked SKETCH file:----------+\n" + ls.trackedInoDocs[trackedInoID].Text + "\n----------------------") + + // If the file is not part of a .ino flie forward the change as-is to clangd + if inoDoc.URI.Ext() != ".ino" { + if cppDoc, err := ls.ino2cppVersionedTextDocumentIdentifier(logger, inoDidChangeParams.TextDocument); err != nil { + return nil, err + } else { + cppDidChangeParams := *inoDidChangeParams + cppDidChangeParams.TextDocument = cppDoc + return &cppDidChangeParams, nil + } } - 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 := ls.sketchMapper.InoToCppLSPRangeOk(doc.URI, inoChange.Range) - if !ok { - return nil, errors.Errorf("invalid change range %s:%s", doc.URI, inoChange.Range) - } + cppChanges := []lsp.TextDocumentContentChangeEvent{} + for _, inoChange := range inoDidChangeParams.ContentChanges { + cppChangeRange, ok := ls.sketchMapper.InoToCppLSPRangeOk(inoDoc.URI, inoChange.Range) + if !ok { + return nil, errors.Errorf("invalid change range %s:%s", inoDoc.URI, inoChange.Range) + } - // Detect changes in critical lines (for example function definitions) - // and trigger arduino-preprocessing + clangd restart. - dirty := false - for _, sym := range ls.buildSketchSymbols { - if sym.SelectionRange.Overlaps(cppRange) { - dirty = true - logger.Logf("--! DIRTY CHANGE detected using symbol tables, force sketch rebuild!") - break - } - } - if ls.sketchMapper.ApplyTextChange(doc.URI, inoChange) { + // Detect changes in critical lines (for example function definitions) + // and trigger arduino-preprocessing + clangd restart. + dirty := false + for _, sym := range ls.buildSketchSymbols { + if sym.SelectionRange.Overlaps(cppChangeRange) { dirty = true - logger.Logf("--! DIRTY CHANGE detected with sketch mapper, force sketch rebuild!") - } - if dirty { - ls.scheduleRebuildEnvironment() - } - - // logger.Logf("New version:----------") - // logger.Logf(ls.sketchMapper.CppText.Text) - // logger.Logf("----------------------") - - cppChange := lsp.TextDocumentContentChangeEvent{ - Range: cppRange, - RangeLength: inoChange.RangeLength, - Text: inoChange.Text, + logger.Logf("--! DIRTY CHANGE detected using symbol tables, force sketch rebuild!") + break } - cppChanges = append(cppChanges, cppChange) } - - ls.CheckCppIncludesChanges() - - // build a cpp equivalent didChange request - cppReq := &lsp.DidChangeTextDocumentParams{ - ContentChanges: cppChanges, - TextDocument: lsp.VersionedTextDocumentIdentifier{ - TextDocumentIdentifier: lsp.TextDocumentIdentifier{ - URI: lsp.NewDocumentURIFromPath(ls.buildSketchCpp), - }, - Version: ls.sketchMapper.CppText.Version, - }, + if ls.sketchMapper.ApplyTextChange(inoDoc.URI, inoChange) { + dirty = true + logger.Logf("--! DIRTY CHANGE detected with sketch mapper, force sketch rebuild!") + } + if dirty { + ls.scheduleRebuildEnvironment() } - return cppReq, nil - } + logger.Logf("New version:----------+\n" + ls.sketchMapper.CppText.Text + "\n----------------------") - // If changes are applied to other files pass them by converting just the URI - cppDoc, err := ls.ino2cppVersionedTextDocumentIdentifier(logger, req.TextDocument) - if err != nil { - return nil, err - } - cppReq := &lsp.DidChangeTextDocumentParams{ - TextDocument: cppDoc, - ContentChanges: req.ContentChanges, + cppChanges = append(cppChanges, lsp.TextDocumentContentChangeEvent{ + Range: cppChangeRange, + RangeLength: inoChange.RangeLength, + Text: inoChange.Text, + }) } - return cppReq, err + + ls.CheckCppIncludesChanges() + + // build a cpp equivalent didChange request + return &lsp.DidChangeTextDocumentParams{ + ContentChanges: cppChanges, + TextDocument: lsp.VersionedTextDocumentIdentifier{ + TextDocumentIdentifier: lsp.TextDocumentIdentifier{ + URI: lsp.NewDocumentURIFromPath(ls.buildSketchCpp), + }, + Version: ls.sketchMapper.CppText.Version, + }, + }, nil } func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { @@ -1429,11 +1428,11 @@ func (ls *INOLanguageServer) inoDocumentURIFromInoPath(logger jsonrpc.FunctionLo if inoPath == sourcemapper.NotIno.File { return sourcemapper.NotInoURI, nil } - doc, ok := ls.docs[inoPath] + doc, ok := ls.trackedInoDocs[inoPath] if !ok { logger.Logf(" !!! Unresolved .ino path: %s", inoPath) logger.Logf(" !!! Known doc paths are:") - for p := range ls.docs { + for p := range ls.trackedInoDocs { logger.Logf(" !!! > %s", p) } uri := lsp.NewDocumentURI(inoPath) From fb4b24270274868a1e9fbc5dc18b912e80d364ff Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 8 Nov 2021 11:43:26 +0100 Subject: [PATCH 14/76] improved logging and some refactorings --- ls/ls.go | 101 +++++++++++++++++++++++------------------ textutils/textutils.go | 12 ++--- 2 files changed, 63 insertions(+), 50 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 91d45d3..0807ef4 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -892,12 +892,12 @@ func (ls *INOLanguageServer) TextDocumentDidSaveNotifFromIDE(logger jsonrpc.Func ls.writeLock(logger, true) defer ls.writeUnlock(logger) - logger.Logf("didSave(%s)", inoParams.TextDocument) + logger.Logf("didSave(%s) hasText=%v", inoParams.TextDocument, inoParams.Text != "") if cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument); err != nil { logger.Logf("--E Error: %s", err) } else if cppTextDocument.URI.AsPath().EquivalentTo(ls.buildSketchCpp) { logger.Logf(" didSave(%s) equals %s", cppTextDocument, ls.buildSketchCpp) - logger.Logf("--| didSave not forwarded to clangd") + logger.Logf(" the notification will be not forwarded to clangd") } else { logger.Logf("LS --> CL NOTIF didSave(%s)", cppTextDocument) if err := ls.Clangd.conn.TextDocumentDidSave(&lsp.DidSaveTextDocumentParams{ @@ -938,7 +938,7 @@ func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.Fu ls.readLock(logger, false) defer ls.readUnlock(logger) - logger.Logf("publishDiagnostics(%s):", cppParams.URI) + logger.Logf("from clang %s (%d diagnostics):", cppParams.URI, cppParams.Diagnostics) for _, diag := range cppParams.Diagnostics { logger.Logf("> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) } @@ -953,12 +953,10 @@ func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.Fu // Push back to IDE the converted diagnostics for _, inoParams := range allInoParams { - logger.Logf("to IDE: publishDiagnostics(%s):", inoParams.URI) + logger.Logf("to IDE: %s (%d diagnostics):", inoParams.URI, len(inoParams.Diagnostics)) for _, diag := range inoParams.Diagnostics { - logger.Logf("> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) + logger.Logf(" > %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) } - logger.Logf("IDE <-- LS NOTIF textDocument/publishDiagnostics:") - if err := ls.IDE.conn.TextDocumentPublishDiagnostics(inoParams); err != nil { logger.Logf(" Error sending diagnostics to IDE: %s", err) return @@ -1265,7 +1263,7 @@ func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose ls.sketchTrackedFilesCount-- logger.Logf(" decreasing .ino tracked files count: %d", ls.sketchTrackedFilesCount) - // notify clang that sketchCpp has been close only once all .ino are closed + // notify clang that sketch.cpp.ino has been closed only once all .ino are closed if ls.sketchTrackedFilesCount != 0 { return nil, nil } @@ -1753,31 +1751,50 @@ func (ls *INOLanguageServer) cpp2inoSymbolInformation(syms []lsp.SymbolInformati panic("not implemented") } -func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, cppDiags *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { - inoDiagsParam := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} +func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, cppDiagsParams *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { - cppURI := cppDiags.URI + cppURI := cppDiagsParams.URI isSketch := cppURI.AsPath().EquivalentTo(ls.buildSketchCpp) - if isSketch { - for inoURI := range ls.inoDocsWithDiagnostics { - inoDiagsParam[inoURI] = &lsp.PublishDiagnosticsParams{ - URI: inoURI, - Diagnostics: []lsp.Diagnostic{}, - } - } - ls.inoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} - } else { + + if !isSketch { inoURI, _, err := ls.cpp2inoDocumentURI(logger, cppURI, lsp.NilRange) if err != nil { return nil, err } - inoDiagsParam[inoURI] = &lsp.PublishDiagnosticsParams{ + inoDiags := []lsp.Diagnostic{} + for _, cppDiag := range cppDiagsParams.Diagnostics { + inoURIofConvertedRange, inoRange, err := ls.cpp2inoDocumentURI(logger, cppURI, cppDiag.Range) + if err != nil { + return nil, err + } + if inoURIofConvertedRange.String() == sourcemapper.NotInoURI.String() { + continue + } + if inoURIofConvertedRange.String() != inoURI.String() { + return nil, fmt.Errorf("unexpected inoURI %s: it should be %s", inoURIofConvertedRange, inoURI) + } + inoDiag := cppDiag + inoDiag.Range = inoRange + inoDiags = append(inoDiags, inoDiag) + } + return []*lsp.PublishDiagnosticsParams{ + { + URI: inoURI, + Diagnostics: inoDiags, + }, + }, nil + } + + allInoDiagsParams := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} + for inoURI := range ls.inoDocsWithDiagnostics { + allInoDiagsParams[inoURI] = &lsp.PublishDiagnosticsParams{ URI: inoURI, Diagnostics: []lsp.Diagnostic{}, } } + ls.inoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} - for _, cppDiag := range cppDiags.Diagnostics { + for _, cppDiag := range cppDiagsParams.Diagnostics { inoURI, inoRange, err := ls.cpp2inoDocumentURI(logger, cppURI, cppDiag.Range) if err != nil { return nil, err @@ -1786,39 +1803,37 @@ func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, c continue } - inoDiagParam, created := inoDiagsParam[inoURI] - if !created { - inoDiagParam = &lsp.PublishDiagnosticsParams{ + inoDiagsParams, ok := allInoDiagsParams[inoURI] + if !ok { + inoDiagsParams = &lsp.PublishDiagnosticsParams{ URI: inoURI, Diagnostics: []lsp.Diagnostic{}, } - inoDiagsParam[inoURI] = inoDiagParam + allInoDiagsParams[inoURI] = inoDiagsParams } inoDiag := cppDiag inoDiag.Range = inoRange - inoDiagParam.Diagnostics = append(inoDiagParam.Diagnostics, inoDiag) - - if isSketch { - ls.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). - var inoDiagCode string - if err := json.Unmarshal(inoDiag.Code, &inoDiagCode); err != nil { - if inoDiagCode == "undeclared_var_use_suggest" || - inoDiagCode == "undeclared_var_use" || - inoDiagCode == "ovl_no_viable_function_in_call" || - inoDiagCode == "pp_file_not_found" { - ls.queueCheckCppDocumentSymbols() - } + inoDiagsParams.Diagnostics = append(inoDiagsParams.Diagnostics, inoDiag) + + ls.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). + var inoDiagCode string + if err := json.Unmarshal(inoDiag.Code, &inoDiagCode); err != nil { + if inoDiagCode == "undeclared_var_use_suggest" || + inoDiagCode == "undeclared_var_use" || + inoDiagCode == "ovl_no_viable_function_in_call" || + inoDiagCode == "pp_file_not_found" { + ls.queueCheckCppDocumentSymbols() } } } inoDiagParams := []*lsp.PublishDiagnosticsParams{} - for _, v := range inoDiagsParam { + for _, v := range allInoDiagsParams { inoDiagParams = append(inoDiagParams, v) } return inoDiagParams, nil diff --git a/textutils/textutils.go b/textutils/textutils.go index fa46d0b..8668e73 100644 --- a/textutils/textutils.go +++ b/textutils/textutils.go @@ -7,18 +7,16 @@ import ( ) // ApplyLSPTextDocumentContentChangeEvent applies the LSP change in the given text -func ApplyLSPTextDocumentContentChangeEvent(textDoc *lsp.TextDocumentItem, changes []lsp.TextDocumentContentChangeEvent, version int) error { - newText := textDoc.Text +func ApplyLSPTextDocumentContentChangeEvent(textDoc lsp.TextDocumentItem, changes []lsp.TextDocumentContentChangeEvent, version int) (lsp.TextDocumentItem, error) { for _, change := range changes { - if t, err := ApplyTextChange(newText, change.Range, change.Text); err == nil { - newText = t + if t, err := ApplyTextChange(textDoc.Text, change.Range, change.Text); err == nil { + textDoc.Text = t } else { - return err + return lsp.TextDocumentItem{}, err } } - textDoc.Text = newText textDoc.Version = version - return nil + return textDoc, nil } // ApplyTextChange replaces startingText substring specified by replaceRange with insertText From 592b11bc513a81964a7e24ff9b901d0f6e386fc7 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 8 Nov 2021 11:44:13 +0100 Subject: [PATCH 15/76] fix: when rebuilding sketch file send a didSave notification instead of a didChange --- ls/ls.go | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 0807ef4..eb0eb2a 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1056,20 +1056,13 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, if params == nil { // If we are restarting re-synchronize clangd cppURI := lsp.NewDocumentURIFromPath(ls.buildSketchCpp) - cppTextDocumentIdentifier := lsp.TextDocumentIdentifier{URI: cppURI} - syncEvent := &lsp.DidChangeTextDocumentParams{ - TextDocument: lsp.VersionedTextDocumentIdentifier{ - TextDocumentIdentifier: cppTextDocumentIdentifier, - Version: ls.sketchMapper.CppText.Version, - }, - ContentChanges: []lsp.TextDocumentContentChangeEvent{ - {Text: ls.sketchMapper.CppText.Text}, // Full text change - }, + logger.Logf("LS --> CL NOTIF textDocument/didSave:") + didSaveParams := &lsp.DidSaveTextDocumentParams{ + TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, + Text: ls.sketchMapper.CppText.Text, } - - logger.Logf("LS --> CL NOTIF textDocument/didChange:") - if err := ls.Clangd.conn.TextDocumentDidChange(syncEvent); err != nil { + if err := ls.Clangd.conn.TextDocumentDidSave(didSaveParams); err != nil { logger.Logf(" error reinitilizing clangd:", err) return err } From 7f463d83162e5e85d57842d1876694796560676c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 9 Nov 2021 09:15:56 +0100 Subject: [PATCH 16/76] Renamed InoMapper -< SketchMapper --- ls/ls.go | 2 +- sourcemapper/ino.go | 146 ++++++++++-------- sourcemapper/ino_test.go | 72 +++++---- .../testdata/sketch_july2a/sketch_july2a.ino | 18 +++ 4 files changed, 138 insertions(+), 100 deletions(-) create mode 100644 sourcemapper/testdata/sketch_july2a/sketch_july2a.ino diff --git a/ls/ls.go b/ls/ls.go index eb0eb2a..77d549e 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -65,7 +65,7 @@ type INOLanguageServer struct { rebuildSketchDeadlineMutex sync.Mutex sketchRoot *paths.Path sketchName string - sketchMapper *sourcemapper.InoMapper + sketchMapper *sourcemapper.SketchMapper sketchTrackedFilesCount int trackedInoDocs map[string]lsp.TextDocumentItem inoDocsWithDiagnostics map[lsp.DocumentURI]bool diff --git a/sourcemapper/ino.go b/sourcemapper/ino.go index b3a1d9e..f367b81 100644 --- a/sourcemapper/ino.go +++ b/sourcemapper/ino.go @@ -15,11 +15,11 @@ import ( "go.bug.st/lsp" ) -// 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,26 @@ 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) + s.mapLine(sourceFile, sourceLine, targetLine) 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,9 +209,13 @@ 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 @@ -218,7 +226,7 @@ func (s *InoMapper) ApplyTextChange(inoURI lsp.DocumentURI, inoChange lsp.TextDo 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 +235,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 +243,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 +290,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 +318,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 +367,25 @@ 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 { + return s[strings.LastIndex(s, "/"):] + } 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/sourcemapper/ino_test.go b/sourcemapper/ino_test.go index 5a89a77..8f444a9 100644 --- a/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 From 14274fa6bd8cd8c4ad00408e9cc8ecba0da8f771 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 9 Nov 2021 09:16:26 +0100 Subject: [PATCH 17/76] Addded missing options in ServerCapabilities --- ls/ls.go | 53 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 77d549e..b61c347 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -279,28 +279,61 @@ func (handler *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logg IncludeText: true, }, }, - HoverProvider: &lsp.HoverOptions{}, // true, CompletionProvider: &lsp.CompletionOptions{ - TriggerCharacters: []string{".", "\u003e", ":"}, + AllCommitCharacters: []string{ + " ", "\t", "(", ")", "[", "]", "{", "}", "<", ">", + ":", ";", ",", "+", "-", "/", "*", "%", "^", "&", + "#", "?", ".", "=", "\"", "'", "|"}, + ResolveProvider: false, + TriggerCharacters: []string{ //".", "\u003e", ":" + ".", "<", ">", ":", "\"", "/"}, }, SignatureHelpProvider: &lsp.SignatureHelpOptions{ TriggerCharacters: []string{"(", ","}, }, - DefinitionProvider: &lsp.DefinitionOptions{}, // true, - // ReferencesProvider: &lsp.ReferenceOptions{}, // TODO: true - DocumentHighlightProvider: &lsp.DocumentHighlightOptions{}, //true, - DocumentSymbolProvider: &lsp.DocumentSymbolOptions{}, //true, - WorkspaceSymbolProvider: &lsp.WorkspaceSymbolOptions{}, //true, + // ReferencesProvider: &lsp.ReferenceOptions{}, + // DeclarationProvider: &lsp.DeclarationRegistrationOptions{}, + // DocumentLinkProvider: &lsp.DocumentLinkOptions{ResolveProvider: false}, + // ImplementationProvider: &lsp.ImplementationRegistrationOptions{}, + // SelectionRangeProvider: &lsp.SelectionRangeRegistrationOptions{}, + DefinitionProvider: &lsp.DefinitionOptions{}, + DocumentHighlightProvider: &lsp.DocumentHighlightOptions{}, + DocumentSymbolProvider: &lsp.DocumentSymbolOptions{}, + WorkspaceSymbolProvider: &lsp.WorkspaceSymbolOptions{}, CodeActionProvider: &lsp.CodeActionOptions{ResolveProvider: true}, - DocumentFormattingProvider: &lsp.DocumentFormattingOptions{}, //true, - DocumentRangeFormattingProvider: &lsp.DocumentRangeFormattingOptions{}, //true, + DocumentFormattingProvider: &lsp.DocumentFormattingOptions{}, + DocumentRangeFormattingProvider: &lsp.DocumentRangeFormattingOptions{}, + HoverProvider: &lsp.HoverOptions{}, DocumentOnTypeFormattingProvider: &lsp.DocumentOnTypeFormattingOptions{ FirstTriggerCharacter: "\n", + MoreTriggerCharacter: []string{}, + }, + RenameProvider: &lsp.RenameOptions{ + // PrepareProvider: true, }, - RenameProvider: &lsp.RenameOptions{PrepareProvider: false}, // TODO: true ExecuteCommandProvider: &lsp.ExecuteCommandOptions{ Commands: []string{"clangd.applyFix", "clangd.applyTweak"}, }, + // SemanticTokensProvider: &lsp.SemanticTokensRegistrationOptions{ + // SemanticTokensOptions: &lsp.SemanticTokensOptions{ + // Full: &lsp.SemantiTokenFullOptions{ + // Delta: true, + // }, + // Legend: lsp.SemanticTokensLegend{ + // TokenModifiers: []string{}, + // TokenTypes: []string{ + // "variable", "variable", "parameter", "function", "method", "function", "property", "variable", + // "class", "enum", "enumMember", "type", "dependent", "dependent", "namespace", "typeParameter", + // "concept", "type", "macro", "comment", + // }, + // }, + // Range: false, + // }, + // }, + }, + ServerInfo: &lsp.InitializeResultServerInfo{ + Name: "arduino-language-server", + Version: "0.5.0-beta", }, } logger.Logf("initialization parameters: %s", string(lsp.EncodeMessage(resp))) From ccbc0c647f2d2bec8d2ef735ea4975292c40bf8f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 9 Nov 2021 09:16:49 +0100 Subject: [PATCH 18/76] Fixed wrong condition in SignatureHelp handler --- ls/ls.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ls/ls.go b/ls/ls.go index b61c347..464c859 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -452,7 +452,7 @@ func (ls *INOLanguageServer) TextDocumentSignatureHelpReqFromIDE(ctx context.Con logger.Logf("%s", inoTextDocumentPosition) cppTextDocumentPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) - if err == nil { + if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } From df25ec5b21a5875fdb13838e813f470b46bc7f61 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 9 Nov 2021 09:17:12 +0100 Subject: [PATCH 19/76] send DidChange notification together with DidSave --- ls/ls.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ls/ls.go b/ls/ls.go index 464c859..f3a0573 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1099,6 +1099,19 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, logger.Logf(" error reinitilizing clangd:", err) return err } + 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 + } } else { // Otherwise start clangd! dataFolder, err := extractDataFolderFromArduinoCLI(logger) From 0073fd32f9efe89adeb3bcfc0b481af4023fc5f5 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 9 Nov 2021 09:17:49 +0100 Subject: [PATCH 20/76] Added error color in loggin to make errors more visible --- ls/lsp_client_clangd.go | 4 +++- ls/lsp_logger.go | 13 +++++++++++-- ls/lsp_server_ide.go | 4 +++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ls/lsp_client_clangd.go b/ls/lsp_client_clangd.go index 9c7c8fa..8255c48 100644 --- a/ls/lsp_client_clangd.go +++ b/ls/lsp_client_clangd.go @@ -43,7 +43,9 @@ func NewClangdLSPClient( IncomingPrefix: "IDE LS <-- Clangd", OutgoingPrefix: "IDE LS --> Clangd", HiColor: color.HiRedString, - LoColor: color.RedString}) + LoColor: color.RedString, + ErrorColor: color.New(color.BgHiMagenta, color.FgHiWhite, color.BlinkSlow).Sprintf, + }) return client } diff --git a/ls/lsp_logger.go b/ls/lsp_logger.go index 65ca457..2e9f417 100644 --- a/ls/lsp_logger.go +++ b/ls/lsp_logger.go @@ -12,6 +12,7 @@ import ( 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) { @@ -21,7 +22,11 @@ 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) { - log.Print(l.LoColor("%s RESP %s %s", l.IncomingPrefix, method, id)) + 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)) @@ -39,7 +44,11 @@ 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) { - log.Print(l.LoColor("%s RESP %s %s", l.OutgoingPrefix, method, id)) + 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 := " " diff --git a/ls/lsp_server_ide.go b/ls/lsp_server_ide.go index eae71b9..a986e20 100644 --- a/ls/lsp_server_ide.go +++ b/ls/lsp_server_ide.go @@ -24,7 +24,9 @@ func NewIDELSPServer(logger jsonrpc.FunctionLogger, in io.Reader, out io.Writer, IncomingPrefix: "IDE --> LS", OutgoingPrefix: "IDE <-- LS", HiColor: color.HiGreenString, - LoColor: color.GreenString}) + LoColor: color.GreenString, + ErrorColor: color.New(color.BgHiMagenta, color.FgHiWhite, color.BlinkSlow).Sprintf, + }) return server } From c8d2e5e753439f3d0e25b1cb573f119280705905 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 9 Nov 2021 15:26:12 +0100 Subject: [PATCH 21/76] Refactoring of the builder --- ls/builder.go | 49 ++++++++++++++++++++++++++++-------- ls/ls.go | 54 +++++++++------------------------------- sourcemapper/ino.go | 2 +- sourcemapper/ino_test.go | 2 +- textutils/textutils.go | 2 +- 5 files changed, 54 insertions(+), 55 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 5f3aafc..40d149d 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -2,9 +2,11 @@ package ls import ( "bytes" + "runtime" "strings" "time" + "github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/arduino/libraries" "github.com/arduino/arduino-cli/executils" "github.com/arduino/arduino-language-server/streams" @@ -85,17 +87,17 @@ func (handler *INOLanguageServer) rebuildEnvironmentLoop() { } } -func (handler *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.FunctionLogger, buildPath *paths.Path) error { - sketchDir := handler.sketchRoot - fqbn := handler.config.SelectedBoard.Fqbn +func (ls *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.FunctionLogger) error { + sketchDir := ls.sketchRoot + fqbn := ls.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.trackedInoDocs { - rel, err := paths.New(uri).RelFrom(handler.sketchRoot) + for uri, trackedFile := range ls.trackedInoDocs { + rel, err := paths.New(uri).RelFrom(ls.sketchRoot) if err != nil { return errors.WithMessage(err, "dumping tracked files") } @@ -104,12 +106,12 @@ func (handler *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.Functi 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 { + } else if tmp, err := paths.WriteToTempFile(jsonBytes, nil, ""); err != nil { return errors.WithMessage(err, "dumping tracked files") } else { - // logger.Logf("Dumped overrides: %s", string(jsonBytes)) - overridesJSON = tmpFile - defer tmpFile.Remove() + logger.Logf("Dumped overrides: %s", string(jsonBytes)) + overridesJSON = tmp + defer tmp.Remove() } // XXX: do this from IDE or via gRPC @@ -120,7 +122,7 @@ func (handler *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.Functi "--only-compilation-database", "--clean", "--source-override", overridesJSON.String(), - "--build-path", buildPath.String(), + "--build-path", ls.buildPath.String(), "--format", "json", sketchDir.String(), } @@ -153,5 +155,32 @@ func (handler *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.Functi } logger.Logf("arduino-cli output: %s", cmdOutput) + // TODO: do canonicalization directly in `arduino-cli` + canonicalizeCompileCommandsJSON(ls.buildPath) + return nil } + +func canonicalizeCompileCommandsJSON(compileCommandsDir *paths.Path) { + compileCommandsJSONPath := compileCommandsDir.Join("compile_commands.json") + 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 index f3a0573..d753235 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -6,13 +6,11 @@ import ( "fmt" "io" "log" - "runtime" "strconv" "strings" "sync" "time" - "github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/executils" "github.com/arduino/arduino-language-server/sourcemapper" "github.com/arduino/arduino-language-server/streams" @@ -1062,20 +1060,21 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, currCppTextVersion := 0 if params != nil { logger.Logf(" --> initialize(%s)", params.RootURI) - ls.lspInitializeParams = params ls.sketchRoot = params.RootURI.AsPath() ls.sketchName = ls.sketchRoot.Base() + ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") + + ls.lspInitializeParams = params + ls.lspInitializeParams.RootPath = ls.buildSketchRoot.String() + ls.lspInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) } else { logger.Logf(" --> RE-initialize()") currCppTextVersion = ls.sketchMapper.CppText.Version } - if err := ls.generateBuildEnvironment(logger, ls.buildPath); err != nil { + if err := ls.generateBuildEnvironment(logger); err != nil { return err } - ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") - ls.lspInitializeParams.RootPath = ls.buildSketchRoot.String() - ls.lspInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { ls.sketchMapper = sourcemapper.CreateInoMapper(cppContent) @@ -1084,13 +1083,12 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, return errors.WithMessage(err, "reading generated cpp file from sketch") } - canonicalizeCompileCommandsJSON(ls.buildPath) - if params == nil { // If we are restarting re-synchronize clangd cppURI := lsp.NewDocumentURIFromPath(ls.buildSketchCpp) - logger.Logf("LS --> CL NOTIF textDocument/didSave:") + logger.Logf("Sending 'didSave' notification to Clangd") + didSaveParams := &lsp.DidSaveTextDocumentParams{ TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, Text: ls.sketchMapper.CppText.Text, @@ -1099,6 +1097,8 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, logger.Logf(" error reinitilizing clangd:", err) return err } + + logger.Logf("Sending 'didChange' notification to Clangd") didChangeParams := &lsp.DidChangeTextDocumentParams{ TextDocument: lsp.VersionedTextDocumentIdentifier{ TextDocumentIdentifier: lsp.TextDocumentIdentifier{URI: cppURI}, @@ -1258,36 +1258,6 @@ func (ls *INOLanguageServer) CheckCppIncludesChanges() { } } -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 (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { inoIdentifier := inoDidClose.TextDocument if _, exist := ls.trackedInoDocs[inoIdentifier.URI.AsPath().String()]; exist { @@ -1368,7 +1338,7 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChan cppChanges := []lsp.TextDocumentContentChangeEvent{} for _, inoChange := range inoDidChangeParams.ContentChanges { - cppChangeRange, ok := ls.sketchMapper.InoToCppLSPRangeOk(inoDoc.URI, inoChange.Range) + cppChangeRange, ok := ls.sketchMapper.InoToCppLSPRangeOk(inoDoc.URI, *inoChange.Range) if !ok { return nil, errors.Errorf("invalid change range %s:%s", inoDoc.URI, inoChange.Range) } @@ -1394,7 +1364,7 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChan logger.Logf("New version:----------+\n" + ls.sketchMapper.CppText.Text + "\n----------------------") cppChanges = append(cppChanges, lsp.TextDocumentContentChangeEvent{ - Range: cppChangeRange, + Range: &cppChangeRange, RangeLength: inoChange.RangeLength, Text: inoChange.Text, }) diff --git a/sourcemapper/ino.go b/sourcemapper/ino.go index f367b81..2e14a8f 100644 --- a/sourcemapper/ino.go +++ b/sourcemapper/ino.go @@ -210,7 +210,7 @@ func unquoteCppString(str string) string { // 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 *SketchMapper) ApplyTextChange(inoURI lsp.DocumentURI, inoChange lsp.TextDocumentContentChangeEvent) (dirty bool) { - inoRange := inoChange.Range + inoRange := *inoChange.Range cppRange, ok := s.InoToCppLSPRangeOk(inoURI, inoRange) if !ok { panic("Invalid sketch range " + inoURI.String() + ":" + inoRange.String()) diff --git a/sourcemapper/ino_test.go b/sourcemapper/ino_test.go index 8f444a9..a2e4da6 100644 --- a/sourcemapper/ino_test.go +++ b/sourcemapper/ino_test.go @@ -58,7 +58,7 @@ func TestCreateSourceMaps(t *testing.T) { sourceMap.DebugLogAll() sourceMap.ApplyTextChange(lsp.NewDocumentURIFromPath(sketch), lsp.TextDocumentContentChangeEvent{ - Range: lsp.Range{ + Range: &lsp.Range{ Start: lsp.Position{Line: 3, Character: 0}, End: lsp.Position{Line: 3, Character: 0}, }, diff --git a/textutils/textutils.go b/textutils/textutils.go index 8668e73..430267f 100644 --- a/textutils/textutils.go +++ b/textutils/textutils.go @@ -9,7 +9,7 @@ import ( // ApplyLSPTextDocumentContentChangeEvent applies the LSP change in the given text func ApplyLSPTextDocumentContentChangeEvent(textDoc lsp.TextDocumentItem, changes []lsp.TextDocumentContentChangeEvent, version int) (lsp.TextDocumentItem, error) { for _, change := range changes { - if t, err := ApplyTextChange(textDoc.Text, change.Range, change.Text); err == nil { + if t, err := ApplyTextChange(textDoc.Text, *change.Range, change.Text); err == nil { textDoc.Text = t } else { return lsp.TextDocumentItem{}, err From f0a3a3005ae93069df9014515c526ceb08ff9cc0 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 9 Nov 2021 15:26:29 +0100 Subject: [PATCH 22/76] Do not send full text in didSave notification to clangd --- ls/ls.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ls/ls.go b/ls/ls.go index d753235..607ffca 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1091,7 +1091,6 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, didSaveParams := &lsp.DidSaveTextDocumentParams{ TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, - Text: ls.sketchMapper.CppText.Text, } if err := ls.Clangd.conn.TextDocumentDidSave(didSaveParams); err != nil { logger.Logf(" error reinitilizing clangd:", err) From b30c074224a385924b2917d4dc22138d39502a7a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 9 Nov 2021 15:50:56 +0100 Subject: [PATCH 23/76] Factored initializeWorkbench function --- ls/builder.go | 4 +- ls/ls.go | 174 ++++++++++++++++++++++++-------------------------- 2 files changed, 86 insertions(+), 92 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 40d149d..a6c7649 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -79,8 +79,8 @@ func (handler *INOLanguageServer) rebuildEnvironmentLoop() { } }() - handler.writeLock(logger, false) - handler.initializeWorkbench(logger, nil) + handler.writeLock(logger, true) + handler.initializeWorkbench(logger) handler.writeUnlock(logger) done <- true close(done) diff --git a/ls/ls.go b/ls/ls.go index 607ffca..626f4a6 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -50,7 +50,6 @@ type INOLanguageServer struct { closing chan bool clangdStarted *sync.Cond dataMux sync.RWMutex - lspInitializeParams *lsp.InitializeParams buildPath *paths.Path buildSketchRoot *paths.Path buildSketchCpp *paths.Path @@ -245,21 +244,70 @@ func (handler *INOLanguageServer) CheckCppDocumentSymbols() error { return nil } -func (handler *INOLanguageServer) startClangd(inoParams *lsp.InitializeParams) { +func (ls *INOLanguageServer) startClangd(inoParams *lsp.InitializeParams) error { logger := NewLSPFunctionLogger(color.HiCyanString, "INIT --- ") - logger.Logf("initializing workbench") + logger.Logf("initializing workbench: %s", inoParams.RootURI) // Start clangd asynchronously - handler.writeLock(logger, false) // do not wait for clangd... we are starting it :-) - defer handler.writeUnlock(logger) + ls.writeLock(logger, false) // do not wait for clangd... we are starting it :-) + defer ls.writeUnlock(logger) + + ls.sketchRoot = inoParams.RootURI.AsPath() + ls.sketchName = ls.sketchRoot.Base() + ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") + + if err := ls.generateBuildEnvironment(logger); err != nil { + return err + } + + if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { + ls.sketchMapper = sourcemapper.CreateInoMapper(cppContent) + ls.sketchMapper.CppText.Version = 1 + } else { + return errors.WithMessage(err, "reading generated cpp file from sketch") + } - // TODO: Inline this function - handler.initializeWorkbench(logger, inoParams) + // Let's start clangd! + dataFolder, err := extractDataFolderFromArduinoCLI(logger) + if err != nil { + logger.Logf("error: %s", err) + } + + // Start clangd + ls.Clangd = NewClangdLSPClient(logger, ls.buildPath, ls.buildSketchCpp, 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() + cppInitializeParams := *inoParams + cppInitializeParams.RootPath = ls.buildSketchRoot.String() + cppInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) + if initRes, clangErr, err := ls.Clangd.conn.Initialize(ctx, &cppInitializeParams); err != nil { + logger.Logf("error initilizing clangd: %v", err) + return err + } else if clangErr != nil { + logger.Logf("error initilizing clangd: %v", clangErr.AsError()) + return clangErr.AsError() + } else { + logger.Logf("clangd successfully started: %s", string(lsp.EncodeMessage(initRes))) + } + + if err := ls.Clangd.conn.Initialized(&lsp.InitializedParams{}); err != nil { + logger.Logf("error sending initialized notification to clangd: %v", err) + return err + } // signal that clangd is running now... - handler.clangdStarted.Broadcast() + ls.clangdStarted.Broadcast() logger.Logf("Done initializing workbench") + return nil } func (handler *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { @@ -1051,26 +1099,9 @@ func (ls *INOLanguageServer) CleanUp() { } } -func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, params *lsp.InitializeParams) error { - // TODO: This function must be split into two - // -> start clang (when params != nil) - // -> reser clang status (when params == nil) - // the two flows shares very little - - currCppTextVersion := 0 - if params != nil { - logger.Logf(" --> initialize(%s)", params.RootURI) - ls.sketchRoot = params.RootURI.AsPath() - ls.sketchName = ls.sketchRoot.Base() - ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") - - ls.lspInitializeParams = params - ls.lspInitializeParams.RootPath = ls.buildSketchRoot.String() - ls.lspInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) - } else { - logger.Logf(" --> RE-initialize()") - currCppTextVersion = ls.sketchMapper.CppText.Version - } +func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger) error { + logger.Logf("--> RE-initialize()") + currCppTextVersion := ls.sketchMapper.CppText.Version if err := ls.generateBuildEnvironment(logger); err != nil { return err @@ -1083,69 +1114,31 @@ func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger, return errors.WithMessage(err, "reading generated cpp file from sketch") } - if params == nil { - // If we are restarting re-synchronize clangd - cppURI := lsp.NewDocumentURIFromPath(ls.buildSketchCpp) - - logger.Logf("Sending 'didSave' notification to Clangd") - - 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 - } - - logger.Logf("Sending '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 - } - } else { - // Otherwise start clangd! - dataFolder, err := extractDataFolderFromArduinoCLI(logger) - if err != nil { - logger.Logf(" error: %s", err) - } - - // Start clangd - ls.Clangd = NewClangdLSPClient(logger, ls.buildPath, ls.buildSketchCpp, dataFolder, ls) - go func() { - defer streams.CatchAndLogPanic() - ls.Clangd.Run() - logger.Logf("Lost connection with clangd!") - ls.Close() - }() - - // Send initialization command to clangd - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - initRes, clangErr, err := ls.Clangd.conn.Initialize(ctx, ls.lspInitializeParams) - if err != nil { - logger.Logf(" error initilizing clangd: %v", err) - return err - } - if clangErr != nil { - logger.Logf(" error initilizing clangd: %v", clangErr.AsError()) - return clangErr.AsError() - } else { - logger.Logf("clangd successfully started: %s", string(lsp.EncodeMessage(initRes))) - } + // 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 + } - if err := ls.Clangd.conn.Initialized(&lsp.InitializedParams{}); err != nil { - logger.Logf(" error sending initialized notification to clangd: %v", 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 @@ -1359,6 +1352,7 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChan if dirty { ls.scheduleRebuildEnvironment() } + ls.sketchMapper.DebugLogAll() logger.Logf("New version:----------+\n" + ls.sketchMapper.CppText.Text + "\n----------------------") From 162db7ffc55ef8b26fce3aff0c5c73d44047f89b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 9 Nov 2021 17:33:30 +0100 Subject: [PATCH 24/76] Factored out clang starter --- ls/ls.go | 120 +++++++++++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 61 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 626f4a6..5696c40 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -244,76 +244,74 @@ func (handler *INOLanguageServer) CheckCppDocumentSymbols() error { return nil } -func (ls *INOLanguageServer) startClangd(inoParams *lsp.InitializeParams) error { - logger := NewLSPFunctionLogger(color.HiCyanString, "INIT --- ") - logger.Logf("initializing workbench: %s", inoParams.RootURI) - - // Start clangd asynchronously - ls.writeLock(logger, false) // do not wait for clangd... we are starting it :-) - defer ls.writeUnlock(logger) +func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { + ls.writeLock(logger, false /* do not wait for clangd... we are going to start it! */) + go func() { + defer streams.CatchAndLogPanic() + // the lock is "moved" into the goroutine + defer ls.writeUnlock(logger) - ls.sketchRoot = inoParams.RootURI.AsPath() - ls.sketchName = ls.sketchRoot.Base() - ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") + logger := NewLSPFunctionLogger(color.HiCyanString, "INIT --- ") + logger.Logf("initializing workbench: %s", inoParams.RootURI) - if err := ls.generateBuildEnvironment(logger); err != nil { - return err - } - - if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { - ls.sketchMapper = sourcemapper.CreateInoMapper(cppContent) - ls.sketchMapper.CppText.Version = 1 - } else { - return errors.WithMessage(err, "reading generated cpp file from sketch") - } + ls.sketchRoot = inoParams.RootURI.AsPath() + ls.sketchName = ls.sketchRoot.Base() + ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") - // Let's start clangd! - dataFolder, err := extractDataFolderFromArduinoCLI(logger) - if err != nil { - logger.Logf("error: %s", err) - } + if err := ls.generateBuildEnvironment(logger); err != nil { + logger.Logf("error starting clang: %s", err) + return + } - // Start clangd - ls.Clangd = NewClangdLSPClient(logger, ls.buildPath, ls.buildSketchCpp, dataFolder, ls) - go func() { - defer streams.CatchAndLogPanic() - ls.Clangd.Run() - logger.Logf("Lost connection with clangd!") - ls.Close() - }() + if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { + ls.sketchMapper = sourcemapper.CreateInoMapper(cppContent) + ls.sketchMapper.CppText.Version = 1 + } else { + logger.Logf("error starting clang: reading generated cpp file from sketch: %s", err) + return + } - // Send initialization command to clangd (1 sec. timeout) - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - cppInitializeParams := *inoParams - cppInitializeParams.RootPath = ls.buildSketchRoot.String() - cppInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) - if initRes, clangErr, err := ls.Clangd.conn.Initialize(ctx, &cppInitializeParams); err != nil { - logger.Logf("error initilizing clangd: %v", err) - return err - } else if clangErr != nil { - logger.Logf("error initilizing clangd: %v", clangErr.AsError()) - return clangErr.AsError() - } else { - logger.Logf("clangd successfully started: %s", string(lsp.EncodeMessage(initRes))) - } + // Retrieve data folder + dataFolder, err := extractDataFolderFromArduinoCLI(logger) + if err != nil { + logger.Logf("error retrieving data folder from arduino-cli: %s", err) + return + } - if err := ls.Clangd.conn.Initialized(&lsp.InitializedParams{}); err != nil { - logger.Logf("error sending initialized notification to clangd: %v", err) - return err - } + // Start clangd + ls.Clangd = NewClangdLSPClient(logger, ls.buildPath, ls.buildSketchCpp, 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() + cppInitializeParams := *inoParams + cppInitializeParams.RootPath = ls.buildSketchRoot.String() + cppInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) + if initRes, clangErr, err := ls.Clangd.conn.Initialize(ctx, &cppInitializeParams); 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(initRes))) + } - // signal that clangd is running now... - ls.clangdStarted.Broadcast() + 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") - return nil -} + // signal that clangd is running now... + ls.clangdStarted.Broadcast() -func (handler *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { - go func() { - defer streams.CatchAndLogPanic() - handler.startClangd(inoParams) + logger.Logf("Done initializing workbench") }() resp := &lsp.InitializeResult{ From ff7d9bda6761140a8f85fd1cbbb164154916594a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 10 Nov 2021 00:14:59 +0100 Subject: [PATCH 25/76] Updating go.mod --- go.mod | 14 +- go.sum | 639 +++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 513 insertions(+), 140 deletions(-) diff --git a/go.mod b/go.mod index f86e8be..0fb9239 100644 --- a/go.mod +++ b/go.mod @@ -2,16 +2,12 @@ module github.com/arduino/arduino-language-server go 1.12 -replace go.bug.st/lsp => ../lsp - -replace go.bug.st/json => ../go-json - require ( - github.com/arduino/arduino-cli v0.0.0-20201215104024-6a177ebf56f2 + github.com/arduino/arduino-cli v0.0.0-20211105112240-8cae8809aad9 github.com/arduino/go-paths-helper v1.6.1 - github.com/fatih/color v1.7.0 + github.com/fatih/color v1.13.0 github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.6.1 - go.bug.st/json v1.0.0 - go.bug.st/lsp v0.0.0-00010101000000-000000000000 + github.com/stretchr/testify v1.7.0 + go.bug.st/json v1.15.6 + go.bug.st/lsp v0.0.0-20211109230950-26242be380a2 ) diff --git a/go.sum b/go.sum index ba1bf0c..9c71c2d 100644 --- a/go.sum +++ b/go.sum @@ -1,112 +1,209 @@ 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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/arduino/arduino-cli v0.0.0-20211105112240-8cae8809aad9 h1:/in/9/XSzoD5j4+0yegDQoej4Lgr5ItevWgmQNXGiyg= +github.com/arduino/arduino-cli v0.0.0-20211105112240-8cae8809aad9/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/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/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU= 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.3.0 h1:4No/vQopB36e7WUIk6H6TxiSEJPiMrVOCZylYmua39o= -github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= +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/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/go.mod h1:AF3bOWkvdOpp8/S3UL8qbQ4N7DiISIbJtj54GWFPAsc= github.com/cmaglie/pb v1.0.27/go.mod h1:GilkKZMXYjBA4NxItWFfO+lwkp59PLHQ+IOW/b/kmZI= +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/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/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/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/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= 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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -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/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/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/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/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 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 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/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/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/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/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 h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 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= @@ -121,95 +218,98 @@ 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/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= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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/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/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +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/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/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +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/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/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/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/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 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/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/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= @@ -217,97 +317,362 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/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/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-20211109230950-26242be380a2 h1:UMl3K6gir2vJaZb9S8OoQ7PM2XMbndMPIJE+Jpynevw= +go.bug.st/lsp v0.0.0-20211109230950-26242be380a2/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/go.mod h1:VmYBeyJWp5BnJ0tw2NUJHZdJTGl2ecBGABHlzRK1knY= +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.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/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 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 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= @@ -316,33 +681,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/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/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= 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= From 98bbd42c5a13da97347f3c8d40134c50b1381443 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 10 Nov 2021 10:03:31 +0100 Subject: [PATCH 26/76] Fixed synchronization problem with clangd https://github.com/clangd/clangd/issues/717#issuecomment-793220007 --- ls/ls.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ls/ls.go b/ls/ls.go index 5696c40..6938740 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1296,6 +1296,14 @@ func (ls *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogg } func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChangeParams *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { + // 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 + // docuemtn" error for all subsequent requests. + // https://github.com/clangd/clangd/issues/717#issuecomment-793220007 + for i := range inoDidChangeParams.ContentChanges { + inoDidChangeParams.ContentChanges[i].RangeLength = nil + } + inoDoc := inoDidChangeParams.TextDocument // Apply the change to the tracked sketch file. From d0c01685ddbae80c4380956513d0a524c07ed6ef Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 10 Nov 2021 14:57:23 +0100 Subject: [PATCH 27/76] Moved textutils in lsp package, slightly improved logging --- .gitignore | 3 +- ls/builder.go | 6 +- ls/ls.go | 37 ++++---- sourcemapper/ino.go | 4 +- textutils/textutils.go | 123 -------------------------- textutils/textutils_test.go | 169 ------------------------------------ 6 files changed, 27 insertions(+), 315 deletions(-) delete mode 100644 textutils/textutils.go delete mode 100644 textutils/textutils_test.go 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/ls/builder.go b/ls/builder.go index a6c7649..e1e4607 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -103,13 +103,17 @@ func (ls *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.FunctionLog } data.Overrides[rel.String()] = trackedFile.Text } + + for filename, override := range data.Overrides { + logger.Logf("Dumped %s override:\n%s", filename, override) + } + var overridesJSON *paths.Path if jsonBytes, err := json.MarshalIndent(data, "", " "); err != nil { return errors.WithMessage(err, "dumping tracked files") } else if tmp, err := paths.WriteToTempFile(jsonBytes, nil, ""); err != nil { return errors.WithMessage(err, "dumping tracked files") } else { - logger.Logf("Dumped overrides: %s", string(jsonBytes)) overridesJSON = tmp defer tmp.Remove() } diff --git a/ls/ls.go b/ls/ls.go index 6938740..ed25cd4 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -14,13 +14,13 @@ import ( "github.com/arduino/arduino-cli/executils" "github.com/arduino/arduino-language-server/sourcemapper" "github.com/arduino/arduino-language-server/streams" - "github.com/arduino/arduino-language-server/textutils" "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" ) var globalCliPath string @@ -944,17 +944,17 @@ func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.Fu logger.Logf("didChange(%s)", inoParams.TextDocument) for _, change := range inoParams.ContentChanges { - logger.Logf("> %s", change) + logger.Logf(" > %s", change) } if cppParams, err := ls.didChange(logger, inoParams); err != nil { - logger.Logf("--E Error: %s", err) + logger.Logf("Error: %s", err) } else if cppParams == nil { - logger.Logf("--X Notification is not propagated to clangd") + logger.Logf("Notification is not propagated to clangd") } else { - logger.Logf("LS --> CL NOTIF didChange(%s@%d)", cppParams.TextDocument) + logger.Logf("to Clang: didChange(%s@%d)", cppParams.TextDocument) for _, change := range cppParams.ContentChanges { - logger.Logf(" > %s", change) + logger.Logf(" > %s", change) } if err := ls.Clangd.conn.TextDocumentDidChange(cppParams); err != nil { // Exit the process and trigger a restart by the client in case of a severe error @@ -1015,27 +1015,28 @@ func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.Fu ls.readLock(logger, false) defer ls.readUnlock(logger) - logger.Logf("from clang %s (%d diagnostics):", cppParams.URI, cppParams.Diagnostics) + logger.Logf("%s (%d diagnostics):", cppParams.URI, len(cppParams.Diagnostics)) for _, diag := range cppParams.Diagnostics { - logger.Logf("> %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) + 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... allInoParams, err := ls.cpp2inoDiagnostics(logger, cppParams) if err != nil { - logger.Logf(" Error converting diagnostics to .ino: %s", err) + logger.Logf("Error converting diagnostics to .ino: %s", err) return } // Push back to IDE the converted diagnostics + logger.Logf("diagnostics to IDE:") for _, inoParams := range allInoParams { - logger.Logf("to IDE: %s (%d diagnostics):", inoParams.URI, len(inoParams.Diagnostics)) + logger.Logf(" - %s (%d diagnostics):", inoParams.URI, len(inoParams.Diagnostics)) for _, diag := range inoParams.Diagnostics { - logger.Logf(" > %d:%d - %v: %s", diag.Range.Start.Line, diag.Range.Start.Character, diag.Severity, diag.Code) + logger.Logf(" > %s - %s: %s", diag.Range.Start, diag.Severity, diag.Code) } if err := ls.IDE.conn.TextDocumentPublishDiagnostics(inoParams); err != nil { - logger.Logf(" Error sending diagnostics to IDE: %s", err) + logger.Logf("Error sending diagnostics to IDE: %s", err) return } } @@ -1216,7 +1217,7 @@ func (ls *INOLanguageServer) refreshCppDocumentSymbols(logger jsonrpc.FunctionLo symbolsCanary := "" for _, symbol := range cppDocumentSymbols { logger.Logf(" symbol: %s %s %s", symbol.Kind, symbol.Name, symbol.Range) - if symbolText, err := textutils.ExtractRange(ls.sketchMapper.CppText.Text, symbol.Range); err != nil { + if symbolText, err := textedits.ExtractRange(ls.sketchMapper.CppText.Text, symbol.Range); err != nil { logger.Logf(" > invalid range: %s", err) symbolsCanary += "/" } else if end := strings.Index(symbolText, "{"); end != -1 { @@ -1298,7 +1299,7 @@ func (ls *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogg func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChangeParams *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { // 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 - // docuemtn" error for all subsequent requests. + // document" error for all subsequent requests. // https://github.com/clangd/clangd/issues/717#issuecomment-793220007 for i := range inoDidChangeParams.ContentChanges { inoDidChangeParams.ContentChanges[i].RangeLength = nil @@ -1308,14 +1309,12 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChan // Apply the change to the tracked sketch file. trackedInoID := inoDoc.URI.AsPath().String() - trackedInoDoc, ok := ls.trackedInoDocs[trackedInoID] - if !ok { + if doc, ok := ls.trackedInoDocs[trackedInoID]; !ok { return nil, unknownURI(inoDoc.URI) - } - if updatedTrackedInoDoc, err := textutils.ApplyLSPTextDocumentContentChangeEvent(trackedInoDoc, inoDidChangeParams.ContentChanges, inoDoc.Version); err != nil { + } else if updatedDoc, err := textedits.ApplyLSPTextDocumentContentChangeEvent(doc, inoDidChangeParams); err != nil { return nil, err } else { - ls.trackedInoDocs[trackedInoID] = updatedTrackedInoDoc + ls.trackedInoDocs[trackedInoID] = updatedDoc } logger.Logf("Tracked SKETCH file:----------+\n" + ls.trackedInoDocs[trackedInoID].Text + "\n----------------------") diff --git a/sourcemapper/ino.go b/sourcemapper/ino.go index 2e14a8f..096be28 100644 --- a/sourcemapper/ino.go +++ b/sourcemapper/ino.go @@ -9,10 +9,10 @@ import ( "strconv" "strings" - "github.com/arduino/arduino-language-server/textutils" "github.com/arduino/go-paths-helper" "github.com/pkg/errors" "go.bug.st/lsp" + "go.bug.st/lsp/textedits" ) // SketchMapper is a mapping between the .ino sketch and the preprocessed .cpp file @@ -219,7 +219,7 @@ func (s *SketchMapper) ApplyTextChange(inoURI lsp.DocumentURI, inoChange lsp.Tex 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()) } diff --git a/textutils/textutils.go b/textutils/textutils.go deleted file mode 100644 index 430267f..0000000 --- a/textutils/textutils.go +++ /dev/null @@ -1,123 +0,0 @@ -package textutils - -import ( - "fmt" - - "go.bug.st/lsp" -) - -// ApplyLSPTextDocumentContentChangeEvent applies the LSP change in the given text -func ApplyLSPTextDocumentContentChangeEvent(textDoc lsp.TextDocumentItem, changes []lsp.TextDocumentContentChangeEvent, version int) (lsp.TextDocumentItem, error) { - for _, change := range changes { - if t, err := ApplyTextChange(textDoc.Text, *change.Range, change.Text); err == nil { - textDoc.Text = t - } else { - return lsp.TextDocumentItem{}, err - } - } - textDoc.Version = version - return textDoc, 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/textutils/textutils_test.go b/textutils/textutils_test.go deleted file mode 100644 index 24997b0..0000000 --- a/textutils/textutils_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package textutils - -import ( - "strings" - "testing" - - "go.bug.st/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) - } - } -} From ad47a5232cf926067183ba3ef15bffe159fa39ad Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 10 Nov 2021 16:09:26 +0100 Subject: [PATCH 28/76] change go.mod --- go.mod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.mod b/go.mod index 0fb9239..da6f254 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/arduino/arduino-language-server go 1.12 +replace go.bug.st/lsp => ../go-lsp + require ( github.com/arduino/arduino-cli v0.0.0-20211105112240-8cae8809aad9 github.com/arduino/go-paths-helper v1.6.1 From 43755fd19e9bb40077db20afcbbe645ef90a9928 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 11 Nov 2021 17:40:43 +0100 Subject: [PATCH 29/76] BIG REFACTOR: build sketch is triggered on every change The sketch build may be interrupted if new changes comes during the build (invalidating it). Since we auto rebuild on every change we do not need to monitor includes or symbols anymore greatly reducing code complexity. Build progress is no more handled by a goroutine, we lose the cycling dots (. -> .. -> ...) but we simplify the code a lot. The compile_commands.json is now kept separated from the one created by the CLI (that is touched multiple times, and it confuses clangd). Now the compile_commands.json seen by clangd is touched only once after a successful build. --- go.mod | 2 +- go.sum | 36 +++++ ls/builder.go | 217 ++++++++++++++++++---------- ls/ls.go | 307 +++++++--------------------------------- ls/lsp_client_clangd.go | 65 +++++---- sourcemapper/ino.go | 6 +- 6 files changed, 270 insertions(+), 363 deletions(-) diff --git a/go.mod b/go.mod index da6f254..f6fc4af 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.12 replace go.bug.st/lsp => ../go-lsp require ( - github.com/arduino/arduino-cli v0.0.0-20211105112240-8cae8809aad9 + 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 diff --git a/go.sum b/go.sum index 9c71c2d..1422335 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,11 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/arduino/arduino-cli v0.0.0-20211105112240-8cae8809aad9 h1:/in/9/XSzoD5j4+0yegDQoej4Lgr5ItevWgmQNXGiyg= github.com/arduino/arduino-cli v0.0.0-20211105112240-8cae8809aad9/go.mod h1:cU/LNzIePCU/dvW39BClyZVPuz19Zsrt2dAIbWPtTAA= +github.com/arduino/arduino-cli v0.0.0-20211111094916-c8a3f2efad9d h1:dNv1HPy9IbjhZ2+Y4kzoXRVWSZmRw6tMk8a7FPUn41w= +github.com/arduino/arduino-cli v0.0.0-20211111094916-c8a3f2efad9d/go.mod h1:cU/LNzIePCU/dvW39BClyZVPuz19Zsrt2dAIbWPtTAA= +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 h1:agh2JT96G8egU7FEb13L4dq3fnCN7lxXhJ86t69+W7s= 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/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= @@ -51,6 +56,7 @@ github.com/arduino/go-paths-helper v1.6.1 h1:lha+/BuuBsx0qTZ3gy6IO1kU23lObWdQ/UI 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 h1:9hDi4F2st6dbLC3y4i02zFT5quS4X6iioWifGlVwfy4= 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= @@ -65,22 +71,27 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR 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/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/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/codeclysm/cc v1.2.2 h1:1ChS4EvWTjw6bH2sd6QiMcmih0itVVrWdh9MmOliX/I= 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/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 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= 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/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/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= @@ -92,7 +103,9 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 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 h1:C6sOwknxwWfLBEQ91zhmptlfxf7pVEs5s6wOnDxNpS4= 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.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -103,6 +116,7 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 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 h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 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= @@ -199,6 +213,7 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: 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/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -218,6 +233,7 @@ 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/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.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -234,6 +250,7 @@ github.com/leonelquinteros/gotext v1.4.0 h1:2NHPCto5IoMXbrT0bldPrxj0qM5asOCwtb1a github.com/leonelquinteros/gotext v1.4.0/go.mod h1:yZGXREmoGTtBvZHNcc+Yfug49G/2spuF/i/Qlsvz1Us= 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 h1:hyAgCuG5nqTMDeUD8KZs7HSPs6KprPgPP8QmGV8nyvk= 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= @@ -243,14 +260,17 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx 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 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 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.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= 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 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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= @@ -264,6 +284,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN 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/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= @@ -279,18 +300,25 @@ github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583 h1:ogHi8YLNeIxABO github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583/go.mod h1:sFPiU/UgDcsQVu3vkqpZLCXWFwUoQRpHGu9ATihPAl0= 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/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 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 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e h1:uO75wNGioszjmIzcY/tvdDYKRLVvzggtAmmJkn9j4GQ= 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.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -310,6 +338,7 @@ 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 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= 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= @@ -323,6 +352,7 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc 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/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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -339,7 +369,9 @@ go.bug.st/lsp v0.0.0-20211109230950-26242be380a2 h1:UMl3K6gir2vJaZb9S8OoQ7PM2XMb go.bug.st/lsp v0.0.0-20211109230950-26242be380a2/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.3.2 h1:6BFZZd/wngoL5PPYYTrFUounF54SIkykHpT98eq6zvk= go.bug.st/serial v1.3.2/go.mod h1:jDkjqASf/qSjmaOxHSHljwUQ6eHo/ZX/bxJLQqSlvZg= +go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45 h1:mACY1anK6HNCZtm/DK2Rf2ZPHggVqeB0+7rY9Gl6wyI= go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45/go.mod h1:dRSl/CVCTf56CkXgJMDOdSwNfo2g1orOGE/gBGdvjZw= 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= @@ -363,6 +395,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 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= @@ -698,9 +731,12 @@ 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/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.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/ls/builder.go b/ls/builder.go index e1e4607..6345e4e 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -2,13 +2,17 @@ package ls import ( "bytes" + "context" + "fmt" "runtime" "strings" + "sync" "time" "github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/arduino/libraries" "github.com/arduino/arduino-cli/executils" + "github.com/arduino/arduino-language-server/sourcemapper" "github.com/arduino/arduino-language-server/streams" "github.com/arduino/go-paths-helper" "github.com/fatih/color" @@ -18,101 +22,165 @@ import ( "go.bug.st/lsp/jsonrpc" ) -func (handler *INOLanguageServer) scheduleRebuildEnvironment() { - handler.rebuildSketchDeadlineMutex.Lock() - defer handler.rebuildSketchDeadlineMutex.Unlock() - d := time.Now().Add(time.Second) - handler.rebuildSketchDeadline = &d +type SketchRebuilder struct { + ls *INOLanguageServer + trigger chan bool + cancel func() + mutex sync.Mutex } -func (handler *INOLanguageServer) rebuildEnvironmentLoop() { - logger := NewLSPFunctionLogger(color.HiMagentaString, "RBLD---") +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() +} - grabDeadline := func() *time.Time { - handler.rebuildSketchDeadlineMutex.Lock() - defer handler.rebuildSketchDeadlineMutex.Unlock() +func (r *SketchRebuilder) TriggerRebuild() { + r.mutex.Lock() + defer r.mutex.Unlock() - res := handler.rebuildSketchDeadline - handler.rebuildSketchDeadline = nil - return res + r.cancel() // Stop possibly already running builds + select { + case r.trigger <- true: + default: } +} +func (r *SketchRebuilder) rebuilderLoop() { + logger := NewLSPFunctionLogger(color.HiMagentaString, "SKETCH REBUILD: ") for { - // Wait for someone to schedule a preprocessing... - time.Sleep(100 * time.Millisecond) - deadline := grabDeadline() - if deadline == nil { - continue + <-r.trigger + + for { + // Concede a 200ms delay to accumulate bursts of changes + select { + case <-r.trigger: + continue + case <-time.After(time.Second): + } + break } - for time.Now().Before(*deadline) { - time.Sleep(100 * time.Millisecond) + r.ls.progressHandler.Create("arduinoLanguageServerRebuild") + r.ls.progressHandler.Begin("arduinoLanguageServerRebuild", &lsp.WorkDoneProgressBegin{Title: "Building sketch"}) - if d := grabDeadline(); d != nil { - deadline = d - } + 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) } - // 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 - } - } - }() + 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) - handler.writeLock(logger, true) - handler.initializeWorkbench(logger) - handler.writeUnlock(logger) - done <- true - close(done) + // 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(logger jsonrpc.FunctionLogger) error { - sketchDir := ls.sketchRoot +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 fqbn := ls.config.SelectedBoard.Fqbn - - // Export temporary files + buildPath := ls.buildPath type overridesFile struct { Overrides map[string]string `json:"overrides"` } data := overridesFile{Overrides: map[string]string{}} for uri, trackedFile := range ls.trackedInoDocs { - rel, err := paths.New(uri).RelFrom(ls.sketchRoot) + rel, err := paths.New(uri).RelFrom(sketchRoot) if err != nil { - return errors.WithMessage(err, "dumping tracked files") + ls.readUnlock(logger) + return false, errors.WithMessage(err, "dumping tracked files") } data.Overrides[rel.String()] = trackedFile.Text } + ls.readUnlock(logger) + // Run arduino-cli to perform the build for filename, override := range data.Overrides { - logger.Logf("Dumped %s override:\n%s", filename, override) + logger.Logf("Dumping %s override:\n%s", filename, override) } - var overridesJSON *paths.Path if jsonBytes, err := json.MarshalIndent(data, "", " "); err != nil { - return errors.WithMessage(err, "dumping tracked files") + return false, errors.WithMessage(err, "dumping tracked files") } else if tmp, err := paths.WriteToTempFile(jsonBytes, nil, ""); err != nil { - return errors.WithMessage(err, "dumping tracked files") + return false, errors.WithMessage(err, "dumping tracked files") } else { overridesJSON = tmp defer tmp.Remove() @@ -124,22 +192,22 @@ func (ls *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.FunctionLog "compile", "--fqbn", fqbn, "--only-compilation-database", - "--clean", + //"--clean", "--source-override", overridesJSON.String(), - "--build-path", ls.buildPath.String(), + "--build-path", buildPath.String(), "--format", "json", - sketchDir.String(), + sketchRoot.String(), } cmd, err := executils.NewProcess(args...) if err != nil { - return errors.Errorf("running %s: %s", strings.Join(args, " "), err) + return false, errors.Errorf("running %s: %s", strings.Join(args, " "), err) } cmdOutput := &bytes.Buffer{} cmd.RedirectStdoutTo(cmdOutput) - cmd.SetDirFromPath(sketchDir) + cmd.SetDirFromPath(sketchRoot) logger.Logf("running: %s", strings.Join(args, " ")) - if err := cmd.Run(); err != nil { - return errors.Errorf("running %s: %s", strings.Join(args, " "), err) + 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 @@ -155,18 +223,17 @@ func (ls *INOLanguageServer) generateBuildEnvironment(logger jsonrpc.FunctionLog } var res cmdRes if err := json.Unmarshal(cmdOutput.Bytes(), &res); err != nil { - return errors.Errorf("parsing arduino-cli output: %s", err) + return false, errors.Errorf("parsing arduino-cli output: %s", err) } logger.Logf("arduino-cli output: %s", cmdOutput) // TODO: do canonicalization directly in `arduino-cli` - canonicalizeCompileCommandsJSON(ls.buildPath) + canonicalizeCompileCommandsJSON(buildPath.Join("compile_commands.json")) - return nil + return res.Success, nil } -func canonicalizeCompileCommandsJSON(compileCommandsDir *paths.Path) { - compileCommandsJSONPath := compileCommandsDir.Join("compile_commands.json") +func canonicalizeCompileCommandsJSON(compileCommandsJSONPath *paths.Path) { compileCommands, err := builder.LoadCompilationDatabase(compileCommandsJSONPath) if err != nil { panic("could not find compile_commands.json") diff --git a/ls/ls.go b/ls/ls.go index ed25cd4..cec4584 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -47,25 +47,20 @@ type INOLanguageServer struct { progressHandler *ProgressProxyHandler - closing chan bool - clangdStarted *sync.Cond - dataMux sync.RWMutex - buildPath *paths.Path - buildSketchRoot *paths.Path - buildSketchCpp *paths.Path - buildSketchSymbols []lsp.DocumentSymbol - buildSketchIncludesCanary string - buildSketchSymbolsCanary string - buildSketchSymbolsLoad chan bool - buildSketchSymbolsCheck chan bool - rebuildSketchDeadline *time.Time - rebuildSketchDeadlineMutex sync.Mutex - sketchRoot *paths.Path - sketchName string - sketchMapper *sourcemapper.SketchMapper - sketchTrackedFilesCount int - trackedInoDocs map[string]lsp.TextDocumentItem - inoDocsWithDiagnostics map[lsp.DocumentURI]bool + 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 + trackedInoDocs map[string]lsp.TextDocumentItem + inoDocsWithDiagnostics map[lsp.DocumentURI]bool + sketchRebuilder *SketchRebuilder config BoardConfig } @@ -143,27 +138,36 @@ func (ls *INOLanguageServer) waitClangdStart(logger jsonrpc.FunctionLogger) erro func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLanguageServer { logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") ls := &INOLanguageServer{ - trackedInoDocs: map[string]lsp.TextDocumentItem{}, - inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, - closing: make(chan bool), - buildSketchSymbolsLoad: make(chan bool, 1), - buildSketchSymbolsCheck: make(chan bool, 1), + trackedInoDocs: map[string]lsp.TextDocumentItem{}, + inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, + closing: make(chan bool), + // buildSketchSymbolsLoad: make(chan bool, 1), + // buildSketchSymbolsCheck: make(chan bool, 1), config: BoardConfig{ SelectedBoard: board, }, } ls.clangdStarted = sync.NewCond(&ls.dataMux) + ls.sketchRebuilder = NewSketchBuilder(ls) - if buildPath, err := paths.MkTempDir("", "arduino-language-server"); err != nil { + if tmp, err := paths.MkTempDir("", "arduino-language-server"); err != nil { log.Fatalf("Could not create temp folder: %s", err) } else { - ls.buildPath = buildPath.Canonical() + 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") } + if enableLogging { logger.Logf("Initial board configuration: %s", board) 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) @@ -175,81 +179,12 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLa ls.Close() }() - go func() { - defer streams.CatchAndLogPanic() - for { - select { - case <-ls.buildSketchSymbolsLoad: - // ...also un-queue buildSketchSymbolsCheck - select { - case <-ls.buildSketchSymbolsCheck: - default: - } - ls.LoadCppDocumentSymbols() - - case <-ls.buildSketchSymbolsCheck: - ls.CheckCppDocumentSymbols() - - case <-ls.closing: - return - } - } - }() - - go func() { - defer streams.CatchAndLogPanic() - ls.rebuildEnvironmentLoop() - }() return ls } -func (ls *INOLanguageServer) queueLoadCppDocumentSymbols() { - select { - case ls.buildSketchSymbolsLoad <- true: - default: - } -} - -func (handler *INOLanguageServer) queueCheckCppDocumentSymbols() { - select { - case handler.buildSketchSymbolsCheck <- true: - default: - } -} - -func (handler *INOLanguageServer) LoadCppDocumentSymbols() error { - logger := NewLSPFunctionLogger(color.HiBlueString, "SYLD --- ") - defer logger.Logf("(done)") - handler.readLock(logger, true) - defer handler.readUnlock(logger) - - return handler.refreshCppDocumentSymbols(logger) -} - -func (handler *INOLanguageServer) CheckCppDocumentSymbols() error { - logger := NewLSPFunctionLogger(color.HiBlueString, "SYCK --- ") - defer logger.Logf("(done)") - handler.readLock(logger, true) - defer handler.readUnlock(logger) - - oldSymbols := handler.buildSketchSymbols - canary := handler.buildSketchSymbolsCanary - if err := handler.refreshCppDocumentSymbols(logger); err != nil { - return err - } - if len(oldSymbols) != len(handler.buildSketchSymbols) || canary != handler.buildSketchSymbolsCanary { - logger.Logf("function symbols change detected, triggering sketch rebuild!") - handler.scheduleRebuildEnvironment() - } - return nil -} - func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { - ls.writeLock(logger, false /* do not wait for clangd... we are going to start it! */) go func() { defer streams.CatchAndLogPanic() - // the lock is "moved" into the goroutine - defer ls.writeUnlock(logger) logger := NewLSPFunctionLogger(color.HiCyanString, "INIT --- ") logger.Logf("initializing workbench: %s", inoParams.RootURI) @@ -258,9 +193,15 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js ls.sketchName = ls.sketchRoot.Base() ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") - if err := ls.generateBuildEnvironment(logger); err != nil { + 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!") + } + + 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 { @@ -907,6 +848,8 @@ func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.Func ls.writeLock(logger, true) defer ls.writeUnlock(logger) + ls.triggerRebuild() + // Add the TextDocumentItem in the tracked files list inoTextDocItem := inoParam.TextDocument ls.trackedInoDocs[inoTextDocItem.URI.AsPath().String()] = inoTextDocItem @@ -921,9 +864,6 @@ func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.Func logger.Logf("Clang already notified, do not notify it anymore") return } - - // Queue a load of ino.cpp document symbols - ls.queueLoadCppDocumentSymbols() } if cppItem, err := ls.ino2cppTextDocumentItem(logger, inoTextDocItem); err != nil { @@ -942,6 +882,8 @@ func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.Fu ls.writeLock(logger, true) defer ls.writeUnlock(logger) + ls.triggerRebuild() + logger.Logf("didChange(%s)", inoParams.TextDocument) for _, change := range inoParams.ContentChanges { logger.Logf(" > %s", change) @@ -969,6 +911,8 @@ func (ls *INOLanguageServer) TextDocumentDidSaveNotifFromIDE(logger jsonrpc.Func ls.writeLock(logger, true) defer ls.writeUnlock(logger) + ls.triggerRebuild() + logger.Logf("didSave(%s) hasText=%v", inoParams.TextDocument, inoParams.Text != "") if cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument); err != nil { logger.Logf("--E Error: %s", err) @@ -993,6 +937,8 @@ func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.Fun ls.writeLock(logger, true) defer ls.writeUnlock(logger) + ls.triggerRebuild() + logger.Logf("didClose(%s)", inoParams.TextDocument) if cppParams, err := ls.didClose(logger, inoParams); err != nil { @@ -1011,7 +957,6 @@ func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.Fun } func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.FunctionLogger, cppParams *lsp.PublishDiagnosticsParams) { - // Default to read lock ls.readLock(logger, false) defer ls.readUnlock(logger) @@ -1073,7 +1018,7 @@ func (ls *INOLanguageServer) WindowWorkDoneProgressCreateReqFromClangd(ctx conte return nil } -// Close closes all the json-rpc connections. +// Close closes all the json-rpc connections and clean-up temp folders. func (ls *INOLanguageServer) Close() { if ls.Clangd != nil { ls.Clangd.Close() @@ -1083,6 +1028,12 @@ func (ls *INOLanguageServer) Close() { 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 @@ -1098,51 +1049,6 @@ func (ls *INOLanguageServer) CleanUp() { } } -func (ls *INOLanguageServer) initializeWorkbench(logger jsonrpc.FunctionLogger) error { - logger.Logf("--> RE-initialize()") - currCppTextVersion := ls.sketchMapper.CppText.Version - - if err := ls.generateBuildEnvironment(logger); err != nil { - return err - } - - if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { - ls.sketchMapper = sourcemapper.CreateInoMapper(cppContent) - ls.sketchMapper.CppText.Version = currCppTextVersion + 1 - } 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 extractDataFolderFromArduinoCLI(logger jsonrpc.FunctionLogger) (*paths.Path, error) { // XXX: do this from IDE or via gRPC args := []string{globalCliPath, @@ -1176,79 +1082,6 @@ func extractDataFolderFromArduinoCLI(logger jsonrpc.FunctionLogger) (*paths.Path return paths.New(res.Directories.Data), nil } -func (ls *INOLanguageServer) refreshCppDocumentSymbols(logger jsonrpc.FunctionLogger) error { - // Query source code symbols - ls.readUnlock(logger) - cppURI := lsp.NewDocumentURIFromPath(ls.buildSketchCpp) - logger.Logf("requesting documentSymbol for %s", cppURI) - - cppParams := &lsp.DocumentSymbolParams{ - TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, - } - cppDocumentSymbols, _ /* cppSymbolInformation */, cppErr, err := ls.Clangd.conn.TextDocumentDocumentSymbol(context.Background(), cppParams) - ls.readLock(logger, true) - if err != nil { - logger.Logf("error: %s", err) - return fmt.Errorf("quering source code symbols: %w", err) - } - if cppErr != nil { - logger.Logf("error: %s", cppErr.AsError()) - return fmt.Errorf("quering source code symbols: %w", cppErr.AsError()) - } - - if cppDocumentSymbols == nil { - err := errors.New("expected DocumenSymbol array but got SymbolInformation instead") - logger.Logf("error: %s", err) - return err - } - - // Filter non-functions symbols - i := 0 - for _, symbol := range cppDocumentSymbols { - if symbol.Kind != lsp.SymbolKindFunction { - continue - } - cppDocumentSymbols[i] = symbol - i++ - } - cppDocumentSymbols = cppDocumentSymbols[:i] - ls.buildSketchSymbols = cppDocumentSymbols - - symbolsCanary := "" - for _, symbol := range cppDocumentSymbols { - logger.Logf(" symbol: %s %s %s", symbol.Kind, symbol.Name, symbol.Range) - if symbolText, err := textedits.ExtractRange(ls.sketchMapper.CppText.Text, symbol.Range); err != nil { - logger.Logf(" > invalid range: %s", err) - symbolsCanary += "/" - } else if end := strings.Index(symbolText, "{"); end != -1 { - logger.Logf(" TRIMMED> %s", symbolText[:end]) - symbolsCanary += symbolText[:end] - } else { - logger.Logf(" > %s", symbolText) - symbolsCanary += symbolText - } - } - ls.buildSketchSymbolsCanary = symbolsCanary - return nil -} - -func (ls *INOLanguageServer) CheckCppIncludesChanges() { - logger := NewLSPFunctionLogger(color.HiBlueString, "INCK --- ") - logger.Logf("check for Cpp Include Changes") - includesCanary := "" - for _, line := range strings.Split(ls.sketchMapper.CppText.Text, "\n") { - if strings.Contains(line, "#include ") { - includesCanary += line - } - } - - if includesCanary != ls.buildSketchIncludesCanary { - ls.buildSketchIncludesCanary = includesCanary - logger.Logf("#include change detected, triggering sketch rebuild!") - ls.scheduleRebuildEnvironment() - } -} - func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { inoIdentifier := inoDidClose.TextDocument if _, exist := ls.trackedInoDocs[inoIdentifier.URI.AsPath().String()]; exist { @@ -1340,26 +1173,9 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChan return nil, errors.Errorf("invalid change range %s:%s", inoDoc.URI, inoChange.Range) } - // Detect changes in critical lines (for example function definitions) - // and trigger arduino-preprocessing + clangd restart. - dirty := false - for _, sym := range ls.buildSketchSymbols { - if sym.SelectionRange.Overlaps(cppChangeRange) { - dirty = true - logger.Logf("--! DIRTY CHANGE detected using symbol tables, force sketch rebuild!") - break - } - } - if ls.sketchMapper.ApplyTextChange(inoDoc.URI, inoChange) { - dirty = true - logger.Logf("--! DIRTY CHANGE detected with sketch mapper, force sketch rebuild!") - } - if dirty { - ls.scheduleRebuildEnvironment() - } - ls.sketchMapper.DebugLogAll() + _ = ls.sketchMapper.ApplyTextChange(inoDoc.URI, inoChange) - logger.Logf("New version:----------+\n" + ls.sketchMapper.CppText.Text + "\n----------------------") + ls.sketchMapper.DebugLogAll() cppChanges = append(cppChanges, lsp.TextDocumentContentChangeEvent{ Range: &cppChangeRange, @@ -1368,8 +1184,6 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChan }) } - ls.CheckCppIncludesChanges() - // build a cpp equivalent didChange request return &lsp.DidChangeTextDocumentParams{ ContentChanges: cppChanges, @@ -1824,19 +1638,6 @@ func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, c inoDiagsParams.Diagnostics = append(inoDiagsParams.Diagnostics, inoDiag) ls.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). - var inoDiagCode string - if err := json.Unmarshal(inoDiag.Code, &inoDiagCode); err != nil { - if inoDiagCode == "undeclared_var_use_suggest" || - inoDiagCode == "undeclared_var_use" || - inoDiagCode == "ovl_no_viable_function_in_call" || - inoDiagCode == "pp_file_not_found" { - ls.queueCheckCppDocumentSymbols() - } - } } inoDiagParams := []*lsp.PublishDiagnosticsParams{} diff --git a/ls/lsp_client_clangd.go b/ls/lsp_client_clangd.go index 8255c48..cd73e5b 100644 --- a/ls/lsp_client_clangd.go +++ b/ls/lsp_client_clangd.go @@ -21,35 +21,7 @@ type ClangdLSPClient struct { ls *INOLanguageServer } -func NewClangdLSPClient( - logger jsonrpc.FunctionLogger, - buildPath, buildSketchCpp, dataFolder *paths.Path, - inoLanguageServer *INOLanguageServer, -) *ClangdLSPClient { - clangdStdout, clangdStdin, clangdStderr := startClangd(logger, buildPath, 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) - } - - client := &ClangdLSPClient{ - ls: inoLanguageServer, - } - 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 startClangd(logger jsonrpc.FunctionLogger, compileCommandsDir, sketchCpp, dataFolder *paths.Path) (io.WriteCloser, io.ReadCloser, io.ReadCloser) { +func NewClangdLSPClient(logger jsonrpc.FunctionLogger, compileCommandsDir, buildSketchCpp, dataFolder *paths.Path, inoLanguageServer *INOLanguageServer) *ClangdLSPClient { // Start clangd args := []string{ globalClangdPath, @@ -59,20 +31,47 @@ func startClangd(logger jsonrpc.FunctionLogger, compileCommandsDir, sketchCpp, d if dataFolder != nil { args = append(args, fmt.Sprintf("-query-driver=%s", dataFolder.Join("packages", "**"))) } + + // clangdStdout, clangdStdin, clangdStderr := startClangd(logger, compileCommandsDir, buildSketchCpp, dataFolder) 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 clangdIn, err := clangdCmd.StdinPipe(); err != nil { + } else if cin, err := clangdCmd.StdinPipe(); err != nil { panic("getting clangd stdin: " + err.Error()) - } else if clangdOut, err := clangdCmd.StdoutPipe(); err != nil { + } else if cout, err := clangdCmd.StdoutPipe(); err != nil { panic("getting clangd stdout: " + err.Error()) - } else if clangdErr, err := clangdCmd.StderrPipe(); err != nil { + } 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 { - return clangdIn, clangdOut, clangdErr + clangdStdin = cin + clangdStdout = cout + clangdStderr = cerr } + + clangdStdio := streams.NewReadWriteCloser(clangdStdout, clangdStdin) + 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) + } + + client := &ClangdLSPClient{ + ls: inoLanguageServer, + } + 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() { diff --git a/sourcemapper/ino.go b/sourcemapper/ino.go index 096be28..87c0b3f 100644 --- a/sourcemapper/ino.go +++ b/sourcemapper/ino.go @@ -369,7 +369,11 @@ func dumpInoToCppMap(s map[InoLine]int) { // DebugLogAll dumps the internal status of the mapper func (s *SketchMapper) DebugLogAll() { stripFile := func(s string) string { - return s[strings.LastIndex(s, "/"):] + l := strings.LastIndex(s, "/") + if l == -1 { + return s + } + return s[l:] } cpp := strings.Split(s.CppText.Text, "\n") log.Printf(" > Current sketchmapper content:") From 84c418a45b6ea7f4c6d4fcdc444767775ae3ef96 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 11 Nov 2021 17:45:31 +0100 Subject: [PATCH 30/76] Removed outdated launch config --- .vscode/launch.json | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .vscode/launch.json 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 From e709702da2e1bd4e4330eb61909109e95c6dd2be Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 15 Nov 2021 14:33:59 +0100 Subject: [PATCH 31/76] updated dependencies --- go.mod | 7 +++---- go.sum | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index f6fc4af..9c29012 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/arduino/arduino-language-server -go 1.12 - -replace go.bug.st/lsp => ../go-lsp +go 1.16 require ( github.com/arduino/arduino-cli v0.0.0-20211111113528-bf4a7844a79b @@ -11,5 +9,6 @@ require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 go.bug.st/json v1.15.6 - go.bug.st/lsp v0.0.0-20211109230950-26242be380a2 + go.bug.st/lsp v0.0.0-20211111165110-143dff941d53 + google.golang.org/grpc v1.42.0 ) diff --git a/go.sum b/go.sum index 1422335..1f79d5f 100644 --- a/go.sum +++ b/go.sum @@ -42,10 +42,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/arduino/arduino-cli v0.0.0-20211105112240-8cae8809aad9 h1:/in/9/XSzoD5j4+0yegDQoej4Lgr5ItevWgmQNXGiyg= -github.com/arduino/arduino-cli v0.0.0-20211105112240-8cae8809aad9/go.mod h1:cU/LNzIePCU/dvW39BClyZVPuz19Zsrt2dAIbWPtTAA= -github.com/arduino/arduino-cli v0.0.0-20211111094916-c8a3f2efad9d h1:dNv1HPy9IbjhZ2+Y4kzoXRVWSZmRw6tMk8a7FPUn41w= -github.com/arduino/arduino-cli v0.0.0-20211111094916-c8a3f2efad9d/go.mod h1:cU/LNzIePCU/dvW39BClyZVPuz19Zsrt2dAIbWPtTAA= 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 h1:agh2JT96G8egU7FEb13L4dq3fnCN7lxXhJ86t69+W7s= @@ -67,6 +63,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY 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/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= @@ -76,6 +73,10 @@ github.com/cmaglie/pb v1.0.27/go.mod h1:GilkKZMXYjBA4NxItWFfO+lwkp59PLHQ+IOW/b/k 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 h1:1ChS4EvWTjw6bH2sd6QiMcmih0itVVrWdh9MmOliX/I= github.com/codeclysm/cc v1.2.2/go.mod h1:XtW4ArCNgQwFphcRGG9+sPX5WM1J6/u0gMy5ZdV3obA= github.com/codeclysm/extract/v3 v3.0.2 h1:sB4LcE3Php7LkhZwN0n2p8GCwZe92PEQutdbGURf5xc= @@ -99,6 +100,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m 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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= @@ -182,7 +184,6 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 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 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 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= @@ -219,7 +220,6 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS 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 h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 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= @@ -322,9 +322,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -342,6 +340,7 @@ github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= 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 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= 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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -365,8 +364,8 @@ 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-20211109230950-26242be380a2 h1:UMl3K6gir2vJaZb9S8OoQ7PM2XMbndMPIJE+Jpynevw= -go.bug.st/lsp v0.0.0-20211109230950-26242be380a2/go.mod h1:oYTh1uf5hI1teV5crrWut41Pk8vD/NqIjs4zD+No5FE= +go.bug.st/lsp v0.0.0-20211111165110-143dff941d53 h1:6fjqjqNIhfIPaNoKJtlhJHpdKsk5aXP1QOTRDbdCyFg= +go.bug.st/lsp v0.0.0-20211111165110-143dff941d53/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.3.2 h1:6BFZZd/wngoL5PPYYTrFUounF54SIkykHpT98eq6zvk= @@ -383,6 +382,7 @@ 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= @@ -706,6 +706,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= 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= From 27c3eb67418e678f3449c3499a5d8143742354d3 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 15 Nov 2021 14:34:19 +0100 Subject: [PATCH 32/76] Use arduino-cli RPC to ger sketch compiled --- ls/builder.go | 156 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 105 insertions(+), 51 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 6345e4e..00f0c05 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "io" "runtime" "strings" "sync" @@ -12,6 +13,7 @@ import ( "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" @@ -20,6 +22,7 @@ import ( "go.bug.st/json" "go.bug.st/lsp" "go.bug.st/lsp/jsonrpc" + "google.golang.org/grpc" ) type SketchRebuilder struct { @@ -172,65 +175,116 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge } ls.readUnlock(logger) - // Run arduino-cli to perform the build - 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") + var success bool + if true { + // Establish a connection with the gRPC server, started with the command: + // arduino-cli daemon + conn, err := grpc.Dial("localhost:50051", 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) + compRespStream, err := client.Compile(context.Background(), + &rpc.CompileRequest{ + Instance: &rpc.Instance{Id: 1}, // XXX + Fqbn: fqbn, + SketchPath: sketchRoot.String(), + SourceOverride: data.Overrides, + BuildPath: buildPath.String(), + CreateCompilationDatabaseOnly: true, + }, + ) + if err != nil { + return false, fmt.Errorf("error running compile: %w", err) + } + + // Loop and consume the server stream until all the operations are done. + for { + compResp, err := compRespStream.Recv() + if err == io.EOF { + success = true + logger.Logf("Compile successful!") + break + } + if err != nil { + return false, fmt.Errorf("error running compile: %w", err) + } + + // TODO: we can accumulate stdout/stderr buffer if needed + _ = compResp + // if resp := compResp.GetOutStream(); resp != nil { + // logger.Logf("STDOUT: %s", resp) + // } + // if resperr := compResp.GetErrStream(); resperr != nil { + // logger.Logf("STDERR: %s", resperr) + // } + } + } else { - overridesJSON = tmp - defer tmp.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", - 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) - } + // 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() + } - // 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) + // Run arduino-cli to perform the build + args := []string{globalCliPath, + "--config-file", globalCliConfigPath, + "compile", + "--fqbn", fqbn, + "--only-compilation-database", + //"--clean", + "--source-override", overridesJSON.String(), + "--build-path", buildPath.String(), + "--format", "json", + 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 } - logger.Logf("arduino-cli output: %s", cmdOutput) // TODO: do canonicalization directly in `arduino-cli` canonicalizeCompileCommandsJSON(buildPath.Join("compile_commands.json")) - return res.Success, nil + return success, nil } func canonicalizeCompileCommandsJSON(compileCommandsJSONPath *paths.Path) { From 87d8a62aa6c3763cbae0efb52986755116b591fe Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 15 Nov 2021 14:34:31 +0100 Subject: [PATCH 33/76] Added profiler in debug mode --- main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main.go b/main.go index f30381b..5532f59 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,8 @@ import ( "flag" "io" "log" + "net/http" + _ "net/http/pprof" "os" "os/signal" @@ -44,6 +46,9 @@ func main() { 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)) + }() } else { log.SetOutput(os.Stderr) } From e13dcc8a66823c19491a6f2d884b9b7a626a0151 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 16 Nov 2021 11:58:43 +0100 Subject: [PATCH 34/76] Added logging of failing builds --- ls/builder.go | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 00f0c05..4c14d50 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -186,21 +186,26 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge defer conn.Close() client := rpc.NewArduinoCoreServiceClient(conn) - compRespStream, err := client.Compile(context.Background(), - &rpc.CompileRequest{ - Instance: &rpc.Instance{Id: 1}, // XXX - Fqbn: fqbn, - SketchPath: sketchRoot.String(), - SourceOverride: data.Overrides, - BuildPath: buildPath.String(), - CreateCompilationDatabaseOnly: true, - }, - ) + compileReq := &rpc.CompileRequest{ + Instance: &rpc.Instance{Id: 1}, // XXX + Fqbn: 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 { @@ -209,17 +214,19 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge 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) } - // TODO: we can accumulate stdout/stderr buffer if needed - _ = compResp - // if resp := compResp.GetOutStream(); resp != nil { - // logger.Logf("STDOUT: %s", resp) - // } - // if resperr := compResp.GetErrStream(); resperr != nil { - // logger.Logf("STDERR: %s", resperr) - // } + if resp := compResp.GetOutStream(); resp != nil { + stdout += string(resp) + } + if resperr := compResp.GetErrStream(); resperr != nil { + stderr += string(resperr) + } } } else { From 0869a5f843da75cbd92b1c8e7f873bc2215a6987 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 17 Nov 2021 16:50:36 +0100 Subject: [PATCH 35/76] Terminate LS if we are unable to start clangd --- ls/ls.go | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index cec4584..d53a4db 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "log" + "os" "strconv" "strings" "sync" @@ -82,8 +83,18 @@ 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.waitClangdStart(logger) + 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) + } } } @@ -117,23 +128,6 @@ func (ls *INOLanguageServer) readUnlock(logger jsonrpc.FunctionLogger) { ls.dataMux.RUnlock() } -func (ls *INOLanguageServer) waitClangdStart(logger jsonrpc.FunctionLogger) error { - if ls.Clangd != nil { - return nil - } - - 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: aborting call") - return errors.New("could not start clangd, aborted") - } - return nil -} - // NewINOLanguageServer creates and configures an Arduino Language Server. func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLanguageServer { logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") @@ -185,6 +179,8 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLa func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *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", inoParams.RootURI) @@ -198,6 +194,7 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js 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 { @@ -249,9 +246,6 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js return } - // signal that clangd is running now... - ls.clangdStarted.Broadcast() - logger.Logf("Done initializing workbench") }() From 3ba5af00100c7ba8af4a27438712a111ec94ee03 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 18 Nov 2021 12:22:57 +0100 Subject: [PATCH 36/76] Factored configuration paramaters for the language server --- ls/builder.go | 10 +++--- ls/ls.go | 73 +++++++++++++---------------------------- ls/lsp_client_clangd.go | 11 +++---- ls/unused.go | 5 ++- main.go | 21 ++++++++++-- 5 files changed, 54 insertions(+), 66 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 4c14d50..3c2579e 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -159,8 +159,10 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge // Extract all build information from language server status ls.readLock(logger, false) sketchRoot := ls.sketchRoot - fqbn := ls.config.SelectedBoard.Fqbn + fqbn := ls.config.Fqbn buildPath := ls.buildPath + cliPath := ls.config.CliPath + cliConfigPath := ls.config.CliConfigPath type overridesFile struct { Overrides map[string]string `json:"overrides"` } @@ -176,7 +178,7 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge ls.readUnlock(logger) var success bool - if true { + if cliPath == nil { // Establish a connection with the gRPC server, started with the command: // arduino-cli daemon conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock()) @@ -246,8 +248,8 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge } // Run arduino-cli to perform the build - args := []string{globalCliPath, - "--config-file", globalCliConfigPath, + args := []string{cliPath.String(), + "--config-file", cliConfigPath.String(), "compile", "--fqbn", fqbn, "--only-compilation-database", diff --git a/ls/ls.go b/ls/ls.go index d53a4db..11111ae 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -24,30 +24,13 @@ import ( "go.bug.st/lsp/textedits" ) -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 -} - // INOLanguageServer is a JSON-RPC handler that delegates messages to clangd. type INOLanguageServer struct { + config *Config IDE *IDELSPServer Clangd *ClangdLSPClient - progressHandler *ProgressProxyHandler - + progressHandler *ProgressProxyHandler closing chan bool clangdStarted *sync.Cond dataMux sync.RWMutex @@ -62,20 +45,16 @@ type INOLanguageServer struct { trackedInoDocs map[string]lsp.TextDocumentItem inoDocsWithDiagnostics map[lsp.DocumentURI]bool sketchRebuilder *SketchRebuilder - - config BoardConfig } -// 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"` +// Config describes the language server configuration. +type Config struct { + Fqbn string + CliPath *paths.Path + CliConfigPath *paths.Path + ClangdPath *paths.Path + FormatterConf *paths.Path + EnableLogging bool } var yellow = color.New(color.FgHiYellow) @@ -129,17 +108,13 @@ func (ls *INOLanguageServer) readUnlock(logger jsonrpc.FunctionLogger) { } // NewINOLanguageServer creates and configures an Arduino Language Server. -func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLanguageServer { +func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, config *Config) *INOLanguageServer { logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") ls := &INOLanguageServer{ trackedInoDocs: map[string]lsp.TextDocumentItem{}, inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, closing: make(chan bool), - // buildSketchSymbolsLoad: make(chan bool, 1), - // buildSketchSymbolsCheck: make(chan bool, 1), - config: BoardConfig{ - SelectedBoard: board, - }, + config: config, } ls.clangdStarted = sync.NewCond(&ls.dataMux) ls.sketchRebuilder = NewSketchBuilder(ls) @@ -157,12 +132,10 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, board Board) *INOLa ls.buildSketchRoot = ls.buildPath.Join("sketch") } - if enableLogging { - logger.Logf("Initial board configuration: %s", board) - 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")) - } + 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) @@ -210,14 +183,14 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js } // Retrieve data folder - dataFolder, err := extractDataFolderFromArduinoCLI(logger) + 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, ls.buildPath, ls.buildSketchCpp, dataFolder, ls) + ls.Clangd = NewClangdLSPClient(logger, dataFolder, ls) go func() { defer streams.CatchAndLogPanic() ls.Clangd.Run() @@ -1043,10 +1016,10 @@ func (ls *INOLanguageServer) CleanUp() { } } -func extractDataFolderFromArduinoCLI(logger jsonrpc.FunctionLogger) (*paths.Path, error) { +func (ls *INOLanguageServer) extractDataFolderFromArduinoCLI(logger jsonrpc.FunctionLogger) (*paths.Path, error) { // XXX: do this from IDE or via gRPC - args := []string{globalCliPath, - "--config-file", globalCliConfigPath, + args := []string{ls.config.CliPath.String(), + "--config-file", ls.config.CliConfigPath.String(), "config", "dump", "--format", "json", @@ -1806,9 +1779,9 @@ WhitespaceSensitiveMacros: [] 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 globalFormatterConf != nil && globalFormatterConf.Exist() { + } else if ls.config.FormatterConf != nil && ls.config.FormatterConf.Exist() { // Otherwise if a global config file is present, use that one - try(globalFormatterConf) + try(ls.config.FormatterConf) } targetFile := cppuri.AsPath() diff --git a/ls/lsp_client_clangd.go b/ls/lsp_client_clangd.go index cd73e5b..2bad456 100644 --- a/ls/lsp_client_clangd.go +++ b/ls/lsp_client_clangd.go @@ -21,18 +21,17 @@ type ClangdLSPClient struct { ls *INOLanguageServer } -func NewClangdLSPClient(logger jsonrpc.FunctionLogger, compileCommandsDir, buildSketchCpp, dataFolder *paths.Path, inoLanguageServer *INOLanguageServer) *ClangdLSPClient { +func NewClangdLSPClient(logger jsonrpc.FunctionLogger, dataFolder *paths.Path, ls *INOLanguageServer) *ClangdLSPClient { // Start clangd args := []string{ - globalClangdPath, + ls.config.ClangdPath.String(), "-log=verbose", - fmt.Sprintf(`--compile-commands-dir=%s`, compileCommandsDir), + fmt.Sprintf(`--compile-commands-dir=%s`, ls.buildPath), } if dataFolder != nil { args = append(args, fmt.Sprintf("-query-driver=%s", dataFolder.Join("packages", "**"))) } - // clangdStdout, clangdStdin, clangdStderr := startClangd(logger, compileCommandsDir, buildSketchCpp, dataFolder) logger.Logf(" Starting clangd: %s", strings.Join(args, " ")) var clangdStdin io.WriteCloser var clangdStdout, clangdStderr io.ReadCloser @@ -53,7 +52,7 @@ func NewClangdLSPClient(logger jsonrpc.FunctionLogger, compileCommandsDir, build } clangdStdio := streams.NewReadWriteCloser(clangdStdout, clangdStdin) - if enableLogging { + if ls.config.EnableLogging { clangdStdio = streams.LogReadWriteCloserAs(clangdStdio, "inols-clangd.log") go io.Copy(streams.OpenLogFileAs("inols-clangd-err.log"), clangdStderr) } else { @@ -61,7 +60,7 @@ func NewClangdLSPClient(logger jsonrpc.FunctionLogger, compileCommandsDir, build } client := &ClangdLSPClient{ - ls: inoLanguageServer, + ls: ls, } client.conn = lsp.NewClient(clangdStdio, clangdStdio, client) client.conn.SetLogger(&LSPLogger{ diff --git a/ls/unused.go b/ls/unused.go index e1c90f3..d870bbc 100644 --- a/ls/unused.go +++ b/ls/unused.go @@ -21,9 +21,8 @@ func (ls *INOLanguageServer) handleError(logger jsonrpc.FunctionLogger, err erro submatch := exp.FindStringSubmatch(errorStr) message = submatch[1] } else if strings.Contains(errorStr, "platform not installed") || strings.Contains(errorStr, "no FQBN provided") { - if len(ls.config.SelectedBoard.Name) > 0 { - board := ls.config.SelectedBoard.Name - message = "Editor support may be inaccurate because the core for the board `" + board + "` is not installed." + 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 diff --git a/main.go b/main.go index 5532f59..d52a007 100644 --- a/main.go +++ b/main.go @@ -56,16 +56,31 @@ func main() { if cliConfigPath == "" { log.Fatal("Path to ArduinoCLI config file must be set.") } + if clangdPath == "" { + log.Fatal("Path to Clangd must be set.") + } - ls.Setup(cliPath, cliConfigPath, clangdPath, formatFilePath, enableLogging) - initialBoard := ls.Board{Fqbn: initialFqbn, Name: initialBoardName} + config := &ls.Config{ + Fqbn: initialFqbn, + ClangdPath: paths.New(clangdPath), + EnableLogging: enableLogging, + } + if cliPath != "" { + config.CliPath = paths.New(cliPath) + } + if cliConfigPath != "" { + config.CliConfigPath = paths.New(cliConfigPath) + } + if formatFilePath != "" { + config.FormatterConf = paths.New(formatFilePath) + } stdio := streams.NewReadWriteCloser(os.Stdin, os.Stdout) if enableLogging { stdio = streams.LogReadWriteCloserAs(stdio, "inols.log") } - inoHandler := ls.NewINOLanguageServer(stdio, stdio, initialBoard) + inoHandler := ls.NewINOLanguageServer(stdio, stdio, config) // Intercept kill signal c := make(chan os.Signal, 2) From 387a275a243e205ffe3da8400f5cbf5ecc6fa167 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 18 Nov 2021 12:57:22 +0100 Subject: [PATCH 37/76] Added command line options to allow daemon connection --- ls/builder.go | 20 +++++------ ls/ls.go | 14 ++++---- main.go | 93 ++++++++++++++++++++++++++++++--------------------- 3 files changed, 71 insertions(+), 56 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 3c2579e..35512a0 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -159,10 +159,8 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge // Extract all build information from language server status ls.readLock(logger, false) sketchRoot := ls.sketchRoot - fqbn := ls.config.Fqbn buildPath := ls.buildPath - cliPath := ls.config.CliPath - cliConfigPath := ls.config.CliConfigPath + config := ls.config type overridesFile struct { Overrides map[string]string `json:"overrides"` } @@ -178,10 +176,10 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge ls.readUnlock(logger) var success bool - if cliPath == nil { + if config.CliPath == nil { // Establish a connection with the gRPC server, started with the command: // arduino-cli daemon - conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock()) + 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) } @@ -189,8 +187,8 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge client := rpc.NewArduinoCoreServiceClient(conn) compileReq := &rpc.CompileRequest{ - Instance: &rpc.Instance{Id: 1}, // XXX - Fqbn: fqbn, + Instance: &rpc.Instance{Id: int32(config.CliInstanceNumber)}, + Fqbn: config.Fqbn, SketchPath: sketchRoot.String(), SourceOverride: data.Overrides, BuildPath: buildPath.String(), @@ -248,15 +246,15 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge } // Run arduino-cli to perform the build - args := []string{cliPath.String(), - "--config-file", cliConfigPath.String(), + args := []string{config.CliPath.String(), + "--config-file", config.CliConfigPath.String(), "compile", - "--fqbn", fqbn, + "--fqbn", config.Fqbn, "--only-compilation-database", - //"--clean", "--source-override", overridesJSON.String(), "--build-path", buildPath.String(), "--format", "json", + //"--clean", sketchRoot.String(), } cmd, err := executils.NewProcess(args...) diff --git a/ls/ls.go b/ls/ls.go index 11111ae..77a3172 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -49,12 +49,14 @@ type INOLanguageServer struct { // Config describes the language server configuration. type Config struct { - Fqbn string - CliPath *paths.Path - CliConfigPath *paths.Path - ClangdPath *paths.Path - FormatterConf *paths.Path - EnableLogging bool + 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) diff --git a/main.go b/main.go index d52a007..e3622f4 100644 --- a/main.go +++ b/main.go @@ -14,69 +14,84 @@ import ( "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 == "" { + if *clangdPath == "" { log.Fatal("Path to Clangd must be set.") } config := &ls.Config{ - Fqbn: initialFqbn, - ClangdPath: paths.New(clangdPath), - EnableLogging: enableLogging, - } - if cliPath != "" { - config.CliPath = paths.New(cliPath) - } - if cliConfigPath != "" { - config.CliConfigPath = paths.New(cliConfigPath) - } - if formatFilePath != "" { - config.FormatterConf = paths.New(formatFilePath) + 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") } From 829114737e205f469bab01febf57c81e4fc4fb1c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 18 Nov 2021 13:11:30 +0100 Subject: [PATCH 38/76] Allow querying datafolder via gprc --- ls/builder.go | 5 ++-- ls/ls.go | 80 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 35512a0..f9f608b 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -177,15 +177,14 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge var success bool if config.CliPath == nil { - // Establish a connection with the gRPC server, started with the command: - // arduino-cli daemon + // 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, diff --git a/ls/ls.go b/ls/ls.go index 77a3172..0d14327 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -13,6 +13,7 @@ import ( "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" @@ -22,6 +23,7 @@ import ( "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. @@ -1019,36 +1021,58 @@ func (ls *INOLanguageServer) CleanUp() { } func (ls *INOLanguageServer) extractDataFolderFromArduinoCLI(logger jsonrpc.FunctionLogger) (*paths.Path, error) { - // XXX: do this from IDE or via gRPC - 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) - } + 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) - 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) + 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) + } + var dataDir string + 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) + return paths.New(dataDir), nil + } 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) + return paths.New(res.Directories.Data), nil } - // Return only the build path - logger.Logf("Arduino Data Dir -> %s", res.Directories.Data) - return paths.New(res.Directories.Data), nil } func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { From a2942cdea93248eefe9e7daefd4369d6b9b94369 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 18 Nov 2021 18:19:44 +0100 Subject: [PATCH 39/76] Correctly handle shutdown --- ls/ls.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 0d14327..161304a 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -297,10 +297,7 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js } func (ls *INOLanguageServer) ShutdownReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger) *jsonrpc.ResponseError { - logger.Logf("Sending shutdown notification to clangd...") ls.Clangd.conn.Shutdown(context.Background()) - logger.Logf("Arduino Language Server is shutting down.") - ls.Close() return nil } @@ -812,7 +809,9 @@ func (ls *INOLanguageServer) InitializedNotifFromIDE(logger jsonrpc.FunctionLogg } func (ls *INOLanguageServer) ExitNotifFromIDE(logger jsonrpc.FunctionLogger) { - logger.Logf("Notification is not propagated to clangd") + ls.Clangd.conn.Exit() + logger.Logf("Arduino Language Server is shutting down.") + os.Exit(0) } func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.FunctionLogger, inoParam *lsp.DidOpenTextDocumentParams) { From e01da53baf92d9cbd054503f31cce9bb723ed4a3 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 19 Nov 2021 17:22:40 +0100 Subject: [PATCH 40/76] Fixed Hover coordinates; started refactoring jargon (ino/cpp -> ide/clang) --- ls/ls.go | 142 ++++++++++++++++++++++++++----------------------------- 1 file changed, 67 insertions(+), 75 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 161304a..d690316 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -306,7 +306,7 @@ func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Contex defer ls.readUnlock(logger) logger.Logf("--> completion(%s)\n", inoParams.TextDocument) - cppTextDocPositionParams, err := ls.ino2cppTextDocumentPositionParams(logger, inoParams.TextDocumentPositionParams) + cppTextDocPositionParams, err := ls.ide2clangTextDocumentPositionParams(logger, inoParams.TextDocumentPositionParams) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -350,25 +350,20 @@ func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Contex return &inoResp, nil } -func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.HoverParams) (*lsp.Hover, *jsonrpc.ResponseError) { +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) - inoURI := inoParams.TextDocument.URI - inoTextDocPosition := inoParams.TextDocumentPositionParams - logger.Logf("--> hover(%s)\n", inoTextDocPosition) - - cppTextDocPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocPosition) + 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()} } - logger.Logf(" --> hover(%s)\n", cppTextDocPosition) - cppParams := &lsp.HoverParams{ - TextDocumentPositionParams: cppTextDocPosition, + clangParams := &lsp.HoverParams{ + TextDocumentPositionParams: clangTextDocPosition, } - clangResp, clangErr, err := ls.Clangd.conn.TextDocumentHover(ctx, cppParams) + clangResp, clangErr, err := ls.Clangd.conn.TextDocumentHover(ctx, clangParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) ls.Close() @@ -380,21 +375,26 @@ func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, lo } if clangResp == nil { + logger.Logf("response: nil") return nil, nil } - inoResp := *clangResp - // TODO: ???? - // if len(clangResp.Contents.Value) == 0 { - // return nil - // } - cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(ls.buildSketchCpp) - if cppToIno { - _, inoRange := ls.sketchMapper.CppToInoRange(*clangResp.Range) - inoResp.Range = &inoRange + _, r, 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()} } - logger.Logf("<-- hover(%s)", strconv.Quote(inoResp.Contents.Value)) - return &inoResp, nil + ideResp := lsp.Hover{ + Contents: clangResp.Contents, + Range: &r, + } + logger.Logf("Hover content: %s", strconv.Quote(ideResp.Contents.Value)) + return &ideResp, nil +} + +func (ls *INOLanguageServer) clangURIRefersToIno(uri lsp.DocumentURI) bool { + return uri.AsPath().EquivalentTo(ls.buildSketchCpp) } func (ls *INOLanguageServer) TextDocumentSignatureHelpReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.SignatureHelpParams) (*lsp.SignatureHelp, *jsonrpc.ResponseError) { @@ -404,7 +404,7 @@ func (ls *INOLanguageServer) TextDocumentSignatureHelpReqFromIDE(ctx context.Con inoTextDocumentPosition := inoParams.TextDocumentPositionParams logger.Logf("%s", inoTextDocumentPosition) - cppTextDocumentPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -436,7 +436,7 @@ func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Contex inoTextDocPosition := p.TextDocumentPositionParams logger.Logf("%s", inoTextDocPosition) - cppTextDocPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocPosition) + cppTextDocPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -481,7 +481,7 @@ func (ls *INOLanguageServer) TextDocumentTypeDefinitionReqFromIDE(ctx context.Co logger.Logf("%s", inoTextDocumentPosition) // inoURI := inoTextDocumentPosition.TextDocument.URI - cppTextDocumentPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -527,7 +527,7 @@ func (ls *INOLanguageServer) TextDocumentImplementationReqFromIDE(ctx context.Co inoTextDocumentPosition := inoParams.TextDocumentPositionParams logger.Logf("%s", inoTextDocumentPosition) - cppTextDocumentPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -569,7 +569,7 @@ func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context defer ls.readUnlock(logger) inoTextDocumentPosition := inoParams.TextDocumentPositionParams - cppTextDocumentPosition, err := ls.ino2cppTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -1235,7 +1235,7 @@ func (ls *INOLanguageServer) ino2cppDocumentURI(logger jsonrpc.FunctionLogger, i return lsp.NilURI, err } -func (ls *INOLanguageServer) inoDocumentURIFromInoPath(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { +func (ls *INOLanguageServer) idePathToIdeURI(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { if inoPath == sourcemapper.NotIno.File { return sourcemapper.NotInoURI, nil } @@ -1252,65 +1252,57 @@ func (ls *INOLanguageServer) inoDocumentURIFromInoPath(logger jsonrpc.FunctionLo return doc.URI, nil } -func (ls *INOLanguageServer) cpp2inoDocumentURI(logger jsonrpc.FunctionLogger, 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(ls.buildSketchCpp) { - inoPath, inoRange, err := ls.sketchMapper.CppToInoRangeOk(cppRange) +func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangRange lsp.Range) (lsp.DocumentURI, lsp.Range, 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 err == nil { - if ls.sketchMapper.IsPreprocessedCppLine(cppRange.Start.Line) { - inoPath = sourcemapper.NotIno.File - logger.Logf(" URI: is in preprocessed section") - logger.Logf(" converted %s to %s:%s", cppRange, inoPath, inoRange) - } else { - logger.Logf(" URI: converted %s to %s:%s", cppRange, inoPath, inoRange) + if ls.sketchMapper.IsPreprocessedCppLine(clangRange.Start.Line) { + idePath = sourcemapper.NotIno.File + logger.Logf(" Range is in PREPROCESSED section of the sketch") } } else if _, ok := err.(sourcemapper.AdjustedRangeErr); ok { - logger.Logf(" URI: converted %s to %s:%s (END LINE ADJUSTED)", cppRange, inoPath, inoRange) + logger.Logf(" Range has been END LINE ADJSUTED") err = nil } else { - logger.Logf(" URI: ERROR: %s", err) + logger.Logf(" Range conversion ERROR: %s", err) ls.sketchMapper.DebugLogAll() return lsp.NilURI, lsp.NilRange, err } - inoURI, err := ls.inoDocumentURIFromInoPath(logger, inoPath) - return inoURI, inoRange, err + ideURI, err := ls.idePathToIdeURI(logger, idePath) + logger.Logf(" Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + return ideURI, ideRange, err } - inside, err := cppPath.IsInsideDir(ls.buildSketchRoot) + // /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(" could not determine if '%s' is inside '%s'", cppPath, ls.buildSketchRoot) + logger.Logf("ERROR: could not determine if '%s' is inside '%s'", clangURI, ls.buildSketchRoot) return lsp.NilURI, lsp.NilRange, err } if !inside { - logger.Logf(" '%s' is not inside '%s'", cppPath, ls.buildSketchRoot) - logger.Logf(" keep doc identifier to '%s' as-is", cppPath) - return cppURI, cppRange, nil + ideURI := clangURI + logger.Logf(" Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + return clangURI, clangRange, nil } - rel, err := ls.buildSketchRoot.RelTo(cppPath) - if err == nil { - inoPath := ls.sketchRoot.JoinPath(rel).String() - logger.Logf(" URI: '%s' -> '%s'", cppPath, inoPath) - inoURI, err := ls.inoDocumentURIFromInoPath(logger, inoPath) - logger.Logf(" as URI: '%s'", inoURI) - return inoURI, cppRange, err + // Sketchbook/Sketch/AnotherFile.cpp <-> build-path/sketch/AnotherFile.cpp (same range) + 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, err } - - logger.Logf(" could not determine rel-path of '%s' in '%s': %s", cppPath, ls.buildSketchRoot, err) - return lsp.NilURI, lsp.NilRange, err + idePath := ls.sketchRoot.JoinPath(rel).String() + ideURI, err := ls.idePathToIdeURI(logger, idePath) + logger.Logf(" Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + return ideURI, clangRange, err } -func (ls *INOLanguageServer) ino2cppTextDocumentPositionParams(logger jsonrpc.FunctionLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { +func (ls *INOLanguageServer) ide2clangTextDocumentPositionParams(logger jsonrpc.FunctionLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { inoTextDocument := inoParams.TextDocument inoPosition := inoParams.Position inoURI := inoTextDocument.URI @@ -1447,7 +1439,7 @@ func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, // ...otherwise convert edits to the sketch.ino.cpp into multilpe .ino edits for _, edit := range edits { - inoURI, inoRange, err := ls.cpp2inoDocumentURI(logger, editURI, edit.Range) + inoURI, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, editURI, edit.Range) if err != nil { logger.Logf(" error converting edit %s:%s: %s", editURI, edit.Range, err) continue @@ -1468,7 +1460,7 @@ func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, } func (ls *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppLocation lsp.Location) (lsp.Location, error) { - inoURI, inoRange, err := ls.cpp2inoDocumentURI(logger, cppLocation.URI, cppLocation.Range) + inoURI, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppLocation.URI, cppLocation.Range) return lsp.Location{ URI: inoURI, Range: inoRange, @@ -1476,7 +1468,7 @@ func (ls *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppL } func (ls *INOLanguageServer) cpp2inoDocumentHighlight(logger jsonrpc.FunctionLogger, cppHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, error) { - _, inoRange, err := ls.cpp2inoDocumentURI(logger, cppURI, cppHighlight.Range) + _, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, cppHighlight.Range) if err != nil { return lsp.DocumentHighlight{}, err } @@ -1510,7 +1502,7 @@ func (ls *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger, cpp } func (ls *INOLanguageServer) cpp2inoTextEdit(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { - inoURI, inoRange, err := ls.cpp2inoDocumentURI(logger, cppURI, cppEdit.Range) + inoURI, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, cppEdit.Range) inoEdit := cppEdit inoEdit.Range = inoRange return inoURI, inoEdit, err @@ -1570,13 +1562,13 @@ func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, c isSketch := cppURI.AsPath().EquivalentTo(ls.buildSketchCpp) if !isSketch { - inoURI, _, err := ls.cpp2inoDocumentURI(logger, cppURI, lsp.NilRange) + inoURI, _, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, lsp.NilRange) if err != nil { return nil, err } inoDiags := []lsp.Diagnostic{} for _, cppDiag := range cppDiagsParams.Diagnostics { - inoURIofConvertedRange, inoRange, err := ls.cpp2inoDocumentURI(logger, cppURI, cppDiag.Range) + inoURIofConvertedRange, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, cppDiag.Range) if err != nil { return nil, err } @@ -1608,7 +1600,7 @@ func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, c ls.inoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} for _, cppDiag := range cppDiagsParams.Diagnostics { - inoURI, inoRange, err := ls.cpp2inoDocumentURI(logger, cppURI, cppDiag.Range) + inoURI, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, cppDiag.Range) if err != nil { return nil, err } From a1bfd838d6ce3a97ef5f7fa4cbd170a131f3b85c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 19 Nov 2021 17:53:37 +0100 Subject: [PATCH 41/76] Fixed Highlight coordinates; continued refactoring jargon (ino/cpp -> ide/clang) --- ls/ls.go | 169 +++++++++++++++++++++++++++---------------------------- 1 file changed, 84 insertions(+), 85 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index d690316..d719fd1 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -306,7 +306,7 @@ func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Contex defer ls.readUnlock(logger) logger.Logf("--> completion(%s)\n", inoParams.TextDocument) - cppTextDocPositionParams, err := ls.ide2clangTextDocumentPositionParams(logger, inoParams.TextDocumentPositionParams) + cppTextDocPositionParams, err := ls.ide2ClangTextDocumentPositionParams(logger, inoParams.TextDocumentPositionParams) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -354,7 +354,7 @@ func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, lo ls.readLock(logger, true) defer ls.readUnlock(logger) - clangTextDocPosition, err := ls.ide2clangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + 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()} @@ -404,7 +404,7 @@ func (ls *INOLanguageServer) TextDocumentSignatureHelpReqFromIDE(ctx context.Con inoTextDocumentPosition := inoParams.TextDocumentPositionParams logger.Logf("%s", inoTextDocumentPosition) - cppTextDocumentPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -436,7 +436,7 @@ func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Contex inoTextDocPosition := p.TextDocumentPositionParams logger.Logf("%s", inoTextDocPosition) - cppTextDocPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocPosition) + cppTextDocPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, inoTextDocPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -481,7 +481,7 @@ func (ls *INOLanguageServer) TextDocumentTypeDefinitionReqFromIDE(ctx context.Co logger.Logf("%s", inoTextDocumentPosition) // inoURI := inoTextDocumentPosition.TextDocument.URI - cppTextDocumentPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -527,7 +527,7 @@ func (ls *INOLanguageServer) TextDocumentImplementationReqFromIDE(ctx context.Co inoTextDocumentPosition := inoParams.TextDocumentPositionParams logger.Logf("%s", inoTextDocumentPosition) - cppTextDocumentPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocumentPosition) + cppTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, inoTextDocumentPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -564,45 +564,45 @@ func (ls *INOLanguageServer) TextDocumentImplementationReqFromIDE(ctx context.Co return inoLocations, inoLocationLinks, nil } -func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, *jsonrpc.ResponseError) { +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) - inoTextDocumentPosition := inoParams.TextDocumentPositionParams - cppTextDocumentPosition, err := ls.ide2clangTextDocumentPositionParams(logger, inoTextDocumentPosition) + clangTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) if err != nil { - logger.Logf("Error: %s", err) + logger.Logf("ERROR: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - cppURI := cppTextDocumentPosition.TextDocument.URI + clangURI := clangTextDocumentPosition.TextDocument.URI - cppParams := *inoParams - cppParams.TextDocumentPositionParams = cppTextDocumentPosition - cppHighlights, clangErr, err := ls.Clangd.conn.TextDocumentDocumentHighlight(ctx, &cppParams) + clangParams := *ideParams + clangParams.TextDocumentPositionParams = clangTextDocumentPosition + clangHighlights, clangErr, err := ls.Clangd.conn.TextDocumentDocumentHighlight(ctx, &clangParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd connectiono 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()) + logger.Logf("clangd response ERROR: %v", clangErr.AsError()) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} } - if cppHighlights != nil { + if clangHighlights == nil { + logger.Logf("null response from clangd") return nil, nil } - inoHighlights := []lsp.DocumentHighlight{} - for _, cppHighlight := range cppHighlights { - inoHighlight, err := ls.cpp2inoDocumentHighlight(logger, cppHighlight, cppURI) + ideHighlights := []lsp.DocumentHighlight{} + for _, clangHighlight := range clangHighlights { + ideHighlight, err := ls.clang2IdeDocumentHighlight(logger, clangHighlight, clangURI) if err != nil { - logger.Logf("ERROR converting location %s:%s: %s", cppURI, cppHighlight.Range, err) + logger.Logf("ERROR converting highlight %s:%s: %s", clangURI, clangHighlight.Range, err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} } - inoHighlights = append(inoHighlights, inoHighlight) + ideHighlights = append(ideHighlights, ideHighlight) } - return inoHighlights, nil + return ideHighlights, nil } func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, []lsp.SymbolInformation, *jsonrpc.ResponseError) { @@ -613,7 +613,7 @@ func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Co inoURI := inoTextDocument.URI logger.Logf("--> %s") - cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -655,7 +655,7 @@ func (ls *INOLanguageServer) TextDocumentCodeActionReqFromIDE(ctx context.Contex logger.Logf("--> codeAction(%s:%s)", inoTextDocument, inoParams.Range.Start) cppParams := *inoParams - cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -710,7 +710,7 @@ func (ls *INOLanguageServer) TextDocumentFormattingReqFromIDE(ctx context.Contex inoURI := inoTextDocument.URI logger.Logf("--> formatting(%s)", inoTextDocument) - cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -884,7 +884,7 @@ func (ls *INOLanguageServer) TextDocumentDidSaveNotifFromIDE(logger jsonrpc.Func ls.triggerRebuild() logger.Logf("didSave(%s) hasText=%v", inoParams.TextDocument, inoParams.Text != "") - if cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument); err != nil { + if cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoParams.TextDocument); err != nil { logger.Logf("--E Error: %s", err) } else if cppTextDocument.URI.AsPath().EquivalentTo(ls.buildSketchCpp) { logger.Logf(" didSave(%s) equals %s", cppTextDocument, ls.buildSketchCpp) @@ -1094,14 +1094,14 @@ func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose } } - cppIdentifier, err := ls.ino2cppTextDocumentIdentifier(logger, inoIdentifier) + cppIdentifier, err := ls.ide2ClangTextDocumentIdentifier(logger, inoIdentifier) return &lsp.DidCloseTextDocumentParams{ TextDocument: cppIdentifier, }, err } func (ls *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogger, inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { - cppURI, err := ls.ino2cppDocumentURI(logger, inoItem.URI) + cppURI, err := ls.ide2ClangDocumentURI(logger, inoItem.URI) if err != nil { return cppItem, err } @@ -1189,50 +1189,50 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChan } func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { - cppURI, err := ls.ino2cppDocumentURI(logger, doc.URI) + cppURI, err := ls.ide2ClangDocumentURI(logger, doc.URI) res := doc res.URI = cppURI return res, err } -func (ls *INOLanguageServer) ino2cppTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { - cppURI, err := ls.ino2cppDocumentURI(logger, doc.URI) - res := doc - res.URI = cppURI - return res, err +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) ino2cppDocumentURI(logger jsonrpc.FunctionLogger, inoURI lsp.DocumentURI) (lsp.DocumentURI, error) { +func (ls *INOLanguageServer) ide2ClangDocumentURI(logger jsonrpc.FunctionLogger, ideURI 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(ls.buildSketchCpp), nil + idePath := ideURI.AsPath() + if idePath.Ext() == ".ino" { + clangURI := lsp.NewDocumentURIFromPath(ls.buildSketchCpp) + logger.Logf("URI: %s -> %s", ideURI, clangURI) + return clangURI, nil } - inside, err := inoPath.IsInsideDir(ls.sketchRoot) + // another/path/source.cpp -> another/path/source.cpp (unchanged) + inside, err := idePath.IsInsideDir(ls.sketchRoot) if err != nil { - logger.Logf(" could not determine if '%s' is inside '%s'", inoPath, ls.sketchRoot) - return lsp.NilURI, unknownURI(inoURI) + logger.Logf("ERROR: could not determine if '%s' is inside '%s'", idePath, ls.sketchRoot) + return lsp.NilURI, unknownURI(ideURI) } if !inside { - logger.Logf(" '%s' not inside sketchroot '%s', passing doc identifier to as-is", ls.sketchRoot, inoPath) - return inoURI, nil + clangURI := ideURI + logger.Logf("URI: %s -> %s", ideURI, clangURI) + return clangURI, nil } - rel, err := ls.sketchRoot.RelTo(inoPath) - if err == nil { - cppPath := ls.buildSketchRoot.JoinPath(rel) - logger.Logf(" URI: '%s' -> '%s'", inoPath, cppPath) - return lsp.NewDocumentURIFromPath(cppPath), 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, err } - logger.Logf(" could not determine rel-path of '%s' in '%s': %s", inoPath, ls.sketchRoot, err) - return lsp.NilURI, err + clangPath := ls.buildSketchRoot.JoinPath(rel) + clangURI := lsp.NewDocumentURIFromPath(clangPath) + logger.Logf("URI: %s -> %s", ideURI, clangURI) + return clangURI, nil } func (ls *INOLanguageServer) idePathToIdeURI(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { @@ -1261,18 +1261,18 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio if err == nil { if ls.sketchMapper.IsPreprocessedCppLine(clangRange.Start.Line) { idePath = sourcemapper.NotIno.File - logger.Logf(" Range is in PREPROCESSED section of the sketch") + logger.Logf("Range is in PREPROCESSED section of the sketch") } } else if _, ok := err.(sourcemapper.AdjustedRangeErr); ok { - logger.Logf(" Range has been END LINE ADJSUTED") + logger.Logf("Range has been END LINE ADJSUTED") err = nil } else { - logger.Logf(" Range conversion ERROR: %s", err) + logger.Logf("Range conversion ERROR: %s", err) ls.sketchMapper.DebugLogAll() return lsp.NilURI, lsp.NilRange, err } ideURI, err := ls.idePathToIdeURI(logger, idePath) - logger.Logf(" Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) return ideURI, ideRange, err } @@ -1286,7 +1286,7 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio } if !inside { ideURI := clangURI - logger.Logf(" Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) return clangURI, clangRange, nil } @@ -1298,40 +1298,39 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio } idePath := ls.sketchRoot.JoinPath(rel).String() ideURI, err := ls.idePathToIdeURI(logger, idePath) - logger.Logf(" Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) return ideURI, clangRange, err } -func (ls *INOLanguageServer) ide2clangTextDocumentPositionParams(logger jsonrpc.FunctionLogger, inoParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { - inoTextDocument := inoParams.TextDocument - inoPosition := inoParams.Position - inoURI := inoTextDocument.URI - prefix := fmt.Sprintf("TextDocumentIdentifier %s", inoParams) +func (ls *INOLanguageServer) ide2ClangTextDocumentPositionParams(logger jsonrpc.FunctionLogger, ideParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { + ideTextDocument := ideParams.TextDocument + idePosition := ideParams.Position + ideURI := ideTextDocument.URI - cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoTextDocument) + clangTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) if err != nil { - logger.Logf("%s -> invalid text document: %s", prefix, err) + logger.Logf("%s -> invalid text document: %s", ideParams, err) return lsp.TextDocumentPositionParams{}, err } - cppPosition := inoPosition - if inoURI.Ext() == ".ino" { - if cppLine, ok := ls.sketchMapper.InoToCppLineOk(inoURI, inoPosition.Line); ok { - cppPosition.Line = cppLine + clangPosition := idePosition + if ls.clangURIRefersToIno(clangTextDocument.URI) { + if cppLine, ok := ls.sketchMapper.InoToCppLineOk(ideURI, idePosition.Line); ok { + clangPosition.Line = cppLine } else { - logger.Logf("%s -> invalid line requested: %s:%d", prefix, inoURI, inoPosition.Line) - return lsp.TextDocumentPositionParams{}, unknownURI(inoURI) + logger.Logf("%s -> invalid line requested: %s:%d", ideParams, ideURI, idePosition.Line) + return lsp.TextDocumentPositionParams{}, unknownURI(ideURI) } } - cppParams := lsp.TextDocumentPositionParams{ - TextDocument: cppTextDocument, - Position: cppPosition, + clangParams := lsp.TextDocumentPositionParams{ + TextDocument: clangTextDocument, + Position: clangPosition, } - logger.Logf("%s -> %s", prefix, cppParams) - return cppParams, nil + logger.Logf("%s -> %s", ideParams, clangParams) + return clangParams, nil } func (ls *INOLanguageServer) ino2cppRange(logger jsonrpc.FunctionLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { - cppURI, err := ls.ino2cppDocumentURI(logger, inoURI) + cppURI, err := ls.ide2ClangDocumentURI(logger, inoURI) if err != nil { return lsp.NilURI, lsp.Range{}, err } @@ -1356,7 +1355,7 @@ func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, } func (ls *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { - cppTextDocument, err := ls.ino2cppTextDocumentIdentifier(logger, inoParams.TextDocument) + cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoParams.TextDocument) if err != nil { return nil, err } @@ -1467,14 +1466,14 @@ func (ls *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppL }, err } -func (ls *INOLanguageServer) cpp2inoDocumentHighlight(logger jsonrpc.FunctionLogger, cppHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, error) { - _, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, cppHighlight.Range) +func (ls *INOLanguageServer) clang2IdeDocumentHighlight(logger jsonrpc.FunctionLogger, clangHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, error) { + _, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, clangHighlight.Range) if err != nil { return lsp.DocumentHighlight{}, err } return lsp.DocumentHighlight{ - Kind: cppHighlight.Kind, - Range: inoRange, + Kind: clangHighlight.Kind, + Range: ideRange, }, nil } From e2637a2274f1d4f2dc8bbda2c4635c6e42ebb954 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 19 Nov 2021 22:56:04 +0100 Subject: [PATCH 42/76] Moved completed conversion functions to separate files --- ls/builder.go | 2 +- ls/ls.go | 349 ++---------------------------------------- ls/ls_clang_to_ide.go | 68 ++++++++ ls/ls_formatter.go | 191 +++++++++++++++++++++++ ls/ls_ide_to_clang.go | 91 +++++++++++ 5 files changed, 361 insertions(+), 340 deletions(-) create mode 100644 ls/ls_clang_to_ide.go create mode 100644 ls/ls_formatter.go create mode 100644 ls/ls_ide_to_clang.go diff --git a/ls/builder.go b/ls/builder.go index f9f608b..1357d33 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -165,7 +165,7 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge Overrides map[string]string `json:"overrides"` } data := overridesFile{Overrides: map[string]string{}} - for uri, trackedFile := range ls.trackedInoDocs { + for uri, trackedFile := range ls.trackedIDEDocs { rel, err := paths.New(uri).RelFrom(sketchRoot) if err != nil { ls.readUnlock(logger) diff --git a/ls/ls.go b/ls/ls.go index d719fd1..19b948d 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -44,7 +44,7 @@ type INOLanguageServer struct { sketchName string sketchMapper *sourcemapper.SketchMapper sketchTrackedFilesCount int - trackedInoDocs map[string]lsp.TextDocumentItem + trackedIDEDocs map[string]lsp.TextDocumentItem inoDocsWithDiagnostics map[lsp.DocumentURI]bool sketchRebuilder *SketchRebuilder } @@ -115,7 +115,7 @@ func (ls *INOLanguageServer) readUnlock(logger jsonrpc.FunctionLogger) { func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, config *Config) *INOLanguageServer { logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") ls := &INOLanguageServer{ - trackedInoDocs: map[string]lsp.TextDocumentItem{}, + trackedIDEDocs: map[string]lsp.TextDocumentItem{}, inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, closing: make(chan bool), config: config, @@ -822,7 +822,7 @@ func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.Func // Add the TextDocumentItem in the tracked files list inoTextDocItem := inoParam.TextDocument - ls.trackedInoDocs[inoTextDocItem.URI.AsPath().String()] = inoTextDocItem + ls.trackedIDEDocs[inoTextDocItem.URI.AsPath().String()] = inoTextDocItem // If we are tracking a .ino... if inoTextDocItem.URI.Ext() == ".ino" { @@ -1076,8 +1076,8 @@ func (ls *INOLanguageServer) extractDataFolderFromArduinoCLI(logger jsonrpc.Func func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { inoIdentifier := inoDidClose.TextDocument - if _, exist := ls.trackedInoDocs[inoIdentifier.URI.AsPath().String()]; exist { - delete(ls.trackedInoDocs, inoIdentifier.URI.AsPath().String()) + 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 nil, unknownURI(inoIdentifier.URI) @@ -1114,8 +1114,8 @@ func (ls *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogg } else { cppItem.LanguageID = inoItem.LanguageID inoPath := inoItem.URI.AsPath().String() - cppItem.Text = ls.trackedInoDocs[inoPath].Text - cppItem.Version = ls.trackedInoDocs[inoPath].Version + cppItem.Text = ls.trackedIDEDocs[inoPath].Text + cppItem.Version = ls.trackedIDEDocs[inoPath].Version } return cppItem, nil @@ -1134,15 +1134,15 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChan // Apply the change to the tracked sketch file. trackedInoID := inoDoc.URI.AsPath().String() - if doc, ok := ls.trackedInoDocs[trackedInoID]; !ok { + if doc, ok := ls.trackedIDEDocs[trackedInoID]; !ok { return nil, unknownURI(inoDoc.URI) } else if updatedDoc, err := textedits.ApplyLSPTextDocumentContentChangeEvent(doc, inoDidChangeParams); err != nil { return nil, err } else { - ls.trackedInoDocs[trackedInoID] = updatedDoc + ls.trackedIDEDocs[trackedInoID] = updatedDoc } - logger.Logf("Tracked SKETCH file:----------+\n" + ls.trackedInoDocs[trackedInoID].Text + "\n----------------------") + logger.Logf("Tracked SKETCH file:----------+\n" + ls.trackedIDEDocs[trackedInoID].Text + "\n----------------------") // If the file is not part of a .ino flie forward the change as-is to clangd if inoDoc.URI.Ext() != ".ino" { @@ -1195,140 +1195,6 @@ func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonr return res, err } -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, 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, 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, unknownURI(ideURI) - } - if !inside { - clangURI := ideURI - logger.Logf("URI: %s -> %s", ideURI, clangURI) - return clangURI, 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, err - } - - clangPath := ls.buildSketchRoot.JoinPath(rel) - clangURI := lsp.NewDocumentURIFromPath(clangPath) - logger.Logf("URI: %s -> %s", ideURI, clangURI) - return clangURI, nil -} - -func (ls *INOLanguageServer) idePathToIdeURI(logger jsonrpc.FunctionLogger, inoPath string) (lsp.DocumentURI, error) { - if inoPath == sourcemapper.NotIno.File { - return sourcemapper.NotInoURI, nil - } - doc, ok := ls.trackedInoDocs[inoPath] - if !ok { - logger.Logf(" !!! Unresolved .ino path: %s", inoPath) - logger.Logf(" !!! Known doc paths are:") - for p := range ls.trackedInoDocs { - logger.Logf(" !!! > %s", p) - } - uri := lsp.NewDocumentURI(inoPath) - return uri, unknownURI(uri) - } - return doc.URI, nil -} - -func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangRange lsp.Range) (lsp.DocumentURI, lsp.Range, 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 err == nil { - if ls.sketchMapper.IsPreprocessedCppLine(clangRange.Start.Line) { - idePath = sourcemapper.NotIno.File - logger.Logf("Range is in PREPROCESSED section of the sketch") - } - } else if _, ok := err.(sourcemapper.AdjustedRangeErr); ok { - logger.Logf("Range has been END LINE ADJSUTED") - err = nil - } else { - logger.Logf("Range conversion ERROR: %s", err) - ls.sketchMapper.DebugLogAll() - return lsp.NilURI, lsp.NilRange, err - } - ideURI, err := ls.idePathToIdeURI(logger, idePath) - logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) - return ideURI, ideRange, 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, err - } - if !inside { - ideURI := clangURI - logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) - return clangURI, clangRange, nil - } - - // Sketchbook/Sketch/AnotherFile.cpp <-> build-path/sketch/AnotherFile.cpp (same range) - 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, err - } - idePath := ls.sketchRoot.JoinPath(rel).String() - ideURI, err := ls.idePathToIdeURI(logger, idePath) - logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) - return ideURI, clangRange, err -} - -func (ls *INOLanguageServer) ide2ClangTextDocumentPositionParams(logger jsonrpc.FunctionLogger, ideParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { - ideTextDocument := ideParams.TextDocument - idePosition := ideParams.Position - ideURI := ideTextDocument.URI - - clangTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) - if err != nil { - logger.Logf("%s -> invalid text document: %s", ideParams, err) - return lsp.TextDocumentPositionParams{}, err - } - clangPosition := idePosition - if ls.clangURIRefersToIno(clangTextDocument.URI) { - if cppLine, ok := ls.sketchMapper.InoToCppLineOk(ideURI, idePosition.Line); ok { - clangPosition.Line = cppLine - } else { - logger.Logf("%s -> invalid line requested: %s:%d", ideParams, ideURI, idePosition.Line) - return lsp.TextDocumentPositionParams{}, unknownURI(ideURI) - } - } - clangParams := lsp.TextDocumentPositionParams{ - TextDocument: clangTextDocument, - Position: clangPosition, - } - logger.Logf("%s -> %s", ideParams, clangParams) - return clangParams, nil -} - func (ls *INOLanguageServer) ino2cppRange(logger jsonrpc.FunctionLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { cppURI, err := ls.ide2ClangDocumentURI(logger, inoURI) if err != nil { @@ -1466,17 +1332,6 @@ func (ls *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppL }, err } -func (ls *INOLanguageServer) clang2IdeDocumentHighlight(logger jsonrpc.FunctionLogger, clangHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, error) { - _, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, clangHighlight.Range) - if err != nil { - return lsp.DocumentHighlight{}, err - } - return lsp.DocumentHighlight{ - Kind: clangHighlight.Kind, - Range: ideRange, - }, nil -} - func (ls *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { logger.Logf("%s cpp/textEdit (%d elements)", cppURI, len(cppEdits)) allInoEdits := map[lsp.DocumentURI][]lsp.TextEdit{} @@ -1630,190 +1485,6 @@ func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, c return inoDiagParams, nil } -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 -} - func unknownURI(uri lsp.DocumentURI) error { return errors.New("Document is not available: " + uri.String()) } diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go new file mode 100644 index 0000000..962b492 --- /dev/null +++ b/ls/ls_clang_to_ide.go @@ -0,0 +1,68 @@ +package ls + +import ( + "github.com/arduino/arduino-language-server/sourcemapper" + "go.bug.st/lsp" + "go.bug.st/lsp/jsonrpc" +) + +func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangRange lsp.Range) (lsp.DocumentURI, lsp.Range, 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 err == nil { + if ls.sketchMapper.IsPreprocessedCppLine(clangRange.Start.Line) { + idePath = sourcemapper.NotIno.File + logger.Logf("Range is in PREPROCESSED section of the sketch") + } + } else if _, ok := err.(sourcemapper.AdjustedRangeErr); ok { + logger.Logf("Range has been END LINE ADJSUTED") + err = nil + } else { + logger.Logf("Range conversion ERROR: %s", err) + ls.sketchMapper.DebugLogAll() + return lsp.NilURI, lsp.NilRange, err + } + ideURI, err := ls.idePathToIdeURI(logger, idePath) + logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + return ideURI, ideRange, 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, err + } + if !inside { + ideURI := clangURI + logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + return clangURI, clangRange, nil + } + + // Sketchbook/Sketch/AnotherFile.cpp <-> build-path/sketch/AnotherFile.cpp (same range) + 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, err + } + idePath := ls.sketchRoot.JoinPath(rel).String() + ideURI, err := ls.idePathToIdeURI(logger, idePath) + logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + return ideURI, clangRange, err +} + +func (ls *INOLanguageServer) clang2IdeDocumentHighlight(logger jsonrpc.FunctionLogger, clangHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, error) { + _, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, clangHighlight.Range) + if err != nil { + return lsp.DocumentHighlight{}, err + } + return lsp.DocumentHighlight{ + Kind: clangHighlight.Kind, + Range: ideRange, + }, nil +} 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..c35e09e --- /dev/null +++ b/ls/ls_ide_to_clang.go @@ -0,0 +1,91 @@ +package ls + +import ( + "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, 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, 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, unknownURI(ideURI) + } + if !inside { + clangURI := ideURI + logger.Logf("URI: %s -> %s", ideURI, clangURI) + return clangURI, 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, err + } + + clangPath := ls.buildSketchRoot.JoinPath(rel) + clangURI := lsp.NewDocumentURIFromPath(clangPath) + logger.Logf("URI: %s -> %s", ideURI, clangURI) + return clangURI, nil +} + +func (ls *INOLanguageServer) ide2ClangTextDocumentPositionParams(logger jsonrpc.FunctionLogger, ideParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { + ideTextDocument := ideParams.TextDocument + idePosition := ideParams.Position + ideURI := ideTextDocument.URI + + clangTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) + if err != nil { + logger.Logf("%s -> invalid text document: %s", ideParams, err) + return lsp.TextDocumentPositionParams{}, err + } + clangPosition := idePosition + if ls.clangURIRefersToIno(clangTextDocument.URI) { + if cppLine, ok := ls.sketchMapper.InoToCppLineOk(ideURI, idePosition.Line); ok { + clangPosition.Line = cppLine + } else { + logger.Logf("%s -> invalid line requested: %s:%d", ideParams, ideURI, idePosition.Line) + return lsp.TextDocumentPositionParams{}, unknownURI(ideURI) + } + } + clangParams := lsp.TextDocumentPositionParams{ + TextDocument: clangTextDocument, + Position: clangPosition, + } + logger.Logf("%s -> %s", ideParams, clangParams) + return clangParams, nil +} From 1cc4c0c4ab548fc8d9fc1f5c16e677be932172af Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 22 Nov 2021 09:53:45 +0100 Subject: [PATCH 43/76] Some more moving/renaming of functions --- ls/ls.go | 150 ++++++++++-------------------------------- ls/ls_clang_to_ide.go | 81 +++++++++++++++++++++++ 2 files changed, 117 insertions(+), 114 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 19b948d..b487413 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -32,21 +32,21 @@ type INOLanguageServer struct { 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 - inoDocsWithDiagnostics map[lsp.DocumentURI]bool - sketchRebuilder *SketchRebuilder + 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. @@ -115,10 +115,10 @@ func (ls *INOLanguageServer) readUnlock(logger jsonrpc.FunctionLogger) { func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, config *Config) *INOLanguageServer { logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") ls := &INOLanguageServer{ - trackedIDEDocs: map[string]lsp.TextDocumentItem{}, - inoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, - closing: make(chan bool), - config: config, + 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) @@ -641,7 +641,7 @@ func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Co var inoSymbolInformation []lsp.SymbolInformation if cppSymbolInformation != nil { logger.Logf(" <-- documentSymbol(%d symbol information)", len(cppSymbolInformation)) - inoSymbolInformation = ls.cpp2inoSymbolInformation(cppSymbolInformation) + inoSymbolInformation = ls.clang2IdeSymbolInformation(cppSymbolInformation) } return inoDocSymbols, inoSymbolInformation, nil } @@ -762,7 +762,7 @@ func (ls *INOLanguageServer) TextDocumentRangeFormattingReqFromIDE(ctx context.C // Method: "textDocument/rangeFormatting" logger.Logf("%s", inoParams.TextDocument) inoURI := inoParams.TextDocument.URI - cppParams, err := ls.ino2cppDocumentRangeFormattingParams(logger, inoParams) + cppParams, err := ls.ide2ClangDocumentRangeFormattingParams(logger, inoParams) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -937,7 +937,7 @@ func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.Fu // the diagnostics on sketch.cpp.ino once mapped into their // .ino counter parts may span over multiple .ino files... - allInoParams, err := ls.cpp2inoDiagnostics(logger, cppParams) + allInoParams, err := ls.clang2IdeDiagnostics(logger, cppParams) if err != nil { logger.Logf("Error converting diagnostics to .ino: %s", err) return @@ -1195,16 +1195,16 @@ func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonr return res, err } -func (ls *INOLanguageServer) ino2cppRange(logger jsonrpc.FunctionLogger, inoURI lsp.DocumentURI, inoRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { - cppURI, err := ls.ide2ClangDocumentURI(logger, inoURI) +func (ls *INOLanguageServer) ide2ClangRange(logger jsonrpc.FunctionLogger, ideURI lsp.DocumentURI, ideRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { + clangURI, err := ls.ide2ClangDocumentURI(logger, ideURI) if err != nil { - return lsp.NilURI, lsp.Range{}, err + return lsp.DocumentURI{}, lsp.Range{}, err } - if cppURI.AsPath().EquivalentTo(ls.buildSketchCpp) { - cppRange := ls.sketchMapper.InoToCppLSPRange(inoURI, inoRange) - return cppURI, cppRange, nil + if clangURI.AsPath().EquivalentTo(ls.buildSketchCpp) { + cppRange := ls.sketchMapper.InoToCppLSPRange(ideURI, ideRange) + return clangURI, cppRange, nil } - return cppURI, inoRange, nil + return clangURI, ideRange, nil } func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, cppLocations []lsp.Location) ([]lsp.Location, error) { @@ -1220,17 +1220,18 @@ func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, return inoLocations, nil } -func (ls *INOLanguageServer) ino2cppDocumentRangeFormattingParams(logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { - cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoParams.TextDocument) +func (ls *INOLanguageServer) ide2ClangDocumentRangeFormattingParams(logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { + clangTextDocumentIdentifier, err := ls.ide2ClangTextDocumentIdentifier(logger, ideParams.TextDocument) if err != nil { return nil, err } - _, cppRange, err := ls.ino2cppRange(logger, inoParams.TextDocument.URI, inoParams.Range) + _, clangRange, err := ls.ide2ClangRange(logger, ideParams.TextDocument.URI, ideParams.Range) return &lsp.DocumentRangeFormattingParams{ - TextDocument: cppTextDocument, - Range: cppRange, - Options: inoParams.Options, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + Options: ideParams.Options, + TextDocument: clangTextDocumentIdentifier, + Range: clangRange, }, err } @@ -1406,85 +1407,6 @@ func (ls *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.FunctionLogge return inoSymbols } -func (ls *INOLanguageServer) cpp2inoSymbolInformation(syms []lsp.SymbolInformation) []lsp.SymbolInformation { - panic("not implemented") -} - -func (ls *INOLanguageServer) cpp2inoDiagnostics(logger jsonrpc.FunctionLogger, cppDiagsParams *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { - - cppURI := cppDiagsParams.URI - isSketch := cppURI.AsPath().EquivalentTo(ls.buildSketchCpp) - - if !isSketch { - inoURI, _, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, lsp.NilRange) - if err != nil { - return nil, err - } - inoDiags := []lsp.Diagnostic{} - for _, cppDiag := range cppDiagsParams.Diagnostics { - inoURIofConvertedRange, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, cppDiag.Range) - if err != nil { - return nil, err - } - if inoURIofConvertedRange.String() == sourcemapper.NotInoURI.String() { - continue - } - if inoURIofConvertedRange.String() != inoURI.String() { - return nil, fmt.Errorf("unexpected inoURI %s: it should be %s", inoURIofConvertedRange, inoURI) - } - inoDiag := cppDiag - inoDiag.Range = inoRange - inoDiags = append(inoDiags, inoDiag) - } - return []*lsp.PublishDiagnosticsParams{ - { - URI: inoURI, - Diagnostics: inoDiags, - }, - }, nil - } - - allInoDiagsParams := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} - for inoURI := range ls.inoDocsWithDiagnostics { - allInoDiagsParams[inoURI] = &lsp.PublishDiagnosticsParams{ - URI: inoURI, - Diagnostics: []lsp.Diagnostic{}, - } - } - ls.inoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} - - for _, cppDiag := range cppDiagsParams.Diagnostics { - inoURI, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, cppDiag.Range) - if err != nil { - return nil, err - } - if inoURI.String() == sourcemapper.NotInoURI.String() { - continue - } - - inoDiagsParams, ok := allInoDiagsParams[inoURI] - if !ok { - inoDiagsParams = &lsp.PublishDiagnosticsParams{ - URI: inoURI, - Diagnostics: []lsp.Diagnostic{}, - } - allInoDiagsParams[inoURI] = inoDiagsParams - } - - inoDiag := cppDiag - inoDiag.Range = inoRange - inoDiagsParams.Diagnostics = append(inoDiagsParams.Diagnostics, inoDiag) - - ls.inoDocsWithDiagnostics[inoURI] = true - } - - inoDiagParams := []*lsp.PublishDiagnosticsParams{} - for _, v := range allInoDiagsParams { - inoDiagParams = append(inoDiagParams, v) - } - return inoDiagParams, nil -} - func unknownURI(uri lsp.DocumentURI) error { return errors.New("Document is not available: " + uri.String()) } diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 962b492..efdc719 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -1,6 +1,8 @@ package ls import ( + "fmt" + "github.com/arduino/arduino-language-server/sourcemapper" "go.bug.st/lsp" "go.bug.st/lsp/jsonrpc" @@ -66,3 +68,82 @@ func (ls *INOLanguageServer) clang2IdeDocumentHighlight(logger jsonrpc.FunctionL Range: ideRange, }, nil } + +func (ls *INOLanguageServer) clang2IdeDiagnostics(logger jsonrpc.FunctionLogger, clangDiagsParams *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { + clangURI := clangDiagsParams.URI + if !ls.clangURIRefersToIno(clangURI) { + ideDiags := []lsp.Diagnostic{} + ideDiagsURI := lsp.DocumentURI{} + for _, clangDiag := range clangDiagsParams.Diagnostics { + ideURI, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, clangURI, clangDiag.Range) + if err != nil { + return nil, err + } + if ideURI.String() == sourcemapper.NotInoURI.String() { + continue + } + if ideDiagsURI.String() == "" { + ideDiagsURI = ideURI + } else if ideDiagsURI.String() != ideURI.String() { + return nil, fmt.Errorf("unexpected URI %s: it should be %s", ideURI, ideURI) + } + ideDiag := clangDiag + ideDiag.Range = ideRange + ideDiags = append(ideDiags, ideDiag) + } + return []*lsp.PublishDiagnosticsParams{ + { + URI: ideDiagsURI, + Diagnostics: ideDiags, + }, + }, nil + } + + // Diagnostics coming from sketch.ino.cpp refers to all .ino files, so it must update + // the diagnostics list of all .ino files altogether. + // XXX: maybe this logic can be moved outside of this conversion function, make it much + // more straighforward. + allIdeInoDiagsParams := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} + for ideInoURI := range ls.ideInoDocsWithDiagnostics { + allIdeInoDiagsParams[ideInoURI] = &lsp.PublishDiagnosticsParams{ + URI: ideInoURI, + Diagnostics: []lsp.Diagnostic{}, + } + } + ls.ideInoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} + + for _, clangDiag := range clangDiagsParams.Diagnostics { + ideURI, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, clangURI, clangDiag.Range) + if err != nil { + return nil, err + } + if ideURI.String() == sourcemapper.NotInoURI.String() { + continue + } + + ideInoDiagsParams, ok := allIdeInoDiagsParams[ideURI] + if !ok { + ideInoDiagsParams = &lsp.PublishDiagnosticsParams{ + URI: ideURI, + Diagnostics: []lsp.Diagnostic{}, + } + allIdeInoDiagsParams[ideURI] = ideInoDiagsParams + } + + ideInoDiag := clangDiag + ideInoDiag.Range = ideRange + ideInoDiagsParams.Diagnostics = append(ideInoDiagsParams.Diagnostics, ideInoDiag) + + ls.ideInoDocsWithDiagnostics[ideURI] = true + } + + ideInoDiagParams := []*lsp.PublishDiagnosticsParams{} + for _, v := range allIdeInoDiagsParams { + ideInoDiagParams = append(ideInoDiagParams, v) + } + return ideInoDiagParams, nil +} + +func (ls *INOLanguageServer) clang2IdeSymbolInformation(clangSymbolsInformation []lsp.SymbolInformation) []lsp.SymbolInformation { + panic("not implemented") +} From b515c35aa609ae5bd3ce1ae942393de3d5b4771f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 22 Nov 2021 12:21:23 +0100 Subject: [PATCH 44/76] Dramatically simplified diagnostics conversion --- ls/ls.go | 36 +++++++++++++++++----- ls/ls_clang_to_ide.go | 70 ++++++++----------------------------------- 2 files changed, 40 insertions(+), 66 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index b487413..d7540e3 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -926,31 +926,51 @@ func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.Fun } } -func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.FunctionLogger, cppParams *lsp.PublishDiagnosticsParams) { +func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.FunctionLogger, clangParams *lsp.PublishDiagnosticsParams) { ls.readLock(logger, false) defer ls.readUnlock(logger) - logger.Logf("%s (%d diagnostics):", cppParams.URI, len(cppParams.Diagnostics)) - for _, diag := range cppParams.Diagnostics { + 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... - allInoParams, err := ls.clang2IdeDiagnostics(logger, cppParams) + 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 _, inoParams := range allInoParams { - logger.Logf(" - %s (%d diagnostics):", inoParams.URI, len(inoParams.Diagnostics)) - for _, diag := range inoParams.Diagnostics { + 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(inoParams); err != nil { + if err := ls.IDE.conn.TextDocumentPublishDiagnostics(ideParams); err != nil { logger.Logf("Error sending diagnostics to IDE: %s", err) return } diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index efdc719..f182036 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -1,8 +1,6 @@ package ls import ( - "fmt" - "github.com/arduino/arduino-language-server/sourcemapper" "go.bug.st/lsp" "go.bug.st/lsp/jsonrpc" @@ -69,51 +67,13 @@ func (ls *INOLanguageServer) clang2IdeDocumentHighlight(logger jsonrpc.FunctionL }, nil } -func (ls *INOLanguageServer) clang2IdeDiagnostics(logger jsonrpc.FunctionLogger, clangDiagsParams *lsp.PublishDiagnosticsParams) ([]*lsp.PublishDiagnosticsParams, error) { - clangURI := clangDiagsParams.URI - if !ls.clangURIRefersToIno(clangURI) { - ideDiags := []lsp.Diagnostic{} - ideDiagsURI := lsp.DocumentURI{} - for _, clangDiag := range clangDiagsParams.Diagnostics { - ideURI, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, clangURI, clangDiag.Range) - if err != nil { - return nil, err - } - if ideURI.String() == sourcemapper.NotInoURI.String() { - continue - } - if ideDiagsURI.String() == "" { - ideDiagsURI = ideURI - } else if ideDiagsURI.String() != ideURI.String() { - return nil, fmt.Errorf("unexpected URI %s: it should be %s", ideURI, ideURI) - } - ideDiag := clangDiag - ideDiag.Range = ideRange - ideDiags = append(ideDiags, ideDiag) - } - return []*lsp.PublishDiagnosticsParams{ - { - URI: ideDiagsURI, - Diagnostics: ideDiags, - }, - }, 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{} - // Diagnostics coming from sketch.ino.cpp refers to all .ino files, so it must update - // the diagnostics list of all .ino files altogether. - // XXX: maybe this logic can be moved outside of this conversion function, make it much - // more straighforward. - allIdeInoDiagsParams := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} - for ideInoURI := range ls.ideInoDocsWithDiagnostics { - allIdeInoDiagsParams[ideInoURI] = &lsp.PublishDiagnosticsParams{ - URI: ideInoURI, - Diagnostics: []lsp.Diagnostic{}, - } - } - ls.ideInoDocsWithDiagnostics = map[lsp.DocumentURI]bool{} - - for _, clangDiag := range clangDiagsParams.Diagnostics { - ideURI, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, clangURI, clangDiag.Range) + for _, clangDiagnostic := range clangDiagsParams.Diagnostics { + ideURI, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, clangDiagsParams.URI, clangDiagnostic.Range) if err != nil { return nil, err } @@ -121,27 +81,21 @@ func (ls *INOLanguageServer) clang2IdeDiagnostics(logger jsonrpc.FunctionLogger, continue } - ideInoDiagsParams, ok := allIdeInoDiagsParams[ideURI] + ideDiagsParams, ok := allIdeDiagsParams[ideURI] if !ok { - ideInoDiagsParams = &lsp.PublishDiagnosticsParams{ + ideDiagsParams = &lsp.PublishDiagnosticsParams{ URI: ideURI, Diagnostics: []lsp.Diagnostic{}, } - allIdeInoDiagsParams[ideURI] = ideInoDiagsParams + allIdeDiagsParams[ideURI] = ideDiagsParams } - ideInoDiag := clangDiag + ideInoDiag := clangDiagnostic ideInoDiag.Range = ideRange - ideInoDiagsParams.Diagnostics = append(ideInoDiagsParams.Diagnostics, ideInoDiag) - - ls.ideInoDocsWithDiagnostics[ideURI] = true + ideDiagsParams.Diagnostics = append(ideDiagsParams.Diagnostics, ideInoDiag) } - ideInoDiagParams := []*lsp.PublishDiagnosticsParams{} - for _, v := range allIdeInoDiagsParams { - ideInoDiagParams = append(ideInoDiagParams, v) - } - return ideInoDiagParams, nil + return allIdeDiagsParams, nil } func (ls *INOLanguageServer) clang2IdeSymbolInformation(clangSymbolsInformation []lsp.SymbolInformation) []lsp.SymbolInformation { From 00590aa3ab38b702b5201b3ecad68c6997013409 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 22 Nov 2021 14:37:02 +0100 Subject: [PATCH 45/76] Refactored and rationalized in-preprocessed section updates --- ls/ls.go | 41 ++++++++++++----- ls/ls_clang_to_ide.go | 100 ++++++++++++++++++++++++++++-------------- 2 files changed, 96 insertions(+), 45 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index d7540e3..5c4a8a8 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -379,12 +379,15 @@ func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, lo return nil, nil } - _, r, err := ls.clang2IdeRangeAndDocumentURI(logger, clangParams.TextDocument.URI, *clangResp.Range) + _, 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 + } ideResp := lsp.Hover{ Contents: clangResp.Contents, Range: &r, @@ -595,7 +598,10 @@ func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context ideHighlights := []lsp.DocumentHighlight{} for _, clangHighlight := range clangHighlights { - ideHighlight, err := ls.clang2IdeDocumentHighlight(logger, clangHighlight, clangURI) + 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()} @@ -1230,11 +1236,15 @@ func (ls *INOLanguageServer) ide2ClangRange(logger jsonrpc.FunctionLogger, ideUR func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, cppLocations []lsp.Location) ([]lsp.Location, error) { inoLocations := []lsp.Location{} for _, cppLocation := range cppLocations { - inoLocation, err := ls.cpp2inoLocation(logger, cppLocation) + inoLocation, inPreprocessed, err := ls.cpp2inoLocation(logger, cppLocation) if err != nil { logger.Logf("ERROR converting location %s: %s", cppLocation, err) return nil, err } + if inPreprocessed { + logger.Logf("ignored in-preprocessed-section location") + continue + } inoLocations = append(inoLocations, inoLocation) } return inoLocations, nil @@ -1325,11 +1335,16 @@ func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, // ...otherwise convert edits to the sketch.ino.cpp into multilpe .ino edits for _, edit := range edits { - inoURI, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, editURI, edit.Range) + 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 { @@ -1345,12 +1360,12 @@ func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, return inoWorkspaceEdit } -func (ls *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppLocation lsp.Location) (lsp.Location, error) { - inoURI, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppLocation.URI, cppLocation.Range) +func (ls *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppLocation lsp.Location) (lsp.Location, bool, error) { + inoURI, inoRange, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, cppLocation.URI, cppLocation.Range) return lsp.Location{ URI: inoURI, Range: inoRange, - }, err + }, inPreprocessed, err } func (ls *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { @@ -1358,10 +1373,14 @@ func (ls *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger, cpp allInoEdits := map[lsp.DocumentURI][]lsp.TextEdit{} for _, cppEdit := range cppEdits { logger.Logf(" > %s -> %s", cppEdit.Range, strconv.Quote(cppEdit.NewText)) - inoURI, inoEdit, err := ls.cpp2inoTextEdit(logger, cppURI, cppEdit) + inoURI, inoEdit, inPreprocessed, err := ls.cpp2inoTextEdit(logger, cppURI, cppEdit) if err != nil { return nil, err } + if inPreprocessed { + logger.Logf(("ignoring in-preprocessed-section edit")) + continue + } allInoEdits[inoURI] = append(allInoEdits[inoURI], inoEdit) } @@ -1376,11 +1395,11 @@ func (ls *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger, cpp return allInoEdits, nil } -func (ls *INOLanguageServer) cpp2inoTextEdit(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdit lsp.TextEdit) (lsp.DocumentURI, lsp.TextEdit, error) { - inoURI, inoRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, cppEdit.Range) +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) inoEdit := cppEdit inoEdit.Range = inoRange - return inoURI, inoEdit, err + return inoURI, inoEdit, inPreprocessed, err } func (ls *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.FunctionLogger, cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index f182036..010b8be 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -6,28 +6,31 @@ import ( "go.bug.st/lsp/jsonrpc" ) -func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangRange lsp.Range) (lsp.DocumentURI, lsp.Range, 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 err == nil { - if ls.sketchMapper.IsPreprocessedCppLine(clangRange.Start.Line) { - idePath = sourcemapper.NotIno.File - logger.Logf("Range is in PREPROCESSED section of the sketch") - } - } else if _, ok := err.(sourcemapper.AdjustedRangeErr); ok { + if _, ok := err.(sourcemapper.AdjustedRangeErr); ok { logger.Logf("Range has been END LINE ADJSUTED") - err = nil - } else { + } else if err != nil { logger.Logf("Range conversion ERROR: %s", err) ls.sketchMapper.DebugLogAll() - return lsp.NilURI, lsp.NilRange, err + 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", clangURI, clangRange, ideURI, ideRange) - return ideURI, ideRange, err + return ideURI, ideRange, inPreprocessed, err } // /another/global/path/to/source.cpp <-> /another/global/path/to/source.cpp (same range) @@ -36,35 +39,35 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio 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, err + return lsp.NilURI, lsp.NilRange, false, err } if !inside { ideURI := clangURI logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) - return clangURI, clangRange, nil + return clangURI, clangRange, false, nil } // Sketchbook/Sketch/AnotherFile.cpp <-> build-path/sketch/AnotherFile.cpp (same range) 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, err + return lsp.NilURI, lsp.NilRange, false, err } idePath := ls.sketchRoot.JoinPath(rel).String() ideURI, err := ls.idePathToIdeURI(logger, idePath) logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) - return ideURI, clangRange, err + return ideURI, clangRange, false, err } -func (ls *INOLanguageServer) clang2IdeDocumentHighlight(logger jsonrpc.FunctionLogger, clangHighlight lsp.DocumentHighlight, cppURI lsp.DocumentURI) (lsp.DocumentHighlight, error) { - _, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, cppURI, clangHighlight.Range) - if err != nil { - return lsp.DocumentHighlight{}, 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, - }, nil + }, false, nil } func (ls *INOLanguageServer) clang2IdeDiagnostics(logger jsonrpc.FunctionLogger, clangDiagsParams *lsp.PublishDiagnosticsParams) (map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams, error) { @@ -73,31 +76,60 @@ func (ls *INOLanguageServer) clang2IdeDiagnostics(logger jsonrpc.FunctionLogger, allIdeDiagsParams := map[lsp.DocumentURI]*lsp.PublishDiagnosticsParams{} for _, clangDiagnostic := range clangDiagsParams.Diagnostics { - ideURI, ideRange, err := ls.clang2IdeRangeAndDocumentURI(logger, clangDiagsParams.URI, clangDiagnostic.Range) + ideURI, ideDiagnostic, inPreprocessed, err := ls.clang2IdeDiagnostic(logger, clangDiagsParams.URI, clangDiagnostic) if err != nil { return nil, err } - if ideURI.String() == sourcemapper.NotInoURI.String() { + if inPreprocessed { continue } - - ideDiagsParams, ok := allIdeDiagsParams[ideURI] - if !ok { - ideDiagsParams = &lsp.PublishDiagnosticsParams{ - URI: ideURI, - Diagnostics: []lsp.Diagnostic{}, - } - allIdeDiagsParams[ideURI] = ideDiagsParams + if _, ok := allIdeDiagsParams[ideURI]; !ok { + allIdeDiagsParams[ideURI] = &lsp.PublishDiagnosticsParams{URI: ideURI} } - - ideInoDiag := clangDiagnostic - ideInoDiag.Range = ideRange - ideDiagsParams.Diagnostics = append(ideDiagsParams.Diagnostics, ideInoDiag) + 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.cpp2inoLocation(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) clang2IdeSymbolInformation(clangSymbolsInformation []lsp.SymbolInformation) []lsp.SymbolInformation { panic("not implemented") } From c8ad100d7b1a70c1a8c6a450298c07ee26c6ad21 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 22 Nov 2021 15:51:46 +0100 Subject: [PATCH 46/76] Made proper error for UnknownURI --- ls/ls.go | 12 ++++++++---- ls/ls_ide_to_clang.go | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 5c4a8a8..e605f33 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1106,7 +1106,7 @@ func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose delete(ls.trackedIDEDocs, inoIdentifier.URI.AsPath().String()) } else { logger.Logf(" didClose of untracked document: %s", inoIdentifier.URI) - return nil, unknownURI(inoIdentifier.URI) + return nil, &UnknownURI{inoIdentifier.URI} } // If we are tracking a .ino... @@ -1161,7 +1161,7 @@ func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChan // Apply the change to the tracked sketch file. trackedInoID := inoDoc.URI.AsPath().String() if doc, ok := ls.trackedIDEDocs[trackedInoID]; !ok { - return nil, unknownURI(inoDoc.URI) + return nil, &UnknownURI{inoDoc.URI} } else if updatedDoc, err := textedits.ApplyLSPTextDocumentContentChangeEvent(doc, inoDidChangeParams); err != nil { return nil, err } else { @@ -1446,6 +1446,10 @@ func (ls *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.FunctionLogge return inoSymbols } -func unknownURI(uri lsp.DocumentURI) error { - return errors.New("Document is not available: " + uri.String()) +type UnknownURI struct { + URI lsp.DocumentURI +} + +func (e *UnknownURI) Error() string { + return "Document is not available: " + e.URI.String() } diff --git a/ls/ls_ide_to_clang.go b/ls/ls_ide_to_clang.go index c35e09e..fcbcfa1 100644 --- a/ls/ls_ide_to_clang.go +++ b/ls/ls_ide_to_clang.go @@ -18,7 +18,7 @@ func (ls *INOLanguageServer) idePathToIdeURI(logger jsonrpc.FunctionLogger, inoP logger.Logf(" !!! > %s", p) } uri := lsp.NewDocumentURI(inoPath) - return uri, unknownURI(uri) + return uri, &UnknownURI{uri} } return doc.URI, nil } @@ -42,7 +42,7 @@ func (ls *INOLanguageServer) ide2ClangDocumentURI(logger jsonrpc.FunctionLogger, 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, unknownURI(ideURI) + return lsp.NilURI, &UnknownURI{ideURI} } if !inside { clangURI := ideURI @@ -79,7 +79,7 @@ func (ls *INOLanguageServer) ide2ClangTextDocumentPositionParams(logger jsonrpc. clangPosition.Line = cppLine } else { logger.Logf("%s -> invalid line requested: %s:%d", ideParams, ideURI, idePosition.Line) - return lsp.TextDocumentPositionParams{}, unknownURI(ideURI) + return lsp.TextDocumentPositionParams{}, &UnknownURI{ideURI} } } clangParams := lsp.TextDocumentPositionParams{ From d9872e2be655fb8bd585dd7c3f4e39d2c5ad649b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 22 Nov 2021 16:49:36 +0100 Subject: [PATCH 47/76] Moved documentSymbol handlers in the proper place --- ls/ls.go | 91 ++++++++----------------------------------- ls/ls_clang_to_ide.go | 56 +++++++++++++++++++++++++- ls/ls_ide_to_clang.go | 18 +++++++++ 3 files changed, 89 insertions(+), 76 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index e605f33..9420e57 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -611,24 +611,22 @@ func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context return ideHighlights, nil } -func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, []lsp.SymbolInformation, *jsonrpc.ResponseError) { +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) + ideTextDocument := ideParams.TextDocument - inoTextDocument := inoParams.TextDocument - inoURI := inoTextDocument.URI - logger.Logf("--> %s") - - cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoTextDocument) + // Convert request for clang + cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } + clangParams := *ideParams + clangParams.TextDocument = cppTextDocument - cppParams := *inoParams - cppParams.TextDocument = cppTextDocument - logger.Logf(" --> documentSymbol(%s)", cppTextDocument) - cppDocSymbols, cppSymbolInformation, clangErr, err := ls.Clangd.conn.TextDocumentDocumentSymbol(ctx, &cppParams) + // Send request to clang + clangDocSymbols, clangSymbolsInformation, clangErr, err := ls.Clangd.conn.TextDocumentDocumentSymbol(ctx, &clangParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) ls.Close() @@ -639,17 +637,16 @@ func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Co return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} } - var inoDocSymbols []lsp.DocumentSymbol - if cppDocSymbols != nil { - logger.Logf(" <-- documentSymbol(%d document symbols)", len(cppDocSymbols)) - inoDocSymbols = ls.cpp2inoDocumentSymbols(logger, cppDocSymbols, inoURI) + // Convert response for IDE + var ideDocSymbols []lsp.DocumentSymbol + if clangDocSymbols != nil { + ideDocSymbols = ls.clang2IdeDocumentSymbols(logger, clangDocSymbols, ideTextDocument.URI) } - var inoSymbolInformation []lsp.SymbolInformation - if cppSymbolInformation != nil { - logger.Logf(" <-- documentSymbol(%d symbol information)", len(cppSymbolInformation)) - inoSymbolInformation = ls.clang2IdeSymbolInformation(cppSymbolInformation) + var ideSymbolsInformation []lsp.SymbolInformation + if clangSymbolsInformation != nil { + ideSymbolsInformation = ls.clang2IdeSymbolsInformation(logger, clangSymbolsInformation) } - return inoDocSymbols, inoSymbolInformation, nil + return ideDocSymbols, ideSymbolsInformation, nil } func (ls *INOLanguageServer) TextDocumentCodeActionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CodeActionParams) ([]lsp.CommandOrCodeAction, *jsonrpc.ResponseError) { @@ -1221,18 +1218,6 @@ func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonr return res, err } -func (ls *INOLanguageServer) ide2ClangRange(logger jsonrpc.FunctionLogger, ideURI lsp.DocumentURI, ideRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { - clangURI, err := ls.ide2ClangDocumentURI(logger, ideURI) - if err != nil { - return lsp.DocumentURI{}, lsp.Range{}, err - } - if clangURI.AsPath().EquivalentTo(ls.buildSketchCpp) { - cppRange := ls.sketchMapper.InoToCppLSPRange(ideURI, ideRange) - return clangURI, cppRange, nil - } - return clangURI, ideRange, nil -} - func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, cppLocations []lsp.Location) ([]lsp.Location, error) { inoLocations := []lsp.Location{} for _, cppLocation := range cppLocations { @@ -1402,50 +1387,6 @@ func (ls *INOLanguageServer) cpp2inoTextEdit(logger jsonrpc.FunctionLogger, cppU return inoURI, inoEdit, inPreprocessed, err } -func (ls *INOLanguageServer) cpp2inoDocumentSymbols(logger jsonrpc.FunctionLogger, cppSymbols []lsp.DocumentSymbol, inoRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { - inoRequested := inoRequestedURI.AsPath().String() - logger.Logf(" filtering for requested ino file: %s", inoRequested) - if inoRequestedURI.Ext() != ".ino" || len(cppSymbols) == 0 { - return cppSymbols - } - - inoSymbols := []lsp.DocumentSymbol{} - for _, symbol := range cppSymbols { - logger.Logf(" > convert %s %s", symbol.Kind, symbol.Range) - if ls.sketchMapper.IsPreprocessedCppLine(symbol.Range.Start.Line) { - logger.Logf(" symbol is in the preprocessed section of the sketch.ino.cpp") - continue - } - - inoFile, inoRange := ls.sketchMapper.CppToInoRange(symbol.Range) - inoSelectionURI, inoSelectionRange := ls.sketchMapper.CppToInoRange(symbol.SelectionRange) - - if inoFile != inoSelectionURI { - logger.Logf(" ERROR: symbol range and selection belongs to different URI!") - logger.Logf(" symbol %s != selection %s", symbol.Range, symbol.SelectionRange) - logger.Logf(" %s:%s != %s:%s", inoFile, inoRange, inoSelectionURI, inoSelectionRange) - continue - } - - if inoFile != inoRequested { - logger.Logf(" 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: ls.cpp2inoDocumentSymbols(logger, symbol.Children, inoRequestedURI), - }) - } - - return inoSymbols -} - type UnknownURI struct { URI lsp.DocumentURI } diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 010b8be..8cf9152 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -130,6 +130,60 @@ func (ls *INOLanguageServer) clang2IdeDiagnosticRelatedInformationArray(logger j return ideInfos, nil } -func (ls *INOLanguageServer) clang2IdeSymbolInformation(clangSymbolsInformation []lsp.SymbolInformation) []lsp.SymbolInformation { +func (ls *INOLanguageServer) clang2IdeDocumentSymbols(logger jsonrpc.FunctionLogger, clangSymbols []lsp.DocumentSymbol, ideRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { + logger.Logf("documentSymbol(%d document symbols)", len(clangSymbols)) + ideRequestedPath := ideRequestedURI.AsPath().String() + logger.Logf(" filtering for requested ino file: %s", ideRequestedPath) + if ideRequestedURI.Ext() != ".ino" || len(clangSymbols) == 0 { + return clangSymbols + } + + ideSymbols := []lsp.DocumentSymbol{} + for _, clangSymbol := range clangSymbols { + logger.Logf(" > convert %s %s", clangSymbol.Kind, clangSymbol.Range) + if ls.sketchMapper.IsPreprocessedCppLine(clangSymbol.Range.Start.Line) { + logger.Logf(" symbol is in the preprocessed section of the sketch.ino.cpp") + continue + } + + idePath, ideRange := ls.sketchMapper.CppToInoRange(clangSymbol.Range) + ideSelectionPath, ideSelectionRange := ls.sketchMapper.CppToInoRange(clangSymbol.SelectionRange) + + if idePath != ideSelectionPath { + logger.Logf(" ERROR: symbol range and selection belongs to different URI!") + logger.Logf(" symbol %s != selection %s", clangSymbol.Range, clangSymbol.SelectionRange) + logger.Logf(" %s:%s != %s:%s", idePath, ideRange, ideSelectionPath, ideSelectionRange) + continue + } + + if idePath != ideRequestedPath { + logger.Logf(" skipping symbol related to %s", idePath) + continue + } + + ideSymbols = append(ideSymbols, lsp.DocumentSymbol{ + Name: clangSymbol.Name, + Detail: clangSymbol.Detail, + Deprecated: clangSymbol.Deprecated, + Kind: clangSymbol.Kind, + Range: ideRange, + SelectionRange: ideSelectionRange, + Children: ls.clang2IdeDocumentSymbols(logger, clangSymbol.Children, ideRequestedURI), + Tags: ls.clang2IdeSymbolTags(logger, clangSymbol.Tags), + }) + } + + return ideSymbols +} + +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") } diff --git a/ls/ls_ide_to_clang.go b/ls/ls_ide_to_clang.go index fcbcfa1..00285f5 100644 --- a/ls/ls_ide_to_clang.go +++ b/ls/ls_ide_to_clang.go @@ -1,6 +1,8 @@ package ls import ( + "fmt" + "github.com/arduino/arduino-language-server/sourcemapper" "go.bug.st/lsp" "go.bug.st/lsp/jsonrpc" @@ -89,3 +91,19 @@ func (ls *INOLanguageServer) ide2ClangTextDocumentPositionParams(logger jsonrpc. logger.Logf("%s -> %s", ideParams, clangParams) return clangParams, nil } + +func (ls *INOLanguageServer) ide2ClangRange(logger jsonrpc.FunctionLogger, ideURI lsp.DocumentURI, ideRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { + clangURI, err := ls.ide2ClangDocumentURI(logger, ideURI) + if err != nil { + return lsp.DocumentURI{}, lsp.Range{}, err + } + clangRange := ideRange + if ls.clangURIRefersToIno(clangURI) { + if r, ok := ls.sketchMapper.InoToCppLSPRangeOk(ideURI, ideRange); ok { + clangRange = r + } else { + return lsp.DocumentURI{}, lsp.Range{}, fmt.Errorf("invalid range %s:%s: could not be mapped to Arduino-preprocessed sketck.ino.cpp", ideURI, ideRange) + } + } + return clangURI, clangRange, nil +} From f590f7a69c6de2f14eb0372698338a1cb5cc9cca Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 23 Nov 2021 14:48:50 +0100 Subject: [PATCH 48/76] Fixed format and selection-format; still refactoring/renaming --- ls/ls.go | 104 ++++++++++++++---------------------------- ls/ls_clang_to_ide.go | 29 ++++++++++++ 2 files changed, 63 insertions(+), 70 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 9420e57..1e25ba9 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -705,33 +705,33 @@ func (ls *INOLanguageServer) TextDocumentCodeActionReqFromIDE(ctx context.Contex return inoResp, nil } -func (ls *INOLanguageServer) TextDocumentFormattingReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentFormattingReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) - inoTextDocument := inoParams.TextDocument - inoURI := inoTextDocument.URI - logger.Logf("--> formatting(%s)", inoTextDocument) + ideTextDocument := ideParams.TextDocument + ideURI := ideTextDocument.URI - cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoTextDocument) + clangTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - cppURI := cppTextDocument.URI - - logger.Logf(" --> formatting(%s)", cppTextDocument) + clangURI := clangTextDocument.URI - if cleanup, e := ls.createClangdFormatterConfig(logger, cppURI); e != nil { + 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() } - cppParams := *inoParams - cppParams.TextDocument = cppTextDocument - cppEdits, clangErr, err := ls.Clangd.conn.TextDocumentFormatting(ctx, &cppParams) + 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 connectiono error: %v", err) ls.Close() @@ -742,44 +742,49 @@ func (ls *INOLanguageServer) TextDocumentFormattingReqFromIDE(ctx context.Contex return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} } - if cppEdits == nil { + if clangEdits == nil { return nil, nil } - sketchEdits, err := ls.cpp2inoTextEdits(logger, cppURI, cppEdits) + 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()} } - if inoEdits, ok := sketchEdits[inoURI]; !ok { + + // 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, inoParams *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { +func (ls *INOLanguageServer) TextDocumentRangeFormattingReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { ls.readLock(logger, true) defer ls.readUnlock(logger) - // Method: "textDocument/rangeFormatting" - logger.Logf("%s", inoParams.TextDocument) - inoURI := inoParams.TextDocument.URI - cppParams, err := ls.ide2ClangDocumentRangeFormattingParams(logger, inoParams) + 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()} } - cppURI := cppParams.TextDocument.URI - logger.Logf("-> %s", cppParams.TextDocument) - if cleanup, e := ls.createClangdFormatterConfig(logger, cppURI); e != nil { + 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() } - cppEdits, clangErr, err := ls.Clangd.conn.TextDocumentRangeFormatting(ctx, cppParams) + clangEdits, clangErr, err := ls.Clangd.conn.TextDocumentRangeFormatting(ctx, clangParams) if err != nil { logger.Logf("clangd connectiono error: %v", err) ls.Close() @@ -790,17 +795,18 @@ func (ls *INOLanguageServer) TextDocumentRangeFormattingReqFromIDE(ctx context.C return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} } - // Transform and return the result - if cppEdits != nil { + if clangEdits == nil { return nil, nil } - sketchEdits, err := ls.cpp2inoTextEdits(logger, cppURI, cppEdits) + 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()} } - if inoEdits, ok := sketchEdits[inoURI]; !ok { + + // 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 @@ -1235,21 +1241,6 @@ func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, return inoLocations, nil } -func (ls *INOLanguageServer) ide2ClangDocumentRangeFormattingParams(logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentRangeFormattingParams) (*lsp.DocumentRangeFormattingParams, error) { - clangTextDocumentIdentifier, err := ls.ide2ClangTextDocumentIdentifier(logger, ideParams.TextDocument) - if err != nil { - return nil, err - } - - _, clangRange, err := ls.ide2ClangRange(logger, ideParams.TextDocument.URI, ideParams.Range) - return &lsp.DocumentRangeFormattingParams{ - WorkDoneProgressParams: ideParams.WorkDoneProgressParams, - Options: ideParams.Options, - TextDocument: clangTextDocumentIdentifier, - Range: clangRange, - }, err -} - func (ls *INOLanguageServer) cpp2inoCodeAction(logger jsonrpc.FunctionLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { inoCodeAction := lsp.CodeAction{ Title: codeAction.Title, @@ -1353,33 +1344,6 @@ func (ls *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppL }, inPreprocessed, err } -func (ls *INOLanguageServer) cpp2inoTextEdits(logger jsonrpc.FunctionLogger, cppURI lsp.DocumentURI, cppEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { - logger.Logf("%s cpp/textEdit (%d elements)", cppURI, len(cppEdits)) - allInoEdits := map[lsp.DocumentURI][]lsp.TextEdit{} - for _, cppEdit := range cppEdits { - logger.Logf(" > %s -> %s", cppEdit.Range, strconv.Quote(cppEdit.NewText)) - inoURI, inoEdit, inPreprocessed, err := ls.cpp2inoTextEdit(logger, cppURI, cppEdit) - if err != nil { - return nil, err - } - if inPreprocessed { - logger.Logf(("ignoring in-preprocessed-section edit")) - continue - } - allInoEdits[inoURI] = append(allInoEdits[inoURI], inoEdit) - } - - logger.Logf("converted to:") - - for inoURI, inoEdits := range allInoEdits { - logger.Logf("-> %s ino/textEdit (%d elements)", inoURI, len(inoEdits)) - for _, inoEdit := range inoEdits { - logger.Logf(" > %s -> %s", inoEdit.Range, strconv.Quote(inoEdit.NewText)) - } - } - return allInoEdits, nil -} - 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) inoEdit := cppEdit diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 8cf9152..9f912cc 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -1,6 +1,8 @@ package ls import ( + "strconv" + "github.com/arduino/arduino-language-server/sourcemapper" "go.bug.st/lsp" "go.bug.st/lsp/jsonrpc" @@ -176,6 +178,33 @@ func (ls *INOLanguageServer) clang2IdeDocumentSymbols(logger jsonrpc.FunctionLog return ideSymbols } +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) clang2IdeSymbolTags(logger jsonrpc.FunctionLogger, clangSymbolTags []lsp.SymbolTag) []lsp.SymbolTag { if len(clangSymbolTags) == 0 || clangSymbolTags == nil { return clangSymbolTags From 1ca00ec8464da71d9ec0974fc24eadb28d9c1eae Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 23 Nov 2021 16:01:15 +0100 Subject: [PATCH 49/76] Updated go version used by gh-actions build workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: | From fd61ed48c0b391bd020053be9a54b89a1f0b517a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 23 Nov 2021 18:59:15 +0100 Subject: [PATCH 50/76] Fixed typos --- ls/ls.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 1e25ba9..8a7d9b2 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -365,7 +365,7 @@ func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, lo } clangResp, clangErr, err := ls.Clangd.conn.TextDocumentHover(ctx, clangParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd communication error: %v", err) ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } @@ -418,7 +418,7 @@ func (ls *INOLanguageServer) TextDocumentSignatureHelpReqFromIDE(ctx context.Con cppParams.TextDocumentPositionParams = cppTextDocumentPosition cppSignatureHelp, cppErr, err := ls.Clangd.conn.TextDocumentSignatureHelp(ctx, inoParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd communication error: %v", err) ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } @@ -450,7 +450,7 @@ func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Contex cppParams.TextDocumentPositionParams = cppTextDocPosition cppLocations, cppLocationLinks, cppErr, err := ls.Clangd.conn.TextDocumentDefinition(ctx, &cppParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd communication error: %v", err) ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } @@ -497,7 +497,7 @@ func (ls *INOLanguageServer) TextDocumentTypeDefinitionReqFromIDE(ctx context.Co cppParams.TextDocumentPositionParams = cppTextDocumentPosition cppLocations, cppLocationLinks, cppErr, err := ls.Clangd.conn.TextDocumentTypeDefinition(ctx, &cppParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd communication error: %v", err) ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } @@ -541,7 +541,7 @@ func (ls *INOLanguageServer) TextDocumentImplementationReqFromIDE(ctx context.Co cppParams.TextDocumentPositionParams = cppTextDocumentPosition cppLocations, cppLocationLinks, cppErr, err := ls.Clangd.conn.TextDocumentImplementation(ctx, &cppParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd communication error: %v", err) ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } @@ -582,7 +582,7 @@ func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context clangParams.TextDocumentPositionParams = clangTextDocumentPosition clangHighlights, clangErr, err := ls.Clangd.conn.TextDocumentDocumentHighlight(ctx, &clangParams) if err != nil { - logger.Logf("clangd connectiono ERROR: %v", err) + logger.Logf("clangd communication ERROR: %v", err) ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } @@ -628,7 +628,7 @@ func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Co // Send request to clang clangDocSymbols, clangSymbolsInformation, clangErr, err := ls.Clangd.conn.TextDocumentDocumentSymbol(ctx, &clangParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd communication error: %v", err) ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } @@ -675,7 +675,7 @@ func (ls *INOLanguageServer) TextDocumentCodeActionReqFromIDE(ctx context.Contex cppResp, cppErr, err := ls.Clangd.conn.TextDocumentCodeAction(ctx, &cppParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd communication error: %v", err) ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } @@ -733,7 +733,7 @@ func (ls *INOLanguageServer) TextDocumentFormattingReqFromIDE(ctx context.Contex } clangEdits, clangErr, err := ls.Clangd.conn.TextDocumentFormatting(ctx, clangParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd communication error: %v", err) ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } @@ -786,7 +786,7 @@ func (ls *INOLanguageServer) TextDocumentRangeFormattingReqFromIDE(ctx context.C clangEdits, clangErr, err := ls.Clangd.conn.TextDocumentRangeFormatting(ctx, clangParams) if err != nil { - logger.Logf("clangd connectiono error: %v", err) + logger.Logf("clangd communication error: %v", err) ls.Close() return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } From aea355dcefa70ec0371c78fef102a9b1f05cc213 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 23 Nov 2021 19:14:27 +0100 Subject: [PATCH 51/76] Dramatically simplified ide2ClangTextDocumentPositionParams Also added new very useful helper method ide2ClangPosition --- ls/ls.go | 66 ++++++++++++++----------------------------- ls/ls_clang_to_ide.go | 27 +++++++++++++++++- ls/ls_ide_to_clang.go | 24 ++++++---------- 3 files changed, 55 insertions(+), 62 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 8a7d9b2..2eb7128 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -432,48 +432,49 @@ func (ls *INOLanguageServer) TextDocumentSignatureHelpReqFromIDE(ctx context.Con return cppSignatureHelp, nil } -func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, p *lsp.DefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { +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) - inoTextDocPosition := p.TextDocumentPositionParams - - logger.Logf("%s", inoTextDocPosition) - cppTextDocPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, inoTextDocPosition) + ideTextDocPosition := ideParams.TextDocumentPositionParams + clangTextDocPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideTextDocPosition) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - logger.Logf("-> %s", cppTextDocPosition) - cppParams := *p - cppParams.TextDocumentPositionParams = cppTextDocPosition - cppLocations, cppLocationLinks, cppErr, err := ls.Clangd.conn.TextDocumentDefinition(ctx, &cppParams) + 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 cppErr != nil { - logger.Logf("clangd response error: %v", cppErr.AsError()) - return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().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 inoLocations []lsp.Location - if cppLocations != nil { - inoLocations, err = ls.cpp2inoLocationArray(logger, cppLocations) + 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 cppLocationLinks != nil { + var ideLocationLinks []lsp.LocationLink + if clangLocationLinks != nil { panic("unimplemented") } - return inoLocations, inoLocationLinks, nil + return ideLocations, ideLocationLinks, nil } func (ls *INOLanguageServer) TextDocumentTypeDefinitionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { @@ -508,7 +509,7 @@ func (ls *INOLanguageServer) TextDocumentTypeDefinitionReqFromIDE(ctx context.Co var inoLocations []lsp.Location if cppLocations != nil { - inoLocations, err = ls.cpp2inoLocationArray(logger, cppLocations) + inoLocations, err = ls.clang2IdeLocationsArray(logger, cppLocations) if err != nil { ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -552,7 +553,7 @@ func (ls *INOLanguageServer) TextDocumentImplementationReqFromIDE(ctx context.Co var inoLocations []lsp.Location if cppLocations != nil { - inoLocations, err = ls.cpp2inoLocationArray(logger, cppLocations) + inoLocations, err = ls.clang2IdeLocationsArray(logger, cppLocations) if err != nil { ls.Close() return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} @@ -1224,23 +1225,6 @@ func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonr return res, err } -func (ls *INOLanguageServer) cpp2inoLocationArray(logger jsonrpc.FunctionLogger, cppLocations []lsp.Location) ([]lsp.Location, error) { - inoLocations := []lsp.Location{} - for _, cppLocation := range cppLocations { - inoLocation, inPreprocessed, err := ls.cpp2inoLocation(logger, cppLocation) - if err != nil { - logger.Logf("ERROR converting location %s: %s", cppLocation, err) - return nil, err - } - if inPreprocessed { - logger.Logf("ignored in-preprocessed-section location") - continue - } - inoLocations = append(inoLocations, inoLocation) - } - return inoLocations, nil -} - func (ls *INOLanguageServer) cpp2inoCodeAction(logger jsonrpc.FunctionLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { inoCodeAction := lsp.CodeAction{ Title: codeAction.Title, @@ -1336,14 +1320,6 @@ func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, return inoWorkspaceEdit } -func (ls *INOLanguageServer) cpp2inoLocation(logger jsonrpc.FunctionLogger, cppLocation lsp.Location) (lsp.Location, bool, error) { - inoURI, inoRange, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, cppLocation.URI, cppLocation.Range) - return lsp.Location{ - URI: inoURI, - Range: inoRange, - }, inPreprocessed, err -} - 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) inoEdit := cppEdit diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 9f912cc..dc0eaf9 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -116,7 +116,7 @@ func (ls *INOLanguageServer) clang2IdeDiagnostic(logger jsonrpc.FunctionLogger, func (ls *INOLanguageServer) clang2IdeDiagnosticRelatedInformationArray(logger jsonrpc.FunctionLogger, clangInfos []lsp.DiagnosticRelatedInformation) ([]lsp.DiagnosticRelatedInformation, error) { ideInfos := []lsp.DiagnosticRelatedInformation{} for _, clangInfo := range clangInfos { - ideLocation, inPreprocessed, err := ls.cpp2inoLocation(logger, clangInfo.Location) + ideLocation, inPreprocessed, err := ls.clang2IdeLocation(logger, clangInfo.Location) if err != nil { return nil, err } @@ -205,6 +205,31 @@ func (ls *INOLanguageServer) cland2IdeTextEdits(logger jsonrpc.FunctionLogger, c 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 diff --git a/ls/ls_ide_to_clang.go b/ls/ls_ide_to_clang.go index 00285f5..4c81bc8 100644 --- a/ls/ls_ide_to_clang.go +++ b/ls/ls_ide_to_clang.go @@ -66,32 +66,24 @@ func (ls *INOLanguageServer) ide2ClangDocumentURI(logger jsonrpc.FunctionLogger, } func (ls *INOLanguageServer) ide2ClangTextDocumentPositionParams(logger jsonrpc.FunctionLogger, ideParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { - ideTextDocument := ideParams.TextDocument - idePosition := ideParams.Position - ideURI := ideTextDocument.URI - - clangTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) + clangURI, clangPosition, err := ls.ide2ClangPosition(logger, ideParams.TextDocument.URI, ideParams.Position) if err != nil { - logger.Logf("%s -> invalid text document: %s", ideParams, err) + logger.Logf("Error converting position %s: %s", ideParams, err) return lsp.TextDocumentPositionParams{}, err } - clangPosition := idePosition - if ls.clangURIRefersToIno(clangTextDocument.URI) { - if cppLine, ok := ls.sketchMapper.InoToCppLineOk(ideURI, idePosition.Line); ok { - clangPosition.Line = cppLine - } else { - logger.Logf("%s -> invalid line requested: %s:%d", ideParams, ideURI, idePosition.Line) - return lsp.TextDocumentPositionParams{}, &UnknownURI{ideURI} - } - } clangParams := lsp.TextDocumentPositionParams{ - TextDocument: clangTextDocument, + 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, err := ls.ide2ClangDocumentURI(logger, ideURI) if err != nil { From 08c8827c29dd4a6946a0f37e78e160d03261cac0 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 23 Nov 2021 19:20:54 +0100 Subject: [PATCH 52/76] Some more renaming --- ls/ls.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 2eb7128..ecdcef9 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -153,16 +153,16 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, config *Config) *IN return ls } -func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.InitializeParams) (*lsp.InitializeResult, *jsonrpc.ResponseError) { +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", inoParams.RootURI) + logger.Logf("initializing workbench: %s", ideParams.RootURI) - ls.sketchRoot = inoParams.RootURI.AsPath() + ls.sketchRoot = ideParams.RootURI.AsPath() ls.sketchName = ls.sketchRoot.Base() ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") @@ -178,8 +178,8 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js logger.Logf("ERROR: updating compile_commands: %s", err) } - if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { - ls.sketchMapper = sourcemapper.CreateInoMapper(cppContent) + 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) @@ -205,17 +205,17 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js // Send initialization command to clangd (1 sec. timeout) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - cppInitializeParams := *inoParams - cppInitializeParams.RootPath = ls.buildSketchRoot.String() - cppInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) - if initRes, clangErr, err := ls.Clangd.conn.Initialize(ctx, &cppInitializeParams); err != nil { + 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(initRes))) + logger.Logf("clangd successfully started: %s", string(lsp.EncodeMessage(clangInitializeResult))) } if err := ls.Clangd.conn.Initialized(&lsp.InitializedParams{}); err != nil { @@ -297,7 +297,7 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js } func (ls *INOLanguageServer) ShutdownReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger) *jsonrpc.ResponseError { - ls.Clangd.conn.Shutdown(context.Background()) + _, _ = ls.Clangd.conn.Shutdown(context.Background()) return nil } From de32cd314b7819f7aff019ebd607d584e2c661a8 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 23 Nov 2021 21:14:38 +0100 Subject: [PATCH 53/76] Refactored and simplified TextDocumentCompletionReqFromIDE --- ls/ls.go | 88 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index ecdcef9..e17bcbe 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -301,28 +301,24 @@ func (ls *INOLanguageServer) ShutdownReqFromIDE(ctx context.Context, logger json return nil } -func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CompletionParams) (*lsp.CompletionList, *jsonrpc.ResponseError) { +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) - logger.Logf("--> completion(%s)\n", inoParams.TextDocument) - cppTextDocPositionParams, err := ls.ide2ClangTextDocumentPositionParams(logger, inoParams.TextDocumentPositionParams) + cppTextDocPositionParams, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - cppParams := inoParams - cppParams.TextDocumentPositionParams = cppTextDocPositionParams - logger.Logf(" --> completion(%s)\n", inoParams.TextDocument) - inoURI := inoParams.TextDocument.URI - - if err != nil { - logger.Logf("Error: %s", err) - return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} + clangParams := &lsp.CompletionParams{ + TextDocumentPositionParams: cppTextDocPositionParams, + Context: ideParams.Context, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + PartialResultParams: ideParams.PartialResultParams, } - clangResp, clangErr, err := ls.Clangd.conn.TextDocumentCompletion(ctx, cppParams) + clangCompletionList, clangErr, err := ls.Clangd.conn.TextDocumentCompletion(ctx, clangParams) if err != nil { logger.Logf("clangd connection error: %v", err) ls.Close() @@ -333,21 +329,67 @@ func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Contex return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: clangErr.AsError().Error()} } - cppToIno := inoURI != lsp.NilURI && inoURI.AsPath().EquivalentTo(ls.buildSketchCpp) + ideCompletionList := &lsp.CompletionList{ + IsIncomplete: clangCompletionList.IsIncomplete, + } + for _, clangItem := range clangCompletionList.Items { + if strings.HasPrefix(clangItem.InsertText, "_") { + // XXX: Should be really ignored? + continue + } - inoResp := *clangResp - inoItems := make([]lsp.CompletionItem, 0) - for _, item := range clangResp.Items { - if !strings.HasPrefix(item.InsertText, "_") { - if cppToIno && item.TextEdit != nil { - _, item.TextEdit.Range = ls.sketchMapper.CppToInoRange(item.TextEdit.Range) + 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()} } - inoItems = append(inoItems, item) + ideAdditionalTextEdits = _ideAdditionalTextEdits[ideParams.TextDocument.URI] } + + var ideCommand *lsp.Command + if clangItem.Command != nil { + c := ls.cpp2inoCommand(logger, *clangItem.Command) + 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, + }) } - inoResp.Items = inoItems - logger.Logf("<-- completion(%d items) cppToIno=%v", len(inoResp.Items), cppToIno) - return &inoResp, nil + 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) { From 5189b8b663a10788d9b400743dcba52489725ca6 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 23 Nov 2021 21:19:03 +0100 Subject: [PATCH 54/76] Fixed TextDocumentHoverReqFromIDE handling of null ranges --- ls/ls.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index e17bcbe..129c2a6 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -404,6 +404,7 @@ func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, lo clangParams := &lsp.HoverParams{ TextDocumentPositionParams: clangTextDocPosition, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, } clangResp, clangErr, err := ls.Clangd.conn.TextDocumentHover(ctx, clangParams) if err != nil { @@ -417,22 +418,26 @@ func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, lo } if clangResp == nil { - logger.Logf("response: nil") + logger.Logf("null response") return nil, 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 + 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: &r, + Range: ideRange, } logger.Logf("Hover content: %s", strconv.Quote(ideResp.Contents.Value)) return &ideResp, nil From ceb1c21d85a554f7ec0739e085f51c8111773c48 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 23 Nov 2021 21:30:04 +0100 Subject: [PATCH 55/76] Fixed TextDocumentSignatureHelpReqFromIDE --- ls/ls.go | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 129c2a6..e6b8f33 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -443,40 +443,39 @@ func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, lo return &ideResp, nil } -func (ls *INOLanguageServer) clangURIRefersToIno(uri lsp.DocumentURI) bool { - return uri.AsPath().EquivalentTo(ls.buildSketchCpp) +func (ls *INOLanguageServer) clangURIRefersToIno(clangURI lsp.DocumentURI) bool { + return clangURI.AsPath().EquivalentTo(ls.buildSketchCpp) } -func (ls *INOLanguageServer) TextDocumentSignatureHelpReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.SignatureHelpParams) (*lsp.SignatureHelp, *jsonrpc.ResponseError) { +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) - inoTextDocumentPosition := inoParams.TextDocumentPositionParams - - logger.Logf("%s", inoTextDocumentPosition) - cppTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, inoTextDocumentPosition) + 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()} } - logger.Logf("-> %s", cppTextDocumentPosition) - cppParams := *inoParams - cppParams.TextDocumentPositionParams = cppTextDocumentPosition - cppSignatureHelp, cppErr, err := ls.Clangd.conn.TextDocumentSignatureHelp(ctx, inoParams) + 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 cppErr != nil { - logger.Logf("clangd response error: %v", cppErr.AsError()) - return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().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 - - return cppSignatureHelp, nil + ideSignatureHelp := clangSignatureHelp + return ideSignatureHelp, nil } func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { From d46307ca6326b3558a0b572f36cd10833cf9ad7c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 24 Nov 2021 10:26:41 +0100 Subject: [PATCH 56/76] Adjusted advertised server capabilities --- go.mod | 2 +- go.sum | 38 +----------------- ls/ls.go | 118 +++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 95 insertions(+), 63 deletions(-) diff --git a/go.mod b/go.mod index 9c29012..58a9e19 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,6 @@ require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 go.bug.st/json v1.15.6 - go.bug.st/lsp v0.0.0-20211111165110-143dff941d53 + go.bug.st/lsp v0.0.0-20211124092226-438f5e4ddeb8 google.golang.org/grpc v1.42.0 ) diff --git a/go.sum b/go.sum index 1f79d5f..e1a1dc8 100644 --- a/go.sum +++ b/go.sum @@ -44,7 +44,6 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU 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 h1:agh2JT96G8egU7FEb13L4dq3fnCN7lxXhJ86t69+W7s= 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/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= @@ -52,7 +51,6 @@ github.com/arduino/go-paths-helper v1.6.1 h1:lha+/BuuBsx0qTZ3gy6IO1kU23lObWdQ/UI 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 h1:9hDi4F2st6dbLC3y4i02zFT5quS4X6iioWifGlVwfy4= 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= @@ -68,7 +66,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR 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/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/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= @@ -77,22 +74,18 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP 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 h1:1ChS4EvWTjw6bH2sd6QiMcmih0itVVrWdh9MmOliX/I= 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/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 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= 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/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/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= @@ -105,9 +98,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 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 h1:C6sOwknxwWfLBEQ91zhmptlfxf7pVEs5s6wOnDxNpS4= 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.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -118,7 +109,6 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 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 h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 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= @@ -214,7 +204,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: 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/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -233,7 +222,6 @@ 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/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.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -250,7 +238,6 @@ github.com/leonelquinteros/gotext v1.4.0 h1:2NHPCto5IoMXbrT0bldPrxj0qM5asOCwtb1a github.com/leonelquinteros/gotext v1.4.0/go.mod h1:yZGXREmoGTtBvZHNcc+Yfug49G/2spuF/i/Qlsvz1Us= 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 h1:hyAgCuG5nqTMDeUD8KZs7HSPs6KprPgPP8QmGV8nyvk= 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= @@ -260,17 +247,14 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx 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 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 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.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= 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 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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= @@ -284,7 +268,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN 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/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= @@ -300,25 +283,18 @@ github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583 h1:ogHi8YLNeIxABO github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583/go.mod h1:sFPiU/UgDcsQVu3vkqpZLCXWFwUoQRpHGu9ATihPAl0= 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/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 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 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e h1:uO75wNGioszjmIzcY/tvdDYKRLVvzggtAmmJkn9j4GQ= 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.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -336,11 +312,9 @@ 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 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= 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 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= 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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -351,7 +325,6 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc 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/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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -364,13 +337,11 @@ 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-20211111165110-143dff941d53 h1:6fjqjqNIhfIPaNoKJtlhJHpdKsk5aXP1QOTRDbdCyFg= -go.bug.st/lsp v0.0.0-20211111165110-143dff941d53/go.mod h1:oYTh1uf5hI1teV5crrWut41Pk8vD/NqIjs4zD+No5FE= +go.bug.st/lsp v0.0.0-20211124092226-438f5e4ddeb8 h1:QSRqT2ezfvXfeUmK5znFE4KsnTYe+26oQdFV2Yvtwrc= +go.bug.st/lsp v0.0.0-20211124092226-438f5e4ddeb8/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.3.2 h1:6BFZZd/wngoL5PPYYTrFUounF54SIkykHpT98eq6zvk= go.bug.st/serial v1.3.2/go.mod h1:jDkjqASf/qSjmaOxHSHljwUQ6eHo/ZX/bxJLQqSlvZg= -go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45 h1:mACY1anK6HNCZtm/DK2Rf2ZPHggVqeB0+7rY9Gl6wyI= go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45/go.mod h1:dRSl/CVCTf56CkXgJMDOdSwNfo2g1orOGE/gBGdvjZw= 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= @@ -395,7 +366,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 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= @@ -704,7 +674,6 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 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 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= 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= @@ -733,12 +702,9 @@ 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/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.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/ls/ls.go b/ls/ls.go index e6b8f33..61ffbfc 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -225,7 +225,67 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js 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{ @@ -236,33 +296,37 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js }, }, CompletionProvider: &lsp.CompletionOptions{ + TriggerCharacters: []string{".", "<", ">", ":", "\"", "/"}, AllCommitCharacters: []string{ " ", "\t", "(", ")", "[", "]", "{", "}", "<", ">", ":", ";", ",", "+", "-", "/", "*", "%", "^", "&", "#", "?", ".", "=", "\"", "'", "|"}, ResolveProvider: false, - TriggerCharacters: []string{ //".", "\u003e", ":" - ".", "<", ">", ":", "\"", "/"}, + CompletionItem: &lsp.CompletionItemOptions{}, }, + HoverProvider: &lsp.HoverOptions{}, SignatureHelpProvider: &lsp.SignatureHelpOptions{ TriggerCharacters: []string{"(", ","}, }, - // ReferencesProvider: &lsp.ReferenceOptions{}, // DeclarationProvider: &lsp.DeclarationRegistrationOptions{}, - // DocumentLinkProvider: &lsp.DocumentLinkOptions{ResolveProvider: false}, + DefinitionProvider: &lsp.DefinitionOptions{}, // ImplementationProvider: &lsp.ImplementationRegistrationOptions{}, - // SelectionRangeProvider: &lsp.SelectionRangeRegistrationOptions{}, - DefinitionProvider: &lsp.DefinitionOptions{}, - DocumentHighlightProvider: &lsp.DocumentHighlightOptions{}, - DocumentSymbolProvider: &lsp.DocumentSymbolOptions{}, - WorkspaceSymbolProvider: &lsp.WorkspaceSymbolOptions{}, - CodeActionProvider: &lsp.CodeActionOptions{ResolveProvider: true}, + // 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{}, - HoverProvider: &lsp.HoverOptions{}, + // SelectionRangeProvider: &lsp.SelectionRangeRegistrationOptions{}, DocumentOnTypeFormattingProvider: &lsp.DocumentOnTypeFormattingOptions{ FirstTriggerCharacter: "\n", - MoreTriggerCharacter: []string{}, }, RenameProvider: &lsp.RenameOptions{ // PrepareProvider: true, @@ -270,22 +334,24 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js ExecuteCommandProvider: &lsp.ExecuteCommandOptions{ Commands: []string{"clangd.applyFix", "clangd.applyTweak"}, }, - // SemanticTokensProvider: &lsp.SemanticTokensRegistrationOptions{ - // SemanticTokensOptions: &lsp.SemanticTokensOptions{ - // Full: &lsp.SemantiTokenFullOptions{ - // Delta: true, + // 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", // }, - // Legend: lsp.SemanticTokensLegend{ - // TokenModifiers: []string{}, - // TokenTypes: []string{ - // "variable", "variable", "parameter", "function", "method", "function", "property", "variable", - // "class", "enum", "enumMember", "type", "dependent", "dependent", "namespace", "typeParameter", - // "concept", "type", "macro", "comment", - // }, - // }, - // Range: false, + // TokenModifiers: []string{}, + // }, + // Range: false, + // Full: &lsp.SemantiTokenFullOptions{ + // Delta: true, // }, // }, + WorkspaceSymbolProvider: &lsp.WorkspaceSymbolOptions{}, }, ServerInfo: &lsp.InitializeResultServerInfo{ Name: "arduino-language-server", From a5d6bfd564a93b7432408f2019c0ad33d038e4d8 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 24 Nov 2021 10:27:23 +0100 Subject: [PATCH 57/76] More variable naming adjustments... --- ls/ls.go | 98 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 61ffbfc..e7f1b6d 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -548,8 +548,7 @@ func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Contex ls.readLock(logger, true) defer ls.readUnlock(logger) - ideTextDocPosition := ideParams.TextDocumentPositionParams - clangTextDocPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideTextDocPosition) + 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()} @@ -589,95 +588,94 @@ func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Contex return ideLocations, ideLocationLinks, nil } -func (ls *INOLanguageServer) TextDocumentTypeDefinitionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { +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) - inoTextDocumentPosition := inoParams.TextDocumentPositionParams - - logger.Logf("%s", inoTextDocumentPosition) - // inoURI := inoTextDocumentPosition.TextDocument.URI - cppTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, inoTextDocumentPosition) + 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()} } - // cppURI := cppTextDocumentPosition.TextDocument.URI - logger.Logf("-> %s", cppTextDocumentPosition) - - cppParams := *inoParams - cppParams.TextDocumentPositionParams = cppTextDocumentPosition - cppLocations, cppLocationLinks, cppErr, err := ls.Clangd.conn.TextDocumentTypeDefinition(ctx, &cppParams) + 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 cppErr != nil { - logger.Logf("clangd response error: %v", cppErr.AsError()) - return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().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 inoLocations []lsp.Location - if cppLocations != nil { - inoLocations, err = ls.clang2IdeLocationsArray(logger, cppLocations) + 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 cppLocationLinks != nil { + var ideLocationLinks []lsp.LocationLink + if clangLocationLinks != nil { panic("unimplemented") } - return inoLocations, inoLocationLinks, nil + return ideLocations, ideLocationLinks, nil } -func (ls *INOLanguageServer) TextDocumentImplementationReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.ImplementationParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { +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) - inoTextDocumentPosition := inoParams.TextDocumentPositionParams - logger.Logf("%s", inoTextDocumentPosition) - - cppTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, inoTextDocumentPosition) + 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()} } - logger.Logf("-> %s", cppTextDocumentPosition) - cppParams := *inoParams - cppParams.TextDocumentPositionParams = cppTextDocumentPosition - cppLocations, cppLocationLinks, cppErr, err := ls.Clangd.conn.TextDocumentImplementation(ctx, &cppParams) + clangParams := &lsp.ImplementationParams{ + TextDocumentPositionParams: cppTextDocumentPosition, + 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 cppErr != nil { - logger.Logf("clangd response error: %v", cppErr.AsError()) - return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().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 inoLocations []lsp.Location - if cppLocations != nil { - inoLocations, err = ls.clang2IdeLocationsArray(logger, cppLocations) + 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 cppLocationLinks != nil { + if clangLocationLinks != nil { panic("unimplemented") } - return inoLocations, inoLocationLinks, nil + return ideLocations, inoLocationLinks, nil } func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, *jsonrpc.ResponseError) { @@ -691,9 +689,12 @@ func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context } clangURI := clangTextDocumentPosition.TextDocument.URI - clangParams := *ideParams - clangParams.TextDocumentPositionParams = clangTextDocumentPosition - clangHighlights, clangErr, err := ls.Clangd.conn.TextDocumentDocumentHighlight(ctx, &clangParams) + 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() @@ -730,16 +731,19 @@ func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Co ideTextDocument := ideParams.TextDocument // Convert request for clang - cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) + clangTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - clangParams := *ideParams - clangParams.TextDocument = cppTextDocument // Send request to clang - clangDocSymbols, clangSymbolsInformation, clangErr, err := ls.Clangd.conn.TextDocumentDocumentSymbol(ctx, &clangParams) + 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() From 76cda4aba48a9444b935649bcc9e4a9feb5db8fc Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 24 Nov 2021 11:23:13 +0100 Subject: [PATCH 58/76] Refactored and straighted up TextDocumentDocumentSymbolReqFromIDE --- ls/ls.go | 11 +++++++--- ls/ls_clang_to_ide.go | 49 +++++++++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index e7f1b6d..7392529 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -728,10 +728,9 @@ func (ls *INOLanguageServer) TextDocumentDocumentHighlightReqFromIDE(ctx context 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) - ideTextDocument := ideParams.TextDocument // Convert request for clang - clangTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) + 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()} @@ -757,7 +756,13 @@ func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Co // Convert response for IDE var ideDocSymbols []lsp.DocumentSymbol if clangDocSymbols != nil { - ideDocSymbols = ls.clang2IdeDocumentSymbols(logger, clangDocSymbols, ideTextDocument.URI) + 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 { diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index dc0eaf9..6cc5623 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -132,37 +132,40 @@ func (ls *INOLanguageServer) clang2IdeDiagnosticRelatedInformationArray(logger j return ideInfos, nil } -func (ls *INOLanguageServer) clang2IdeDocumentSymbols(logger jsonrpc.FunctionLogger, clangSymbols []lsp.DocumentSymbol, ideRequestedURI lsp.DocumentURI) []lsp.DocumentSymbol { - logger.Logf("documentSymbol(%d document symbols)", len(clangSymbols)) - ideRequestedPath := ideRequestedURI.AsPath().String() - logger.Logf(" filtering for requested ino file: %s", ideRequestedPath) - if ideRequestedURI.Ext() != ".ino" || len(clangSymbols) == 0 { - return clangSymbols - } +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) - if ls.sketchMapper.IsPreprocessedCppLine(clangSymbol.Range.Start.Line) { - logger.Logf(" symbol is in the preprocessed section of the sketch.ino.cpp") + 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 } - - idePath, ideRange := ls.sketchMapper.CppToInoRange(clangSymbol.Range) - ideSelectionPath, ideSelectionRange := ls.sketchMapper.CppToInoRange(clangSymbol.SelectionRange) - - if idePath != ideSelectionPath { - logger.Logf(" ERROR: symbol range and selection belongs to different URI!") - logger.Logf(" symbol %s != selection %s", clangSymbol.Range, clangSymbol.SelectionRange) - logger.Logf(" %s:%s != %s:%s", idePath, ideRange, ideSelectionPath, ideSelectionRange) + if ideURI != origIdeURI { + logger.Logf(" filtering out symbol related to %s", ideURI) continue } - - if idePath != ideRequestedPath { - logger.Logf(" skipping symbol related to %s", idePath) + 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, @@ -170,12 +173,12 @@ func (ls *INOLanguageServer) clang2IdeDocumentSymbols(logger jsonrpc.FunctionLog Kind: clangSymbol.Kind, Range: ideRange, SelectionRange: ideSelectionRange, - Children: ls.clang2IdeDocumentSymbols(logger, clangSymbol.Children, ideRequestedURI), + Children: ideChildren, Tags: ls.clang2IdeSymbolTags(logger, clangSymbol.Tags), }) } - return ideSymbols + return ideSymbols, nil } func (ls *INOLanguageServer) cland2IdeTextEdits(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI, clangTextEdits []lsp.TextEdit) (map[lsp.DocumentURI][]lsp.TextEdit, error) { From 525f6a8d307242ec6e310a0511878939ad45da16 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 26 Nov 2021 17:03:15 +0100 Subject: [PATCH 59/76] Some refactoring of CodeActions handling, still work in progres... --- ls/ls.go | 142 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 57 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 7392529..5a79589 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -429,8 +429,11 @@ func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Contex var ideCommand *lsp.Command if clangItem.Command != nil { - c := ls.cpp2inoCommand(logger, *clangItem.Command) - ideCommand = &c + 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{ @@ -771,60 +774,74 @@ func (ls *INOLanguageServer) TextDocumentDocumentSymbolReqFromIDE(ctx context.Co return ideDocSymbols, ideSymbolsInformation, nil } -func (ls *INOLanguageServer) TextDocumentCodeActionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, inoParams *lsp.CodeActionParams) ([]lsp.CommandOrCodeAction, *jsonrpc.ResponseError) { +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) - inoTextDocument := inoParams.TextDocument - inoURI := inoTextDocument.URI - logger.Logf("--> codeAction(%s:%s)", inoTextDocument, inoParams.Range.Start) + ideTextDocument := ideParams.TextDocument + ideURI := ideTextDocument.URI + logger.Logf("--> codeAction(%s:%s)", ideTextDocument, ideParams.Range.Start) - cppParams := *inoParams - cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoTextDocument) + cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) if err != nil { logger.Logf("Error: %s", err) return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: err.Error()} } - cppParams.TextDocument = cppTextDocument + clangParams := &lsp.CodeActionParams{ + TextDocument: cppTextDocument, + WorkDoneProgressParams: ideParams.WorkDoneProgressParams, + PartialResultParams: ideParams.PartialResultParams, + Range: ideParams.Range, + Context: ideParams.Context, + } if cppTextDocument.URI.AsPath().EquivalentTo(ls.buildSketchCpp) { - cppParams.Range = ls.sketchMapper.InoToCppLSPRange(inoURI, inoParams.Range) - for i, inoDiag := range inoParams.Context.Diagnostics { - cppParams.Context.Diagnostics[i].Range = ls.sketchMapper.InoToCppLSPRange(inoURI, inoDiag.Range) + clangParams.Range = ls.sketchMapper.InoToCppLSPRange(ideURI, ideParams.Range) + for i, inoDiag := range ideParams.Context.Diagnostics { + clangParams.Context.Diagnostics[i].Range = ls.sketchMapper.InoToCppLSPRange(ideURI, inoDiag.Range) } } - logger.Logf(" --> codeAction(%s:%s)", cppParams.TextDocument, inoParams.Range.Start) + logger.Logf(" --> codeAction(%s:%s)", clangParams.TextDocument, ideParams.Range.Start) - cppResp, cppErr, err := ls.Clangd.conn.TextDocumentCodeAction(ctx, &cppParams) + 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 cppErr != nil { - logger.Logf("clangd response error: %v", cppErr.AsError()) - return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInternalError, Message: cppErr.AsError().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? - inoResp := []lsp.CommandOrCodeAction{} - if cppResp != nil { - logger.Logf(" <-- codeAction(%d elements)", len(cppResp)) - for _, cppItem := range cppResp { - inoItem := lsp.CommandOrCodeAction{} - switch i := cppItem.Get().(type) { - case lsp.Command: - logger.Logf(" > Command: %s", i.Title) - inoItem.Set(ls.cpp2inoCommand(logger, i)) - case lsp.CodeAction: - logger.Logf(" > CodeAction: %s", i.Title) - inoItem.Set(ls.cpp2inoCodeAction(logger, i, inoURI)) + 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 } - inoResp = append(inoResp, inoItem) + 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) } - logger.Logf("<-- codeAction(%d elements)", len(inoResp)) + ideCommandsOrCodeActions = append(ideCommandsOrCodeActions, ideItem) } - return inoResp, nil + 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) { @@ -1346,39 +1363,47 @@ func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonr return res, err } -func (ls *INOLanguageServer) cpp2inoCodeAction(logger jsonrpc.FunctionLogger, codeAction lsp.CodeAction, uri lsp.DocumentURI) lsp.CodeAction { - inoCodeAction := lsp.CodeAction{ - Title: codeAction.Title, - Kind: codeAction.Kind, - Edit: ls.cpp2inoWorkspaceEdit(logger, codeAction.Edit), - Diagnostics: codeAction.Diagnostics, - } - if codeAction.Command != nil { - inoCommand := ls.cpp2inoCommand(logger, *codeAction.Command) - inoCodeAction.Command = &inoCommand +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 uri.Ext() == ".ino" { - for i, diag := range inoCodeAction.Diagnostics { - _, inoCodeAction.Diagnostics[i].Range = ls.sketchMapper.CppToInoRange(diag.Range) + if origIdeURI.Ext() == ".ino" { + for i, diag := range ideCodeAction.Diagnostics { + _, ideCodeAction.Diagnostics[i].Range = ls.sketchMapper.CppToInoRange(diag.Range) } } - return inoCodeAction + return ideCodeAction } -func (ls *INOLanguageServer) cpp2inoCommand(logger jsonrpc.FunctionLogger, command lsp.Command) lsp.Command { - inoCommand := lsp.Command{ - Title: command.Title, - Command: command.Command, - Arguments: command.Arguments, - } - if command.Command == "clangd.applyTweak" { - for i := range command.Arguments { +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(command.Arguments[0], &v); err == nil { + + 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) { @@ -1393,10 +1418,13 @@ func (ls *INOLanguageServer) cpp2inoCommand(logger jsonrpc.FunctionLogger, comma if err != nil { panic("Internal Error: json conversion of codeAcion command arguments") } - inoCommand.Arguments[i] = converted + ideCommand.Arguments[i] = converted } + return ideCommand + default: + logger.Logf("ERROR: could not convert Command '%s'", clangCommand.Command) + return nil } - return inoCommand } func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, cppWorkspaceEdit *lsp.WorkspaceEdit) *lsp.WorkspaceEdit { From a6848504f090dd80fcb0ff1d64c2cb4c5ba704ef Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 29 Nov 2021 16:08:56 +0100 Subject: [PATCH 60/76] Reorganization of didClose (wip) --- ls/builder.go | 2 +- ls/ls.go | 204 +++++++++++++++++++++--------------------- ls/ls_ide_to_clang.go | 12 ++- 3 files changed, 114 insertions(+), 104 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 1357d33..f4c2a47 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -165,7 +165,7 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge Overrides map[string]string `json:"overrides"` } data := overridesFile{Overrides: map[string]string{}} - for uri, trackedFile := range ls.trackedIDEDocs { + for uri, trackedFile := range ls.trackedIdeDocs { rel, err := paths.New(uri).RelFrom(sketchRoot) if err != nil { ls.readUnlock(logger) diff --git a/ls/ls.go b/ls/ls.go index 5a79589..b006885 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -44,7 +44,7 @@ type INOLanguageServer struct { sketchName string sketchMapper *sourcemapper.SketchMapper sketchTrackedFilesCount int - trackedIDEDocs map[string]lsp.TextDocumentItem + trackedIdeDocs map[string]lsp.TextDocumentItem ideInoDocsWithDiagnostics map[lsp.DocumentURI]bool sketchRebuilder *SketchRebuilder } @@ -115,7 +115,7 @@ func (ls *INOLanguageServer) readUnlock(logger jsonrpc.FunctionLogger) { func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, config *Config) *INOLanguageServer { logger := NewLSPFunctionLogger(color.HiWhiteString, "LS: ") ls := &INOLanguageServer{ - trackedIDEDocs: map[string]lsp.TextDocumentItem{}, + trackedIdeDocs: map[string]lsp.TextDocumentItem{}, ideInoDocsWithDiagnostics: map[lsp.DocumentURI]bool{}, closing: make(chan bool), config: config, @@ -952,7 +952,7 @@ func (ls *INOLanguageServer) TextDocumentRangeFormattingReqFromIDE(ctx context.C } } -func (ls *INOLanguageServer) InitializedNotifFromIDE(logger jsonrpc.FunctionLogger, params *lsp.InitializedParams) { +func (ls *INOLanguageServer) InitializedNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.InitializedParams) { logger.Logf("Notification is not propagated to clangd") } @@ -962,18 +962,18 @@ func (ls *INOLanguageServer) ExitNotifFromIDE(logger jsonrpc.FunctionLogger) { os.Exit(0) } -func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.FunctionLogger, inoParam *lsp.DidOpenTextDocumentParams) { +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 - inoTextDocItem := inoParam.TextDocument - ls.trackedIDEDocs[inoTextDocItem.URI.AsPath().String()] = inoTextDocItem + ideTextDocItem := ideParam.TextDocument + ls.trackedIdeDocs[ideTextDocItem.URI.AsPath().String()] = ideTextDocItem // If we are tracking a .ino... - if inoTextDocItem.URI.Ext() == ".ino" { + if ideTextDocItem.URI.Ext() == ".ino" { ls.sketchTrackedFilesCount++ logger.Logf("Increasing .ino tracked files count to %d", ls.sketchTrackedFilesCount) @@ -984,10 +984,10 @@ func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.Func } } - if cppItem, err := ls.ino2cppTextDocumentItem(logger, inoTextDocItem); err != nil { + if clangTextDocItem, err := ls.ino2cppTextDocumentItem(logger, ideTextDocItem); err != nil { logger.Logf("Error: %s", err) } else if err := ls.Clangd.conn.TextDocumentDidOpen(&lsp.DidOpenTextDocumentParams{ - TextDocument: cppItem, + 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) @@ -996,33 +996,109 @@ func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.Func } } -func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.FunctionLogger, inoParams *lsp.DidChangeTextDocumentParams) { +func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.DidChangeTextDocumentParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) ls.triggerRebuild() - logger.Logf("didChange(%s)", inoParams.TextDocument) - for _, change := range inoParams.ContentChanges { + logger.Logf("didChange(%s)", ideParams.TextDocument) + for _, change := range ideParams.ContentChanges { logger.Logf(" > %s", change) } - if cppParams, err := ls.didChange(logger, inoParams); err != nil { + // 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) - } else if cppParams == nil { - logger.Logf("Notification is not propagated to clangd") + return } else { - logger.Logf("to Clang: didChange(%s@%d)", cppParams.TextDocument) - for _, change := range cppParams.ContentChanges { - logger.Logf(" > %s", change) + ls.trackedIdeDocs[trackedIdeDocID] = updatedDoc + logger.Logf("Tracked SKETCH file:----------+\n" + updatedDoc.Text + "\n----------------------") + } + + // If the file is not part of a .ino flie forward the change as-is to clangd + var clangParams *lsp.DidChangeTextDocumentParams + + if ideTextDocIdentifier.URI.Ext() != ".ino" { + + clangTextDocIdentifier, err := ls.ide2ClangVersionedTextDocumentIdentifier(logger, ideTextDocIdentifier) + if err != nil { + logger.Logf("Error: %s", err) + return } - if err := ls.Clangd.conn.TextDocumentDidChange(cppParams); err != nil { - // Exit the process and trigger a restart by the client in case of a severe error - logger.Logf("Connection error with clangd server: %v", err) - logger.Logf("Please restart the language server.") - ls.Close() + clangParams = &lsp.DidChangeTextDocumentParams{ + TextDocument: clangTextDocIdentifier, + ContentChanges: ideParams.ContentChanges, + } + + } else { + + // If changes are applied to a .ino file we increment the global .ino.cpp versioning + // for each increment of the single .ino file. + + clangChanges := []lsp.TextDocumentContentChangeEvent{} + for _, ideChange := range ideParams.ContentChanges { + var clangChangeRange *lsp.Range + if ideChange.Range != nil { + clangURI, clangRange, err := ls.ide2ClangRange(logger, ideTextDocIdentifier.URI, *ideChange.Range) + if err != nil { + logger.Logf("Error: %s", err) + return + } + if !ls.clangURIRefersToIno(clangURI) { + logger.Logf("Error: change to .ino does not maps to a change in sketch.ino.cpp") + return + } + clangChangeRange = &clangRange + + _ = ls.sketchMapper.ApplyTextChange(ideTextDocIdentifier.URI, ideChange) + ls.sketchMapper.DebugLogAll() + } else { + panic("full-text change in .ino not implemented") + } + clangChanges = append(clangChanges, lsp.TextDocumentContentChangeEvent{ + Range: clangChangeRange, + RangeLength: ideChange.RangeLength, + Text: ideChange.Text, + }) + } + + // build a cpp equivalent didChange request + clangParams = &lsp.DidChangeTextDocumentParams{ + ContentChanges: clangChanges, + TextDocument: lsp.VersionedTextDocumentIdentifier{ + TextDocumentIdentifier: lsp.TextDocumentIdentifier{ + URI: lsp.NewDocumentURIFromPath(ls.buildSketchCpp), + }, + Version: ls.sketchMapper.CppText.Version, + }, } } + + logger.Logf("to Clang: didChange(%s@%d)", clangParams.TextDocument) + for _, change := range clangParams.ContentChanges { + logger.Logf(" > %s", change) + } + if err := ls.Clangd.conn.TextDocumentDidChange(clangParams); err != nil { + // Exit the process and trigger a restart by the client in case of a severe error + 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, inoParams *lsp.DidSaveTextDocumentParams) { @@ -1244,8 +1320,8 @@ func (ls *INOLanguageServer) extractDataFolderFromArduinoCLI(logger jsonrpc.Func func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { inoIdentifier := inoDidClose.TextDocument - if _, exist := ls.trackedIDEDocs[inoIdentifier.URI.AsPath().String()]; exist { - delete(ls.trackedIDEDocs, inoIdentifier.URI.AsPath().String()) + 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 nil, &UnknownURI{inoIdentifier.URI} @@ -1282,87 +1358,13 @@ func (ls *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogg } else { cppItem.LanguageID = inoItem.LanguageID inoPath := inoItem.URI.AsPath().String() - cppItem.Text = ls.trackedIDEDocs[inoPath].Text - cppItem.Version = ls.trackedIDEDocs[inoPath].Version + cppItem.Text = ls.trackedIdeDocs[inoPath].Text + cppItem.Version = ls.trackedIdeDocs[inoPath].Version } return cppItem, nil } -func (ls *INOLanguageServer) didChange(logger jsonrpc.FunctionLogger, inoDidChangeParams *lsp.DidChangeTextDocumentParams) (*lsp.DidChangeTextDocumentParams, error) { - // 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 inoDidChangeParams.ContentChanges { - inoDidChangeParams.ContentChanges[i].RangeLength = nil - } - - inoDoc := inoDidChangeParams.TextDocument - - // Apply the change to the tracked sketch file. - trackedInoID := inoDoc.URI.AsPath().String() - if doc, ok := ls.trackedIDEDocs[trackedInoID]; !ok { - return nil, &UnknownURI{inoDoc.URI} - } else if updatedDoc, err := textedits.ApplyLSPTextDocumentContentChangeEvent(doc, inoDidChangeParams); err != nil { - return nil, err - } else { - ls.trackedIDEDocs[trackedInoID] = updatedDoc - } - - logger.Logf("Tracked SKETCH file:----------+\n" + ls.trackedIDEDocs[trackedInoID].Text + "\n----------------------") - - // If the file is not part of a .ino flie forward the change as-is to clangd - if inoDoc.URI.Ext() != ".ino" { - if cppDoc, err := ls.ino2cppVersionedTextDocumentIdentifier(logger, inoDidChangeParams.TextDocument); err != nil { - return nil, err - } else { - cppDidChangeParams := *inoDidChangeParams - cppDidChangeParams.TextDocument = cppDoc - return &cppDidChangeParams, nil - } - } - - // If changes are applied to a .ino file we increment the global .ino.cpp versioning - // for each increment of the single .ino file. - - cppChanges := []lsp.TextDocumentContentChangeEvent{} - for _, inoChange := range inoDidChangeParams.ContentChanges { - cppChangeRange, ok := ls.sketchMapper.InoToCppLSPRangeOk(inoDoc.URI, *inoChange.Range) - if !ok { - return nil, errors.Errorf("invalid change range %s:%s", inoDoc.URI, inoChange.Range) - } - - _ = ls.sketchMapper.ApplyTextChange(inoDoc.URI, inoChange) - - ls.sketchMapper.DebugLogAll() - - cppChanges = append(cppChanges, lsp.TextDocumentContentChangeEvent{ - Range: &cppChangeRange, - RangeLength: inoChange.RangeLength, - Text: inoChange.Text, - }) - } - - // build a cpp equivalent didChange request - return &lsp.DidChangeTextDocumentParams{ - ContentChanges: cppChanges, - TextDocument: lsp.VersionedTextDocumentIdentifier{ - TextDocumentIdentifier: lsp.TextDocumentIdentifier{ - URI: lsp.NewDocumentURIFromPath(ls.buildSketchCpp), - }, - Version: ls.sketchMapper.CppText.Version, - }, - }, nil -} - -func (ls *INOLanguageServer) ino2cppVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, doc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { - cppURI, err := ls.ide2ClangDocumentURI(logger, doc.URI) - res := doc - res.URI = cppURI - return res, err -} - func (ls *INOLanguageServer) clang2IdeCodeAction(logger jsonrpc.FunctionLogger, clangCodeAction lsp.CodeAction, origIdeURI lsp.DocumentURI) *lsp.CodeAction { ideCodeAction := &lsp.CodeAction{ Title: clangCodeAction.Title, diff --git a/ls/ls_ide_to_clang.go b/ls/ls_ide_to_clang.go index 4c81bc8..c78c205 100644 --- a/ls/ls_ide_to_clang.go +++ b/ls/ls_ide_to_clang.go @@ -12,11 +12,11 @@ func (ls *INOLanguageServer) idePathToIdeURI(logger jsonrpc.FunctionLogger, inoP if inoPath == sourcemapper.NotIno.File { return sourcemapper.NotInoURI, nil } - doc, ok := ls.trackedIDEDocs[inoPath] + 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 { + for p := range ls.trackedIdeDocs { logger.Logf(" !!! > %s", p) } uri := lsp.NewDocumentURI(inoPath) @@ -99,3 +99,11 @@ func (ls *INOLanguageServer) ide2ClangRange(logger jsonrpc.FunctionLogger, ideUR } 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 +} From 5446598ef834ae87ae60f509ee4c3757efe9e8f5 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 29 Nov 2021 22:08:41 +0100 Subject: [PATCH 61/76] Completed refactoring of didChange message handler --- ls/ls.go | 85 +++++++++++++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index b006885..7f42b4a 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1030,71 +1030,62 @@ func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.Fu logger.Logf("Tracked SKETCH file:----------+\n" + updatedDoc.Text + "\n----------------------") } - // If the file is not part of a .ino flie forward the change as-is to clangd + 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") + } - if ideTextDocIdentifier.URI.Ext() != ".ino" { - - clangTextDocIdentifier, err := ls.ide2ClangVersionedTextDocumentIdentifier(logger, ideTextDocIdentifier) + clangRangeURI, clangRange, err := ls.ide2ClangRange(logger, ideTextDocIdentifier.URI, *ideChange.Range) if err != nil { logger.Logf("Error: %s", err) return } - clangParams = &lsp.DidChangeTextDocumentParams{ - TextDocument: clangTextDocIdentifier, - ContentChanges: ideParams.ContentChanges, + + // 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 } - } else { + // 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() + } - clangChanges := []lsp.TextDocumentContentChangeEvent{} - for _, ideChange := range ideParams.ContentChanges { - var clangChangeRange *lsp.Range - if ideChange.Range != nil { - clangURI, clangRange, err := ls.ide2ClangRange(logger, ideTextDocIdentifier.URI, *ideChange.Range) - if err != nil { - logger.Logf("Error: %s", err) - return - } - if !ls.clangURIRefersToIno(clangURI) { - logger.Logf("Error: change to .ino does not maps to a change in sketch.ino.cpp") - return - } - clangChangeRange = &clangRange - - _ = ls.sketchMapper.ApplyTextChange(ideTextDocIdentifier.URI, ideChange) - ls.sketchMapper.DebugLogAll() - } else { - panic("full-text change in .ino not implemented") - } - clangChanges = append(clangChanges, lsp.TextDocumentContentChangeEvent{ - Range: clangChangeRange, - RangeLength: ideChange.RangeLength, - Text: ideChange.Text, - }) - } - - // build a cpp equivalent didChange request - clangParams = &lsp.DidChangeTextDocumentParams{ - ContentChanges: clangChanges, - TextDocument: lsp.VersionedTextDocumentIdentifier{ - TextDocumentIdentifier: lsp.TextDocumentIdentifier{ - URI: lsp.NewDocumentURIFromPath(ls.buildSketchCpp), - }, - Version: ls.sketchMapper.CppText.Version, - }, - } + // 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@%d)", clangParams.TextDocument) + 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 { - // Exit the process and trigger a restart by the client in case of a severe error logger.Logf("Connection error with clangd server: %v", err) logger.Logf("Please restart the language server.") ls.Close() From de600c534661b6d07cebdfa12d03a23132e2af35 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 30 Nov 2021 16:39:05 +0100 Subject: [PATCH 62/76] Handling of non-standard $/setTraceNotification message --- go.mod | 2 +- go.sum | 4 ++-- ls/ls.go | 5 +++++ ls/lsp_server_ide.go | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 58a9e19..486c352 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,6 @@ require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 go.bug.st/json v1.15.6 - go.bug.st/lsp v0.0.0-20211124092226-438f5e4ddeb8 + go.bug.st/lsp v0.0.0-20211130152916-c597b0a0439f google.golang.org/grpc v1.42.0 ) diff --git a/go.sum b/go.sum index e1a1dc8..82443cd 100644 --- a/go.sum +++ b/go.sum @@ -337,8 +337,8 @@ 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-20211124092226-438f5e4ddeb8 h1:QSRqT2ezfvXfeUmK5znFE4KsnTYe+26oQdFV2Yvtwrc= -go.bug.st/lsp v0.0.0-20211124092226-438f5e4ddeb8/go.mod h1:oYTh1uf5hI1teV5crrWut41Pk8vD/NqIjs4zD+No5FE= +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/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.3.2/go.mod h1:jDkjqASf/qSjmaOxHSHljwUQ6eHo/ZX/bxJLQqSlvZg= diff --git a/ls/ls.go b/ls/ls.go index 7f42b4a..cd43312 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1223,6 +1223,11 @@ func (ls *INOLanguageServer) WindowWorkDoneProgressCreateReqFromClangd(ctx conte 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 { diff --git a/ls/lsp_server_ide.go b/ls/lsp_server_ide.go index a986e20..d0db9ca 100644 --- a/ls/lsp_server_ide.go +++ b/ls/lsp_server_ide.go @@ -217,7 +217,7 @@ func (server *IDELSPServer) Exit(logger jsonrpc.FunctionLogger) { } func (server *IDELSPServer) SetTrace(logger jsonrpc.FunctionLogger, params *lsp.SetTraceParams) { - panic("unimplemented") + server.ls.SetTraceNotifFromIDE(logger, params) } func (server *IDELSPServer) WindowWorkDoneProgressCancel(logger jsonrpc.FunctionLogger, params *lsp.WorkDoneProgressCancelParams) { From cf3008db665d0ec97936aad381de8d82a1c732d1 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 30 Nov 2021 17:05:44 +0100 Subject: [PATCH 63/76] Correct handling of diagnostics from .cpp/.h --- ls/ls_clang_to_ide.go | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 6cc5623..797d91f 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -61,6 +61,45 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio return ideURI, clangRange, 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 { @@ -77,6 +116,21 @@ func (ls *INOLanguageServer) clang2IdeDiagnostics(logger jsonrpc.FunctionLogger, // 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 { From 975d40fb2ecc4fca9edc3f85321aab53906a1975 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 1 Dec 2021 10:47:53 +0100 Subject: [PATCH 64/76] Correct handling of textchanges in .cpp/.h --- ls/ls.go | 86 ++++++++++++++++++------------------------- ls/ls_clang_to_ide.go | 10 ++++- ls/ls_ide_to_clang.go | 26 ++++++++----- 3 files changed, 60 insertions(+), 62 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index cd43312..e7e80aa 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -984,9 +984,29 @@ func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.Func } } - if clangTextDocItem, err := ls.ino2cppTextDocumentItem(logger, ideTextDocItem); err != nil { + clangURI, _, err := ls.ide2ClangDocumentURI(logger, ideTextDocItem.URI) + if err != nil { logger.Logf("Error: %s", err) - } else if err := ls.Clangd.conn.TextDocumentDidOpen(&lsp.DidOpenTextDocumentParams{ + 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 @@ -1092,47 +1112,32 @@ func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.Fu } } -func (ls *INOLanguageServer) TextDocumentDidSaveNotifFromIDE(logger jsonrpc.FunctionLogger, inoParams *lsp.DidSaveTextDocumentParams) { +func (ls *INOLanguageServer) TextDocumentDidSaveNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.DidSaveTextDocumentParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) - ls.triggerRebuild() + // 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") - logger.Logf("didSave(%s) hasText=%v", inoParams.TextDocument, inoParams.Text != "") - if cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, inoParams.TextDocument); err != nil { - logger.Logf("--E Error: %s", err) - } else if cppTextDocument.URI.AsPath().EquivalentTo(ls.buildSketchCpp) { - logger.Logf(" didSave(%s) equals %s", cppTextDocument, ls.buildSketchCpp) - logger.Logf(" the notification will be not forwarded to clangd") - } else { - logger.Logf("LS --> CL NOTIF didSave(%s)", cppTextDocument) - if err := ls.Clangd.conn.TextDocumentDidSave(&lsp.DidSaveTextDocumentParams{ - TextDocument: cppTextDocument, - Text: inoParams.Text, - }); err != nil { - // Exit the process and trigger a restart by the client in case of a severe error - logger.Logf("Connection error with clangd server: %v", err) - logger.Logf("Please restart the language server.") - ls.Close() - } - } + ls.triggerRebuild() } -func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.FunctionLogger, inoParams *lsp.DidCloseTextDocumentParams) { +func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.DidCloseTextDocumentParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) ls.triggerRebuild() - logger.Logf("didClose(%s)", inoParams.TextDocument) + logger.Logf("didClose(%s)", ideParams.TextDocument) - if cppParams, err := ls.didClose(logger, inoParams); err != nil { + if clangParams, err := ls.didClose(logger, ideParams); err != nil { logger.Logf("--E Error: %s", err) - } else if cppParams == nil { + } else if clangParams == nil { logger.Logf("--X Notification is not propagated to clangd") } else { - logger.Logf("--> CL NOTIF didClose(%s)", cppParams.TextDocument) - if err := ls.Clangd.conn.TextDocumentDidClose(cppParams); err != nil { + logger.Logf("--> CL NOTIF 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.") @@ -1314,8 +1319,8 @@ func (ls *INOLanguageServer) extractDataFolderFromArduinoCLI(logger jsonrpc.Func } } -func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { - inoIdentifier := inoDidClose.TextDocument +func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, ideParams *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { + inoIdentifier := ideParams.TextDocument if _, exist := ls.trackedIdeDocs[inoIdentifier.URI.AsPath().String()]; exist { delete(ls.trackedIdeDocs, inoIdentifier.URI.AsPath().String()) } else { @@ -1340,27 +1345,6 @@ func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, inoDidClose }, err } -func (ls *INOLanguageServer) ino2cppTextDocumentItem(logger jsonrpc.FunctionLogger, inoItem lsp.TextDocumentItem) (cppItem lsp.TextDocumentItem, err error) { - cppURI, err := ls.ide2ClangDocumentURI(logger, inoItem.URI) - if err != nil { - return cppItem, err - } - cppItem.URI = cppURI - - if cppURI.AsPath().EquivalentTo(ls.buildSketchCpp) { - cppItem.LanguageID = "cpp" - cppItem.Text = ls.sketchMapper.CppText.Text - cppItem.Version = ls.sketchMapper.CppText.Version - } else { - cppItem.LanguageID = inoItem.LanguageID - inoPath := inoItem.URI.AsPath().String() - cppItem.Text = ls.trackedIdeDocs[inoPath].Text - cppItem.Version = ls.trackedIdeDocs[inoPath].Version - } - - return cppItem, nil -} - func (ls *INOLanguageServer) clang2IdeCodeAction(logger jsonrpc.FunctionLogger, clangCodeAction lsp.CodeAction, origIdeURI lsp.DocumentURI) *lsp.CodeAction { ideCodeAction := &lsp.CodeAction{ Title: clangCodeAction.Title, diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 797d91f..7a66026 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -49,7 +49,7 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio return clangURI, clangRange, false, nil } - // Sketchbook/Sketch/AnotherFile.cpp <-> build-path/sketch/AnotherFile.cpp (same range) + // 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) @@ -57,8 +57,14 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio } 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", clangURI, clangRange, ideURI, ideRange) - return ideURI, clangRange, false, err + return ideURI, ideRange, false, err } func (ls *INOLanguageServer) clang2IdeDocumentURI(logger jsonrpc.FunctionLogger, clangURI lsp.DocumentURI) (lsp.DocumentURI, error) { diff --git a/ls/ls_ide_to_clang.go b/ls/ls_ide_to_clang.go index c78c205..d439922 100644 --- a/ls/ls_ide_to_clang.go +++ b/ls/ls_ide_to_clang.go @@ -26,43 +26,43 @@ func (ls *INOLanguageServer) idePathToIdeURI(logger jsonrpc.FunctionLogger, inoP } func (ls *INOLanguageServer) ide2ClangTextDocumentIdentifier(logger jsonrpc.FunctionLogger, ideTextDocIdentifier lsp.TextDocumentIdentifier) (lsp.TextDocumentIdentifier, error) { - clangURI, err := ls.ide2ClangDocumentURI(logger, ideTextDocIdentifier.URI) + 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, error) { +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, nil + 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, &UnknownURI{ideURI} + return lsp.NilURI, false, &UnknownURI{ideURI} } if !inside { clangURI := ideURI logger.Logf("URI: %s -> %s", ideURI, clangURI) - return clangURI, nil + 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, err + return lsp.NilURI, false, err } clangPath := ls.buildSketchRoot.JoinPath(rel) clangURI := lsp.NewDocumentURIFromPath(clangPath) logger.Logf("URI: %s -> %s", ideURI, clangURI) - return clangURI, nil + return clangURI, true, nil } func (ls *INOLanguageServer) ide2ClangTextDocumentPositionParams(logger jsonrpc.FunctionLogger, ideParams lsp.TextDocumentPositionParams) (lsp.TextDocumentPositionParams, error) { @@ -85,11 +85,13 @@ func (ls *INOLanguageServer) ide2ClangPosition(logger jsonrpc.FunctionLogger, id } func (ls *INOLanguageServer) ide2ClangRange(logger jsonrpc.FunctionLogger, ideURI lsp.DocumentURI, ideRange lsp.Range) (lsp.DocumentURI, lsp.Range, error) { - clangURI, err := ls.ide2ClangDocumentURI(logger, ideURI) + clangURI, inSketch, err := ls.ide2ClangDocumentURI(logger, ideURI) if err != nil { return lsp.DocumentURI{}, lsp.Range{}, err } clangRange := ideRange + + // Convert .ino ranges using sketchmapper if ls.clangURIRefersToIno(clangURI) { if r, ok := ls.sketchMapper.InoToCppLSPRangeOk(ideURI, ideRange); ok { clangRange = r @@ -97,11 +99,17 @@ func (ls *INOLanguageServer) ide2ClangRange(logger jsonrpc.FunctionLogger, ideUR return lsp.DocumentURI{}, lsp.Range{}, fmt.Errorf("invalid range %s:%s: could not be mapped to Arduino-preprocessed sketck.ino.cpp", ideURI, ideRange) } } + + // Convert other sketch file ranges (.cpp/.h) + if inSketch { + clangRange.Start.Line++ + clangRange.End.Line++ + } return clangURI, clangRange, nil } func (ls *INOLanguageServer) ide2ClangVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, ideVersionedDoc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { - clangURI, err := ls.ide2ClangDocumentURI(logger, ideVersionedDoc.URI) + clangURI, _, err := ls.ide2ClangDocumentURI(logger, ideVersionedDoc.URI) return lsp.VersionedTextDocumentIdentifier{ TextDocumentIdentifier: lsp.TextDocumentIdentifier{URI: clangURI}, Version: ideVersionedDoc.Version, From a8ccf1246ec68e4c85061acd0e41e241c758bf0a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 2 Dec 2021 00:34:56 +0100 Subject: [PATCH 65/76] Inlined didClose method --- ls/ls.go | 72 ++++++++++++++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index e7e80aa..098afd0 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1047,7 +1047,7 @@ func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.Fu return } else { ls.trackedIdeDocs[trackedIdeDocID] = updatedDoc - logger.Logf("Tracked SKETCH file:----------+\n" + updatedDoc.Text + "\n----------------------") + logger.Logf("-----Tracked SKETCH file-----\n" + updatedDoc.Text + "\n-----------------------------") } clangChanges := []lsp.TextDocumentContentChangeEvent{} @@ -1129,21 +1129,41 @@ func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.Fun ls.triggerRebuild() - logger.Logf("didClose(%s)", ideParams.TextDocument) - - if clangParams, err := ls.didClose(logger, ideParams); err != nil { - logger.Logf("--E Error: %s", err) - } else if clangParams == nil { - logger.Logf("--X Notification is not propagated to clangd") + inoIdentifier := ideParams.TextDocument + if _, exist := ls.trackedIdeDocs[inoIdentifier.URI.AsPath().String()]; exist { + delete(ls.trackedIdeDocs, inoIdentifier.URI.AsPath().String()) } else { - logger.Logf("--> CL NOTIF 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() + 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) { @@ -1319,32 +1339,6 @@ func (ls *INOLanguageServer) extractDataFolderFromArduinoCLI(logger jsonrpc.Func } } -func (ls *INOLanguageServer) didClose(logger jsonrpc.FunctionLogger, ideParams *lsp.DidCloseTextDocumentParams) (*lsp.DidCloseTextDocumentParams, error) { - 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 nil, &UnknownURI{inoIdentifier.URI} - } - - // 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 { - return nil, nil - } - } - - cppIdentifier, err := ls.ide2ClangTextDocumentIdentifier(logger, inoIdentifier) - return &lsp.DidCloseTextDocumentParams{ - TextDocument: cppIdentifier, - }, err -} - func (ls *INOLanguageServer) clang2IdeCodeAction(logger jsonrpc.FunctionLogger, clangCodeAction lsp.CodeAction, origIdeURI lsp.DocumentURI) *lsp.CodeAction { ideCodeAction := &lsp.CodeAction{ Title: clangCodeAction.Title, From b5031d3e2dbcc03159c712b2910eb14dd828644e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 2 Dec 2021 00:35:23 +0100 Subject: [PATCH 66/76] Fixed line conversion for .cpp/.h --- ls/ls_ide_to_clang.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ls/ls_ide_to_clang.go b/ls/ls_ide_to_clang.go index d439922..41ed930 100644 --- a/ls/ls_ide_to_clang.go +++ b/ls/ls_ide_to_clang.go @@ -89,23 +89,25 @@ func (ls *INOLanguageServer) ide2ClangRange(logger jsonrpc.FunctionLogger, ideUR if err != nil { return lsp.DocumentURI{}, lsp.Range{}, err } - clangRange := ideRange // Convert .ino ranges using sketchmapper if ls.clangURIRefersToIno(clangURI) { - if r, ok := ls.sketchMapper.InoToCppLSPRangeOk(ideURI, ideRange); ok { - clangRange = r + 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) } - } - - // Convert other sketch file ranges (.cpp/.h) - if inSketch { + } 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 } - return clangURI, clangRange, nil } func (ls *INOLanguageServer) ide2ClangVersionedTextDocumentIdentifier(logger jsonrpc.FunctionLogger, ideVersionedDoc lsp.VersionedTextDocumentIdentifier) (lsp.VersionedTextDocumentIdentifier, error) { From 6847aa5816f44271d9604bbc604122d5cade0520 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 2 Dec 2021 00:36:53 +0100 Subject: [PATCH 67/76] Ensure write-lock for text-format --- ls/ls.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 098afd0..ba14f00 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -845,8 +845,8 @@ func (ls *INOLanguageServer) TextDocumentCodeActionReqFromIDE(ctx context.Contex } func (ls *INOLanguageServer) TextDocumentFormattingReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { - ls.readLock(logger, true) - defer ls.readUnlock(logger) + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) ideTextDocument := ideParams.TextDocument ideURI := ideTextDocument.URI @@ -900,8 +900,8 @@ func (ls *INOLanguageServer) TextDocumentFormattingReqFromIDE(ctx context.Contex } func (ls *INOLanguageServer) TextDocumentRangeFormattingReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, *jsonrpc.ResponseError) { - ls.readLock(logger, true) - defer ls.readUnlock(logger) + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) ideURI := ideParams.TextDocument.URI clangURI, clangRange, err := ls.ide2ClangRange(logger, ideURI, ideParams.Range) From 71dd136db960ac6aaa3c9f3d7fdf6792c4843e31 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 2 Dec 2021 17:08:26 +0100 Subject: [PATCH 68/76] Partial refactoring of CodeAction conversions subroutines --- ls/ls.go | 23 +++++++---------- ls/ls_clang_to_ide.go | 4 +++ ls/ls_ide_to_clang.go | 60 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index ba14f00..1106741 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -512,10 +512,6 @@ func (ls *INOLanguageServer) TextDocumentHoverReqFromIDE(ctx context.Context, lo return &ideResp, nil } -func (ls *INOLanguageServer) clangURIRefersToIno(clangURI lsp.DocumentURI) bool { - return clangURI.AsPath().EquivalentTo(ls.buildSketchCpp) -} - 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) @@ -782,24 +778,23 @@ func (ls *INOLanguageServer) TextDocumentCodeActionReqFromIDE(ctx context.Contex ideURI := ideTextDocument.URI logger.Logf("--> codeAction(%s:%s)", ideTextDocument, ideParams.Range.Start) - cppTextDocument, err := ls.ide2ClangTextDocumentIdentifier(logger, ideTextDocument) + 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{ - TextDocument: cppTextDocument, WorkDoneProgressParams: ideParams.WorkDoneProgressParams, PartialResultParams: ideParams.PartialResultParams, - Range: ideParams.Range, - Context: ideParams.Context, - } - if cppTextDocument.URI.AsPath().EquivalentTo(ls.buildSketchCpp) { - clangParams.Range = ls.sketchMapper.InoToCppLSPRange(ideURI, ideParams.Range) - for i, inoDiag := range ideParams.Context.Diagnostics { - clangParams.Context.Diagnostics[i].Range = ls.sketchMapper.InoToCppLSPRange(ideURI, inoDiag.Range) - } + TextDocument: lsp.TextDocumentIdentifier{URI: clangURI}, + Range: clangRange, + Context: clangContext, } logger.Logf(" --> codeAction(%s:%s)", clangParams.TextDocument, ideParams.Range.Start) diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 7a66026..442d545 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -8,6 +8,10 @@ import ( "go.bug.st/lsp/jsonrpc" ) +func (ls *INOLanguageServer) clangURIRefersToIno(clangURI lsp.DocumentURI) bool { + return clangURI.AsPath().EquivalentTo(ls.buildSketchCpp) +} + 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) diff --git a/ls/ls_ide_to_clang.go b/ls/ls_ide_to_clang.go index 41ed930..fc49771 100644 --- a/ls/ls_ide_to_clang.go +++ b/ls/ls_ide_to_clang.go @@ -117,3 +117,63 @@ func (ls *INOLanguageServer) ide2ClangVersionedTextDocumentIdentifier(logger jso 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 +} From 5ee9731d5622608317bd2e1cef633e5b78b80edf Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 2 Dec 2021 17:22:49 +0100 Subject: [PATCH 69/76] Handle ctags failing to report the correct line number --- ls/ls_clang_to_ide.go | 6 +++--- sourcemapper/ino.go | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 442d545..5e9cab2 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -35,7 +35,7 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio if inPreprocessed { logger.Logf("Range is in PREPROCESSED section of the sketch") } - logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + logger.Logf("Range: %s:%s -> %s:%s (.ino)", clangURI, clangRange, ideURI, ideRange) return ideURI, ideRange, inPreprocessed, err } @@ -49,7 +49,7 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio } if !inside { ideURI := clangURI - logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + logger.Logf("Range: %s:%s -> %s:%s (ext file)", clangURI, clangRange, ideURI, ideRange) return clangURI, clangRange, false, nil } @@ -67,7 +67,7 @@ func (ls *INOLanguageServer) clang2IdeRangeAndDocumentURI(logger jsonrpc.Functio if ideRange.Start.Line > 0 { ideRange.Start.Line-- } - logger.Logf("Range: %s:%s -> %s:%s", clangURI, clangRange, ideURI, ideRange) + logger.Logf("Range: %s:%s -> %s:%s (.cpp/.h)", clangURI, clangRange, ideURI, ideRange) return ideURI, ideRange, false, err } diff --git a/sourcemapper/ino.go b/sourcemapper/ino.go index 87c0b3f..6911efd 100644 --- a/sourcemapper/ino.go +++ b/sourcemapper/ino.go @@ -177,7 +177,14 @@ func (s *SketchMapper) regeneratehMapping() { sourceFile = paths.New(unquoteCppString(tokens[2])).Canonical().String() s.cppToIno[targetLine] = NotIno } else if sourceFile != "" { - s.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 { s.cppToIno[targetLine] = NotIno From b3d9169e091bad59fba5b47a5ff5db67ba6a1027 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 3 Dec 2021 12:59:06 +0100 Subject: [PATCH 70/76] Do not block on clangd notification --- ls/lsp_client_clangd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ls/lsp_client_clangd.go b/ls/lsp_client_clangd.go index 2bad456..e98f94c 100644 --- a/ls/lsp_client_clangd.go +++ b/ls/lsp_client_clangd.go @@ -141,5 +141,5 @@ func (client *ClangdLSPClient) TelemetryEvent(jsonrpc.FunctionLogger, json.RawMe } func (client *ClangdLSPClient) TextDocumentPublishDiagnostics(logger jsonrpc.FunctionLogger, params *lsp.PublishDiagnosticsParams) { - client.ls.PublishDiagnosticsNotifFromClangd(logger, params) + go client.ls.PublishDiagnosticsNotifFromClangd(logger, params) } From bd5ee6b4a9361553e1426291822e85b9c7f2cb51 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 3 Dec 2021 12:59:41 +0100 Subject: [PATCH 71/76] Handle rename symbol --- go.mod | 2 +- go.sum | 2 ++ ls/ls.go | 42 ++++++++++++++++++++++++++++++++++++++---- ls/ls_clang_to_ide.go | 36 ++++++++++++++++++++++++++++++++++++ ls/lsp_server_ide.go | 2 +- 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 486c352..030da01 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,6 @@ require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 go.bug.st/json v1.15.6 - go.bug.st/lsp v0.0.0-20211130152916-c597b0a0439f + 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 82443cd..e42d045 100644 --- a/go.sum +++ b/go.sum @@ -339,6 +339,8 @@ 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.3.2/go.mod h1:jDkjqASf/qSjmaOxHSHljwUQ6eHo/ZX/bxJLQqSlvZg= diff --git a/ls/ls.go b/ls/ls.go index 1106741..f018de5 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -371,14 +371,14 @@ func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Contex ls.readLock(logger, true) defer ls.readUnlock(logger) - cppTextDocPositionParams, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + 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: cppTextDocPositionParams, + TextDocumentPositionParams: clangTextDocPositionParams, Context: ideParams.Context, WorkDoneProgressParams: ideParams.WorkDoneProgressParams, PartialResultParams: ideParams.PartialResultParams, @@ -637,14 +637,14 @@ func (ls *INOLanguageServer) TextDocumentImplementationReqFromIDE(ctx context.Co ls.readLock(logger, true) defer ls.readUnlock(logger) - cppTextDocumentPosition, err := ls.ide2ClangTextDocumentPositionParams(logger, ideParams.TextDocumentPositionParams) + 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: cppTextDocumentPosition, + TextDocumentPositionParams: clangTextDocumentPosition, WorkDoneProgressParams: ideParams.WorkDoneProgressParams, PartialResultParams: ideParams.PartialResultParams, } @@ -1212,6 +1212,40 @@ func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.Fu } } +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()} + } + return ideWorkspaceEdit, nil +} + func (ls *INOLanguageServer) ProgressNotifFromClangd(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { var token string if err := json.Unmarshal(progress.Token, &token); err != nil { diff --git a/ls/ls_clang_to_ide.go b/ls/ls_clang_to_ide.go index 5e9cab2..b3b2c8c 100644 --- a/ls/ls_clang_to_ide.go +++ b/ls/ls_clang_to_ide.go @@ -12,6 +12,11 @@ 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) @@ -308,3 +313,34 @@ func (ls *INOLanguageServer) clang2IdeSymbolsInformation(logger jsonrpc.Function 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/lsp_server_ide.go b/ls/lsp_server_ide.go index d0db9ca..2f295d5 100644 --- a/ls/lsp_server_ide.go +++ b/ls/lsp_server_ide.go @@ -155,7 +155,7 @@ func (server *IDELSPServer) TextDocumentOnTypeFormatting(ctx context.Context, lo } func (server *IDELSPServer) TextDocumentRename(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.RenameParams) (*lsp.WorkspaceEdit, *jsonrpc.ResponseError) { - panic("unimplemented") + return server.ls.TextDocumentRenameReqFromIDE(ctx, logger, params) } func (server *IDELSPServer) TextDocumentFoldingRange(ctx context.Context, logger jsonrpc.FunctionLogger, params *lsp.FoldingRangeParams) ([]lsp.FoldingRange, *jsonrpc.ResponseError) { From 4b8b569e56fcb9952aa907c1b8fa7296cd76b6db Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 3 Dec 2021 13:26:07 +0100 Subject: [PATCH 72/76] Do not rename if the operation spans out of the sketch --- ls/ls.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ls/ls.go b/ls/ls.go index f018de5..5ccd37f 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1243,9 +1243,22 @@ func (ls *INOLanguageServer) TextDocumentRenameReqFromIDE(ctx context.Context, l 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 { + go ls.showMessage(logger, lsp.MessageTypeError, "Could not rename symbol, it requires changes outside the sketch.") + 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 { From 83d82b5ce83b728beb9f815436fb4edca3e2a180 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 3 Dec 2021 14:38:01 +0100 Subject: [PATCH 73/76] Remove unsupported showMessage message... oops --- ls/ls.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ls/ls.go b/ls/ls.go index 5ccd37f..a95143d 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1246,7 +1246,6 @@ func (ls *INOLanguageServer) TextDocumentRenameReqFromIDE(ctx context.Context, l // Check if all edits belongs to the sketch for ideURI := range ideWorkspaceEdit.Changes { - go ls.showMessage(logger, lsp.MessageTypeError, "Could not rename symbol, it requires changes outside the sketch.") if !ls.ideURIIsPartOfTheSketch(ideURI) { return nil, &jsonrpc.ResponseError{Code: jsonrpc.ErrorCodesInvalidParams, Message: "Could not rename symbol, it requires changes outside the sketch."} } From 4cbf9f3fe5a7ec1ac278bff7c16ae930c9c8af45 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 10 Dec 2021 00:50:20 +0100 Subject: [PATCH 74/76] Handle special case in text-edits --- ls/ls.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ls/ls.go b/ls/ls.go index a95143d..c430dcb 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1488,6 +1488,25 @@ func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, 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 From 287e8bab202a556a3440a5683070ff8c998475e3 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 10 Dec 2021 16:58:34 +0100 Subject: [PATCH 75/76] Canonicalize dataDir path before passing it to clangd See https://github.com/arduino/arduino-ide/issues/603 --- ls/ls.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index c430dcb..2bc638c 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1326,6 +1326,7 @@ func (ls *INOLanguageServer) CleanUp() { } 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()) @@ -1341,12 +1342,10 @@ func (ls *INOLanguageServer) extractDataFolderFromArduinoCLI(logger jsonrpc.Func if err != nil { return nil, fmt.Errorf("error getting arduino data dir: %w", err) } - var dataDir string 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) - return paths.New(dataDir), nil } else { args := []string{ls.config.CliPath.String(), "--config-file", ls.config.CliConfigPath.String(), @@ -1376,8 +1375,11 @@ func (ls *INOLanguageServer) extractDataFolderFromArduinoCLI(logger jsonrpc.Func } // Return only the build path logger.Logf("Arduino Data Dir -> %s", res.Directories.Data) - return paths.New(res.Directories.Data), nil + 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 { From 52d96f02aa3a70169e117d633fb92349cd41ae36 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 13 Dec 2021 10:20:28 +0100 Subject: [PATCH 76/76] updated README --- README.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) 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 ```