github.com/ladydascalie/elvish@v0.0.0-20170703214355-2964dd3ece7f/edit/highlight/emitter.go (about) 1 package highlight 2 3 import ( 4 "strings" 5 6 "github.com/elves/elvish/edit/nodeutil" 7 "github.com/elves/elvish/edit/ui" 8 "github.com/elves/elvish/parse" 9 ) 10 11 type Emitter struct { 12 GoodFormHead func(string) bool 13 AddStyling func(begin, end int, style string) 14 } 15 16 func (e *Emitter) EmitAll(n parse.Node) { 17 switch n := n.(type) { 18 case *parse.Form: 19 e.form(n) 20 case *parse.Primary: 21 e.primary(n) 22 case *parse.Sep: 23 e.sep(n) 24 } 25 for _, child := range n.Children() { 26 e.EmitAll(child) 27 } 28 } 29 30 func (e *Emitter) form(n *parse.Form) { 31 for _, an := range n.Assignments { 32 if an.Left != nil && an.Left.Head != nil { 33 v := an.Left.Head 34 e.AddStyling(v.Begin(), v.End(), styleForGoodVariable.String()) 35 } 36 } 37 for _, cn := range n.Vars { 38 if len(cn.Indexings) > 0 && cn.Indexings[0].Head != nil { 39 v := cn.Indexings[0].Head 40 e.AddStyling(v.Begin(), v.End(), styleForGoodVariable.String()) 41 } 42 } 43 if n.Head != nil { 44 e.formHead(n.Head) 45 // Special forms 46 switch n.Head.SourceText() { 47 case "for": 48 if len(n.Args) >= 1 && len(n.Args[0].Indexings) > 0 { 49 v := n.Args[0].Indexings[0].Head 50 e.AddStyling(v.Begin(), v.End(), styleForGoodVariable.String()) 51 } 52 if len(n.Args) >= 4 && n.Args[3].SourceText() == "else" { 53 a := n.Args[3] 54 e.AddStyling(a.Begin(), a.End(), styleForSep["else"]) 55 } 56 case "try": 57 i := 1 58 highlightKeyword := func(name string) bool { 59 if i >= len(n.Args) { 60 return false 61 } 62 a := n.Args[i] 63 if a.SourceText() != name { 64 return false 65 } 66 e.AddStyling(a.Begin(), a.End(), styleForSep[name]) 67 return true 68 } 69 if highlightKeyword("except") { 70 if i+1 < len(n.Args) && len(n.Args[i+1].Indexings) > 0 { 71 v := n.Args[i+1].Indexings[0] 72 e.AddStyling(v.Begin(), v.End(), styleForGoodVariable.String()) 73 } 74 i += 3 75 } 76 if highlightKeyword("else") { 77 i += 2 78 } 79 highlightKeyword("finally") 80 } 81 // TODO(xiaq): Handle other special forms. 82 } 83 } 84 85 func (e *Emitter) formHead(n *parse.Compound) { 86 simple, head, err := nodeutil.SimpleCompound(n, nil) 87 st := ui.Styles{} 88 if simple { 89 if e.GoodFormHead(head) { 90 st = styleForGoodCommand 91 } else { 92 st = styleForBadCommand 93 } 94 } else if err != nil { 95 st = styleForBadCommand 96 } 97 if len(st) > 0 { 98 e.AddStyling(n.Begin(), n.End(), st.String()) 99 } 100 } 101 102 func (e *Emitter) primary(n *parse.Primary) { 103 e.AddStyling(n.Begin(), n.End(), styleForPrimary[n.Type].String()) 104 } 105 106 func (e *Emitter) sep(n *parse.Sep) { 107 septext := n.SourceText() 108 switch { 109 case strings.TrimSpace(septext) == "": 110 // Don't do anything. Whitespaces don't get any styling. 111 case strings.HasPrefix(septext, "#"): 112 // Comment. 113 e.AddStyling(n.Begin(), n.End(), styleForComment.String()) 114 default: 115 e.AddStyling(n.Begin(), n.End(), styleForSep[septext]) 116 } 117 }