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
|
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
|
// Paste whatever is in the system clipboard into the buffer
|
||||||
// Delete and paste if the user has a selection
|
// Delete and paste if the user has a selection
|
||||||
func (h *BufPane) Paste() bool {
|
func (h *BufPane) Paste() bool {
|
||||||
|
|
|
@ -757,6 +757,10 @@ var BufKeyActions = map[string]BufKeyAction{
|
||||||
"DeleteLine": (*BufPane).DeleteLine,
|
"DeleteLine": (*BufPane).DeleteLine,
|
||||||
"MoveLinesUp": (*BufPane).MoveLinesUp,
|
"MoveLinesUp": (*BufPane).MoveLinesUp,
|
||||||
"MoveLinesDown": (*BufPane).MoveLinesDown,
|
"MoveLinesDown": (*BufPane).MoveLinesDown,
|
||||||
|
"FoldCollapse": (*BufPane).FoldCollapse,
|
||||||
|
"FoldCollapseRecursive": (*BufPane).FoldCollapseRecursive,
|
||||||
|
"FoldExpand": (*BufPane).FoldExpand,
|
||||||
|
"FoldExpandRecursive": (*BufPane).FoldExpandRecursive,
|
||||||
"IndentSelection": (*BufPane).IndentSelection,
|
"IndentSelection": (*BufPane).IndentSelection,
|
||||||
"OutdentSelection": (*BufPane).OutdentSelection,
|
"OutdentSelection": (*BufPane).OutdentSelection,
|
||||||
"Autocomplete": (*BufPane).Autocomplete,
|
"Autocomplete": (*BufPane).Autocomplete,
|
||||||
|
@ -877,6 +881,10 @@ var MultiActions = map[string]bool{
|
||||||
"DeleteLine": true,
|
"DeleteLine": true,
|
||||||
"MoveLinesUp": true,
|
"MoveLinesUp": true,
|
||||||
"MoveLinesDown": true,
|
"MoveLinesDown": true,
|
||||||
|
"FoldCollapse": true,
|
||||||
|
"FoldCollapseRecursive": true,
|
||||||
|
"FoldExpand": true,
|
||||||
|
"FoldExpandRecursive": true,
|
||||||
"IndentSelection": true,
|
"IndentSelection": true,
|
||||||
"OutdentSelection": true,
|
"OutdentSelection": true,
|
||||||
"OutdentLine": true,
|
"OutdentLine": true,
|
||||||
|
|
|
@ -157,11 +157,121 @@ func (b *SharedBuffer) MarkModified(start, end int) {
|
||||||
b.Highlighter.HighlightMatches(b, start, l)
|
b.Highlighter.HighlightMatches(b, start, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.DepthAssign(start, end + 1)
|
||||||
|
|
||||||
for i := start; i <= end; i++ {
|
for i := start; i <= end; i++ {
|
||||||
b.LineArray.invalidateSearchMatches(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
|
// DisableReload disables future reloads of this sharedbuffer
|
||||||
func (b *SharedBuffer) DisableReload() {
|
func (b *SharedBuffer) DisableReload() {
|
||||||
b.ReloadDisabled = true
|
b.ReloadDisabled = true
|
||||||
|
@ -511,10 +621,99 @@ func (b *Buffer) Remove(start, end Loc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileType returns the buffer's filetype
|
// FileType returns the buffer's filetype
|
||||||
func (b *Buffer) FileType() string {
|
func (b *SharedBuffer) FileType() string {
|
||||||
return b.Settings["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
|
// ExternallyModified returns whether the file being edited has
|
||||||
// been modified by some external process
|
// been modified by some external process
|
||||||
func (b *Buffer) ExternallyModified() bool {
|
func (b *Buffer) ExternallyModified() bool {
|
||||||
|
@ -971,6 +1170,7 @@ func (b *Buffer) UpdateRules() {
|
||||||
go func() {
|
go func() {
|
||||||
b.Highlighter.HighlightStates(b)
|
b.Highlighter.HighlightStates(b)
|
||||||
b.Highlighter.HighlightMatches(b, 0, b.End().Y)
|
b.Highlighter.HighlightMatches(b, 0, b.End().Y)
|
||||||
|
b.DepthAssign(0, b.LinesNum())
|
||||||
screen.Redraw()
|
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) {
|
if start.X-1 >= 0 && start.X-1 < len(curLine) {
|
||||||
leftChar = curLine[start.X-1]
|
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
|
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++ {
|
for y := start.Y; y < b.LinesNum(); y++ {
|
||||||
l := []rune(string(b.LineBytes(y)))
|
l := []rune(string(b.lines[y].data))
|
||||||
xInit := 0
|
xInit := 0
|
||||||
if y == start.Y {
|
if y == start.Y {
|
||||||
if startChar == braceType[0] {
|
if this_open {
|
||||||
xInit = start.X
|
xInit = start.X
|
||||||
} else {
|
} else {
|
||||||
xInit = start.X - 1
|
xInit = start.X - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for x := xInit; x < len(l); x++ {
|
for x := xInit; x < len(l); x++ {
|
||||||
|
curGroup := b.GetGroup(Loc{x, y})
|
||||||
|
if curGroup != startGroup {
|
||||||
|
continue
|
||||||
|
}
|
||||||
r := l[x]
|
r := l[x]
|
||||||
if r == braceType[0] {
|
if r == braceType[0] {
|
||||||
i++
|
i++
|
||||||
} else if r == braceType[1] {
|
} else if r == braceType[1] {
|
||||||
i--
|
i--
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
if startChar == braceType[0] {
|
return Loc{x, y}, !this_open, true
|
||||||
return Loc{x, y}, false, true
|
|
||||||
}
|
|
||||||
return Loc{x, y}, true, true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if startChar == braceType[1] || leftChar == braceType[1] {
|
} else if this_close || left_close {
|
||||||
for y := start.Y; y >= 0; y-- {
|
for y := start.Y; y >= 0; y-- {
|
||||||
l := []rune(string(b.lines[y].data))
|
l := []rune(string(b.lines[y].data))
|
||||||
xInit := len(l) - 1
|
xInit := len(l) - 1
|
||||||
if y == start.Y {
|
if y == start.Y {
|
||||||
if startChar == braceType[1] {
|
if this_close {
|
||||||
xInit = start.X
|
xInit = start.X
|
||||||
} else {
|
} else {
|
||||||
xInit = start.X - 1
|
xInit = start.X - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for x := xInit; x >= 0; x-- {
|
for x := xInit; x >= 0; x-- {
|
||||||
|
curGroup := b.GetGroup(Loc{x, y})
|
||||||
|
if curGroup != startGroup {
|
||||||
|
continue
|
||||||
|
}
|
||||||
r := l[x]
|
r := l[x]
|
||||||
if r == braceType[1] {
|
if r == braceType[1] {
|
||||||
i++
|
i++
|
||||||
} else if r == braceType[0] {
|
} else if r == braceType[0] {
|
||||||
i--
|
i--
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
if startChar == braceType[1] {
|
return Loc{x, y}, !this_close, true
|
||||||
return Loc{x, y}, false, true
|
|
||||||
}
|
|
||||||
return Loc{x, y}, true, true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ func (c *Cursor) Goto(b Cursor) {
|
||||||
// the current cursor its selection too
|
// the current cursor its selection too
|
||||||
func (c *Cursor) GotoLoc(l Loc) {
|
func (c *Cursor) GotoLoc(l Loc) {
|
||||||
c.X, c.Y = l.X, l.Y
|
c.X, c.Y = l.X, l.Y
|
||||||
|
c.buf.FoldExpandOut(l.Y)
|
||||||
c.StoreVisualX()
|
c.StoreVisualX()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,22 +239,35 @@ func (c *Cursor) AddLineToSelection() {
|
||||||
|
|
||||||
// UpN moves the cursor up N lines (if possible)
|
// UpN moves the cursor up N lines (if possible)
|
||||||
func (c *Cursor) UpN(amount int) {
|
func (c *Cursor) UpN(amount int) {
|
||||||
proposedY := c.Y - amount
|
incr := -1
|
||||||
|
if amount < 0 {
|
||||||
|
incr = 1
|
||||||
|
}
|
||||||
|
proposedY := c.Y
|
||||||
|
for amount != 0 {
|
||||||
|
proposedY += incr
|
||||||
if proposedY < 0 {
|
if proposedY < 0 {
|
||||||
proposedY = 0
|
proposedY = 0
|
||||||
} else if proposedY >= len(c.buf.lines) {
|
break
|
||||||
|
}
|
||||||
|
if proposedY >= len(c.buf.lines) {
|
||||||
proposedY = len(c.buf.lines) - 1
|
proposedY = len(c.buf.lines) - 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !c.buf.Hidden(proposedY) {
|
||||||
|
amount += incr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes := c.buf.LineBytes(proposedY)
|
bytes := c.buf.LineBytes(proposedY)
|
||||||
c.X = c.GetCharPosInLine(bytes, c.LastVisualX)
|
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.X = util.CharacterCount(bytes)
|
||||||
c.StoreVisualX()
|
c.StoreVisualX()
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.X < 0 || (amount > 0 && proposedY == c.Y) {
|
if c.X < 0 || (incr < 0 && proposedY == c.Y) {
|
||||||
c.X = 0
|
c.X = 0
|
||||||
c.StoreVisualX()
|
c.StoreVisualX()
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,9 @@ type Line struct {
|
||||||
state highlight.State
|
state highlight.State
|
||||||
match highlight.LineMatch
|
match highlight.LineMatch
|
||||||
lock sync.Mutex
|
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,
|
// The search states for the line, used for highlighting of search matches,
|
||||||
// separately from the syntax highlighting.
|
// separately from the syntax highlighting.
|
||||||
|
@ -363,6 +366,33 @@ func (la *LineArray) Match(lineN int) highlight.LineMatch {
|
||||||
return la.lines[lineN].match
|
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
|
// Locks the whole LineArray
|
||||||
func (la *LineArray) Lock() {
|
func (la *LineArray) Lock() {
|
||||||
la.lock.Lock()
|
la.lock.Lock()
|
||||||
|
@ -373,6 +403,10 @@ func (la *LineArray) Unlock() {
|
||||||
la.lock.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
|
// SearchMatch returns true if the location `pos` is within a match
|
||||||
// of the last search for the buffer `b`.
|
// of the last search for the buffer `b`.
|
||||||
// It is used for efficient highlighting of search matches (separately
|
// 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
|
// We need to know the string length of the largest line number
|
||||||
// so we can pad appropriately when displaying line numbers
|
// 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
|
w.gutterOffset = 0
|
||||||
if w.hasMessage {
|
if w.hasMessage {
|
||||||
|
@ -217,6 +217,7 @@ func (w *BufWindow) Relocate() bool {
|
||||||
activeC := w.Buf.GetActiveCursor()
|
activeC := w.Buf.GetActiveCursor()
|
||||||
scrollmargin := int(b.Settings["scrollmargin"].(float64))
|
scrollmargin := int(b.Settings["scrollmargin"].(float64))
|
||||||
|
|
||||||
|
b.FoldExpandOut(activeC.Loc.Y)
|
||||||
c := w.SLocFromLoc(activeC.Loc)
|
c := w.SLocFromLoc(activeC.Loc)
|
||||||
bStart := SLoc{0, 0}
|
bStart := SLoc{0, 0}
|
||||||
bEnd := w.SLocFromLoc(b.End())
|
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)))
|
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
|
// 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)
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||||
vloc.X++
|
vloc.X++
|
||||||
}
|
}
|
||||||
|
@ -466,6 +480,13 @@ func (w *BufWindow) displayBuffer() {
|
||||||
for ; vloc.Y < w.bufHeight; vloc.Y++ {
|
for ; vloc.Y < w.bufHeight; vloc.Y++ {
|
||||||
vloc.X = 0
|
vloc.X = 0
|
||||||
|
|
||||||
|
for bloc.Y < b.LinesNum() && b.Hidden(bloc.Y) {
|
||||||
|
bloc.Y++
|
||||||
|
}
|
||||||
|
if bloc.Y >= b.LinesNum() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
currentLine := false
|
currentLine := false
|
||||||
for _, c := range cursors {
|
for _, c := range cursors {
|
||||||
if bloc.Y == c.Y && w.active {
|
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
|
// which means scrolling up. The returned location is guaranteed to be
|
||||||
// within the buffer boundaries.
|
// within the buffer boundaries.
|
||||||
func (w *BufWindow) Scroll(s SLoc, n int) SLoc {
|
func (w *BufWindow) Scroll(s SLoc, n int) SLoc {
|
||||||
if !w.Buf.Settings["softwrap"].(bool) {
|
if w.Buf.Settings["softwrap"].(bool) {
|
||||||
s.Line += n
|
// TODO: account for hidden lines when softwrap
|
||||||
|
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 {
|
if s.Line < 0 {
|
||||||
s.Line = 0
|
s.Line = 0
|
||||||
}
|
}
|
||||||
if s.Line > w.Buf.LinesNum()-1 {
|
if s.Line >= w.Buf.LinesNum() {
|
||||||
s.Line = w.Buf.LinesNum() - 1
|
s.Line = w.Buf.LinesNum() - 1
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
|
||||||
return w.scroll(s, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diff returns the difference (the vertical distance) between two SLocs.
|
// 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 {
|
"percentage": func(b *buffer.Buffer) string {
|
||||||
return strconv.Itoa((b.GetActiveCursor().Y + 1) * 100 / b.LinesNum())
|
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) {
|
func SetStatusInfoFnLua(fn string) {
|
||||||
|
|
Loading…
Reference in a new issue