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 }