Pro Yearly is on sale from $80 to $50! »

Go language serverを理解する / Understanding Go language server

Go language serverを理解する / Understanding Go language server

Go 1.12 Release Party in Fukuoka
https://fukuokago.connpass.com/event/118377/

Cd3d2cb2dadf5488935fe0ddaea7938a?s=128

monochromegane

February 15, 2019
Tweet

Transcript

  1. tools/cmd/gopls ࡾ୐༔հ / Pepabo R&D Institute, GMO Pepabo, Inc. 2019.02.15

    Go 1.12 Release Party Go language serverΛཧղ͢Δ
  2. ϓϦϯγύϧΤϯδχΞ ࡾ୐༔հ!NPOPDISPNFHBOF (.0ϖύϘגࣜձࣾϖύϘݚڀॴ IUUQTCMPHNPOPDISPNFHBOFDPN

  3. 1. Language Server Protocol 2. Go language server 3. Conclusion

    3 ໨࣍
  4. 1. Language Server Protocol

  5. l5IF-BOHVBHF4FSWFS1SPUPDPM -41 EFpOFTUIFQSPUPDPMVTFECFUXFFOBO FEJUPSPS*%&BOEBMBOHVBHFTFSWFSUIBUQSPWJEFTMBOHVBHFGFBUVSFTMJLFBVUP DPNQMFUF HPUPEFpOJUJPO pOEBMMSFGFSFODFTFUDl 5 Language Server

    Protocol <-BOHVBHF4FSWFS1SPUPDPM> IUUQTNJDSPTPGUHJUIVCJPMBOHVBHFTFSWFSQSPUPDPM w -BOHVBHF4FSWFS1SPUPDPM͸Τσ Ολ΍*%&ͱ-BOHVBHF4FSWFSͷؒͰ࢖ΘΕΔ ϓϩτίϧ w -BOHVBHF4FSWFS͸ΦʔτίϯϓϦʔτ΍ఆٛݩ΁ͷҠಈɼࢀরઌΛݟ͚ͭΔ ͱ͍ͬͨzMBOVHBHFGFBUVSFTzΛఏڙ͢Δ
  6. • “language features”͸ݴޠ͝ͱʹڞ௨෦෼͕ଟ͍͕ɼݱࡏ͸ɼ։ൃπʔϧ͝ ͱʹɼͦͷ࣮ߦͱ݁Ռ൓өͷ࣮૷͕ඞཁ (ݴޠ਺ x ։ൃπʔϧ਺) • ͦ͜ͰLanguage Server

    Protocol͸ڞ௨ੑʹண໨͢Δɽ • ݴޠ͝ͱͷLanguage ServerͰ”language features”Λ࣮ߦ͠ɼ։ൃπʔϧ͸ ͦͷ݁Ռ൓өͷΈΛߦ͏ํࣜ (ݴޠ਺ + ։ൃπʔϧ਺) • ࣮ࡍʹ͸Language Server΍։ൃπʔϧͷݺͼग़͠ػೳ͸ෳ਺ଘࡏ͢ΔͨΊཧ࿦஋ͱͯ͠ɽ 6 ݴޠͱ։ൃπʔϧͷ૊Έ߹ΘͤരൃΛճආ͢Δ
  7. • ։ൃπʔϧ͔Βͷ”language features”ͷ໰͍߹Θͤʹରͯ͠ɼॲཧΛ࣮ߦ ͠ɼ݁Ռ൓өʹඞཁͳ৘ใΛฦ͢αʔόʔϓϩηε • ௨৴Λܦ༝͢Δ͜ͱʹΑΓ։ൃπʔϧͱ”language features”Λ෼཭͢Δ • ͲͷॲཧΛ࣮ߦ͢Δ͔ɼ࣮ͦͯ͠ߦ͢ΔͨΊʹඞཁͳύϥϝλͱ݁Ռ൓өʹඞ ཁͳύϥϝλͷ࢓༷ΛఆΊͨ΋ͷ͕Language

    Server Protocol (LSP) 7 Language Server
  8. • ݱࡏ version 3.x ܥ • HeaderͱContentύʔτ͔ΒͳΔ • Contentύʔτͷϝοηʔδ෦෼͸JSON-RPCͷ࢓༷Λ ࠾༻

    • IDɼϝιου໊ɼύϥϝλ͔ΒͳΔ 8 LSP Specification <-BOHVBHF4FSWFS1SPUPDPM4QFDJpDBUJPO> IUUQTNJDSPTPGUHJUIVCJPMBOHVBHFTFSWFSQSPUPDPMTQFDJpDBUJPO Content-Length: ...\r\n \r\n { "jsonrpc": "2.0", "id": 1, "method": “initialize", "params": { ... } } • ϦΫΤετ - Ϩεϙϯεͷ૊Έ͔ΒͳΔݺͼग़͠ͷଞɼϦΫΤετͷΈ ͷ”Notification”ܗࣜͷݺͼग़͠΋ଘࡏ͢Δ • IDΛ͚ͭͳ͍ͱNotification
  9. • Notification͸Language Server->։ൃπʔϧ΋ൃߦ͞ΕΔ͜ͱ͔Β઀ଓ͸։ ͖ͬͺͳ͠ʹ͢Δ 9 LSP Specification -BOHVBHF 4FSWFS Content-Length:

    ...\r\n \r\n { "jsonrpc": "2.0", "id": 1, "method": “initialize", "params": { ... } } 3FRVFTU /PUJpDBUJPO %FWFMPQNFOU UPPM Content-Length: ...\r\n \r\n { "jsonrpc": "2.0", "method": "textDocument/publishDiagnostics", "params": { ... } } 3FTQPOTF
  10. • ݴޠఏڙଆ͸Language ServerΛఏڙ͓ͯ͘͜͠ͱͰʢLSPΛαϙʔτ͢Δʣ ։ൃπʔϧ΁ͷରԠ͕׬ྃ͢Δ • ։ൃπʔϧଆ͸LSPΛαϙʔτ͢Δ͜ͱͰʢLanguage Server͕ఏڙ͞Ε͍ͯ Δʣ৽͍͠ݴޠ޲͚ͷ”language features”ରԠ͕ෆཁʹͳΔ •

    ར༻ऀଆ͸Yet Another Language Server͕ఏڙ͞Εͯ΋։ൃ؀ڥͷ࠶੔උ͕ ָʹͳΔʢ͜͜ͷݒ೦఺͸ޙड़ʣ • αʔόʔϓϩηεͰ͋Δ͜ͱ͔ΒΩϟογϡઓུͷ࠾༻͠΍͘͢։ൃޮ཰޲্ ΁ͷ৽͍͠Ξϓϩʔν͕ࠓޙग़ͯ͘Δ͔΋ 10 LSPΛ࠾༻͢ΔϝϦοτ
  11. 2. Go language server

  12. • https://github.com/saibing/bingo • Bingo is a Go language server that

    speaks Language Server Protocol. • https://github.com/sourcegraph/go-langserver • Go language server to add Go support to editors and other tools that use the Language Server Protocol (LSP) • https://github.com/golang/tools/tree/master/cmd/gopls • The golsp command is an LSP server for Go. 12 Go language server
  13. IUUQTHJUIVCDPNHPMBOHUPPMTDPNNJUCGEBCGFDDBGDDGF 13 Go Please!! • Go͕ఏڙ͢ΔLanguage Server͸golspͰ͸ͳ͘gopls!!

  14. 14 4FSWFS $MJFOU 4FSWFS $MJFOU DBODFM3FRVFTU P P UFYU%PDVNFOUQVCMJTI%JBHOPTUJDT P

    JOJUJBMJ[F P UFYU%PDVNFOUDPNQMFUJPO P JOJUJBMJ[FE * DPNQMFUJPO*UFNSFTPMWF Y TIVUEPXO P UFYU%PDVNFOUIPWFS P FYJU P UFYU%PDVNFOUTJHOBUVSF)FMQ P XJOEPXTIPX.FTTBHF P UFYU%PDVNFOUEFDMBSBUJPO  XJOEPXTIPX.FTTBHF3FRVFTU P UFYU%PDVNFOUEFpOJUJPO P XJOEPXMPH.FTTBHF P UFYU%PDVNFOUUZQF%FpOJUJPO P UFMFNFUSZFWFOU P UFYU%PDVNFOUJNQMFNFOUBUJPO Y DMJFOUSFHJTUFS$BQBCJMJUZ P UFYU%PDVNFOUSFGFSFODFT Y DMJFOUVOSFHJTUFS$BQBCJMJUZ P UFYU%PDVNFOUEPDVNFOU)JHIMJHIU Y XPSLTQBDFXPSLTQBDF'PMEFST P UFYU%PDVNFOUEPDVNFOU4ZNCPM Y XPSLTQBDFEJE$IBOHF8PSLTQBDF'PMEFST Y UFYU%PDVNFOUDPEF"DUJPO P XPSLTQBDFEJE$IBOHF$POpHVSBUJPO Y UFYU%PDVNFOUDPEF-FOT * XPSLTQBDFDPOpHVSBUJPO P DPEF-FOTSFTPMWF Y XPSLTQBDFEJE$IBOHF8BUDIFE'JMFT Y UFYU%PDVNFOUEPDVNFOU-JOL * XPSLTQBDFTZNCPM Y EPDVNFOU-JOLSFTPMWF Y XPSLTQBDFFYFDVUF$PNNBOE Y UFYU%PDVNFOUEPDVNFOU$PMPS Y XPSLTQBDFBQQMZ&EJU P UFYU%PDVNFOUDPMPS1SFTFOUBUJPO Y UFYU%PDVNFOUEJE0QFO P UFYU%PDVNFOUGPSNBUUJOH P UFYU%PDVNFOUEJE$IBOHF P UFYU%PDVNFOUSBOHF'PSNBUUJOH P UFYU%PDVNFOUXJMM4BWF Y UFYU%PDVNFOUPO5ZQF'PSNBUUJOH Y UFYU%PDVNFOUXJMM4BWF8BJU6OUJM Y UFYU%PDVNFOUSFOBNF Y UFYU%PDVNFOUEJE4BWF * UFYU%PDVNFOUQSFQBSF3FOBNF  UFYU%PDVNFOUEJE$MPTF P UFYU%PDVNFOUGPMEJOH3BOHF Y SFMFBTFCSBODIHP࣌఺ͰͷHPQMTͷ-41αϙʔτঢ়گPJNQMFNFOUFE YOPUJNQMFNFOUFE *JHOPSF
  15. ৮ͬͯཧղ͢Δgopls

  16. ४උ (Language Server) 16 $ go get -u golang.org/x/tools/... $

    cd $GOPATH/src/golang.org/x/tools # Checkout origin/release-branch.go1.12 $ git checkout 657755b003d86beb7f1e59808a410123ce89b15b $ go run cmd/gopls/main.go server -port 8889 • 2/14࣌఺ͷorigin/master HEADͩͱίέͨͷͰ҆ఆͯͦ͠͏ͳόʔδϣϯ΁ • “server”͸origin/master HEADͩͱ”serve” • σϑΥϧτͰඪ४ೖग़ྗΛར༻ɽ”port”ΦϓγϣϯͰωοτϫʔΫܦ༝΁
  17. 17 ४උ (Development tool) // Dial to Language Server c,

    _ := net.Dial("tcp", ":8889") // Start client conn := jsonrpc2.NewConn(context.Background(, jsonrpc2.NewBufferedStream(c, jsonrpc2.VSCodeObjectCodec{}), &clientHandler{}) done := make(chan bool) go func() { <-conn.DisconnectNotify() done <- true }() • github.com/sourcegraph/jsonrpc2 Λར༻ • LS͔ΒͷNotificationΛ଴ͪड͚ΔͨΊͷhandlerΛొ࿥͓ͯ͘͠
  18. • “The initialize request is sent as the first request

    from the client to the server. “ • ࠷ॳʹΫϥΠΞϯτ͔ΒinitializeϦΫΤετ͕ඞཁ • ύϥϝλʹ͸root directoryͳͲΛࢦఆ͢Δ • ݴޠαʔόʔଆ͕ظ଴͢Δಈ࡞ͳͲΛฦ٫͢Δ • υΩϡϝϯτͷಉظͷ௨஌͕ཉ͍͠ͳͲ 18 ͓࡞๏
  19. 19 Initialize request (c<->s) // Initialize fullpath, _ := filepath.Abs(".")

    uri, _ := pathToURI(".") // file:///fullpath/to/content docUri := DocumentURI(uri) params := InitializeParams{ RootPath: &fullpath, RootURI: &docUri, } var got interface{} err = conn.Call(ctx, "initialize", params, &got) if err != nil { panic(err) } • RootPath͸deprecated && RootURI͕༏ઌύϥϝλͱͷ͜ͱͳͷͰෆཁ͔΋ ͠Εͳ͍
  20. Initialize (log) 20 [Trace - 1:30:04 AM] Sending request 'initialize

    - (0)'. Params: {“processId":null,"rootPath":"/Users/miyakey/ src/github.com/monochromegane/lsc","rootUri":"file:/// Users/miyakey/src/github.com/monochromegane/ lsc","initializationOptions":null,"capabilities": {"workspace":{}},"trace":""} [Trace - 1:30:04 AM] Received response 'initialize - (0)' in 1ms. Params: {"capabilities":{"textDocumentSync": {"openClose":true,"change":1,"save": {}},"hoverProvider":true,"completionProvider": {"triggerCharacters":["."]},"signatureHelpProvider": {"triggerCharacters": ["(",","]},"definitionProvider":true,"typeDefinitionPro vider":true,"codeActionProvider":true,"codeLensProvider ": {},"documentFormattingProvider":true,"documentRangeForm attingProvider":true,"documentOnTypeFormattingProvider" :{"firstTriggerCharacter":""},"documentLinkProvider": {},"executeCommandProvider": {"commands":null},"workspace":{"workspaceFolders":{}}}} { "capabilities": { "codeActionProvider": true, "codeLensProvider": {}, "completionProvider": { "triggerCharacters": [ "." ] }, "definitionProvider": true, "documentFormattingProvider": true, "documentLinkProvider": {}, "documentOnTypeFormattingProvider": { "firstTriggerCharacter": "" }, "documentRangeFormattingProvider": true, "executeCommandProvider": { "commands": null }, "hoverProvider": true, "signatureHelpProvider": { "triggerCharacters": ["(", ","] }, "textDocumentSync": { "change": 1, "openClose": true, "save": {} }, "typeDefinitionProvider": true, "workspace": { "workspaceFolders": {} } } } -PH 3FTQPOTF
  21. textDocument/publishDiagnostics notification (s->c) 21 func (h *clientHandler) Handle(ctx context.Context, conn

    *jsonrpc2.Conn, req *jsonrpc2.Request) { if !req.Notif { return } switch req.Method { case "textDocument/publishDiagnostics": var params PublishDiagnosticsParams b, _ := req.Params.MarshalJSON() json.Unmarshal(b, &params) (snip) } } • ΫϥΠΞϯταʔόʔଆͰϋϯυϥͰର৅ͷॲཧʹԠ࣮ͨ͡૷Λߦ͏
  22. textDocument/publishDiagnostics (log) 22 [Trace - 1:30:06 AM] Received notification 'textDocument/publishDiagnostics'.

    Params: {"uri":"file:///Users/miyakey/src/github.com/ monochromegane/lsc/cmd/lsc/main.go","diagnostics": [{"range":{"start":{"line":4,"character":5},"end": {"line":4,"character":9}},"severity": 1,"source":"LSP","message":"\tother declaration of main"}]} [Trace - 1:30:06 AM] Received notification 'textDocument/publishDiagnostics'. Params: {"uri":"file:///Users/miyakey/src/github.com/ monochromegane/lsc/cmd/lsc/main.g_","diagnostics": [{"range":{"start":{"line":2,"character":5},"end": {"line":2,"character":9}},"severity": 1,"source":"LSP","message":"main redeclared in this block"},{"range":{"start":{"line":3,"character": 0},"end":{"line":3,"character":7}},"severity": 1,"source":"LSP","message":"undeclared name: lsc"}]} -PH /PUJpDBUJPO { "diagnostics": [ { "message": "\tother declaration of main", "range": { "end": { "character": 9, "line": 4 }, "start": { "character": 5, "line": 4 } }, "severity": 1, "source": "LSP" } ], "uri": "file:///Users/miyakey/src/ github.com/monochromegane/lsc/cmd/ lsc/main.go" }
  23. textDocument/didOpen notification (c->s) 23 • ௨஌ͷ৔߹͸”Notify”Λ࢖͏ • didChangeͷ৔߹ʹversionΛΠϯΫϦϝϯτ͍ͯ͘͠ͱͷ͜ͱɽ // DidOpen

    uri, _ = pathToURI("cmd/lsc/main.g_") docUri = DocumentURI(uri) data, _ := ioutil.ReadFile("cmd/lsc/main.g_") openParams := DidOpenTextDocumentParams{ TextDocument: TextDocumentItem{ URI: docUri, LanguageID: "go", Version: 1.0, Text: string(data), }, } err = conn.Notify(ctx, "textDocument/didOpen", openParams) if err != nil { panic(err) }
  24. textDocument/didOpen (log) 24 [Trace - 1:30:04 AM] Sending notification 'textDocument/didOpen'.

    Params: {"textDocument":{"uri":"file:///Users/miyakey/ src/github.com/monochromegane/lsc/cmd/lsc/ main.g_","languageId":"go","version":1,"text":"package main\n\nfunc main() {\nlsc.Run()\n}\n"}} -PH
  25. 25 textDocument/formatting request (c<->s) • gopls͸gofmt૬౰ͷ͜ͱ͔͠͠ͳ͍ (bingoͩͱ͜͜Ͱ࣮ߦ͢Δ) • ϑΥʔϚοτޙͷશςΩετͰ͸ͳ͘ɼमਖ਼ՕॴͷΈΛฦ͢ϓϩτίϧʹͳͬ ͍ͯΔ

    // Formatting formatParams := DocumentFormattingParams{ TextDocument: TextDocumentIdentifier{ URI: docUri, }, } err = conn.Call(ctx, "textDocument/formatting", formatParams, &got) if err != nil { panic(err) }
  26. textDocument/formatting (log) 26 [Trace - 1:30:09 AM] Sending request 'textDocument/

    formatting - (1)'. Params: {"textDocument":{"uri":"file:///Users/miyakey/ src/github.com/monochromegane/lsc/cmd/lsc/ main.g_"},"options":{"tabSize":0,"insertSpaces":false}} [Trace - 1:30:09 AM] Received response 'textDocument/ formatting - (1)' in 1ms. Params: [{"range":{"start":{"line":3,"character": 0},"end":{"line":4,"character":0}},"newText":""}, {"range":{"start":{"line":4,"character":0},"end": {"line":4,"character":0}},"newText":"\tlsc.Run()\n"}] -PH 3FTQPOTF [ { "newText": "\tlsc.Run()\n", "range": { "end": { "character": 0, "line": 4 }, "start": { "character": 0, "line": 4 } } } ]
  27. 27 textDocument/codeAction request (c<->s) • goplsͷࣗಈimport͸͜ͷΞΫγϣϯʹϚοϐϯά͞Ε͍ͯΔ • LSPతʹ͸codeActionContext.onlyΛ࢖ͬͯΞΫγϣϯࢦఆ͕Մೳ͕ͩɼݱ࣌ ఺Ͱ͸gopls͸ࣗಈimport(source.organizeImports)ΞΫγϣϯͷΈ࣮ߦ͢Δ ࣮૷

    // CodeAction actParams := CodeActionParams{ TextDocument: TextDocumentIdentifier{ URI: docUri, }, } err = conn.Call(ctx, "textDocument/codeAction", actParams, &got) if err != nil { panic(err) }
  28. textDocument/codeAction (log) 28 [Trace - 1:30:09 AM] Sending request 'textDocument/

    codeAction - (2)'. Params: {"textDocument":{"uri":"file:///Users/miyakey/ src/github.com/monochromegane/lsc/cmd/lsc/ main.g_"},"range":{"start":{"line":0,"character": 0},"end":{"line":0,"character":0}}} [Trace - 1:30:09 AM] Received response 'textDocument/ codeAction - (2)' in 1ms. Params: [{"title":"Organize Imports","kind":"source.organizeImports","edit": {"changes":{"file:///Users/miyakey/src/github.com/ monochromegane/lsc/cmd/lsc/main.g_":[{"range":{"start": {"line":2,"character":0},"end":{"line":2,"character": 0}},"newText":"import \"github.com/monochromegane/ lsc\"\n"},{"range":{"start":{"line":2,"character": 0},"end":{"line":2,"character":0}},"newText":"\n"}, {"range":{"start":{"line":3,"character":0},"end": {"line":4,"character":0}},"newText":""},{"range": {"start":{"line":4,"character":0},"end":{"line": 4,"character":0}},"newText":"\tlsc.Run() \n"}]}},"command":{"title":"","command":""}}] -PH 3FTQPOTF [ { "command": { "command": "", "title": "" }, "edit": { "changes": { "file:///Users/miyakey/src/github.com/ monochromegane/lsc/cmd/lsc/main.g_": [ { "newText": "import \"github.com/ monochromegane/lsc\"\n", "range": { "end": { "character": 0, "line": 2 }, "start": { "character": 0, "line": 2 } } }, ] } }, "kind": "source.organizeImports", "title": "Organize Imports" } ]
  29. 29 textDocument/definition request (c<->s) • definitionͱtypeDefinition͕͋Δ͕goplsͰ͸ಉͬ͡Ά͍ // Definition uri, _

    = pathToURI("cmd/lsc/main.go") docUri = DocumentURI(uri) posParams := TextDocumentPositionParams{ TextDocument: TextDocumentIdentifier{ URI: docUri, }, Position: Position{ Line: 5, Character: 5, }, } err = conn.Call(ctx, "textDocument/definition", posParams, &got) if err != nil { panic(err) }
  30. textDocument/definition (log) 30 [Trace - 1:30:09 AM] Sending request 'textDocument/

    definition - (3)'. Params: {"textDocument":{"uri":"file:///Users/miyakey/ src/github.com/monochromegane/lsc/cmd/lsc/ main.go"},"position":{"line":5,"character":5}} [Trace - 1:30:09 AM] Received response 'textDocument/ definition - (3)' in 0ms. Params: [{"uri":"file:///Users/miyakey/src/github.com/ monochromegane/lsc/lsc.go","range":{"start":{"line": 17,"character":5},"end":{"line":17,"character":8}}}] -PH 3FTQPOTF [ { "range": { "end": { "character": 8, "line": 17 }, "start": { "character": 5, "line": 17 } }, "uri": "file:///Users/ miyakey/src/github.com/ monochromegane/lsc/lsc.go" } ]
  31. 31 exit notification (c->s) • exitΞΫγϣϯΛݺͿͱgopls͕ऴྃ • `<-conn.DisconnectNotify()`ͷϒϩοΫ͕ղআ͞ΕΔ // Exit

    var dummy interface{} err = conn.Notify(ctx, "exit", dummy) if err != nil { panic(err) } <-done
  32. exit (log) 32 [Trace - 1:30:09 AM] Sending notification 'exit'.

    Params: {} -PH
  33. • Goͷ“language features”͸ैདྷ͔Βఏڙ͞Ε͓ͯΓgopls͸ͦΕΒΛϥοϓ ͢Δ͜ͱͰجຊతͳػೳ࣮૷͸໰୊ͳ͘࢖͑ͦ͏ɽ • ҰํͰ։ൃπʔϧଆʹٻΊΒΕΔϫʔΫεϖʔε΍υΩϡϝϯτಉظɼγϯλο ΫεϋΠϥΠτͳͲ͸͜Ε͔Βɽ • ݴޠಠࣗͷ”language features”͕CodeActionʹԡ͠ࠐ·Ε͍͖ͯͦ͏ͳͷͰ

    ͜ͷ͋ͨΓ͸LSPͱLSͷࠓޙͷཱͪճΓΛݟ͍͖͍ͯͨɽ • LS͝ͱͷڍಈͷҧ͍΋ىಈΦϓγϣϯʹԡ͠ࠐ·Ε͍ͯ͘ͷͰɼࠩ͠ସ͑Մೳ ʹ͢ΔͨΊʹ΋͏Ұ೧Γඞཁͦ͏ 33 ॴײ
  34. 3. Conclusion

  35. • Language Server ProtocolͷجຊΛֶΜͩ • Goͷఏڙ͢ΔLanguage Server - goplsͷݱঢ়Λ֓؍ͨ͠ •

    goplsΛGo͔Β৮ͬͯݟͨ • LSPʹΑͬͯ։ൃ؀ڥվળ΁ͷߩݙ͕ΑΓଟ͘ͷ؀ڥͰ࢖ͬͯ΋Β͑ΔΑ͏ʹ ͳΔͨΊߩݙωλΛߟ͍͖͍͑ͯͨ • Go Please ͔Θ͍͍ • Go 35 Conclusion
  36. None