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  }