all repos

clerk @ 05a35537a550130f7a4cce8aa0551b08ae7dba84

missing tooling for ledger/hledger
9 files changed, 53 insertions(+), 26 deletions(-)
ast: add enum for subdirective kinds
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-08-16 16:32:29 +0300
Authored at: 2026-08-16 14:01:55 +0300
Change ID: rtvlwmlpnztqznzlsyoksxossxwlsvnx
Parent: ed49a0d
M internal/analyzer/build.go
···
        117
        117
         	}

      
        118
        118
         	info.Directives = append(info.Directives, ad)

      
        119
        119
         	for _, sd := range ad.Subdirectives {

      
        120
        
        -		if sd.Name == "alias" {

      
        
        120
        +		if sd.Kind == ast.SubdirectiveAlias {

      
        121
        121
         			a.AccountAliases[sd.Value] = aname

      
        122
        122
         		}

      
        123
        123
         	}

      
M internal/linter/rule_invalid_type_tag.go
···
        37
        37
         			}

      
        38
        38
         		}

      
        39
        39
         		for _, sd := range ad.Subdirectives {

      
        40
        
        -			if sd.Name != "type" || sd.Value == "" {

      
        
        40
        +			if sd.Kind != ast.SubdirectiveType || sd.Value == "" {

      
        41
        41
         				// empty values are flagged by the parser

      
        42
        42
         				continue

      
        43
        43
         			}

      
M internal/lsp/textdocument_hover.go
···
        118
        118
         			return &hoverElement{kind: hoverAccount, span: e.Account.Span, name: e.Account.String()}

      
        119
        119
         		}

      
        120
        120
         		for _, sd := range e.Subdirectives {

      
        121
        
        -			if sd.Name == "alias" && spanContains(content, sd.ValueSpan, cursor) {

      
        
        121
        +			if sd.Kind == ast.SubdirectiveAlias && spanContains(content, sd.ValueSpan, cursor) {

      
        122
        122
         				return &hoverElement{kind: hoverAccount, span: sd.ValueSpan, name: sd.Value}

      
        123
        123
         			}

      
        124
        124
         		}

      
M internal/lsp/textdocument_rename.go
···
        130
        130
         			return &symbolRef{symbolAccount, e.Account.String(), e.Account.Span}

      
        131
        131
         		}

      
        132
        132
         		for _, sd := range e.Subdirectives {

      
        133
        
        -			if sd.Name == "alias" && spanContains(content, sd.ValueSpan, cursor) {

      
        
        133
        +			if sd.Kind == ast.SubdirectiveAlias && spanContains(content, sd.ValueSpan, cursor) {

      
        134
        134
         				return &symbolRef{symbolAccount, sd.Value, sd.ValueSpan}

      
        135
        135
         			}

      
        136
        136
         		}

      ···
        304
        304
         				add(fileIdx, d.Account.Span, newName+strings.TrimPrefix(d.Account.String(), ref.name))

      
        305
        305
         			}

      
        306
        306
         			for _, sd := range d.Subdirectives {

      
        307
        
        -				if sd.Name == "alias" && accountMatches(sd.Value, ref.name) {

      
        
        307
        +				if sd.Kind == ast.SubdirectiveAlias && accountMatches(sd.Value, ref.name) {

      
        308
        308
         					add(fileIdx, sd.ValueSpan, newName+strings.TrimPrefix(sd.Value, ref.name))

      
        309
        309
         				}

      
        310
        310
         			}

      
M internal/lsp/textdocument_semantic_tokens.go
···
        215
        215
         		emit(directiveKeyword(e.Span, "account"), semDirective, 0)

      
        216
        216
         		emit(e.Account.Span, semAccount, 0)

      
        217
        217
         		for _, sd := range e.Subdirectives {

      
        218
        
        -			if sd.Name == "" {

      
        
        218
        +			if sd.Kind == ast.SubdirectiveComment {

      
        219
        219
         				emitComment(sd.Comment, emit)

      
        220
        220
         				continue

      
        221
        221
         			}

      
        222
        222
         			emit(sd.NameSpan, semDirective, 0)

      
        223
        
        -			switch sd.Name {

      
        224
        
        -			case "alias":

      
        
        223
        +			switch sd.Kind {

      
        
        224
        +			case ast.SubdirectiveAlias:

      
        225
        225
         				emit(sd.ValueSpan, semAccount, 0)

      
        226
        
        -			case "type", "note":

      
        
        226
        +			case ast.SubdirectiveType, ast.SubdirectiveNote:

      
        227
        227
         				emit(sd.ValueSpan, semProperty, 0)

      
        228
        228
         			}

      
        229
        229
         			emitComment(sd.Comment, emit)

      
M journal/ast/directives.go
···
        11
        11
         

      
        12
        12
         func (AccountDirective) entryNode() {}

      
        13
        13
         

      
        
        14
        +type AccountSubdirectiveKind uint8

      
        
        15
        +

      
        
        16
        +const (

      
        
        17
        +	SubdirectiveComment AccountSubdirectiveKind = iota

      
        
        18
        +	SubdirectiveAlias

      
        
        19
        +	SubdirectiveType

      
        
        20
        +	SubdirectiveNote

      
        
        21
        +)

      
        
        22
        +

      
        
        23
        +func (k AccountSubdirectiveKind) String() string {

      
        
        24
        +	switch k {

      
        
        25
        +	case SubdirectiveAlias:

      
        
        26
        +		return "alias"

      
        
        27
        +	case SubdirectiveType:

      
        
        28
        +		return "type"

      
        
        29
        +	case SubdirectiveNote:

      
        
        30
        +		return "note"

      
        
        31
        +	default:

      
        
        32
        +		panic("unreachable")

      
        
        33
        +	}

      
        
        34
        +}

      
        
        35
        +

      
        14
        36
         type AccountSubdirective struct {

      
        15
        
        -	Name      string     // "alias" / "type" / "note" / "" for comment lines

      
        16
        
        -	NameSpan  token.Span // keyword span; for comment lines, the marker span

      
        17
        
        -	Value     string     // text after the keyword; "" for comment lines

      
        18
        
        -	ValueSpan token.Span // value span; zero for comment lines and empty values

      
        19
        
        -	Comment   *Comment   // inline comment after the value; for comment lines, the line itself

      
        
        37
        +	Kind      AccountSubdirectiveKind // keyword; SubdirectiveComment for comment lines

      
        
        38
        +	NameSpan  token.Span              // keyword span; for comment lines, the marker span

      
        
        39
        +	Value     string                  // text after the keyword; "" for comment lines

      
        
        40
        +	ValueSpan token.Span              // value span; zero for comment lines and empty values

      
        
        41
        +	Comment   *Comment                // inline comment after the value; for comment lines, the line itself

      
        20
        42
         }

      
        21
        43
         

      
        22
        44
         type CommodityDirective struct {

      
M journal/ast/dump.go
···
        321
        321
         	fmt.Fprintf(b, "AccountDirective %s\n", a.Span)

      
        322
        322
         	dumpAccount(b, a.Account, depth+1)

      
        323
        323
         	for _, sd := range a.Subdirectives {

      
        324
        
        -		if sd.Name == "" {

      
        
        324
        +		if sd.Kind == SubdirectiveComment {

      
        325
        325
         			dumpComment(b, sd.Comment, depth+1)

      
        326
        326
         			continue

      
        327
        327
         		}

      
        328
        328
         		indent(b, depth+1)

      
        329
        329
         		fmt.Fprintf(b, "Subdirective\n")

      
        330
        330
         		indent(b, depth+2)

      
        331
        
        -		fmt.Fprintf(b, "Name: %q %s\n", sd.Name, sd.NameSpan)

      
        
        331
        +		fmt.Fprintf(b, "Name: %q %s\n", sd.Kind.String(), sd.NameSpan)

      
        332
        332
         		indent(b, depth+2)

      
        333
        333
         		fmt.Fprintf(b, "Value: %q %s\n", sd.Value, sd.ValueSpan)

      
        334
        334
         		dumpOptComment(b, sd.Comment, depth+2)

      
M journal/parser/parser.go
···
        357
        357
         			s := p.cur.Span

      
        358
        358
         			c := p.parseCommentRest(s)

      
        359
        359
         			p.expectNewline()

      
        360
        
        -			subs = append(subs, ast.AccountSubdirective{NameSpan: s, Comment: c})

      
        
        360
        +			subs = append(subs, ast.AccountSubdirective{Kind: ast.SubdirectiveComment, NameSpan: s, Comment: c})

      
        361
        361
         		case p.got(token.TEXT) && isCommentMarker(p.cur.Literal):

      
        362
        362
         			// '#', '%' and '*' lex as TEXT in directive mode; treat them as comment lines

      
        363
        363
         			c := p.parseTextComment()

      
        364
        
        -			subs = append(subs, ast.AccountSubdirective{NameSpan: c.Span, Comment: c})

      
        
        364
        +			subs = append(subs, ast.AccountSubdirective{Kind: ast.SubdirectiveComment, NameSpan: c.Span, Comment: c})

      
        365
        365
         		case p.got(token.TEXT):

      
        366
        366
         			name := p.cur.Literal

      
        367
        
        -			if !isAccountSubdirective(name) {

      
        
        367
        +			kind, ok := accountSubdirectiveKind(name)

      
        
        368
        +			if !ok {

      
        368
        369
         				p.errorf("unknown subdirective %q", name)

      
        369
        370
         				p.skipToNewline()

      
        370
        371
         				continue

      ···
        378
        379
         			c := p.parseOptInlineComment()

      
        379
        380
         			p.expectNewline()

      
        380
        381
         			subs = append(subs, ast.AccountSubdirective{

      
        381
        
        -				Name:      name,

      
        
        382
        +				Kind:      kind,

      
        382
        383
         				NameSpan:  kw,

      
        383
        384
         				Value:     value,

      
        384
        385
         				ValueSpan: vspan,

      ···
        1274
        1275
         	})

      
        1275
        1276
         }

      
        1276
        1277
         

      
        1277
        
        -func isAccountSubdirective(name string) bool {

      
        
        1278
        +func accountSubdirectiveKind(name string) (ast.AccountSubdirectiveKind, bool) {

      
        1278
        1279
         	switch name {

      
        1279
        
        -	case "alias", "type", "note":

      
        1280
        
        -		return true

      
        
        1280
        +	case "alias":

      
        
        1281
        +		return ast.SubdirectiveAlias, true

      
        
        1282
        +	case "type":

      
        
        1283
        +		return ast.SubdirectiveType, true

      
        
        1284
        +	case "note":

      
        
        1285
        +		return ast.SubdirectiveNote, true

      
        1281
        1286
         	}

      
        1282
        
        -	return false

      
        
        1287
        +	return 0, false

      
        1283
        1288
         }

      
        1284
        1289
         

      
        1285
        1290
         func isCommentMarker(s string) bool {

      
M journal/printer/directives.go
···
        20
        20
         	for _, sd := range a.Subdirectives {

      
        21
        21
         		p.buf.WriteByte('\n')

      
        22
        22
         		p.buf.WriteString(p.indent)

      
        23
        
        -		if sd.Name == "" {

      
        
        23
        +		if sd.Kind == ast.SubdirectiveComment {

      
        24
        24
         			p.writeComment(sd.Comment)

      
        25
        25
         			continue

      
        26
        26
         		}

      
        27
        
        -		p.buf.WriteString(sd.Name)

      
        
        27
        +		p.buf.WriteString(sd.Kind.String())

      
        28
        28
         		p.buf.WriteByte(' ')

      
        29
        29
         		p.buf.WriteString(sd.Value)

      
        30
        30
         		p.writeInlineComment(sd.Comment)