Slide 1

Slide 1 text

tools/cmd/gopls ࡾ୐༔հ / Pepabo R&D Institute, GMO Pepabo, Inc. 2019.02.15 Go 1.12 Release Party Go language serverΛཧղ͢Δ

Slide 2

Slide 2 text

ϓϦϯγύϧΤϯδχΞ ࡾ୐༔հ!NPOPDISPNFHBOF (.0ϖύϘגࣜձࣾϖύϘݚڀॴ IUUQTCMPHNPOPDISPNFHBOFDPN

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

1. Language Server Protocol

Slide 5

Slide 5 text

l5IF-BOHVBHF4FSWFS1SPUPDPM -41 EFpOFTUIFQSPUPDPMVTFECFUXFFOBO FEJUPSPS*%&BOEBMBOHVBHFTFSWFSUIBUQSPWJEFTMBOHVBHFGFBUVSFTMJLFBVUP DPNQMFUF HPUPEFpOJUJPO pOEBMMSFGFSFODFTFUDl 5 Language Server Protocol <-BOHVBHF4FSWFS1SPUPDPM> IUUQTNJDSPTPGUHJUIVCJPMBOHVBHFTFSWFSQSPUPDPM w -BOHVBHF4FSWFS1SPUPDPM͸Τσ Ολ΍*%&ͱ-BOHVBHF4FSWFSͷؒͰ࢖ΘΕΔ ϓϩτίϧ w -BOHVBHF4FSWFS͸ΦʔτίϯϓϦʔτ΍ఆٛݩ΁ͷҠಈɼࢀরઌΛݟ͚ͭΔ ͱ͍ͬͨzMBOVHBHFGFBUVSFTzΛఏڙ͢Δ

Slide 6

Slide 6 text

• “language features”͸ݴޠ͝ͱʹڞ௨෦෼͕ଟ͍͕ɼݱࡏ͸ɼ։ൃπʔϧ͝ ͱʹɼͦͷ࣮ߦͱ݁Ռ൓өͷ࣮૷͕ඞཁ (ݴޠ਺ x ։ൃπʔϧ਺) • ͦ͜ͰLanguage Server Protocol͸ڞ௨ੑʹண໨͢Δɽ • ݴޠ͝ͱͷLanguage ServerͰ”language features”Λ࣮ߦ͠ɼ։ൃπʔϧ͸ ͦͷ݁Ռ൓өͷΈΛߦ͏ํࣜ (ݴޠ਺ + ։ൃπʔϧ਺) • ࣮ࡍʹ͸Language Server΍։ൃπʔϧͷݺͼग़͠ػೳ͸ෳ਺ଘࡏ͢ΔͨΊཧ࿦஋ͱͯ͠ɽ 6 ݴޠͱ։ൃπʔϧͷ૊Έ߹ΘͤരൃΛճආ͢Δ

Slide 7

Slide 7 text

• ։ൃπʔϧ͔Βͷ”language features”ͷ໰͍߹Θͤʹରͯ͠ɼॲཧΛ࣮ߦ ͠ɼ݁Ռ൓өʹඞཁͳ৘ใΛฦ͢αʔόʔϓϩηε • ௨৴Λܦ༝͢Δ͜ͱʹΑΓ։ൃπʔϧͱ”language features”Λ෼཭͢Δ • ͲͷॲཧΛ࣮ߦ͢Δ͔ɼ࣮ͦͯ͠ߦ͢ΔͨΊʹඞཁͳύϥϝλͱ݁Ռ൓өʹඞ ཁͳύϥϝλͷ࢓༷ΛఆΊͨ΋ͷ͕Language Server Protocol (LSP) 7 Language Server

Slide 8

Slide 8 text

• ݱࡏ 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

Slide 9

Slide 9 text

• 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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

2. Go language server

Slide 12

Slide 12 text

• 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

Slide 13

Slide 13 text

IUUQTHJUIVCDPNHPMBOHUPPMTDPNNJUCGEBCGFDDBGDDGF 13 Go Please!! • Go͕ఏڙ͢ΔLanguage Server͸golspͰ͸ͳ͘gopls!!

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

৮ͬͯཧղ͢Δgopls

Slide 16

Slide 16 text

४උ (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”ΦϓγϣϯͰωοτϫʔΫܦ༝΁

Slide 17

Slide 17 text

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Λొ࿥͓ͯ͘͠

Slide 18

Slide 18 text

• “The initialize request is sent as the first request from the client to the server. “ • ࠷ॳʹΫϥΠΞϯτ͔ΒinitializeϦΫΤετ͕ඞཁ • ύϥϝλʹ͸root directoryͳͲΛࢦఆ͢Δ • ݴޠαʔόʔଆ͕ظ଴͢Δಈ࡞ͳͲΛฦ٫͢Δ • υΩϡϝϯτͷಉظͷ௨஌͕ཉ͍͠ͳͲ 18 ͓࡞๏

Slide 19

Slide 19 text

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͕༏ઌύϥϝλͱͷ͜ͱͳͷͰෆཁ͔΋ ͠Εͳ͍

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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) } } • ΫϥΠΞϯταʔόʔଆͰϋϯυϥͰର৅ͷॲཧʹԠ࣮ͨ͡૷Λߦ͏

Slide 22

Slide 22 text

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" }

Slide 23

Slide 23 text

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) }

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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) }

Slide 26

Slide 26 text

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 } } } ]

Slide 27

Slide 27 text

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) }

Slide 28

Slide 28 text

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" } ]

Slide 29

Slide 29 text

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) }

Slide 30

Slide 30 text

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" } ]

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

exit (log) 32 [Trace - 1:30:09 AM] Sending notification 'exit'. Params: {} -PH

Slide 33

Slide 33 text

• Goͷ“language features”͸ैདྷ͔Βఏڙ͞Ε͓ͯΓgopls͸ͦΕΒΛϥοϓ ͢Δ͜ͱͰجຊతͳػೳ࣮૷͸໰୊ͳ͘࢖͑ͦ͏ɽ • ҰํͰ։ൃπʔϧଆʹٻΊΒΕΔϫʔΫεϖʔε΍υΩϡϝϯτಉظɼγϯλο ΫεϋΠϥΠτͳͲ͸͜Ε͔Βɽ • ݴޠಠࣗͷ”language features”͕CodeActionʹԡ͠ࠐ·Ε͍͖ͯͦ͏ͳͷͰ ͜ͷ͋ͨΓ͸LSPͱLSͷࠓޙͷཱͪճΓΛݟ͍͖͍ͯͨɽ • LS͝ͱͷڍಈͷҧ͍΋ىಈΦϓγϣϯʹԡ͠ࠐ·Ε͍ͯ͘ͷͰɼࠩ͠ସ͑Մೳ ʹ͢ΔͨΊʹ΋͏Ұ೧Γඞཁͦ͏ 33 ॴײ

Slide 34

Slide 34 text

3. Conclusion

Slide 35

Slide 35 text

• Language Server ProtocolͷجຊΛֶΜͩ • Goͷఏڙ͢ΔLanguage Server - goplsͷݱঢ়Λ֓؍ͨ͠ • goplsΛGo͔Β৮ͬͯݟͨ • LSPʹΑͬͯ։ൃ؀ڥվળ΁ͷߩݙ͕ΑΓଟ͘ͷ؀ڥͰ࢖ͬͯ΋Β͑ΔΑ͏ʹ ͳΔͨΊߩݙωλΛߟ͍͖͍͑ͯͨ • Go Please ͔Θ͍͍ • Go 35 Conclusion

Slide 36

Slide 36 text

No content