First step towards implementing code folding
This commit is contained in:
parent
5510317942
commit
dbf524615c
8 changed files with 361 additions and 33 deletions
|
@ -1283,6 +1283,26 @@ func (h *BufPane) MoveLinesDown() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// FoldCollapse
|
||||
func (h *BufPane) FoldCollapse() bool {
|
||||
return h.Buf.FoldCollapse(h.Cursor.Loc.Y, false, false)
|
||||
}
|
||||
|
||||
// FoldCollapseRecursive
|
||||
func (h *BufPane) FoldCollapseRecursive() bool {
|
||||
return h.Buf.FoldCollapse(h.Cursor.Loc.Y, false, true)
|
||||
}
|
||||
|
||||
// FoldExpand
|
||||
func (h *BufPane) FoldExpand() bool {
|
||||
return h.Buf.FoldCollapse(h.Cursor.Loc.Y, true, false)
|
||||
}
|
||||
|
||||
// FoldExpandRecursive
|
||||
func (h *BufPane) FoldExpandRecursive() bool {
|
||||
return h.Buf.FoldCollapse(h.Cursor.Loc.Y, true, true)
|
||||
}
|
||||
|
||||
// Paste whatever is in the system clipboard into the buffer
|
||||
// Delete and paste if the user has a selection
|
||||
func (h *BufPane) Paste() bool {
|
||||
|
|
|
@ -757,6 +757,10 @@ var BufKeyActions = map[string]BufKeyAction{
|
|||
"DeleteLine": (*BufPane).DeleteLine,
|
||||
"MoveLinesUp": (*BufPane).MoveLinesUp,
|
||||
"MoveLinesDown": (*BufPane).MoveLinesDown,
|
||||
"FoldCollapse": (*BufPane).FoldCollapse,
|
||||
"FoldCollapseRecursive": (*BufPane).FoldCollapseRecursive,
|
||||
"FoldExpand": (*BufPane).FoldExpand,
|
||||
"FoldExpandRecursive": (*BufPane).FoldExpandRecursive,
|
||||
"IndentSelection": (*BufPane).IndentSelection,
|
||||
"OutdentSelection": (*BufPane).OutdentSelection,
|
||||
"Autocomplete": (*BufPane).Autocomplete,
|
||||
|
@ -877,6 +881,10 @@ var MultiActions = map[string]bool{
|
|||
"DeleteLine": true,
|
||||
"MoveLinesUp": true,
|
||||
"MoveLinesDown": true,
|
||||
"FoldCollapse": true,
|
||||
"FoldCollapseRecursive": true,
|
||||
"FoldExpand": true,
|
||||
"FoldExpandRecursive": true,
|
||||
"IndentSelection": true,
|
||||
"OutdentSelection": true,
|
||||
"OutdentLine": true,
|
||||
|
|
|
@ -157,11 +157,121 @@ func (b *SharedBuffer) MarkModified(start, end int) {
|
|||
b.Highlighter.HighlightMatches(b, start, l)
|
||||
}
|
||||
|
||||
b.DepthAssign(start, end + 1)
|
||||
|
||||
for i := start; i <= end; i++ {
|
||||
b.LineArray.invalidateSearchMatches(i)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: A parser for the applicable language should parse the buffer and assign the AST depth of each line.
|
||||
// If different tokens from the same line have different depths, assign the minimum of them all.
|
||||
// Of course, it is also acceptable to use other algorithms that produce acceptable results, maybe
|
||||
// something like counting openning and closing brackets for the appropriate filetype.
|
||||
func (b *SharedBuffer) DepthAssign(start, end int) {
|
||||
// TODO: When a parser for the applicable language would be overkill, add an entry to the runtime/syntax/*.yaml file to select one of the predefined algorithms.
|
||||
ft := b.FileType()
|
||||
offSide := ft == "cmake" || ft == "coffeescript" || ft == "elm" || ft == "gdscript" || ft == "haml" || ft == "makefile" || ft == "nim" || ft == "python" || ft == "python2" || ft == "yaml"
|
||||
c := ft == "c" || ft == "c++"
|
||||
modified := false
|
||||
str := highlight.Groups["constant.string"];
|
||||
cmt := highlight.Groups["comment"];
|
||||
tod := highlight.Groups["todo"];
|
||||
err := highlight.Groups["error"];
|
||||
preproc := highlight.Groups["preproc"];
|
||||
|
||||
prevgrp := b.GetGroup(Loc{0, start}.left(b.LineArray))
|
||||
prev_cmt_str := prevgrp == str || prevgrp == cmt || prevgrp == tod || prevgrp == err
|
||||
|
||||
for y := start; y < end; y++ {
|
||||
min_d := 0
|
||||
if offSide {
|
||||
// depth set by indentation
|
||||
l := b.LineBytes(y)
|
||||
t := len(l)
|
||||
w := len(util.GetLeadingWhitespace(l))
|
||||
if t > w {
|
||||
min_d = w
|
||||
} else if y > 0 {
|
||||
min_d = b.lines[y-1].min_depth
|
||||
}
|
||||
} else {
|
||||
// depth set by braces
|
||||
d := 0
|
||||
if y > 0 {
|
||||
d = b.lines[y-1].depth
|
||||
}
|
||||
min_d = d
|
||||
l := []rune(string(b.LineBytes(y)))
|
||||
for x := 0; x < len(l); x++ {
|
||||
grp := b.GetGroup(Loc{x, y})
|
||||
cmt_str := grp == str || grp == cmt || grp == tod || grp == err
|
||||
if prev_cmt_str != cmt_str {
|
||||
prev_cmt_str = cmt_str
|
||||
if cmt_str {
|
||||
d++
|
||||
} else {
|
||||
d--
|
||||
if min_d > d {
|
||||
min_d = d
|
||||
}
|
||||
}
|
||||
}
|
||||
if cmt_str {
|
||||
continue
|
||||
}
|
||||
if c && grp == preproc && l[x] == '#' {
|
||||
i := x + 1
|
||||
for ; l[i] == ' ' || l[i] == '\t' ; i++ {}
|
||||
if l[i] == 'i' && l[i+1] == 'f' { // if
|
||||
d++
|
||||
} else if l[i] == 'e' && (l[i+1] == 'l' || l[i+1] == 'n') { // else, elif, endif
|
||||
d--
|
||||
if min_d > d {
|
||||
min_d = d
|
||||
}
|
||||
if l[i+1] == 'l' { // else, elif
|
||||
d++
|
||||
}
|
||||
}
|
||||
}
|
||||
if l[x] == '{' || l[x] == '(' || l[x] == '[' {
|
||||
d++
|
||||
} else if (l[x] == '}' || l[x] == ')' || l[x] == ']') && d > 0 {
|
||||
d--
|
||||
if min_d > d {
|
||||
min_d = d
|
||||
}
|
||||
}
|
||||
}
|
||||
if b.lines[y].depth != d {
|
||||
b.lines[y].depth = d
|
||||
modified = true
|
||||
if y + 1 == end && end < b.LinesNum() {
|
||||
end++
|
||||
}
|
||||
}
|
||||
}
|
||||
if b.lines[y].min_depth != min_d {
|
||||
b.lines[y].min_depth = min_d
|
||||
modified = true
|
||||
if y + 1 == end && end < b.LinesNum() {
|
||||
end++
|
||||
}
|
||||
}
|
||||
}
|
||||
if modified {
|
||||
if start > 0 {
|
||||
start--
|
||||
}
|
||||
for lineN := start; lineN < end; lineN++ {
|
||||
if b.lines[lineN].collapsed && !b.FoldCollapsible(lineN) {
|
||||
b.lines[lineN].collapsed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DisableReload disables future reloads of this sharedbuffer
|
||||
func (b *SharedBuffer) DisableReload() {
|
||||
b.ReloadDisabled = true
|
||||
|
@ -511,10 +621,99 @@ func (b *Buffer) Remove(start, end Loc) {
|
|||
}
|
||||
|
||||
// FileType returns the buffer's filetype
|
||||
func (b *Buffer) FileType() string {
|
||||
func (b *SharedBuffer) FileType() string {
|
||||
return b.Settings["filetype"].(string)
|
||||
}
|
||||
|
||||
// lineN is collapsible if min_depth(lineN) < min_depth(lineN+1)
|
||||
func (b *SharedBuffer) FoldCollapsible(lineN int) bool {
|
||||
return lineN + 1 < b.LinesNum() && b.lines[lineN + 1].min_depth > b.lines[lineN].min_depth
|
||||
}
|
||||
|
||||
// lineN is the end of a collapsible region if min_depth(lineN-1) > min_depth(lineN)
|
||||
func (b *SharedBuffer) FoldCollapseEnd(lineN int) bool {
|
||||
return lineN > 0 && b.lines[lineN - 1].min_depth > b.lines[lineN].min_depth
|
||||
}
|
||||
|
||||
// lineN is hidden if any collapsible parent is collapsed
|
||||
func (b *Buffer) Hidden(lineN int) bool {
|
||||
d := b.lines[lineN].min_depth
|
||||
for lineT := lineN - 1; lineT >= 0 && d > 0; lineT-- {
|
||||
if d > b.lines[lineT].min_depth {
|
||||
d = b.lines[lineT].min_depth
|
||||
if b.lines[lineT].collapsed {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// lineN must be collapsible
|
||||
func (b *Buffer) FoldCollapseOne(lineN int, recursive bool) {
|
||||
b.lines[lineN].collapsed = true
|
||||
if !recursive {
|
||||
return
|
||||
}
|
||||
d := b.lines[lineN].min_depth
|
||||
for lineT := lineN + 1; lineT < b.LinesNum() && b.lines[lineT].min_depth > d; lineT++ {
|
||||
if b.FoldCollapsible(lineT) {
|
||||
b.lines[lineT].collapsed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lineN should be collapsible
|
||||
func (b *Buffer) FoldExpandOne(lineN int, recursive bool) {
|
||||
b.lines[lineN].collapsed = false
|
||||
if !recursive {
|
||||
return
|
||||
}
|
||||
d := b.lines[lineN].min_depth
|
||||
for lineT := lineN + 1; lineT < b.LinesNum() && b.lines[lineT].min_depth > d; lineT++ {
|
||||
b.lines[lineT].collapsed = false
|
||||
}
|
||||
}
|
||||
|
||||
// Expand all parent folds of lineN to make it visible.
|
||||
func (b *Buffer) FoldExpandOut(lineN int) {
|
||||
d := b.lines[lineN].min_depth
|
||||
for lineT := lineN - 1; lineT >= 0 && d > 0; lineT-- {
|
||||
if d > b.lines[lineT].min_depth {
|
||||
d = b.lines[lineT].min_depth
|
||||
b.lines[lineT].collapsed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FoldCollapse
|
||||
func (b *Buffer) FoldCollapse(lineN int, expand bool, recursive bool) bool {
|
||||
// If this is not a collapsible line, collapse the region it pertains.
|
||||
if !b.FoldCollapsible(lineN) {
|
||||
for d := b.lines[lineN].min_depth; lineN >= 0 && b.lines[lineN].min_depth >= d; lineN-- {}
|
||||
}
|
||||
if lineN >= 0 {
|
||||
if expand {
|
||||
b.FoldExpandOne(lineN, recursive)
|
||||
} else {
|
||||
b.FoldCollapseOne(lineN, recursive)
|
||||
}
|
||||
return true
|
||||
}
|
||||
// Collapse everything
|
||||
lineN = 0
|
||||
for d := b.lines[lineN].min_depth; lineN < b.LinesNum() - 1; lineN++ {
|
||||
if b.lines[lineN].min_depth == d && b.lines[lineN + 1].min_depth > d {
|
||||
if expand {
|
||||
b.FoldExpandOne(lineN, recursive)
|
||||
} else {
|
||||
b.FoldCollapseOne(lineN, recursive)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ExternallyModified returns whether the file being edited has
|
||||
// been modified by some external process
|
||||
func (b *Buffer) ExternallyModified() bool {
|
||||
|
@ -971,6 +1170,7 @@ func (b *Buffer) UpdateRules() {
|
|||
go func() {
|
||||
b.Highlighter.HighlightStates(b)
|
||||
b.Highlighter.HighlightMatches(b, 0, b.End().Y)
|
||||
b.DepthAssign(0, b.LinesNum())
|
||||
screen.Redraw()
|
||||
}()
|
||||
}
|
||||
|
@ -1157,55 +1357,69 @@ func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool, boo
|
|||
if start.X-1 >= 0 && start.X-1 < len(curLine) {
|
||||
leftChar = curLine[start.X-1]
|
||||
}
|
||||
|
||||
this_open := startChar == braceType[0];
|
||||
left_open := leftChar == braceType[0];
|
||||
this_close := startChar == braceType[1];
|
||||
left_close := leftChar == braceType[1];
|
||||
|
||||
startLoc := start
|
||||
if !this_open && (left_open || left_close) {
|
||||
startLoc.X--
|
||||
}
|
||||
startGroup := b.GetGroup(startLoc)
|
||||
|
||||
var i int
|
||||
if startChar == braceType[0] || (leftChar == braceType[0] && startChar != braceType[1]) {
|
||||
if this_open || (left_open && !this_close) {
|
||||
for y := start.Y; y < b.LinesNum(); y++ {
|
||||
l := []rune(string(b.LineBytes(y)))
|
||||
l := []rune(string(b.lines[y].data))
|
||||
xInit := 0
|
||||
if y == start.Y {
|
||||
if startChar == braceType[0] {
|
||||
if this_open {
|
||||
xInit = start.X
|
||||
} else {
|
||||
xInit = start.X - 1
|
||||
}
|
||||
}
|
||||
for x := xInit; x < len(l); x++ {
|
||||
curGroup := b.GetGroup(Loc{x, y})
|
||||
if curGroup != startGroup {
|
||||
continue
|
||||
}
|
||||
r := l[x]
|
||||
if r == braceType[0] {
|
||||
i++
|
||||
} else if r == braceType[1] {
|
||||
i--
|
||||
if i == 0 {
|
||||
if startChar == braceType[0] {
|
||||
return Loc{x, y}, false, true
|
||||
}
|
||||
return Loc{x, y}, true, true
|
||||
return Loc{x, y}, !this_open, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if startChar == braceType[1] || leftChar == braceType[1] {
|
||||
} else if this_close || left_close {
|
||||
for y := start.Y; y >= 0; y-- {
|
||||
l := []rune(string(b.lines[y].data))
|
||||
xInit := len(l) - 1
|
||||
if y == start.Y {
|
||||
if startChar == braceType[1] {
|
||||
if this_close {
|
||||
xInit = start.X
|
||||
} else {
|
||||
xInit = start.X - 1
|
||||
}
|
||||
}
|
||||
for x := xInit; x >= 0; x-- {
|
||||
curGroup := b.GetGroup(Loc{x, y})
|
||||
if curGroup != startGroup {
|
||||
continue
|
||||
}
|
||||
r := l[x]
|
||||
if r == braceType[1] {
|
||||
i++
|
||||
} else if r == braceType[0] {
|
||||
i--
|
||||
if i == 0 {
|
||||
if startChar == braceType[1] {
|
||||
return Loc{x, y}, false, true
|
||||
}
|
||||
return Loc{x, y}, true, true
|
||||
return Loc{x, y}, !this_close, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ func (c *Cursor) Goto(b Cursor) {
|
|||
// the current cursor its selection too
|
||||
func (c *Cursor) GotoLoc(l Loc) {
|
||||
c.X, c.Y = l.X, l.Y
|
||||
c.buf.FoldExpandOut(l.Y)
|
||||
c.StoreVisualX()
|
||||
}
|
||||
|
||||
|
@ -238,22 +239,35 @@ func (c *Cursor) AddLineToSelection() {
|
|||
|
||||
// UpN moves the cursor up N lines (if possible)
|
||||
func (c *Cursor) UpN(amount int) {
|
||||
proposedY := c.Y - amount
|
||||
if proposedY < 0 {
|
||||
proposedY = 0
|
||||
} else if proposedY >= len(c.buf.lines) {
|
||||
proposedY = len(c.buf.lines) - 1
|
||||
incr := -1
|
||||
if amount < 0 {
|
||||
incr = 1
|
||||
}
|
||||
proposedY := c.Y
|
||||
for amount != 0 {
|
||||
proposedY += incr
|
||||
if proposedY < 0 {
|
||||
proposedY = 0
|
||||
break
|
||||
}
|
||||
if proposedY >= len(c.buf.lines) {
|
||||
proposedY = len(c.buf.lines) - 1
|
||||
break
|
||||
}
|
||||
if !c.buf.Hidden(proposedY) {
|
||||
amount += incr
|
||||
}
|
||||
}
|
||||
|
||||
bytes := c.buf.LineBytes(proposedY)
|
||||
c.X = c.GetCharPosInLine(bytes, c.LastVisualX)
|
||||
|
||||
if c.X > util.CharacterCount(bytes) || (amount < 0 && proposedY == c.Y) {
|
||||
if c.X > util.CharacterCount(bytes) || (incr > 0 && proposedY == c.Y) {
|
||||
c.X = util.CharacterCount(bytes)
|
||||
c.StoreVisualX()
|
||||
}
|
||||
|
||||
if c.X < 0 || (amount > 0 && proposedY == c.Y) {
|
||||
if c.X < 0 || (incr < 0 && proposedY == c.Y) {
|
||||
c.X = 0
|
||||
c.StoreVisualX()
|
||||
}
|
||||
|
|
|
@ -49,6 +49,9 @@ type Line struct {
|
|||
state highlight.State
|
||||
match highlight.LineMatch
|
||||
lock sync.Mutex
|
||||
collapsed bool // TODO: This should a per-buffer attribute.
|
||||
min_depth int
|
||||
depth int
|
||||
|
||||
// The search states for the line, used for highlighting of search matches,
|
||||
// separately from the syntax highlighting.
|
||||
|
@ -363,6 +366,33 @@ func (la *LineArray) Match(lineN int) highlight.LineMatch {
|
|||
return la.lines[lineN].match
|
||||
}
|
||||
|
||||
func (la *LineArray) GetGroup(pos Loc) highlight.Group {
|
||||
lMatch := la.Match(pos.Y)
|
||||
closest := -1
|
||||
for key := range lMatch {
|
||||
if key > closest && key <= pos.X && lMatch[key] != 0 {
|
||||
closest = key
|
||||
}
|
||||
}
|
||||
if closest >= 0 {
|
||||
return lMatch[closest]
|
||||
}
|
||||
for pos.Y > 0 {
|
||||
pos.Y--
|
||||
lMatch = la.Match(pos.Y)
|
||||
closest = -1
|
||||
for key := range lMatch {
|
||||
if key > closest {
|
||||
closest = key
|
||||
}
|
||||
}
|
||||
if closest >= 0 {
|
||||
return lMatch[closest]
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Locks the whole LineArray
|
||||
func (la *LineArray) Lock() {
|
||||
la.lock.Lock()
|
||||
|
@ -373,6 +403,10 @@ func (la *LineArray) Unlock() {
|
|||
la.lock.Unlock()
|
||||
}
|
||||
|
||||
func (la *LineArray) Collapsed(lineN int) bool {
|
||||
return la.lines[lineN].collapsed
|
||||
}
|
||||
|
||||
// SearchMatch returns true if the location `pos` is within a match
|
||||
// of the last search for the buffer `b`.
|
||||
// It is used for efficient highlighting of search matches (separately
|
||||
|
|
|
@ -138,7 +138,7 @@ func (w *BufWindow) updateDisplayInfo() {
|
|||
|
||||
// We need to know the string length of the largest line number
|
||||
// so we can pad appropriately when displaying line numbers
|
||||
w.maxLineNumLength = len(strconv.Itoa(b.LinesNum()))
|
||||
w.maxLineNumLength = 1 + len(strconv.Itoa(b.LinesNum()))
|
||||
|
||||
w.gutterOffset = 0
|
||||
if w.hasMessage {
|
||||
|
@ -217,6 +217,7 @@ func (w *BufWindow) Relocate() bool {
|
|||
activeC := w.Buf.GetActiveCursor()
|
||||
scrollmargin := int(b.Settings["scrollmargin"].(float64))
|
||||
|
||||
b.FoldExpandOut(activeC.Loc.Y)
|
||||
c := w.SLocFromLoc(activeC.Loc)
|
||||
bStart := SLoc{0, 0}
|
||||
bEnd := w.SLocFromLoc(b.End())
|
||||
|
@ -330,8 +331,21 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, vloc
|
|||
}
|
||||
lineNum := []rune(strconv.Itoa(util.Abs(lineInt)))
|
||||
|
||||
// Indicate if this is a collapsed region
|
||||
fold := ' '
|
||||
if w.Buf.FoldCollapsible(bloc.Y) {
|
||||
if w.Buf.Collapsed(bloc.Y) {
|
||||
fold = '+'
|
||||
} else {
|
||||
fold = '-'
|
||||
}
|
||||
} else if w.Buf.FoldCollapseEnd(bloc.Y) {
|
||||
fold = '^'
|
||||
}
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, fold, nil, lineNumStyle)
|
||||
vloc.X++
|
||||
// Write the spaces before the line number if necessary
|
||||
for i := 0; i < w.maxLineNumLength-len(lineNum) && vloc.X < w.gutterOffset; i++ {
|
||||
for i := 0; i < w.maxLineNumLength-1-len(lineNum) && vloc.X < w.gutterOffset; i++ {
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||
vloc.X++
|
||||
}
|
||||
|
@ -466,6 +480,13 @@ func (w *BufWindow) displayBuffer() {
|
|||
for ; vloc.Y < w.bufHeight; vloc.Y++ {
|
||||
vloc.X = 0
|
||||
|
||||
for bloc.Y < b.LinesNum() && b.Hidden(bloc.Y) {
|
||||
bloc.Y++
|
||||
}
|
||||
if bloc.Y >= b.LinesNum() {
|
||||
break
|
||||
}
|
||||
|
||||
currentLine := false
|
||||
for _, c := range cursors {
|
||||
if bloc.Y == c.Y && w.active {
|
||||
|
|
|
@ -290,17 +290,31 @@ func (w *BufWindow) diff(s1, s2 SLoc) int {
|
|||
// which means scrolling up. The returned location is guaranteed to be
|
||||
// within the buffer boundaries.
|
||||
func (w *BufWindow) Scroll(s SLoc, n int) SLoc {
|
||||
if !w.Buf.Settings["softwrap"].(bool) {
|
||||
s.Line += n
|
||||
if s.Line < 0 {
|
||||
s.Line = 0
|
||||
}
|
||||
if s.Line > w.Buf.LinesNum()-1 {
|
||||
s.Line = w.Buf.LinesNum() - 1
|
||||
}
|
||||
return s
|
||||
if w.Buf.Settings["softwrap"].(bool) {
|
||||
// TODO: account for hidden lines when softwrap
|
||||
return w.scroll(s, n)
|
||||
}
|
||||
return w.scroll(s, n)
|
||||
incr := 1
|
||||
if n < 0 {
|
||||
incr = -1
|
||||
n = -n;
|
||||
}
|
||||
for i := 0; i < n; {
|
||||
s.Line += incr
|
||||
if s.Line < 0 || s.Line >= w.Buf.LinesNum() {
|
||||
break
|
||||
}
|
||||
if !w.Buf.Hidden(s.Line) {
|
||||
i++
|
||||
}
|
||||
}
|
||||
if s.Line < 0 {
|
||||
s.Line = 0
|
||||
}
|
||||
if s.Line >= w.Buf.LinesNum() {
|
||||
s.Line = w.Buf.LinesNum() - 1
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Diff returns the difference (the vertical distance) between two SLocs.
|
||||
|
|
|
@ -53,6 +53,9 @@ var statusInfo = map[string]func(*buffer.Buffer) string{
|
|||
"percentage": func(b *buffer.Buffer) string {
|
||||
return strconv.Itoa((b.GetActiveCursor().Y + 1) * 100 / b.LinesNum())
|
||||
},
|
||||
"group": func(b *buffer.Buffer) string {
|
||||
return b.GetGroup(b.GetActiveCursor().Loc).String()
|
||||
},
|
||||
}
|
||||
|
||||
func SetStatusInfoFnLua(fn string) {
|
||||
|
|
Loading…
Reference in a new issue