Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 02f4198

Browse files
authored
Support git:// in the rootUri (#373)
1 parent a94fb11 commit 02f4198

File tree

6 files changed

+117
-125
lines changed

6 files changed

+117
-125
lines changed

buildserver/bench_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func BenchmarkIntegration(b *testing.B) {
126126
b.ResetTimer()
127127
for i := 0; i < b.N; i++ {
128128
b.StopTimer()
129-
c, done := connectionToNewBuildServer(string(rootURI), b)
129+
c, done := connectionToNewBuildServer(string(rootURI), b, true)
130130
b.StartTimer()
131131

132132
if err := c.Call(ctx, "initialize", lspext.ClientProxyInitializeParams{
@@ -161,7 +161,7 @@ func BenchmarkIntegration(b *testing.B) {
161161

162162
for i := 0; i < b.N; i++ {
163163
b.StopTimer()
164-
c, done := connectionToNewBuildServer(string(rootURI), b)
164+
c, done := connectionToNewBuildServer(string(rootURI), b, true)
165165
b.StartTimer()
166166

167167
if err := c.Call(ctx, "initialize", lspext.ClientProxyInitializeParams{
@@ -270,10 +270,10 @@ func BenchmarkIntegrationShared(b *testing.B) {
270270
for i := 0; i < b.N; i++ {
271271
b.StopTimer()
272272

273-
c, done1 := connectionToNewBuildServer(test.oldRootURI, b)
273+
c, done1 := connectionToNewBuildServer(test.oldRootURI, b, true)
274274
defer done1() // TODO ensure we close between each loop
275275
do(c, test.oldRootURI)
276-
c, done2 := connectionToNewBuildServer(test.rootURI, b)
276+
c, done2 := connectionToNewBuildServer(test.rootURI, b, true)
277277
defer done2()
278278

279279
b.StartTimer()

buildserver/build_server.go

+76-16
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,16 @@ type BuildHandler struct {
7777
findPkg map[findPkgKey]*findPkgValue
7878
langserver.HandlerCommon
7979
*langserver.HandlerShared
80-
init *lspext.InitializeParams // set by "initialize" request
81-
rootImportPath string // root import path of the workspace (e.g., "github.com/foo/bar")
82-
cachingClient *http.Client // http.Client with a cache backed by an in-memory LRU cache
83-
closers []io.Closer // values to dispose of when Close() is called
80+
init *lspext.InitializeParams // set by "initialize" request
81+
originalRootURI *url.URL // derived from InitializeParams.OriginalRootURI
82+
rootImportPath string // root import path of the workspace (e.g., "github.com/foo/bar")
83+
cachingClient *http.Client // http.Client with a cache backed by an in-memory LRU cache
84+
closers []io.Closer // values to dispose of when Close() is called
85+
// Whether URIs in the same workspace begin with:
86+
// - `file://` (true)
87+
// - `git://` (false)
88+
// This affects URI rewriting between the client and server.
89+
clientUsesFileSchemeWithinWorkspace bool
8490
}
8591

8692
// reset clears all internal state in h.
@@ -96,6 +102,11 @@ func (h *BuildHandler) reset(init *lspext.InitializeParams, conn *jsonrpc2.Conn,
96102
return err
97103
}
98104
h.init = init
105+
var err error
106+
h.originalRootURI, err = url.Parse(string(h.init.OriginalRootURI))
107+
if h.init.OriginalRootURI == "" || err != nil {
108+
h.originalRootURI = nil
109+
}
99110
// 100 MiB cache, no age-based eviction
100111
h.cachingClient = &http.Client{Transport: httpcache.NewTransport(lrucache.New(100*1024*1024, 0))}
101112
h.depURLMutex = newKeyMutex()
@@ -178,6 +189,23 @@ func (h *BuildHandler) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jso
178189
return nil, err
179190
}
180191

192+
// In the `rootUri`, clients can send either:
193+
//
194+
// - A `file://` URI, which indicates that:
195+
// - Same-workspace file paths will also be `file://` URIs
196+
// - Out-of-workspace file paths will be `git://` URIs
197+
// - `originalRootUri` is present
198+
// - A `git://` URI, which indicates that:
199+
// - Both same-workspace and out-of-workspace file paths will be non-`file://` URIs
200+
// - `originalRootUri` is absent and `rootUri` contains the original root URI
201+
if strings.HasPrefix(string(params.RootURI), "file://") {
202+
h.clientUsesFileSchemeWithinWorkspace = true
203+
} else {
204+
params.OriginalRootURI = params.RootURI
205+
params.RootURI = "file:///"
206+
h.clientUsesFileSchemeWithinWorkspace = false
207+
}
208+
181209
if Debug {
182210
var b []byte
183211
if req.Params != nil {
@@ -327,10 +355,26 @@ func (h *BuildHandler) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jso
327355
}
328356
}
329357
rewriteURIFromClient := func(uri lsp.DocumentURI) lsp.DocumentURI {
330-
if !strings.HasPrefix(string(uri), "file:///") {
331-
return uri // refers to a resource outside of this workspace
358+
var path string
359+
if h.clientUsesFileSchemeWithinWorkspace {
360+
if !strings.HasPrefix(string(uri), "file:///") {
361+
return uri // refers to a resource outside of this workspace
362+
}
363+
path = strings.TrimPrefix(string(uri), "file://")
364+
} else {
365+
currentURL, err := url.Parse(string(uri))
366+
if err != nil {
367+
return uri
368+
}
369+
if h.originalRootURI == nil {
370+
return uri
371+
}
372+
path = currentURL.Fragment
373+
currentURL.Fragment = ""
374+
if *currentURL != *h.originalRootURI {
375+
return uri // refers to a resource outside of this workspace
376+
}
332377
}
333-
path := strings.TrimPrefix(string(uri), "file://")
334378
path = pathpkg.Join(h.RootFSPath, path)
335379
if !util.PathHasPrefix(path, h.RootFSPath) {
336380
panic(fmt.Sprintf("file path %q must have prefix %q (file URI is %q, root URI is %q)", path, h.RootFSPath, uri, h.init.RootPath))
@@ -472,21 +516,37 @@ func (h *BuildHandler) rewriteURIFromLangServer(uri lsp.DocumentURI) (lsp.Docume
472516
if util.PathHasPrefix(u.Path, goroot) {
473517
fileInGoStdlib := util.PathTrimPrefix(u.Path, goroot)
474518
if h.rootImportPath == "" {
475-
// The workspace is the Go stdlib and this refers to
476-
// something in the Go stdlib, so let's use file:///
477-
// so that the client adds our current rev, instead
478-
// of using runtime.Version() (which is not
479-
// necessarily the commit of the Go stdlib we're
480-
// analyzing).
481-
return lsp.DocumentURI("file:///" + fileInGoStdlib), nil
519+
if h.clientUsesFileSchemeWithinWorkspace {
520+
// The workspace is the Go stdlib and this refers to
521+
// something in the Go stdlib, so let's use file:///
522+
// so that the client adds our current rev, instead
523+
// of using runtime.Version() (which is not
524+
// necessarily the commit of the Go stdlib we're
525+
// analyzing).
526+
return lsp.DocumentURI("file:///" + fileInGoStdlib), nil
527+
}
528+
if h.originalRootURI == nil {
529+
return uri, nil
530+
}
531+
newURI, _ := url.Parse(h.originalRootURI.String())
532+
newURI.Fragment = fileInGoStdlib
533+
return lsp.DocumentURI(newURI.String()), nil
482534
}
483535
return lsp.DocumentURI("git://github.com/golang/go?" + gosrc.RuntimeVersion + "#" + fileInGoStdlib), nil
484536
}
485537

486538
// Refers to a file in the same workspace?
487539
if util.PathHasPrefix(u.Path, h.RootFSPath) {
488-
pathInThisWorkspace := util.PathTrimPrefix(u.Path, h.RootFSPath)
489-
return lsp.DocumentURI("file:///" + pathInThisWorkspace), nil
540+
if h.clientUsesFileSchemeWithinWorkspace {
541+
pathInThisWorkspace := util.PathTrimPrefix(u.Path, h.RootFSPath)
542+
return lsp.DocumentURI("file:///" + pathInThisWorkspace), nil
543+
}
544+
if h.originalRootURI == nil {
545+
return uri, nil
546+
}
547+
newURI, _ := url.Parse(h.originalRootURI.String())
548+
newURI.Fragment = util.PathTrimPrefix(u.Path, h.RootFSPath)
549+
return lsp.DocumentURI(newURI.String()), nil
490550
}
491551

492552
// Refers to a file in the GOPATH (that's from another repo)?

buildserver/integration_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ func TestIntegration(t *testing.T) {
237237

238238
ctx := context.Background()
239239

240-
c, done := connectionToNewBuildServer(string(rootURI), t)
240+
c, done := connectionToNewBuildServer(string(rootURI), t, true)
241241
defer done()
242242

243243
// Prepare the connection.

buildserver/proxy_test.go

+34-102
Original file line numberDiff line numberDiff line change
@@ -131,92 +131,6 @@ func TestProxy(t *testing.T) {
131131
"a_test.go:1:46": "A int",
132132
},
133133
},
134-
"go subdirectory in repo": {
135-
rootURI: "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d",
136-
mode: "go",
137-
fs: map[string]string{
138-
"a.go": "package d; func A() { A() }",
139-
"d2/b.go": `package d2; import "test/pkg/d"; func B() { d.A(); B() }`,
140-
},
141-
wantHover: map[string]string{
142-
"a.go:1:17": "func A()",
143-
"a.go:1:23": "func A()",
144-
"d2/b.go:1:39": "func B()",
145-
"d2/b.go:1:47": "func A()",
146-
"d2/b.go:1:52": "func B()",
147-
},
148-
wantDefinition: map[string]string{
149-
"a.go:1:17": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/a.go:1:17",
150-
"a.go:1:23": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/a.go:1:17",
151-
"d2/b.go:1:39": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:39",
152-
"d2/b.go:1:47": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/a.go:1:17",
153-
"d2/b.go:1:52": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:39",
154-
},
155-
wantXDefinition: map[string]string{
156-
"a.go:1:17": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/a.go:1:17 id:test/pkg/d/-/A name:A package:test/pkg/d packageName:d recv: vendor:false",
157-
"a.go:1:23": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/a.go:1:17 id:test/pkg/d/-/A name:A package:test/pkg/d packageName:d recv: vendor:false",
158-
"d2/b.go:1:39": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:39 id:test/pkg/d/d2/-/B name:B package:test/pkg/d/d2 packageName:d2 recv: vendor:false",
159-
"d2/b.go:1:47": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/a.go:1:17 id:test/pkg/d/-/A name:A package:test/pkg/d packageName:d recv: vendor:false",
160-
"d2/b.go:1:52": "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:39 id:test/pkg/d/d2/-/B name:B package:test/pkg/d/d2 packageName:d2 recv: vendor:false",
161-
},
162-
wantSymbols: map[string][]string{
163-
"": []string{"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/a.go:function:A:0:16", "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:function:B:0:38"},
164-
"is:exported": []string{"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/a.go:function:A:0:16", "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:function:B:0:38"},
165-
},
166-
wantXReferences: map[*lsext.WorkspaceReferencesParams][]string{
167-
// Non-matching name query.
168-
{Query: lsext.SymbolDescriptor{"name": "nope"}}: []string{},
169-
170-
// Matching against invalid field name.
171-
{Query: lsext.SymbolDescriptor{"nope": "A"}}: []string{},
172-
173-
// Matching against an invalid dirs hint.
174-
{Query: lsext.SymbolDescriptor{"package": "test/pkg/d"}, Hints: map[string]interface{}{"dirs": []string{"file:///src/test/pkg/d/d3"}}}: []string{},
175-
176-
// Matching against a dirs hint with multiple dirs.
177-
{Query: lsext.SymbolDescriptor{"package": "test/pkg/d"}, Hints: map[string]interface{}{"dirs": []string{"file:///d2", "file:///invalid"}}}: []string{
178-
"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:20-1:32 -> id:test/pkg/d name: package:test/pkg/d packageName:d recv: vendor:false",
179-
"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:47-1:48 -> id:test/pkg/d/-/A name:A package:test/pkg/d packageName:d recv: vendor:false",
180-
},
181-
182-
// Matching against a dirs hint.
183-
{Query: lsext.SymbolDescriptor{"package": "test/pkg/d"}, Hints: map[string]interface{}{"dirs": []string{"file:///d2"}}}: []string{
184-
"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:20-1:32 -> id:test/pkg/d name: package:test/pkg/d packageName:d recv: vendor:false",
185-
"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:47-1:48 -> id:test/pkg/d/-/A name:A package:test/pkg/d packageName:d recv: vendor:false",
186-
},
187-
188-
// Matching against single field.
189-
{Query: lsext.SymbolDescriptor{"package": "test/pkg/d"}}: []string{
190-
"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:20-1:32 -> id:test/pkg/d name: package:test/pkg/d packageName:d recv: vendor:false",
191-
"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:47-1:48 -> id:test/pkg/d/-/A name:A package:test/pkg/d packageName:d recv: vendor:false",
192-
},
193-
194-
// Matching against no fields.
195-
{Query: lsext.SymbolDescriptor{}}: []string{
196-
"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:20-1:32 -> id:test/pkg/d name: package:test/pkg/d packageName:d recv: vendor:false",
197-
"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:47-1:48 -> id:test/pkg/d/-/A name:A package:test/pkg/d packageName:d recv: vendor:false",
198-
},
199-
{
200-
Query: lsext.SymbolDescriptor{
201-
"name": "",
202-
"package": "test/pkg/d",
203-
"packageName": "d",
204-
"recv": "",
205-
"vendor": false,
206-
},
207-
}: []string{"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:20-1:32 -> id:test/pkg/d name: package:test/pkg/d packageName:d recv: vendor:false"},
208-
{
209-
Query: lsext.SymbolDescriptor{
210-
"name": "A",
211-
"package": "test/pkg/d",
212-
"packageName": "d",
213-
"recv": "",
214-
"vendor": false,
215-
},
216-
}: []string{"git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef#d/d2/b.go:1:47-1:48 -> id:test/pkg/d/-/A name:A package:test/pkg/d packageName:d recv: vendor:false"},
217-
},
218-
wantXPackages: []string{"test/pkg/d", "test/pkg/d/d2"},
219-
},
220134
"go multiple packages in dir": {
221135
rootURI: "git://test/pkg?deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
222136
mode: "go",
@@ -606,18 +520,32 @@ func yza() {}
606520
t.Fatal(err)
607521
}
608522

609-
c, done := connectionToNewBuildServer(string(test.rootURI), t)
610-
defer done()
523+
r := func(clientUsesFileSchemeWithinWorkspace bool) {
524+
c, done := connectionToNewBuildServer(string(test.rootURI), t, clientUsesFileSchemeWithinWorkspace)
525+
defer done()
526+
527+
// Prepare the connection.
528+
var initializeParams lspext.InitializeParams
529+
if clientUsesFileSchemeWithinWorkspace {
530+
initializeParams = lspext.InitializeParams{
531+
InitializeParams: lsp.InitializeParams{RootURI: "file:///"},
532+
OriginalRootURI: test.rootURI,
533+
}
534+
} else {
535+
initializeParams = lspext.InitializeParams{
536+
InitializeParams: lsp.InitializeParams{RootURI: test.rootURI},
537+
OriginalRootURI: "",
538+
}
539+
}
540+
if err := c.Call(ctx, "initialize", initializeParams, nil); err != nil {
541+
t.Fatal("initialize:", err)
542+
}
611543

612-
// Prepare the connection.
613-
if err := c.Call(ctx, "initialize", lspext.InitializeParams{
614-
InitializeParams: lsp.InitializeParams{RootURI: "file:///"},
615-
OriginalRootURI: test.rootURI,
616-
}, nil); err != nil {
617-
t.Fatal("initialize:", err)
544+
lspTests(t, ctx, c, root, test.wantHover, test.wantDefinition, test.wantXDefinition, test.wantReferences, test.wantSymbols, test.wantXDependencies, test.wantXReferences, test.wantXPackages)
618545
}
619546

620-
lspTests(t, ctx, c, root, test.wantHover, test.wantDefinition, test.wantXDefinition, test.wantReferences, test.wantSymbols, test.wantXDependencies, test.wantXReferences, test.wantXPackages)
547+
r(true)
548+
r(false)
621549
})
622550
}
623551
}
@@ -649,7 +577,7 @@ func (c *pipeReadWriteCloser) Close() error {
649577
return err2
650578
}
651579

652-
func connectionToNewBuildServer(root string, t testing.TB) (*jsonrpc2.Conn, func()) {
580+
func connectionToNewBuildServer(root string, t testing.TB, rewriteURIs bool) (*jsonrpc2.Conn, func()) {
653581
rootURI, err := gituri.Parse(root)
654582
if err != nil {
655583
t.Fatal(err)
@@ -684,18 +612,22 @@ func connectionToNewBuildServer(root string, t testing.TB) (*jsonrpc2.Conn, func
684612

685613
onSend := func(req *jsonrpc2.Request, res *jsonrpc2.Response) {
686614
if res == nil {
687-
err := convertURIs(req.Params, RelWorkspaceURI)
688-
if err != nil {
689-
t.Fatal(err)
615+
if rewriteURIs {
616+
err := convertURIs(req.Params, RelWorkspaceURI)
617+
if err != nil {
618+
t.Fatal(err)
619+
}
690620
}
691621
}
692622
}
693623

694624
onRecv := func(req *jsonrpc2.Request, res *jsonrpc2.Response) {
695625
if res != nil && res.Result != nil {
696-
err := convertURIs(res.Result, AbsWorkspaceURI)
697-
if err != nil {
698-
t.Fatal(err)
626+
if rewriteURIs {
627+
err := convertURIs(res.Result, AbsWorkspaceURI)
628+
if err != nil {
629+
t.Fatal(err)
630+
}
699631
}
700632
}
701633
}

buildserver/stress_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func BenchmarkStress(b *testing.B) {
118118
// Don't measure the time it takes to dial and
119119
// initialize, because this is amortized over each
120120
// operation we do.
121-
c, done := connectionToNewBuildServer(string(rootURI), b)
121+
c, done := connectionToNewBuildServer(string(rootURI), b, true)
122122
if err := c.Call(ctx, "initialize", lspext.ClientProxyInitializeParams{
123123
InitializeParams: lsp.InitializeParams{RootURI: lsp.DocumentURI(root.String())},
124124
InitializationOptions: lspext.ClientProxyInitializationOptions{Mode: test.mode},

buildserver/xlang_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ import (
1919
"time"
2020

2121
"github.com/sourcegraph/ctxvfs"
22+
"github.com/sourcegraph/go-langserver/gituri"
2223
"github.com/sourcegraph/go-langserver/pkg/lsp"
2324
lsext "github.com/sourcegraph/go-langserver/pkg/lspext"
2425
"github.com/sourcegraph/go-lsp/lspext"
2526
"github.com/sourcegraph/jsonrpc2"
26-
"github.com/sourcegraph/go-langserver/gituri"
2727
)
2828

2929
var update = flag.Bool("update", false, "update golden files on disk")

0 commit comments

Comments
 (0)