Skip to content

Commit 2ace8fe

Browse files
committed
userpreferences
1 parent f3f42e7 commit 2ace8fe

File tree

9 files changed

+240
-24
lines changed

9 files changed

+240
-24
lines changed

internal/api/api.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (api *API) GetSymbolAtPosition(ctx context.Context, projectId Handle[projec
160160
return nil, errors.New("project not found")
161161
}
162162

163-
languageService := ls.NewLanguageService(project, snapshot.Converters())
163+
languageService := ls.NewLanguageService(project, snapshot.Converters(), snapshot.UserPreferences())
164164
symbol, err := languageService.GetSymbolAtPosition(ctx, fileName, position)
165165
if err != nil || symbol == nil {
166166
return nil, err
@@ -202,7 +202,7 @@ func (api *API) GetSymbolAtLocation(ctx context.Context, projectId Handle[projec
202202
if node == nil {
203203
return nil, fmt.Errorf("node of kind %s not found at position %d in file %q", kind.String(), pos, sourceFile.FileName())
204204
}
205-
languageService := ls.NewLanguageService(project, snapshot.Converters())
205+
languageService := ls.NewLanguageService(project, snapshot.Converters(), snapshot.UserPreferences())
206206
symbol := languageService.GetSymbolAtLocation(ctx, node)
207207
if symbol == nil {
208208
return nil, nil
@@ -232,7 +232,7 @@ func (api *API) GetTypeOfSymbol(ctx context.Context, projectId Handle[project.Pr
232232
if !ok {
233233
return nil, fmt.Errorf("symbol %q not found", symbolHandle)
234234
}
235-
languageService := ls.NewLanguageService(project, snapshot.Converters())
235+
languageService := ls.NewLanguageService(project, snapshot.Converters(), snapshot.UserPreferences())
236236
t := languageService.GetTypeOfSymbol(ctx, symbol)
237237
if t == nil {
238238
return nil, nil

internal/fourslash/fourslash.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type FourslashTest struct {
3838
testData *TestData // !!! consolidate test files from test data and script info
3939
baselines map[string]*strings.Builder
4040
rangesByText *collections.MultiMap[string, *RangeMarker]
41+
config *ls.UserPreferences
4142

4243
scriptInfos map[string]*scriptInfo
4344
converters *ls.Converters
@@ -268,6 +269,29 @@ func sendRequest[Params, Resp any](t *testing.T, f *FourslashTest, info lsproto.
268269
)
269270
f.writeMsg(t, req.Message())
270271
resp := f.readMsg(t)
272+
if resp == nil {
273+
return nil, *new(Resp), false
274+
}
275+
276+
// currently, the only request that may be sent by the server during a client request is one `config` request
277+
// !!! remove if `config` is handled in initialization and there are no other server-initiated requests
278+
if resp.Kind == lsproto.MessageKindRequest {
279+
req := resp.AsRequest()
280+
switch req.Method {
281+
case lsproto.MethodWorkspaceConfiguration:
282+
req := lsproto.ResponseMessage{
283+
ID: req.ID,
284+
JSONRPC: req.JSONRPC,
285+
Result: []any{&f.config},
286+
}
287+
f.writeMsg(t, req.Message())
288+
resp = f.readMsg(t)
289+
default:
290+
// other types of responses not yet used in fourslash; implement them if needed
291+
t.Fatalf("Unexpected request received: %s", req)
292+
}
293+
}
294+
271295
if resp == nil {
272296
return nil, *new(Resp), false
273297
}
@@ -300,6 +324,13 @@ func (f *FourslashTest) readMsg(t *testing.T) *lsproto.Message {
300324
return msg
301325
}
302326

327+
func (f *FourslashTest) configure(t *testing.T, config *ls.UserPreferences) {
328+
f.config = config
329+
sendNotification(t, f, lsproto.WorkspaceDidChangeConfigurationInfo, &lsproto.DidChangeConfigurationParams{
330+
Settings: config,
331+
})
332+
}
333+
303334
func (f *FourslashTest) GoToMarkerOrRange(t *testing.T, markerOrRange MarkerOrRange) {
304335
f.goToMarker(t, markerOrRange)
305336
}
@@ -541,6 +572,11 @@ func (f *FourslashTest) verifyCompletionsWorker(t *testing.T, expected *Completi
541572
Position: f.currentCaretPosition,
542573
Context: &lsproto.CompletionContext{},
543574
}
575+
if expected == nil {
576+
f.configure(t, nil)
577+
} else {
578+
f.configure(t, expected.UserPreferences)
579+
}
544580
resMsg, result, resultOk := sendRequest(t, f, lsproto.TextDocumentCompletionInfo, params)
545581
if resMsg == nil {
546582
t.Fatalf(prefix+"Nil response received for completion request", f.lastKnownMarkerName)

internal/fourslash/tests/autoImportCompletion_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ a/**/
3939
},
4040
})
4141
f.BaselineAutoImportsCompletions(t, []string{""})
42+
f.VerifyCompletions(t, "", &fourslash.CompletionsExpectedList{
43+
UserPreferences: &ls.UserPreferences{
44+
// completion autoimport preferences off; this tests if fourslash server communication correctly registers changes in user preferences
45+
},
46+
IsIncomplete: false,
47+
ItemDefaults: &fourslash.CompletionsExpectedItemDefaults{
48+
CommitCharacters: &DefaultCommitCharacters,
49+
EditRange: Ignored,
50+
},
51+
Items: &fourslash.CompletionsExpectedItems{
52+
Excludes: []string{"anotherVar"},
53+
},
54+
})
4255
}
4356

4457
func TestAutoImportCompletion2(t *testing.T) {

internal/ls/languageservice.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,31 @@ import (
77
)
88

99
type LanguageService struct {
10-
host Host
11-
converters *Converters
10+
host Host
11+
converters *Converters
12+
userPreferences *UserPreferences
1213
}
1314

14-
func NewLanguageService(host Host, converters *Converters) *LanguageService {
15+
func NewLanguageService(host Host, converters *Converters, preferences *UserPreferences) *LanguageService {
1516
return &LanguageService{
16-
host: host,
17-
converters: converters,
17+
host: host,
18+
converters: converters,
19+
userPreferences: preferences,
1820
}
1921
}
2022

2123
func (l *LanguageService) GetProgram() *compiler.Program {
2224
return l.host.GetProgram()
2325
}
2426

27+
func (l *LanguageService) UpdateUserPreferences(preferences *UserPreferences) {
28+
l.userPreferences = preferences
29+
}
30+
31+
func (l *LanguageService) UserPreferences() *UserPreferences {
32+
return l.userPreferences
33+
}
34+
2535
func (l *LanguageService) tryGetProgramAndFile(fileName string) (*compiler.Program, *ast.SourceFile) {
2636
program := l.GetProgram()
2737
file := program.GetSourceFile(fileName)

internal/ls/types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ type UserPreferences struct {
5656
AutoImportFileExcludePatterns []string
5757

5858
UseAliasesForRename *bool
59+
60+
// Inlay Hints
61+
IncludeInlayParameterNameHints string
62+
IncludeInlayParameterNameHintsWhenArgumentMatchesName *bool
63+
IncludeInlayFunctionParameterTypeHints *bool
64+
IncludeInlayVariableTypeHints *bool
65+
IncludeInlayVariableTypeHintsWhenTypeMatchesName *bool
66+
IncludeInlayPropertyDeclarationTypeHints *bool
67+
IncludeInlayFunctionLikeReturnTypeHints *bool
68+
IncludeInlayEnumMemberValueHints *bool
69+
InteractiveInlayHints *bool
70+
}
71+
72+
func (p *UserPreferences) GetOrDefault() UserPreferences {
73+
if p == nil {
74+
return UserPreferences{}
75+
}
76+
return *p
5977
}
6078

6179
func (p *UserPreferences) ModuleSpecifierPreferences() modulespecifiers.UserPreferences {

internal/lsp/server.go

Lines changed: 124 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,101 @@ func (s *Server) RefreshDiagnostics(ctx context.Context) error {
217217
return nil
218218
}
219219

220+
func (s *Server) Configure(ctx context.Context) (*ls.UserPreferences, error) {
221+
result, err := s.sendRequest(ctx, lsproto.MethodWorkspaceConfiguration, &lsproto.ConfigurationParams{
222+
Items: []*lsproto.ConfigurationItem{
223+
{
224+
Section: ptrTo("typescript"),
225+
},
226+
},
227+
})
228+
if err != nil {
229+
return nil, fmt.Errorf("configure request failed: %w", err)
230+
}
231+
configs := result.([]any)
232+
userPreferences := &ls.UserPreferences{}
233+
for _, config := range configs {
234+
if config == nil {
235+
continue
236+
}
237+
if item, ok := config.(map[string]any); ok {
238+
for name, values := range item {
239+
switch name {
240+
case "inlayHints":
241+
inlayHintsPreferences := values.(map[string]any)
242+
if v, ok := inlayHintsPreferences["parameterNames"].(map[string]any); ok && v != nil {
243+
if enabled, ok := v["enabled"]; ok {
244+
if enabledStr, ok := enabled.(string); ok {
245+
userPreferences.IncludeInlayParameterNameHints = enabledStr
246+
} else {
247+
userPreferences.IncludeInlayParameterNameHints = ""
248+
}
249+
}
250+
if supressWhenArgumentMatchesName, ok := v["suppressWhenArgumentMatchesName"]; ok {
251+
userPreferences.IncludeInlayParameterNameHintsWhenArgumentMatchesName = ptrTo(!supressWhenArgumentMatchesName.(bool))
252+
}
253+
}
254+
if v, ok := inlayHintsPreferences["parameterTypes"].(map[string]any); ok && v != nil {
255+
if enabled, ok := v["enabled"]; ok {
256+
userPreferences.IncludeInlayFunctionParameterTypeHints = ptrTo(enabled.(bool))
257+
}
258+
}
259+
if v, ok := inlayHintsPreferences["variableTypes"].(map[string]any); ok && v != nil {
260+
if enabled, ok := v["enabled"]; ok {
261+
userPreferences.IncludeInlayVariableTypeHints = ptrTo(enabled.(bool))
262+
}
263+
if supressWhenTypeMatchesName, ok := v["suppressWhenTypeMatchesName"]; ok {
264+
userPreferences.IncludeInlayVariableTypeHintsWhenTypeMatchesName = ptrTo(!supressWhenTypeMatchesName.(bool))
265+
}
266+
}
267+
if v, ok := inlayHintsPreferences["propertyDeclarationTypes"].(map[string]any); ok && v != nil {
268+
if enabled, ok := v["enabled"]; ok {
269+
userPreferences.IncludeInlayPropertyDeclarationTypeHints = ptrTo(enabled.(bool))
270+
}
271+
}
272+
if v, ok := inlayHintsPreferences["functionLikeReturnTypes"].(map[string]any); ok && v != nil {
273+
if enabled, ok := v["enabled"]; ok {
274+
userPreferences.IncludeInlayFunctionLikeReturnTypeHints = ptrTo(enabled.(bool))
275+
}
276+
}
277+
if v, ok := inlayHintsPreferences["enumMemberValues"].(map[string]any); ok && v != nil {
278+
if enabled, ok := v["enabled"]; ok {
279+
userPreferences.IncludeInlayEnumMemberValueHints = ptrTo(enabled.(bool))
280+
}
281+
}
282+
userPreferences.InteractiveInlayHints = ptrTo(true)
283+
case "tsserver":
284+
// !!!
285+
case "unstable":
286+
// !!!
287+
case "tsc":
288+
// !!!
289+
case "updateImportsOnFileMove":
290+
// !!! moveToFile
291+
case "preferences":
292+
// !!!
293+
case "experimental":
294+
// !!!
295+
case "organizeImports":
296+
// !!!
297+
case "importModuleSpecifierEnding":
298+
// !!!
299+
}
300+
}
301+
continue
302+
}
303+
if item, ok := config.(ls.UserPreferences); ok {
304+
// case for fourslash
305+
userPreferences = &item
306+
break
307+
}
308+
}
309+
// !!! set defaults for services, remove after extension is updated
310+
userPreferences.IncludeCompletionsForModuleExports = ptrTo(true)
311+
userPreferences.IncludeCompletionsForImportStatements = ptrTo(true)
312+
return userPreferences, nil
313+
}
314+
220315
func (s *Server) Run() error {
221316
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
222317
defer stop()
@@ -440,6 +535,7 @@ var handlers = sync.OnceValue(func() handlerMap {
440535
registerRequestHandler(handlers, lsproto.ShutdownInfo, (*Server).handleShutdown)
441536
registerNotificationHandler(handlers, lsproto.ExitInfo, (*Server).handleExit)
442537

538+
registerNotificationHandler(handlers, lsproto.WorkspaceDidChangeConfigurationInfo, (*Server).handleDidChangeWorkspaceConfiguration)
443539
registerNotificationHandler(handlers, lsproto.TextDocumentDidOpenInfo, (*Server).handleDidOpen)
444540
registerNotificationHandler(handlers, lsproto.TextDocumentDidChangeInfo, (*Server).handleDidChange)
445541
registerNotificationHandler(handlers, lsproto.TextDocumentDidSaveInfo, (*Server).handleDidSave)
@@ -638,7 +734,6 @@ func (s *Server) handleInitialized(ctx context.Context, params *lsproto.Initiali
638734
if shouldEnableWatch(s.initializeParams) {
639735
s.watchEnabled = true
640736
}
641-
642737
s.session = project.NewSession(&project.SessionInit{
643738
Options: &project.SessionOptions{
644739
CurrentDirectory: s.cwd,
@@ -655,6 +750,11 @@ func (s *Server) handleInitialized(ctx context.Context, params *lsproto.Initiali
655750
NpmExecutor: s,
656751
ParseCache: s.parseCache,
657752
})
753+
userPreferences, err := s.Configure(ctx)
754+
if err != nil {
755+
return err
756+
}
757+
s.session.Configure(userPreferences)
658758
// !!! temporary; remove when we have `handleDidChangeConfiguration`/implicit project config support
659759
if s.compilerOptionsForInferredProjects != nil {
660760
s.session.DidChangeCompilerOptionsForInferredProjects(ctx, s.compilerOptionsForInferredProjects)
@@ -672,6 +772,16 @@ func (s *Server) handleExit(ctx context.Context, params any) error {
672772
return io.EOF
673773
}
674774

775+
func (s *Server) handleDidChangeWorkspaceConfiguration(ctx context.Context, params *lsproto.DidChangeConfigurationParams) error {
776+
// !!! update user preferences
777+
// !!! only usable by fourslash
778+
if item, ok := params.Settings.(*ls.UserPreferences); ok {
779+
// case for fourslash
780+
s.session.Configure(item)
781+
}
782+
return nil
783+
}
784+
675785
func (s *Server) handleDidOpen(ctx context.Context, params *lsproto.DidOpenTextDocumentParams) error {
676786
s.session.DidOpenFile(ctx, params.TextDocument.Uri, params.TextDocument.Version, params.TextDocument.Text, params.TextDocument.LanguageId)
677787
return nil
@@ -757,10 +867,8 @@ func (s *Server) handleCompletion(ctx context.Context, languageService *ls.Langu
757867
params.Position,
758868
params.Context,
759869
getCompletionClientCapabilities(s.initializeParams),
760-
&ls.UserPreferences{
761-
IncludeCompletionsForModuleExports: ptrTo(true),
762-
IncludeCompletionsForImportStatements: ptrTo(true),
763-
})
870+
languageService.UserPreferences(),
871+
)
764872
}
765873

766874
func (s *Server) handleCompletionItemResolve(ctx context.Context, params *lsproto.CompletionItem, reqMsg *lsproto.RequestMessage) (lsproto.CompletionResolveResponse, error) {
@@ -778,10 +886,7 @@ func (s *Server) handleCompletionItemResolve(ctx context.Context, params *lsprot
778886
params,
779887
data,
780888
getCompletionClientCapabilities(s.initializeParams),
781-
&ls.UserPreferences{
782-
IncludeCompletionsForModuleExports: ptrTo(true),
783-
IncludeCompletionsForImportStatements: ptrTo(true),
784-
},
889+
languageService.UserPreferences(),
785890
)
786891
}
787892

@@ -812,6 +917,13 @@ func (s *Server) handleDocumentOnTypeFormat(ctx context.Context, ls *ls.Language
812917
)
813918
}
814919

920+
func (s *Server) handleInlayHints(ctx context.Context, ls *ls.LanguageService, params *lsproto.InlayHintParams) (lsproto.InlayHintResponse, error) {
921+
// !!!
922+
// userPreferences, _ := s.Configure(ctx)
923+
// return ls.ProvideInlayHints(ctx, params, userPreferences), nil
924+
panic("unimplemented")
925+
}
926+
815927
func (s *Server) handleWorkspaceSymbol(ctx context.Context, params *lsproto.WorkspaceSymbolParams, reqMsg *lsproto.RequestMessage) (lsproto.WorkspaceSymbolResponse, error) {
816928
snapshot, release := s.session.Snapshot()
817929
defer release()
@@ -855,7 +967,9 @@ func isBlockingMethod(method lsproto.Method) bool {
855967
lsproto.MethodTextDocumentDidChange,
856968
lsproto.MethodTextDocumentDidSave,
857969
lsproto.MethodTextDocumentDidClose,
858-
lsproto.MethodWorkspaceDidChangeWatchedFiles:
970+
lsproto.MethodWorkspaceDidChangeWatchedFiles,
971+
lsproto.MethodWorkspaceDidChangeConfiguration,
972+
lsproto.MethodWorkspaceConfiguration:
859973
return true
860974
}
861975
return false

internal/project/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
func (s *Session) OpenProject(ctx context.Context, configFileName string) (*Project, error) {
10-
fileChanges, overlays, ataChanges := s.flushChanges(ctx)
10+
fileChanges, overlays, ataChanges, _ := s.flushChanges(ctx)
1111
newSnapshot := s.UpdateSnapshot(ctx, overlays, SnapshotChange{
1212
fileChanges: fileChanges,
1313
ataChanges: ataChanges,

0 commit comments

Comments
 (0)