5 files changed,
111 insertions(+),
0 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-08-02 20:37:33 +0300
Authored at:
2026-07-31 18:00:22 +0300
Change ID:
uuvppltvorwuolxksqxzunorxsuutuxo
Parent:
4ad565d
A
internal/lsp/config.go
··· 1 +package lsp 2 + 3 +import ( 4 + "github.com/go-json-experiment/json" 5 + "go.lsp.dev/protocol" 6 +) 7 + 8 +type Config struct { 9 + SemanticHighlighting bool 10 + 11 + // TODO: Formatter 12 + // TODO: Linter 13 +} 14 + 15 +var DefaultConfig = Config{ 16 + SemanticHighlighting: true, 17 +} 18 + 19 +func (c *Config) merge(v protocol.LSPAny) { 20 + var patch struct { 21 + SemanticHighlighting *bool `json:"semantic_highlighting,case:ignore"` 22 + } 23 + 24 + if err := json.Unmarshal(v, &patch); err != nil { 25 + return 26 + } 27 + if patch.SemanticHighlighting != nil { 28 + c.SemanticHighlighting = *patch.SemanticHighlighting 29 + } 30 +} 31 + 32 +func (s *server) semanticHighlightingEnabled() bool { 33 + s.cfgMu.RLock() 34 + defer s.cfgMu.RUnlock() 35 + return s.config.SemanticHighlighting 36 +}
A
internal/lsp/config_test.go
··· 1 +package lsp 2 + 3 +import ( 4 + "testing" 5 + 6 + "go.lsp.dev/protocol" 7 +) 8 + 9 +func TestConfigMerge(t *testing.T) { 10 + tests := map[string]struct { 11 + cfg Config 12 + in string 13 + want Config 14 + }{ 15 + "if not set, uses default": {Config{}, `{"semanticHighlighting": true}`, Config{SemanticHighlighting: true}}, 16 + "empty object keeps defaults": {DefaultConfig, `{}`, Config{SemanticHighlighting: true}}, 17 + "nil settings keep defaults": {DefaultConfig, "", Config{SemanticHighlighting: true}}, 18 + 19 + "disables provided option": {DefaultConfig, `{"semanticHighlighting": false}`, Config{SemanticHighlighting: false}}, 20 + "snake_case key": {DefaultConfig, `{"semantic_highlighting": false}`, Config{SemanticHighlighting: false}}, 21 + "kebab-case key": {DefaultConfig, `{"semantic-highlighting": false}`, Config{SemanticHighlighting: false}}, 22 + 23 + "malformed settings ignored": {Config{SemanticHighlighting: true}, `{`, Config{SemanticHighlighting: true}}, 24 + "non-object settings ignored": {Config{SemanticHighlighting: true}, `"clerk"`, Config{SemanticHighlighting: true}}, 25 + "unknown fields ignored": {Config{SemanticHighlighting: true}, `{"lint": true}`, Config{SemanticHighlighting: true}}, 26 + } 27 + for name, tt := range tests { 28 + t.Run(name, func(t *testing.T) { 29 + tt.cfg.merge(protocol.LSPAny(tt.in)) 30 + if tt.cfg != tt.want { 31 + t.Errorf("merge(%q) = %+v, want %+v", tt.in, tt.cfg, tt.want) 32 + } 33 + }) 34 + } 35 +} 36 + 37 +func TestInitialize_AppliesInitializationOptions(t *testing.T) { 38 + srv := NewServer("test") 39 + res, err := srv.server.Initialize(t.Context(), &protocol.InitializeParams{ 40 + InitializationOptions: protocol.LSPAny(`{"semanticHighlighting": false}`), 41 + }) 42 + if err != nil { 43 + t.Fatal(err) 44 + } 45 + if srv.server.semanticHighlightingEnabled() { 46 + t.Error("semanticHighlighting=false in initializationOptions not applied") 47 + } 48 + if res.Capabilities.SemanticTokensProvider == nil { 49 + t.Error("SemanticTokensProvider must always be advertised") 50 + } 51 +}
M
internal/lsp/server.go
··· 31 31 openDocs map[uri.URI]docState 32 32 current *analyzer.Analysis 33 33 34 + cfgMu sync.RWMutex 35 + config Config 36 + 34 37 diagMu sync.Mutex 35 38 diagCancel context.CancelFunc 36 39 } ··· 43 46 } 44 47 45 48 func (s *server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) { 49 + s.applySettings(params.InitializationOptions) 46 50 return &protocol.InitializeResult{ 47 51 ServerInfo: protocol.ServerInfo{ 48 52 Name: s.name, ··· 68 72 return nil 69 73 } 70 74 75 +func (s *server) DidChangeConfiguration(ctx context.Context, params *protocol.DidChangeConfigurationParams) error { 76 + s.applySettings(params.Settings) 77 + return nil 78 +} 79 + 71 80 func (s *server) Shutdown(ctx context.Context) error { 72 81 return nil 73 82 } ··· 75 84 func (s *server) Exit(ctx context.Context) error { 76 85 return nil 77 86 } 87 + 88 +func (s *server) applySettings(v protocol.LSPAny) { 89 + s.cfgMu.Lock() 90 + s.config.merge(v) 91 + s.cfgMu.Unlock() 92 +}
M
internal/lsp/textdocument_semantic.go
··· 14 14 ) 15 15 16 16 func (s *server) SemanticTokensFull(ctx context.Context, params *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) { 17 + if !s.semanticHighlightingEnabled() { 18 + return &protocol.SemanticTokens{}, nil 19 + } 20 + 17 21 tokens, ok := s.tokensForDoc(params.TextDocument.URI) 18 22 if !ok { 19 23 return &protocol.SemanticTokens{}, nil ··· 22 26 } 23 27 24 28 func (s *server) SemanticTokensRange(ctx context.Context, params *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) { 29 + if !s.semanticHighlightingEnabled() { 30 + return &protocol.SemanticTokens{}, nil 31 + } 32 + 25 33 tokens, ok := s.tokensForDoc(params.TextDocument.URI) 26 34 if !ok { 27 35 return &protocol.SemanticTokens{}, nil