src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/edit/filter/highlight.go (about) 1 package filter 2 3 import ( 4 "strings" 5 6 "src.elv.sh/pkg/diag" 7 "src.elv.sh/pkg/parse" 8 "src.elv.sh/pkg/parse/cmpd" 9 "src.elv.sh/pkg/ui" 10 ) 11 12 func Highlight(q string) (ui.Text, []ui.Text) { 13 n, _ := parseFilter(q) 14 w := walker{} 15 w.walk(n) 16 text := ui.StyleRegions(q, w.regions) 17 // TODO: Add errors. 18 return text, nil 19 } 20 21 type walker struct { 22 regions []ui.StylingRegion 23 } 24 25 func (w *walker) emit(r diag.Ranger, s ui.Styling) { 26 region := ui.StylingRegion{Ranging: r.Range(), Styling: s} 27 w.regions = append(w.regions, region) 28 } 29 30 func (w *walker) walk(n parse.Node) { 31 switch n := n.(type) { 32 case *parse.Sep: 33 w.walkSep(n) 34 case *parse.Primary: 35 w.walkPrimary(n) 36 } 37 for _, ch := range parse.Children(n) { 38 w.walk(ch) 39 } 40 } 41 42 func (w *walker) walkSep(n *parse.Sep) { 43 text := parse.SourceText(n) 44 trimmed := strings.TrimLeftFunc(text, parse.IsWhitespace) 45 if trimmed == "" { 46 // Whitespace; nothing to do. 47 return 48 } 49 // Metacharacter; style it bold. 50 w.emit(n, ui.Bold) 51 } 52 53 func (w *walker) walkPrimary(n *parse.Primary) { 54 switch n.Type { 55 case parse.Bareword: 56 // Barewords are unstyled. 57 case parse.SingleQuoted, parse.DoubleQuoted: 58 w.emit(n, ui.FgYellow) 59 case parse.List: 60 if len(n.Elements) == 0 { 61 w.emit(n, ui.FgRed) 62 return 63 } 64 headNode := n.Elements[0] 65 head, ok := cmpd.StringLiteral(headNode) 66 if !ok { 67 w.emit(headNode, ui.FgRed) 68 } 69 switch head { 70 case "re", "and", "or": 71 w.emit(headNode, ui.FgGreen) 72 default: 73 w.emit(headNode, ui.FgRed) 74 } 75 default: 76 // Unsupported primary type. 77 w.emit(n, ui.FgRed) 78 } 79 }