all repos

clerk @ cb22f86145052efb58bb99ca826f29ee6695c6e6

missing tooling for ledger/hledger
5 files changed, 111 insertions(+), 0 deletions(-)
lsp: add config
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/lsp.go
···
        25
        25
         

      
        26
        26
         			openDocs: make(map[uri.URI]docState),

      
        27
        27
         

      
        
        28
        +			config:  DefaultConfig,

      
        28
        29
         			linter:  linter.NewLinter(linter.Rules),

      
        29
        30
         			loader:  journal.NewLoader(),

      
        30
        31
         			printer: printer.DefaultConfig,

      
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