forked from sourcegraph/lsp-adapter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
remote_fs.go
159 lines (127 loc) · 3.8 KB
/
remote_fs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package main
import (
"context"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"sync"
"github.com/neelance/parallel"
"github.com/pkg/errors"
"github.com/sourcegraph/go-langserver/pkg/lsp"
"github.com/sourcegraph/go-langserver/pkg/lspext"
"github.com/sourcegraph/jsonrpc2"
nettrace "golang.org/x/net/trace"
)
type remoteFS struct {
conn *jsonrpc2.Conn
traceID string
}
// BatchOpen opens all of the content for the specified paths.
func (fs *remoteFS) BatchOpen(ctx context.Context, fileURIs []lsp.DocumentURI) ([]batchFile, error) {
par := parallel.NewRun(8)
var mut sync.Mutex
var batchFiles []batchFile
for _, fileURI := range fileURIs {
par.Acquire()
go func(uri lsp.DocumentURI) {
defer par.Release()
text, err := fs.Open(ctx, uri)
if err != nil {
par.Error(err)
return
}
mut.Lock()
defer mut.Unlock()
batchFiles = append(batchFiles, batchFile{uri: uri, content: text})
}(fileURI)
}
if err := par.Wait(); err != nil {
return nil, err
}
return batchFiles, nil
}
type batchFile struct {
uri lsp.DocumentURI
content string
}
// Open returns the content of the text file for the given file uri path.
func (fs *remoteFS) Open(ctx context.Context, fileURI lsp.DocumentURI) (string, error) {
params := lspext.ContentParams{TextDocument: lsp.TextDocumentIdentifier{URI: fileURI}}
var res lsp.TextDocumentItem
if err := fs.conn.Call(ctx, "textDocument/xcontent", params, &res); err != nil {
return "", errors.Wrap(err, "calling textDocument/xcontent failed")
}
return res.Text, nil
}
// Walk returns a list of all file uris.
func (fs *remoteFS) Walk(ctx context.Context) ([]lsp.DocumentURI, error) {
params := lspext.FilesParams{}
var res []lsp.TextDocumentIdentifier
if err := fs.conn.Call(ctx, "workspace/xfiles", ¶ms, &res); err != nil {
return nil, errors.Wrap(err, "calling workspace/xfiles failed")
}
var fileURIs []lsp.DocumentURI
for _, ident := range res {
fileURIs = append(fileURIs, ident.URI)
}
return fileURIs, nil
}
func (fs *remoteFS) Clone(ctx context.Context, baseDir string, globs []string) (err error) {
tr := nettrace.New("clone", fs.traceID)
defer func() {
if err != nil {
tr.LazyPrintf("error: %v", err)
tr.SetError()
}
tr.Finish()
}()
tr.LazyPrintf("starting clone baseDir: %s, globs: %v", baseDir, globs)
if err := os.MkdirAll(baseDir, os.ModePerm); err != nil {
return err
}
filePaths, err := fs.Walk(ctx)
if err != nil {
return errors.Wrap(err, "failed to fetch all filePaths during clone")
}
// filter
if len(globs) > 0 {
i := 0
for _, filePath := range filePaths {
name := path.Base(string(filePath))
for _, pattern := range globs {
if matched, err := path.Match(pattern, name); err != nil {
return errors.Wrapf(err, "bad glob pattern %q", pattern)
} else if matched {
filePaths[i] = filePath
i++
break
}
}
}
filePaths = filePaths[:i]
}
files, err := fs.BatchOpen(ctx, filePaths)
if err != nil {
return errors.Wrap(err, "failed to batch open files during clone")
}
for _, file := range files {
parsedFileURI, err := url.Parse(string(file.uri))
if err != nil {
return errors.Wrapf(err, "failed to parse raw file uri %s for Clone", file.uri)
}
newFilePath := filepath.Join(baseDir, filepath.FromSlash(parsedFileURI.Path))
// There is an assumption here that all paths returned from Walk()
// point to files, not directories
parentDir := filepath.Dir(newFilePath)
if err := os.MkdirAll(parentDir, os.ModePerm); err != nil {
return errors.Wrapf(err, "failed to make parent dirs for %s", newFilePath)
}
if err := ioutil.WriteFile(newFilePath, []byte(file.content), os.ModePerm); err != nil {
return errors.Wrapf(err, "failed to write file content for %s", newFilePath)
}
}
tr.LazyPrintf("cloned %d files", len(files))
return nil
}