github.com/getgauge/gauge@v1.6.9/api/lang/server.go (about) 1 /*---------------------------------------------------------------- 2 * Copyright (c) ThoughtWorks, Inc. 3 * Licensed under the Apache License, Version 2.0 4 * See LICENSE in the project root for license information. 5 *----------------------------------------------------------------*/ 6 7 package lang 8 9 import ( 10 "context" 11 "encoding/json" 12 "fmt" 13 "log" 14 "os" 15 "runtime/debug" 16 17 gm "github.com/getgauge/gauge-proto/go/gauge_messages" 18 "github.com/getgauge/gauge/api/infoGatherer" 19 "github.com/getgauge/gauge/execution" 20 "github.com/getgauge/gauge/gauge" 21 "github.com/sourcegraph/jsonrpc2" 22 ) 23 24 type infoProvider interface { 25 Init() 26 Steps(filterConcepts bool) []*gauge.Step 27 AllSteps(filterConcepts bool) []*gauge.Step 28 Concepts() []*gm.ConceptInfo 29 Params(file string, argType gauge.ArgType) []gauge.StepArg 30 Tags() []string 31 SearchConceptDictionary(string) *gauge.Concept 32 GetAvailableSpecDetails(specs []string) []*infoGatherer.SpecDetail 33 GetSpecDirs() []string 34 } 35 36 var provider infoProvider 37 38 type lspHandler struct { 39 jsonrpc2.Handler 40 } 41 42 type LangHandler struct { 43 } 44 45 type InitializeParams struct { 46 RootPath string `json:"rootPath,omitempty"` 47 Capabilities ClientCapabilities `json:"capabilities,omitempty"` 48 } 49 50 func newHandler() jsonrpc2.Handler { 51 return lspHandler{jsonrpc2.HandlerWithError((&LangHandler{}).handle)} 52 } 53 54 func (h lspHandler) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) { 55 go h.Handler.Handle(ctx, conn, req) 56 } 57 58 func (h *LangHandler) handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (interface{}, error) { 59 defer recoverPanic(req) 60 return h.Handle(ctx, conn, req) 61 } 62 63 func (h *LangHandler) Handle(ctx context.Context, conn jsonrpc2.JSONRPC2, req *jsonrpc2.Request) (interface{}, error) { 64 switch req.Method { 65 case "initialize": 66 if err := cacheInitializeParams(req); err != nil { 67 logError(req, err.Error()) 68 return nil, err 69 } 70 return gaugeLSPCapabilities(), nil 71 case "initialized": 72 err := registerFileWatcher(conn, ctx) 73 if err != nil { 74 logError(req, err.Error()) 75 } 76 err = registerRunnerCapabilities(conn, ctx) 77 if err != nil { 78 logError(req, err.Error()) 79 } 80 go publishDiagnostics(ctx, conn) 81 return nil, nil 82 case "shutdown": 83 return nil, killRunner() 84 case "exit": 85 if c, ok := conn.(*jsonrpc2.Conn); ok { 86 err := c.Close() 87 if err != nil { 88 logError(req, err.Error()) 89 } 90 os.Exit(0) 91 } 92 return nil, nil 93 case "$/cancelRequest": 94 return nil, nil 95 case "textDocument/didOpen": 96 err := documentOpened(req, ctx, conn) 97 if err != nil { 98 logDebug(req, err.Error()) 99 } 100 return nil, err 101 case "textDocument/didClose": 102 err := documentClosed(req, ctx, conn) 103 if err != nil { 104 logDebug(req, err.Error()) 105 } 106 return nil, err 107 case "textDocument/didChange": 108 err := documentChange(req, ctx, conn) 109 if err != nil { 110 logDebug(req, err.Error()) 111 } 112 return nil, err 113 case "workspace/didChangeWatchedFiles": 114 err := documentChangeWatchedFiles(req, ctx, conn) 115 if err != nil { 116 logDebug(req, err.Error()) 117 } 118 return nil, err 119 case "textDocument/completion": 120 val, err := completion(req) 121 if err != nil { 122 logDebug(req, err.Error()) 123 } 124 return val, err 125 case "completionItem/resolve": 126 val, err := resolveCompletion(req) 127 if err != nil { 128 logDebug(req, err.Error()) 129 } 130 return val, err 131 case "textDocument/definition": 132 val, err := definition(req) 133 if err != nil { 134 logDebug(req, err.Error()) 135 if e := showErrorMessageOnClient(ctx, conn, err); e != nil { 136 return nil, fmt.Errorf("unable to send '%s' error to LSP server. %s", err.Error(), e.Error()) 137 } 138 139 } 140 return val, err 141 case "textDocument/formatting": 142 data, err := format(req) 143 if err != nil { 144 logDebug(req, err.Error()) 145 e := showErrorMessageOnClient(ctx, conn, err) 146 if e != nil { 147 return nil, fmt.Errorf("unable to send '%s' error to LSP server. %s", err.Error(), e.Error()) 148 } 149 } 150 return data, err 151 case "textDocument/codeLens": 152 val, err := codeLenses(req) 153 if err != nil { 154 logDebug(req, err.Error()) 155 } 156 return val, err 157 case "textDocument/codeAction": 158 val, err := codeActions(req) 159 if err != nil { 160 logDebug(req, err.Error()) 161 } 162 return val, err 163 case "textDocument/rename": 164 result, err := rename(ctx, conn, req) 165 if err != nil { 166 logDebug(req, err.Error()) 167 e := showErrorMessageOnClient(ctx, conn, err) 168 if e != nil { 169 return nil, fmt.Errorf("unable to send '%s' error to LSP server. %s", err.Error(), e.Error()) 170 } 171 return nil, err 172 } 173 return result, nil 174 case "textDocument/documentSymbol": 175 val, err := documentSymbols(req) 176 if err != nil { 177 logDebug(req, err.Error()) 178 } 179 return val, err 180 case "workspace/symbol": 181 val, err := workspaceSymbols(req) 182 if err != nil { 183 logDebug(req, err.Error()) 184 } 185 return val, err 186 case "gauge/stepReferences": 187 val, err := stepReferences(req) 188 if err != nil { 189 logDebug(req, err.Error()) 190 } 191 return val, err 192 case "gauge/stepValueAt": 193 val, err := stepValueAt(req) 194 if err != nil { 195 logDebug(req, err.Error()) 196 } 197 return val, err 198 case "gauge/scenarios": 199 val, err := scenarios(req) 200 if err != nil { 201 logDebug(req, err.Error()) 202 } 203 return val, err 204 case "gauge/getImplFiles": 205 val, err := getImplFiles(req) 206 if err != nil { 207 logDebug(req, err.Error()) 208 } 209 return val, err 210 case "gauge/putStubImpl": 211 if err := sendSaveFilesRequest(ctx, conn); err != nil { 212 logDebug(req, err.Error()) 213 e := showErrorMessageOnClient(ctx, conn, err) 214 if e != nil { 215 return nil, fmt.Errorf("unable to send '%s' error to LSP server. %s", err.Error(), e.Error()) 216 } 217 return nil, err 218 } 219 val, err := putStubImpl(req) 220 if err != nil { 221 logDebug(req, err.Error()) 222 } 223 return val, err 224 case "gauge/specs": 225 val, err := specs() 226 if err != nil { 227 logDebug(req, err.Error()) 228 } 229 return val, err 230 case "gauge/executionStatus": 231 val, err := execution.ReadLastExecutionResult() 232 if err != nil { 233 logDebug(req, err.Error()) 234 } 235 return val, err 236 case "gauge/generateConcept": 237 if err := sendSaveFilesRequest(ctx, conn); err != nil { 238 e := showErrorMessageOnClient(ctx, conn, err) 239 if e != nil { 240 return nil, fmt.Errorf("unable to send '%s' error to LSP server. %s", err.Error(), e.Error()) 241 } 242 return nil, err 243 } 244 return generateConcept(req) 245 case "gauge/getRunnerLanguage": 246 return lRunner.lspID, nil 247 case "gauge/specDirs": 248 return provider.GetSpecDirs(), nil 249 default: 250 return nil, nil 251 } 252 } 253 254 func cacheInitializeParams(req *jsonrpc2.Request) error { 255 var params InitializeParams 256 var err error 257 if err = json.Unmarshal(*req.Params, ¶ms); err != nil { 258 return err 259 } 260 clientCapabilities = params.Capabilities 261 return nil 262 } 263 264 func startLsp(logLevel string) (context.Context, *jsonrpc2.Conn) { 265 logInfo(nil, "LangServer: reading on stdin, writing on stdout") 266 var connOpt []jsonrpc2.ConnOpt 267 if logLevel == "debug" { 268 connOpt = append(connOpt, jsonrpc2.LogMessages(log.New(lspWriter{}, "", 0))) 269 } 270 ctx := context.Background() 271 return ctx, jsonrpc2.NewConn(ctx, jsonrpc2.NewBufferedStream(stdRWC{}, jsonrpc2.VSCodeObjectCodec{}), newHandler(), connOpt...) 272 } 273 274 func initializeRunner() error { 275 id, err := getLanguageIdentifier() 276 if err != nil || id == "" { 277 e := fmt.Errorf("There are version incompatibilities between Gauge and it's plugins in this project. Some features will not work as expected.") 278 logDebug(nil, "%s", e.Error()) 279 return e 280 } 281 err = startRunner() 282 if err != nil { 283 logDebug(nil, "%s\nSome of the gauge lsp feature will not work as expected.", err.Error()) 284 return err 285 } 286 lRunner.lspID = id 287 return nil 288 } 289 290 func Start(p infoProvider, logLevel string) { 291 provider = p 292 provider.Init() 293 err := initializeRunner() 294 ctx, conn := startLsp(logLevel) 295 if err != nil { 296 _ = showErrorMessageOnClient(ctx, conn, err) 297 } 298 initialize(ctx, conn) 299 <-conn.DisconnectNotify() 300 if killRunner() != nil { 301 logInfo(nil, "failed to kill runner with pid %d", lRunner.runner.Pid()) 302 } 303 logInfo(nil, "Connection closed") 304 } 305 306 func recoverPanic(req *jsonrpc2.Request) { 307 if r := recover(); r != nil { 308 logFatal(req, "%v\n%s", r, string(debug.Stack())) 309 } 310 }