Share
Embed
Go language serverを理解する / Understanding Go language server
Go 1.12 Release Party in Fukuoka
https://fukuokago.connpass.com/event/118377/
monochromegane
February 15, 2019
Transcript
36 slides
-
tools/cmd/gopls ࡾ༔հ / Pepabo R&D Institute, GMO Pepabo, Inc. 2019.02.15
Go 1.12 Release Party Go language serverΛཧղ͢Δ -
l5IF-BOHVBHF4FSWFS1SPUPDPM -41 EFpOFTUIFQSPUPDPMVTFECFUXFFOBO FEJUPSPS*%&BOEBMBOHVBHFTFSWFSUIBUQSPWJEFTMBOHVBHFGFBUVSFTMJLFBVUP DPNQMFUF HPUPEFpOJUJPO pOEBMMSFGFSFODFTFUDl 5 Language Server
Protocol <-BOHVBHF4FSWFS1SPUPDPM> IUUQTNJDSPTPGUHJUIVCJPMBOHVBHFTFSWFSQSPUPDPM w -BOHVBHF4FSWFS1SPUPDPMΤσ Ολ*%&ͱ-BOHVBHF4FSWFSͷؒͰΘΕΔ ϓϩτίϧ w -BOHVBHF4FSWFSΦʔτίϯϓϦʔτఆٛݩͷҠಈɼࢀরઌΛݟ͚ͭΔ ͱ͍ͬͨzMBOVHBHFGFBUVSFTzΛఏڙ͢Δ -
• “language features”ݴޠ͝ͱʹڞ௨෦͕ଟ͍͕ɼݱࡏɼ։ൃπʔϧ͝ ͱʹɼͦͷ࣮ߦͱ݁Ռөͷ࣮͕ඞཁ (ݴޠ x ։ൃπʔϧ) • ͦ͜ͰLanguage Server
Protocolڞ௨ੑʹண͢Δɽ • ݴޠ͝ͱͷLanguage ServerͰ”language features”Λ࣮ߦ͠ɼ։ൃπʔϧ ͦͷ݁ՌөͷΈΛߦ͏ํࣜ (ݴޠ + ։ൃπʔϧ) • ࣮ࡍʹLanguage Server։ൃπʔϧͷݺͼग़͠ػೳෳଘࡏ͢ΔͨΊཧͱͯ͠ɽ 6 ݴޠͱ։ൃπʔϧͷΈ߹ΘͤരൃΛճආ͢Δ -
• ݱࡏ 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 -
• NotificationLanguage 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 -
• ݴޠఏڙଆLanguage ServerΛఏڙ͓ͯ͘͜͠ͱͰʢLSPΛαϙʔτ͢Δʣ ։ൃπʔϧͷରԠ͕ྃ͢Δ • ։ൃπʔϧଆLSPΛαϙʔτ͢Δ͜ͱͰʢLanguage Server͕ఏڙ͞Ε͍ͯ Δʣ৽͍͠ݴޠ͚ͷ”language features”ରԠ͕ෆཁʹͳΔ •
ར༻ऀଆYet Another Language Server͕ఏڙ͞Εͯ։ൃڥͷ࠶උ͕ ָʹͳΔʢ͜͜ͷݒ೦ޙड़ʣ • αʔόʔϓϩηεͰ͋Δ͜ͱ͔ΒΩϟογϡઓུͷ࠾༻͘͢͠։ൃޮ্ ͷ৽͍͠Ξϓϩʔν͕ࠓޙग़ͯ͘Δ͔ 10 LSPΛ࠾༻͢ΔϝϦοτ -
• 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 -
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 -
४උ (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 ४උ (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Λొ͓ͯ͘͠ -
• “The initialize request is sent as the first request
from the client to the server. “ • ࠷ॳʹΫϥΠΞϯτ͔ΒinitializeϦΫΤετ͕ඞཁ • ύϥϝλʹroot directoryͳͲΛࢦఆ͢Δ • ݴޠαʔόʔଆ͕ظ͢Δಈ࡞ͳͲΛฦ٫͢Δ • υΩϡϝϯτͷಉظͷ௨͕ཉ͍͠ͳͲ 18 ͓࡞๏ -
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) } • RootPathdeprecated && RootURI͕༏ઌύϥϝλͱͷ͜ͱͳͷͰෆཁ͔ ͠Εͳ͍ -
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 -
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, ¶ms) (snip) } } • ΫϥΠΞϯταʔόʔଆͰϋϯυϥͰରͷॲཧʹԠ࣮ͨ͡Λߦ͏ -
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" } -
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) } -
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 textDocument/formatting request (c<->s) • goplsgofmt૬ͷ͜ͱ͔͠͠ͳ͍ (bingoͩͱ͜͜Ͱ࣮ߦ͢Δ) • ϑΥʔϚοτޙͷશςΩετͰͳ͘ɼमਖ਼ՕॴͷΈΛฦ͢ϓϩτίϧʹͳͬ ͍ͯΔ
// Formatting formatParams := DocumentFormattingParams{ TextDocument: TextDocumentIdentifier{ URI: docUri, }, } err = conn.Call(ctx, "textDocument/formatting", formatParams, &got) if err != nil { panic(err) } -
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 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) } -
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 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) } -
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 exit notification (c->s) • exitΞΫγϣϯΛݺͿͱgopls͕ऴྃ • `<-conn.DisconnectNotify()`ͷϒϩοΫ͕ղআ͞ΕΔ // Exit
var dummy interface{} err = conn.Notify(ctx, "exit", dummy) if err != nil { panic(err) } <-done -
• Goͷ“language features”ैདྷ͔Βఏڙ͞Ε͓ͯΓgoplsͦΕΒΛϥοϓ ͢Δ͜ͱͰجຊతͳػೳ࣮ͳ͑ͦ͘͏ɽ • ҰํͰ։ൃπʔϧଆʹٻΊΒΕΔϫʔΫεϖʔευΩϡϝϯτಉظɼγϯλο ΫεϋΠϥΠτͳͲ͜Ε͔Βɽ • ݴޠಠࣗͷ”language features”͕CodeActionʹԡ͠ࠐ·Ε͍͖ͯͦ͏ͳͷͰ
͜ͷ͋ͨΓLSPͱLSͷࠓޙͷཱͪճΓΛݟ͍͖͍ͯͨɽ • LS͝ͱͷڍಈͷҧ͍ىಈΦϓγϣϯʹԡ͠ࠐ·Ε͍ͯ͘ͷͰɼࠩ͠ସ͑Մೳ ʹ͢ΔͨΊʹ͏Ұ೧Γඞཁͦ͏ 33 ॴײ -
• Language Server ProtocolͷجຊΛֶΜͩ • Goͷఏڙ͢ΔLanguage Server - goplsͷݱঢ়Λ֓؍ͨ͠ •
goplsΛGo͔Β৮ͬͯݟͨ • LSPʹΑͬͯ։ൃڥվળͷߩݙ͕ΑΓଟ͘ͷڥͰͬͯΒ͑ΔΑ͏ʹ ͳΔͨΊߩݙωλΛߟ͍͖͍͑ͯͨ • Go Please ͔Θ͍͍ • Go 35 Conclusion