Skip to content

Commit a6bad90

Browse files
authored
Snapshot LSP (#1505)
1 parent bca9518 commit a6bad90

File tree

105 files changed

+11034
-10193
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+11034
-10193
lines changed

internal/api/api.go

Lines changed: 63 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,27 @@ import (
1212
"github.com/microsoft/typescript-go/internal/astnav"
1313
"github.com/microsoft/typescript-go/internal/checker"
1414
"github.com/microsoft/typescript-go/internal/core"
15-
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
15+
"github.com/microsoft/typescript-go/internal/ls"
1616
"github.com/microsoft/typescript-go/internal/project"
17+
"github.com/microsoft/typescript-go/internal/project/logging"
1718
"github.com/microsoft/typescript-go/internal/tsoptions"
1819
"github.com/microsoft/typescript-go/internal/tspath"
1920
"github.com/microsoft/typescript-go/internal/vfs"
2021
)
2122

2223
type handleMap[T any] map[Handle[T]]*T
2324

24-
type APIOptions struct {
25-
Logger *project.Logger
25+
type APIInit struct {
26+
Logger logging.Logger
27+
FS vfs.FS
28+
SessionOptions *project.SessionOptions
2629
}
2730

2831
type API struct {
29-
host APIHost
30-
options APIOptions
32+
logger logging.Logger
33+
session *project.Session
3134

32-
documentStore *project.DocumentStore
33-
configFileRegistry *project.ConfigFileRegistry
34-
35-
projects handleMap[project.Project]
35+
projects map[Handle[project.Project]]tspath.Path
3636
filesMu sync.Mutex
3737
files handleMap[ast.SourceFile]
3838
symbolsMu sync.Mutex
@@ -41,91 +41,22 @@ type API struct {
4141
types handleMap[checker.Type]
4242
}
4343

44-
var _ project.ProjectHost = (*API)(nil)
45-
46-
func NewAPI(host APIHost, options APIOptions) *API {
44+
func NewAPI(init *APIInit) *API {
4745
api := &API{
48-
host: host,
49-
options: options,
50-
projects: make(handleMap[project.Project]),
46+
session: project.NewSession(&project.SessionInit{
47+
Logger: init.Logger,
48+
FS: init.FS,
49+
Options: init.SessionOptions,
50+
}),
51+
projects: make(map[Handle[project.Project]]tspath.Path),
5152
files: make(handleMap[ast.SourceFile]),
5253
symbols: make(handleMap[ast.Symbol]),
5354
types: make(handleMap[checker.Type]),
5455
}
5556

56-
api.documentStore = project.NewDocumentStore(project.DocumentStoreOptions{
57-
ComparePathsOptions: tspath.ComparePathsOptions{
58-
UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(),
59-
CurrentDirectory: host.GetCurrentDirectory(),
60-
},
61-
Hooks: project.DocumentRegistryHooks{
62-
OnReleaseDocument: func(file *ast.SourceFile) {
63-
_ = api.releaseHandle(string(FileHandle(file)))
64-
},
65-
},
66-
})
67-
68-
api.configFileRegistry = &project.ConfigFileRegistry{
69-
Host: api,
70-
}
7157
return api
7258
}
7359

74-
// DefaultLibraryPath implements ProjectHost.
75-
func (api *API) DefaultLibraryPath() string {
76-
return api.host.DefaultLibraryPath()
77-
}
78-
79-
// TypingsInstaller implements ProjectHost
80-
func (api *API) TypingsInstaller() *project.TypingsInstaller {
81-
return nil
82-
}
83-
84-
// DocumentStore implements ProjectHost.
85-
func (api *API) DocumentStore() *project.DocumentStore {
86-
return api.documentStore
87-
}
88-
89-
// ConfigFileRegistry implements ProjectHost.
90-
func (api *API) ConfigFileRegistry() *project.ConfigFileRegistry {
91-
return api.configFileRegistry
92-
}
93-
94-
// FS implements ProjectHost.
95-
func (api *API) FS() vfs.FS {
96-
return api.host.FS()
97-
}
98-
99-
// GetCurrentDirectory implements ProjectHost.
100-
func (api *API) GetCurrentDirectory() string {
101-
return api.host.GetCurrentDirectory()
102-
}
103-
104-
// Log implements ProjectHost.
105-
func (api *API) Log(s string) {
106-
api.options.Logger.Info(s)
107-
}
108-
109-
// Log implements ProjectHost.
110-
func (api *API) Trace(s string) {
111-
api.options.Logger.Info(s)
112-
}
113-
114-
// PositionEncoding implements ProjectHost.
115-
func (api *API) PositionEncoding() lsproto.PositionEncodingKind {
116-
return lsproto.PositionEncodingKindUTF8
117-
}
118-
119-
// Client implements ProjectHost.
120-
func (api *API) Client() project.Client {
121-
return nil
122-
}
123-
124-
// IsWatchEnabled implements ProjectHost.
125-
func (api *API) IsWatchEnabled() bool {
126-
return false
127-
}
128-
12960
func (api *API) HandleRequest(ctx context.Context, method string, payload []byte) ([]byte, error) {
13061
params, err := unmarshalPayload(method, payload)
13162
if err != nil {
@@ -149,7 +80,7 @@ func (api *API) HandleRequest(ctx context.Context, method string, payload []byte
14980
case MethodParseConfigFile:
15081
return encodeJSON(api.ParseConfigFile(params.(*ParseConfigFileParams).FileName))
15182
case MethodLoadProject:
152-
return encodeJSON(api.LoadProject(params.(*LoadProjectParams).ConfigFileName))
83+
return encodeJSON(api.LoadProject(ctx, params.(*LoadProjectParams).ConfigFileName))
15384
case MethodGetSymbolAtPosition:
15485
params := params.(*GetSymbolAtPositionParams)
15586
return encodeJSON(api.GetSymbolAtPosition(ctx, params.Project, params.FileName, int(params.Position)))
@@ -180,20 +111,20 @@ func (api *API) HandleRequest(ctx context.Context, method string, payload []byte
180111
}
181112

182113
func (api *API) Close() {
183-
api.options.Logger.Close()
114+
api.session.Close()
184115
}
185116

186117
func (api *API) ParseConfigFile(configFileName string) (*ConfigFileResponse, error) {
187118
configFileName = api.toAbsoluteFileName(configFileName)
188-
configFileContent, ok := api.host.FS().ReadFile(configFileName)
119+
configFileContent, ok := api.session.FS().ReadFile(configFileName)
189120
if !ok {
190121
return nil, fmt.Errorf("could not read file %q", configFileName)
191122
}
192123
configDir := tspath.GetDirectoryPath(configFileName)
193124
tsConfigSourceFile := tsoptions.NewTsconfigSourceFileFromFilePath(configFileName, api.toPath(configFileName), configFileContent)
194125
parsedCommandLine := tsoptions.ParseJsonSourceFileConfigFileContent(
195126
tsConfigSourceFile,
196-
api.host,
127+
api.session,
197128
configDir,
198129
nil, /*existingOptions*/
199130
configFileName,
@@ -207,26 +138,29 @@ func (api *API) ParseConfigFile(configFileName string) (*ConfigFileResponse, err
207138
}, nil
208139
}
209140

210-
func (api *API) LoadProject(configFileName string) (*ProjectResponse, error) {
211-
configFileName = api.toAbsoluteFileName(configFileName)
212-
configFilePath := api.toPath(configFileName)
213-
p := project.NewConfiguredProject(configFileName, configFilePath, api)
214-
if err := p.LoadConfig(); err != nil {
141+
func (api *API) LoadProject(ctx context.Context, configFileName string) (*ProjectResponse, error) {
142+
project, err := api.session.OpenProject(ctx, api.toAbsoluteFileName(configFileName))
143+
if err != nil {
215144
return nil, err
216145
}
217-
p.GetProgram()
218-
data := NewProjectResponse(p)
219-
api.projects[data.Id] = p
146+
data := NewProjectResponse(project)
147+
api.projects[data.Id] = project.ConfigFilePath()
220148
return data, nil
221149
}
222150

223151
func (api *API) GetSymbolAtPosition(ctx context.Context, projectId Handle[project.Project], fileName string, position int) (*SymbolResponse, error) {
224-
project, ok := api.projects[projectId]
152+
projectPath, ok := api.projects[projectId]
225153
if !ok {
154+
return nil, errors.New("project ID not found")
155+
}
156+
snapshot, release := api.session.Snapshot()
157+
defer release()
158+
project := snapshot.ProjectCollection.GetProjectByPath(projectPath)
159+
if project == nil {
226160
return nil, errors.New("project not found")
227161
}
228-
languageService, done := project.GetLanguageServiceForRequest(ctx)
229-
defer done()
162+
163+
languageService := ls.NewLanguageService(project, snapshot.Converters())
230164
symbol, err := languageService.GetSymbolAtPosition(ctx, fileName, position)
231165
if err != nil || symbol == nil {
232166
return nil, err
@@ -239,10 +173,17 @@ func (api *API) GetSymbolAtPosition(ctx context.Context, projectId Handle[projec
239173
}
240174

241175
func (api *API) GetSymbolAtLocation(ctx context.Context, projectId Handle[project.Project], location Handle[ast.Node]) (*SymbolResponse, error) {
242-
project, ok := api.projects[projectId]
176+
projectPath, ok := api.projects[projectId]
243177
if !ok {
178+
return nil, errors.New("project ID not found")
179+
}
180+
snapshot, release := api.session.Snapshot()
181+
defer release()
182+
project := snapshot.ProjectCollection.GetProjectByPath(projectPath)
183+
if project == nil {
244184
return nil, errors.New("project not found")
245185
}
186+
246187
fileHandle, pos, kind, err := parseNodeHandle(location)
247188
if err != nil {
248189
return nil, err
@@ -261,8 +202,7 @@ func (api *API) GetSymbolAtLocation(ctx context.Context, projectId Handle[projec
261202
if node == nil {
262203
return nil, fmt.Errorf("node of kind %s not found at position %d in file %q", kind.String(), pos, sourceFile.FileName())
263204
}
264-
languageService, done := project.GetLanguageServiceForRequest(ctx)
265-
defer done()
205+
languageService := ls.NewLanguageService(project, snapshot.Converters())
266206
symbol := languageService.GetSymbolAtLocation(ctx, node)
267207
if symbol == nil {
268208
return nil, nil
@@ -275,18 +215,24 @@ func (api *API) GetSymbolAtLocation(ctx context.Context, projectId Handle[projec
275215
}
276216

277217
func (api *API) GetTypeOfSymbol(ctx context.Context, projectId Handle[project.Project], symbolHandle Handle[ast.Symbol]) (*TypeResponse, error) {
278-
project, ok := api.projects[projectId]
218+
projectPath, ok := api.projects[projectId]
279219
if !ok {
220+
return nil, errors.New("project ID not found")
221+
}
222+
snapshot, release := api.session.Snapshot()
223+
defer release()
224+
project := snapshot.ProjectCollection.GetProjectByPath(projectPath)
225+
if project == nil {
280226
return nil, errors.New("project not found")
281227
}
228+
282229
api.symbolsMu.Lock()
283230
defer api.symbolsMu.Unlock()
284231
symbol, ok := api.symbols[symbolHandle]
285232
if !ok {
286233
return nil, fmt.Errorf("symbol %q not found", symbolHandle)
287234
}
288-
languageService, done := project.GetLanguageServiceForRequest(ctx)
289-
defer done()
235+
languageService := ls.NewLanguageService(project, snapshot.Converters())
290236
t := languageService.GetTypeOfSymbol(ctx, symbol)
291237
if t == nil {
292238
return nil, nil
@@ -295,10 +241,17 @@ func (api *API) GetTypeOfSymbol(ctx context.Context, projectId Handle[project.Pr
295241
}
296242

297243
func (api *API) GetSourceFile(projectId Handle[project.Project], fileName string) (*ast.SourceFile, error) {
298-
project, ok := api.projects[projectId]
244+
projectPath, ok := api.projects[projectId]
299245
if !ok {
246+
return nil, errors.New("project ID not found")
247+
}
248+
snapshot, release := api.session.Snapshot()
249+
defer release()
250+
project := snapshot.ProjectCollection.GetProjectByPath(projectPath)
251+
if project == nil {
300252
return nil, errors.New("project not found")
301253
}
254+
302255
sourceFile := project.GetProgram().GetSourceFile(fileName)
303256
if sourceFile == nil {
304257
return nil, fmt.Errorf("source file %q not found", fileName)
@@ -313,12 +266,11 @@ func (api *API) releaseHandle(handle string) error {
313266
switch handle[0] {
314267
case handlePrefixProject:
315268
projectId := Handle[project.Project](handle)
316-
project, ok := api.projects[projectId]
269+
_, ok := api.projects[projectId]
317270
if !ok {
318271
return fmt.Errorf("project %q not found", handle)
319272
}
320273
delete(api.projects, projectId)
321-
project.Close()
322274
case handlePrefixFile:
323275
fileId := Handle[ast.SourceFile](handle)
324276
api.filesMu.Lock()
@@ -353,11 +305,11 @@ func (api *API) releaseHandle(handle string) error {
353305
}
354306

355307
func (api *API) toAbsoluteFileName(fileName string) string {
356-
return tspath.GetNormalizedAbsolutePath(fileName, api.host.GetCurrentDirectory())
308+
return tspath.GetNormalizedAbsolutePath(fileName, api.session.GetCurrentDirectory())
357309
}
358310

359311
func (api *API) toPath(fileName string) tspath.Path {
360-
return tspath.ToPath(fileName, api.host.GetCurrentDirectory(), api.host.FS().UseCaseSensitiveFileNames())
312+
return tspath.ToPath(fileName, api.session.GetCurrentDirectory(), api.session.FS().UseCaseSensitiveFileNames())
361313
}
362314

363315
func encodeJSON(v any, err error) ([]byte, error) {

internal/api/host.go

Lines changed: 0 additions & 9 deletions
This file was deleted.

internal/api/proto.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ func NewProjectResponse(project *project.Project) *ProjectResponse {
131131
return &ProjectResponse{
132132
Id: ProjectHandle(project),
133133
ConfigFileName: project.Name(),
134-
RootFiles: project.GetRootFileNames(),
135-
CompilerOptions: project.GetCompilerOptions(),
134+
RootFiles: project.CommandLine.FileNames(),
135+
CompilerOptions: project.CommandLine.CompilerOptions(),
136136
}
137137
}
138138

0 commit comments

Comments
 (0)