github.com/jmigpin/editor@v1.6.0/util/iout/iorw/rwedit/comment.go (about)

     1  package rwedit
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"unicode"
     8  
     9  	"github.com/jmigpin/editor/util/iout/iorw"
    10  )
    11  
    12  func Comment(ctx *Ctx) error {
    13  	cstrb := []byte(ctx.Fns.LineCommentStr())
    14  	if len(cstrb) == 0 {
    15  		return nil
    16  	}
    17  
    18  	a, b, newline, err := ctx.CursorSelectionLinesIndexes()
    19  	if err != nil {
    20  		return err
    21  	}
    22  
    23  	isSpaceExceptNewline := func(ru rune) bool {
    24  		return unicode.IsSpace(ru) && ru != '\n'
    25  	}
    26  
    27  	// find smallest comment insertion index
    28  	ii := 1000
    29  	for i := a; i < b; {
    30  		// find insertion index
    31  		rd := ctx.LocalReader(i)
    32  		j, _, err := iorw.RuneIndexFn(rd, i, false, isSpaceExceptNewline)
    33  		if err != nil && !errors.Is(err, io.EOF) {
    34  			return err
    35  		}
    36  
    37  		rd2 := ctx.LocalReader(j)
    38  		u, _, err := iorw.LineEndIndex(rd2, j)
    39  		if err != nil {
    40  			return err
    41  		}
    42  
    43  		// ignore empty lines (j==u all spaces) and keep best
    44  		if j != u && j-i < ii {
    45  			ii = j - i
    46  		}
    47  
    48  		i = u
    49  	}
    50  
    51  	// insert comment
    52  	lines := 0
    53  	for i := a; i < b; {
    54  		rd2 := ctx.LocalReader(i)
    55  		u, _, err := iorw.LineEndIndex(rd2, i)
    56  		if err != nil {
    57  			return err
    58  		}
    59  
    60  		// ignore empty lines
    61  		s, err := ctx.RW.ReadFastAt(i, u-i)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		empty := len(bytes.TrimSpace(s)) == 0
    66  
    67  		if !empty {
    68  			lines++
    69  			if err := ctx.RW.OverwriteAt(i+ii, 0, cstrb); err != nil {
    70  				return err
    71  			}
    72  			b += len(cstrb)
    73  			u += len(cstrb)
    74  		}
    75  
    76  		i = u
    77  	}
    78  
    79  	if lines == 0 {
    80  		// do nothing
    81  	} else if lines == 1 {
    82  		// move cursor to the right due to inserted runes
    83  		ctx.C.SetSelectionOff()
    84  		ci := ctx.C.Index()
    85  		if ci-a >= ii {
    86  			ctx.C.SetIndex(ci + len(cstrb))
    87  		}
    88  	} else {
    89  		// cursor index without the newline
    90  		if newline {
    91  			b--
    92  		}
    93  
    94  		ctx.C.SetSelection(a, b)
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  func Uncomment(ctx *Ctx) error {
   101  	cstrb := []byte(ctx.Fns.LineCommentStr())
   102  	if len(cstrb) == 0 {
   103  		return nil
   104  	}
   105  
   106  	a, b, newline, err := ctx.CursorSelectionLinesIndexes()
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	// remove comments
   112  	lines := 0
   113  	ci := ctx.C.Index()
   114  	for i := a; i < b; {
   115  		// first non space rune (possible multiline jump)
   116  		rd := ctx.LocalReader(i)
   117  		j, _, err := iorw.RuneIndexFn(rd, i, false, unicode.IsSpace)
   118  		if err != nil {
   119  			break
   120  		}
   121  		i = j
   122  
   123  		// remove comment runes
   124  		if iorw.HasPrefix(ctx.RW, i, cstrb) {
   125  			lines++
   126  			if err := ctx.RW.OverwriteAt(i, len(cstrb), nil); err != nil {
   127  				return err
   128  			}
   129  			b -= len(cstrb)
   130  			if i < ci {
   131  				// ci in between the comment string (comment len >=2)
   132  				if i+len(cstrb) > ci {
   133  					ci -= (i + len(cstrb)) - ci
   134  				} else {
   135  					ci -= len(cstrb)
   136  				}
   137  			}
   138  		}
   139  
   140  		// go to end of line
   141  		rd2 := ctx.LocalReader(i)
   142  		u, _, err := iorw.LineEndIndex(rd2, i)
   143  		if err != nil {
   144  			return err
   145  		}
   146  		i = u
   147  	}
   148  
   149  	if lines == 0 {
   150  		// do nothing
   151  	} else if lines == 1 {
   152  		// move cursor to the left due to deleted runes
   153  		ctx.C.SetSelectionOff()
   154  		ctx.C.SetIndex(ci)
   155  	} else {
   156  		// cursor index without the newline
   157  		if newline {
   158  			b--
   159  		}
   160  
   161  		ctx.C.SetSelection(a, b)
   162  	}
   163  
   164  	return nil
   165  }