github.com/jmigpin/editor@v1.6.0/util/drawutil/drawer4/parenthesishighlight.go (about)

     1  package drawer4
     2  
     3  import "github.com/jmigpin/editor/util/iout/iorw"
     4  
     5  func updateParenthesisHighlight(d *Drawer) {
     6  	if !d.Opt.ParenthesisHighlight.On {
     7  		d.Opt.ParenthesisHighlight.Group.Ops = nil
     8  		return
     9  	}
    10  
    11  	if d.opt.parenthesisH.updated {
    12  		return
    13  	}
    14  	d.opt.parenthesisH.updated = true
    15  
    16  	d.Opt.ParenthesisHighlight.Group.Ops = parenthesisHOps(d, 5000)
    17  }
    18  
    19  //----------
    20  
    21  func parenthesisHOps(d *Drawer, maxDist int) []*ColorizeOp {
    22  	if !d.Opt.Cursor.On {
    23  		return nil
    24  	}
    25  
    26  	pairs := []rune{'{', '}', '(', ')', '[', ']'}
    27  	ci := d.opt.cursor.offset
    28  	pi, ok := parenthesisFindPair(d, pairs, ci)
    29  	if !ok {
    30  		// try match the previous parenthesis
    31  		ci--
    32  		if ci < 0 {
    33  			return nil
    34  		}
    35  		pi, ok = parenthesisFindPair(d, pairs, ci)
    36  		if !ok {
    37  			return nil
    38  		}
    39  	}
    40  
    41  	// assign open/close parenthesis
    42  	var open, close rune
    43  	isOpen := pi%2 == 0
    44  	var nextRune func() (rune, int, error)
    45  	if isOpen {
    46  		open, close = pairs[pi], pairs[pi+1]
    47  		ri := ci + len(string(open))
    48  		nextRune = func() (rune, int, error) {
    49  			ru, size, err := iorw.ReadRuneAt(d.reader, ri)
    50  			if err != nil {
    51  				return 0, 0, err
    52  			}
    53  			ri2 := ri
    54  			ri += size
    55  			return ru, ri2, nil
    56  		}
    57  	} else {
    58  		open, close = pairs[pi], pairs[pi-1]
    59  		ri := ci
    60  		nextRune = func() (rune, int, error) {
    61  			ru, size, err := iorw.ReadLastRuneAt(d.reader, ri)
    62  			if err != nil {
    63  				return 0, 0, err
    64  			}
    65  			ri -= size
    66  			return ru, ri, nil
    67  		}
    68  	}
    69  
    70  	// colorize open
    71  	op1 := &ColorizeOp{
    72  		Offset: ci,
    73  		Fg:     d.Opt.ParenthesisHighlight.Fg,
    74  		Bg:     d.Opt.ParenthesisHighlight.Bg,
    75  	}
    76  	op2 := &ColorizeOp{Offset: ci + len(string(open))}
    77  	var ops []*ColorizeOp
    78  	ops = append(ops, op1, op2)
    79  
    80  	// find parenthesis
    81  	match := 0
    82  	for i := 0; i < maxDist; i++ {
    83  		ru, ri, err := nextRune()
    84  		if err != nil {
    85  			break
    86  		}
    87  		if ru == open {
    88  			match++
    89  		} else if ru == close {
    90  			if match > 0 {
    91  				match--
    92  			} else {
    93  				// colorize close
    94  				op1 := &ColorizeOp{
    95  					Offset: ri,
    96  					Fg:     d.Opt.ParenthesisHighlight.Fg,
    97  					Bg:     d.Opt.ParenthesisHighlight.Bg,
    98  				}
    99  				op2 := &ColorizeOp{Offset: ri + len(string(close))}
   100  				ops = append(ops, op1, op2)
   101  				if !isOpen {
   102  					// invert order
   103  					l := len(ops)
   104  					ops[l-4], ops[l-2] = ops[l-2], ops[l-4]
   105  					ops[l-3], ops[l-1] = ops[l-1], ops[l-3]
   106  				}
   107  				break
   108  			}
   109  		}
   110  	}
   111  
   112  	return ops
   113  }
   114  
   115  func parenthesisFindPair(d *Drawer, pairs []rune, ci int) (int, bool) {
   116  	// read current rune
   117  	cru, _, err := iorw.ReadRuneAt(d.reader, ci)
   118  	if err != nil {
   119  		return 0, false
   120  	}
   121  
   122  	// find parenthesis type
   123  	var pi int
   124  	for ; pi < len(pairs); pi++ {
   125  		if pairs[pi] == cru {
   126  			break
   127  		}
   128  	}
   129  	return pi, pi < len(pairs)
   130  }