From 2d5fc15990b5f32d38e92315b17032f07ffc4ca6 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Mon, 8 Mar 2021 13:10:52 -0500 Subject: [PATCH 1/5] Update runewidth version Fixes #1873 --- go.mod | 2 +- go.sum | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index c0f07db7..2c584405 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,6 @@ require ( replace github.com/kballard/go-shellquote => github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655 -replace github.com/mattn/go-runewidth => github.com/p-e-w/go-runewidth v0.0.10-0.20200613030200-3e1705c5c059 +replace github.com/mattn/go-runewidth => github.com/zyedidia/go-runewidth v0.0.12 go 1.11 diff --git a/go.sum b/go.sum index a1ebfccc..44880c9d 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/p-e-w/go-runewidth v0.0.10-0.20200613030200-3e1705c5c059 h1:/+h2b6i15 github.com/p-e-w/go-runewidth v0.0.10-0.20200613030200-3e1705c5c059/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff h1:+6NUiITWwE5q1KO6SAfUX918c+Tab0+tGAM/mtdlUyA= github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= @@ -44,6 +46,8 @@ github.com/zyedidia/clipboard v1.0.3 h1:F/nCDVYMdbDWTmY8s8cJl0tnwX32q96IF09JHM14 github.com/zyedidia/clipboard v1.0.3/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA= github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 h1:oMHjjTLfGXVuyOQBYj5/td9WC0mw4g1xDBPovIqmHew= github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3/go.mod h1:YKbIYP//Eln8eDgAJGI3IDvR3s4Tv9Z9TGIOumiyQ5c= +github.com/zyedidia/go-runewidth v0.0.12 h1:aHWj8qL3aH7caRzoPBJXe1pEaZBXHpKtfTuiBo5p74Q= +github.com/zyedidia/go-runewidth v0.0.12/go.mod h1:vF8djYdLmG8BJaUZ4CznFYCJ3pFR8m4B4VinTvTTarU= github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655 h1:Z3RhH6hvcSx7eX6Q/pP6YVsgea/1eMDG99vtWwi3nK4= github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655/go.mod h1:1sTqqO+kcYzZp43M5VsJe1tns9IzlSeC9jB6c2+o/5Y= github.com/zyedidia/highlight v0.0.0-20170330143449-201131ce5cf5 h1:Zs6mpwXvlqpF9zHl5XaN0p5V4J9XvP+WBuiuXyIgqvc= From c5798b5b8cef385f9f34f256239c855e3db94253 Mon Sep 17 00:00:00 2001 From: Dmitry Maluka Date: Wed, 7 Apr 2021 22:18:51 +0200 Subject: [PATCH 2/5] Fix softwrap scrolling issues (#1981) Softwrap implementation enhanced to fix various issues with scrolling, centering, relocating etc. The main idea is simple: work not with simple line numbers but with (Line, Row) pairs, where Line is a line number in the buffer and Row is a visual line (a row) number within this line. The logic remains mostly the same, but simple arithmetic operations on line numbers are replaced with corresponding operations on (Line, Row) pairs. Fixes #632, #1657 --- cmd/micro/initlua.go | 3 + internal/action/actions.go | 75 ++++++----------- internal/action/globals.go | 15 ---- internal/display/bufwindow.go | 96 +++++++++++---------- internal/display/infowindow.go | 4 + internal/display/softwrap.go | 149 +++++++++++++++++++++++++++++++++ internal/display/window.go | 8 +- runtime/help/plugins.md | 1 + 8 files changed, 240 insertions(+), 111 deletions(-) create mode 100644 internal/display/softwrap.go diff --git a/cmd/micro/initlua.go b/cmd/micro/initlua.go index 2ed94c3d..dcb60830 100644 --- a/cmd/micro/initlua.go +++ b/cmd/micro/initlua.go @@ -118,6 +118,9 @@ func luaImportMicroBuffer() *lua.LTable { ulua.L.SetField(pkg, "Loc", luar.New(ulua.L, func(x, y int) buffer.Loc { return buffer.Loc{x, y} })) + ulua.L.SetField(pkg, "SLoc", luar.New(ulua.L, func(line, row int) display.SLoc { + return display.SLoc{line, row} + })) ulua.L.SetField(pkg, "BTDefault", luar.New(ulua.L, buffer.BTDefault.Kind)) ulua.L.SetField(pkg, "BTHelp", luar.New(ulua.L, buffer.BTHelp.Kind)) ulua.L.SetField(pkg, "BTLog", luar.New(ulua.L, buffer.BTLog.Kind)) diff --git a/internal/action/actions.go b/internal/action/actions.go index 44eadbf8..6db8e822 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -10,6 +10,7 @@ import ( "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/config" + "github.com/zyedidia/micro/v2/internal/display" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" "github.com/zyedidia/micro/v2/internal/util" @@ -19,21 +20,26 @@ import ( // ScrollUp is not an action func (h *BufPane) ScrollUp(n int) { v := h.GetView() - if v.StartLine >= n { - v.StartLine -= n - h.SetView(v) - } else { - v.StartLine = 0 - } + v.StartLine = h.Scroll(v.StartLine, -n) + h.SetView(v) } // ScrollDown is not an action func (h *BufPane) ScrollDown(n int) { v := h.GetView() - if v.StartLine <= h.Buf.LinesNum()-1-n { - v.StartLine += n - h.SetView(v) + v.StartLine = h.Scroll(v.StartLine, n) + h.SetView(v) +} + +// If the user has scrolled past the last line, ScrollAdjust can be used +// to shift the view so that the last line is at the bottom +func (h *BufPane) ScrollAdjust() { + v := h.GetView() + end := h.SLocFromLoc(h.Buf.End()) + if h.Diff(v.StartLine, end) < v.Height-1 { + v.StartLine = h.Scroll(end, -v.Height+1) } + h.SetView(v) } // MousePress is the event that should happen when a normal click happens @@ -111,15 +117,9 @@ func (h *BufPane) ScrollDownAction() bool { // Center centers the view on the cursor func (h *BufPane) Center() bool { v := h.GetView() - v.StartLine = h.Cursor.Y - v.Height/2 - if v.StartLine+v.Height > h.Buf.LinesNum() { - v.StartLine = h.Buf.LinesNum() - v.Height - } - if v.StartLine < 0 { - v.StartLine = 0 - } + v.StartLine = h.Scroll(h.SLocFromLoc(h.Cursor.Loc), -v.Height/2) h.SetView(v) - h.Relocate() + h.ScrollAdjust() return true } @@ -1243,45 +1243,31 @@ func (h *BufPane) JumpLine() bool { // Start moves the viewport to the start of the buffer func (h *BufPane) Start() bool { v := h.GetView() - v.StartLine = 0 + v.StartLine = display.SLoc{0, 0} h.SetView(v) return true } // End moves the viewport to the end of the buffer func (h *BufPane) End() bool { - // TODO: softwrap problems? v := h.GetView() - if v.Height > h.Buf.LinesNum() { - v.StartLine = 0 - h.SetView(v) - } else { - v.StartLine = h.Buf.LinesNum() - v.Height - h.SetView(v) - } + v.StartLine = h.Scroll(h.SLocFromLoc(h.Buf.End()), -v.Height+1) + h.SetView(v) return true } // PageUp scrolls the view up a page func (h *BufPane) PageUp() bool { v := h.GetView() - if v.StartLine > v.Height { - h.ScrollUp(v.Height) - } else { - v.StartLine = 0 - } - h.SetView(v) + h.ScrollUp(v.Height) return true } // PageDown scrolls the view down a page func (h *BufPane) PageDown() bool { v := h.GetView() - if h.Buf.LinesNum()-(v.StartLine+v.Height) > v.Height { - h.ScrollDown(v.Height) - } else if h.Buf.LinesNum() >= v.Height { - v.StartLine = h.Buf.LinesNum() - v.Height - } + h.ScrollDown(v.Height) + h.ScrollAdjust() return true } @@ -1338,24 +1324,15 @@ func (h *BufPane) CursorPageDown() bool { // HalfPageUp scrolls the view up half a page func (h *BufPane) HalfPageUp() bool { v := h.GetView() - if v.StartLine > v.Height/2 { - h.ScrollUp(v.Height / 2) - } else { - v.StartLine = 0 - } - h.SetView(v) + h.ScrollUp(v.Height / 2) return true } // HalfPageDown scrolls the view down half a page func (h *BufPane) HalfPageDown() bool { v := h.GetView() - if h.Buf.LinesNum()-(v.StartLine+v.Height) > v.Height/2 { - h.ScrollDown(v.Height / 2) - } else if h.Buf.LinesNum() >= v.Height { - v.StartLine = h.Buf.LinesNum() - v.Height - } - h.SetView(v) + h.ScrollDown(v.Height / 2) + h.ScrollAdjust() return true } diff --git a/internal/action/globals.go b/internal/action/globals.go index 4a3b8375..e20f61ed 100644 --- a/internal/action/globals.go +++ b/internal/action/globals.go @@ -21,13 +21,6 @@ func WriteLog(s string) { buffer.WriteLog(s) if LogBufPane != nil { LogBufPane.CursorEnd() - v := LogBufPane.GetView() - endY := buffer.LogBuf.End().Y - - if endY > v.StartLine+v.Height { - v.StartLine = buffer.LogBuf.End().Y - v.Height + 2 - LogBufPane.SetView(v) - } } } @@ -37,12 +30,4 @@ func WriteLog(s string) { func (h *BufPane) OpenLogBuf() { LogBufPane = h.HSplitBuf(buffer.LogBuf) LogBufPane.CursorEnd() - - v := LogBufPane.GetView() - endY := buffer.LogBuf.End().Y - - if endY > v.StartLine+v.Height { - v.StartLine = buffer.LogBuf.End().Y - v.Height + 2 - LogBufPane.SetView(v) - } } diff --git a/internal/display/bufwindow.go b/internal/display/bufwindow.go index afcfa5bc..2fcf0ac5 100644 --- a/internal/display/bufwindow.go +++ b/internal/display/bufwindow.go @@ -106,51 +106,35 @@ func (w *BufWindow) Clear() { } } -// Bottomline returns the line number of the lowest line in the view -// You might think that this is obviously just v.StartLine + v.Height -// but if softwrap is enabled things get complicated since one buffer -// line can take up multiple lines in the view -func (w *BufWindow) Bottomline() int { - if !w.Buf.Settings["softwrap"].(bool) { - h := w.StartLine + w.Height - 1 - if w.drawStatus { - h-- - } - return h - } - - l := w.LocFromVisual(buffer.Loc{0, w.Y + w.Height}) - - return l.Y -} - // Relocate moves the view window so that the cursor is in view // This is useful if the user has scrolled far away, and then starts typing // Returns true if the window location is moved func (w *BufWindow) Relocate() bool { b := w.Buf - // how many buffer lines are in the view - height := w.Bottomline() + 1 - w.StartLine - h := w.Height + height := w.Height if w.drawStatus { - h-- + height-- } ret := false activeC := w.Buf.GetActiveCursor() - cy := activeC.Y scrollmargin := int(b.Settings["scrollmargin"].(float64)) - if cy < w.StartLine+scrollmargin && cy > scrollmargin-1 { - w.StartLine = cy - scrollmargin + + c := w.SLocFromLoc(activeC.Loc) + bStart := SLoc{0, 0} + bEnd := w.SLocFromLoc(b.End()) + + if c.LessThan(w.Scroll(w.StartLine, scrollmargin)) && c.GreaterThan(w.Scroll(bStart, scrollmargin-1)) { + w.StartLine = w.Scroll(c, -scrollmargin) ret = true - } else if cy < w.StartLine { - w.StartLine = cy + } else if c.LessThan(w.StartLine) { + w.StartLine = c ret = true } - if cy > w.StartLine+height-1-scrollmargin && cy < b.LinesNum()-scrollmargin { - w.StartLine = cy - height + 1 + scrollmargin + if c.GreaterThan(w.Scroll(w.StartLine, height-1-scrollmargin)) && c.LessThan(w.Scroll(bEnd, -scrollmargin+1)) { + w.StartLine = w.Scroll(c, -height+1+scrollmargin) ret = true - } else if cy >= b.LinesNum()-scrollmargin && cy >= height { - w.StartLine = b.LinesNum() - height + } else if c.GreaterThan(w.Scroll(bEnd, -scrollmargin)) && c.GreaterThan(w.Scroll(w.StartLine, height-1)) { + w.StartLine = w.Scroll(bEnd, -height+1) ret = true } @@ -199,11 +183,15 @@ func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc { // this represents the current draw position // within the current window vloc := buffer.Loc{X: 0, Y: 0} + if softwrap { + // the start line may be partially out of the current window + vloc.Y = -w.StartLine.Row + } // this represents the current draw position in the buffer (char positions) - bloc := buffer.Loc{X: -1, Y: w.StartLine} + bloc := buffer.Loc{X: -1, Y: w.StartLine.Line} - for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ { + for ; vloc.Y < bufHeight; vloc.Y++ { vloc.X = 0 if hasMessage { vloc.X += 2 @@ -473,14 +461,18 @@ func (w *BufWindow) displayBuffer() { // this represents the current draw position // within the current window vloc := buffer.Loc{X: 0, Y: 0} + if softwrap { + // the start line may be partially out of the current window + vloc.Y = -w.StartLine.Row + } // this represents the current draw position in the buffer (char positions) - bloc := buffer.Loc{X: -1, Y: w.StartLine} + bloc := buffer.Loc{X: -1, Y: w.StartLine.Line} cursors := b.GetCursors() curStyle := config.DefStyle - for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ { + for ; vloc.Y < bufHeight; vloc.Y++ { vloc.X = 0 currentLine := false @@ -496,16 +488,28 @@ func (w *BufWindow) displayBuffer() { s = curNumStyle } - if hasMessage { - w.drawGutter(&vloc, &bloc) - } + if vloc.Y >= 0 { + if hasMessage { + w.drawGutter(&vloc, &bloc) + } - if b.Settings["diffgutter"].(bool) { - w.drawDiffGutter(s, false, &vloc, &bloc) - } + if b.Settings["diffgutter"].(bool) { + w.drawDiffGutter(s, false, &vloc, &bloc) + } - if b.Settings["ruler"].(bool) { - w.drawLineNum(s, false, maxLineNumLength, &vloc, &bloc) + if b.Settings["ruler"].(bool) { + w.drawLineNum(s, false, maxLineNumLength, &vloc, &bloc) + } + } else { + if hasMessage { + vloc.X += 2 + } + if b.Settings["diffgutter"].(bool) { + vloc.X++ + } + if b.Settings["ruler"].(bool) { + vloc.X += maxLineNumLength + 1 + } } w.gutterOffset = vloc.X @@ -517,7 +521,7 @@ func (w *BufWindow) displayBuffer() { bloc.X = bslice draw := func(r rune, combc []rune, style tcell.Style, showcursor bool) { - if nColsBeforeStart <= 0 { + if nColsBeforeStart <= 0 && vloc.Y >= 0 { _, origBg, _ := style.Decompose() _, defBg, _ := config.DefStyle.Decompose() @@ -590,6 +594,8 @@ func (w *BufWindow) displayBuffer() { } } } + } + if nColsBeforeStart <= 0 { vloc.X++ } nColsBeforeStart-- @@ -735,7 +741,7 @@ func (w *BufWindow) displayScrollBar() { if barsize < 1 { barsize = 1 } - barstart := w.Y + int(float64(w.StartLine)/float64(w.Buf.LinesNum())*float64(w.Height)) + barstart := w.Y + int(float64(w.StartLine.Line)/float64(w.Buf.LinesNum())*float64(w.Height)) scrollBarStyle := config.DefStyle.Reverse(true) if style, ok := config.Colorscheme["scrollbar"]; ok { diff --git a/internal/display/infowindow.go b/internal/display/infowindow.go index a5d02c7b..4cfbca32 100644 --- a/internal/display/infowindow.go +++ b/internal/display/infowindow.go @@ -72,6 +72,10 @@ func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc { return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 0} } +func (i *InfoWindow) Scroll(s SLoc, n int) SLoc { return s } +func (i *InfoWindow) Diff(s1, s2 SLoc) int { return 0 } +func (i *InfoWindow) SLocFromLoc(loc buffer.Loc) SLoc { return SLoc{0, 0} } + func (i *InfoWindow) Clear() { for x := 0; x < i.Width; x++ { screen.SetContent(x, i.Y, ' ', nil, i.defStyle()) diff --git a/internal/display/softwrap.go b/internal/display/softwrap.go new file mode 100644 index 00000000..0f99b526 --- /dev/null +++ b/internal/display/softwrap.go @@ -0,0 +1,149 @@ +package display + +import ( + "github.com/zyedidia/micro/v2/internal/buffer" + "github.com/zyedidia/micro/v2/internal/util" +) + +// SLoc represents a vertical scrolling location, i.e. a location of a visual line +// in the buffer. When softwrap is enabled, a buffer line may be displayed as +// multiple visual lines (rows). So SLoc stores a number of a line in the buffer +// and a number of a row within this line. +type SLoc struct { + Line, Row int +} + +// LessThan returns true if s is less b +func (s SLoc) LessThan(b SLoc) bool { + if s.Line < b.Line { + return true + } + return s.Line == b.Line && s.Row < b.Row +} + +// GreaterThan returns true if s is bigger than b +func (s SLoc) GreaterThan(b SLoc) bool { + if s.Line > b.Line { + return true + } + return s.Line == b.Line && s.Row > b.Row +} + +type SoftWrap interface { + Scroll(s SLoc, n int) SLoc + Diff(s1, s2 SLoc) int + SLocFromLoc(loc buffer.Loc) SLoc +} + +func (w *BufWindow) getRow(loc buffer.Loc) int { + width := w.Width - w.gutterOffset + if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height { + width-- + } + if width <= 0 { + return 0 + } + // TODO: this doesn't work quite correctly if there is an incomplete tab + // or wide character at the end of a row. See also issue #1979 + x := util.StringWidth(w.Buf.LineBytes(loc.Y), loc.X, util.IntOpt(w.Buf.Settings["tabsize"])) + return x / width +} + +func (w *BufWindow) getRowCount(line int) int { + return w.getRow(buffer.Loc{X: util.CharacterCount(w.Buf.LineBytes(line)), Y: line}) + 1 +} + +func (w *BufWindow) scrollUp(s SLoc, n int) SLoc { + for n > 0 { + if n <= s.Row { + s.Row -= n + n = 0 + } else if s.Line > 0 { + s.Line-- + n -= s.Row + 1 + s.Row = w.getRowCount(s.Line) - 1 + } else { + s.Row = 0 + break + } + } + return s +} + +func (w *BufWindow) scrollDown(s SLoc, n int) SLoc { + for n > 0 { + rc := w.getRowCount(s.Line) + if n < rc-s.Row { + s.Row += n + n = 0 + } else if s.Line < w.Buf.LinesNum()-1 { + s.Line++ + n -= rc - s.Row + s.Row = 0 + } else { + s.Row = rc - 1 + break + } + } + return s +} + +func (w *BufWindow) scroll(s SLoc, n int) SLoc { + if n < 0 { + return w.scrollUp(s, -n) + } + return w.scrollDown(s, n) +} + +func (w *BufWindow) diff(s1, s2 SLoc) int { + n := 0 + for s1.LessThan(s2) { + if s1.Line < s2.Line { + n += w.getRowCount(s1.Line) - s1.Row + s1.Line++ + s1.Row = 0 + } else { + n += s2.Row - s1.Row + s1.Row = s2.Row + } + } + return n +} + +// Scroll returns the location which is n visual lines below the location s +// i.e. the result of scrolling n lines down. n can be negative, +// 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 + } + return w.scroll(s, n) +} + +// Diff returns the difference (the vertical distance) between two SLocs. +func (w *BufWindow) Diff(s1, s2 SLoc) int { + if !w.Buf.Settings["softwrap"].(bool) { + return s2.Line - s1.Line + } + if s1.GreaterThan(s2) { + return -w.diff(s2, s1) + } + return w.diff(s1, s2) +} + +// SLocFromLoc takes a position in the buffer and returns the location +// of the visual line containing this position. +func (w *BufWindow) SLocFromLoc(loc buffer.Loc) SLoc { + if !w.Buf.Settings["softwrap"].(bool) { + return SLoc{loc.Y, 0} + } + return SLoc{loc.Y, w.getRow(loc)} +} diff --git a/internal/display/window.go b/internal/display/window.go index 56787fff..eb71970f 100644 --- a/internal/display/window.go +++ b/internal/display/window.go @@ -8,10 +8,13 @@ type View struct { X, Y int // X,Y location of the view Width, Height int // Width and height of the view - // Start line and start column of the view (vertical/horizontal scroll) + // Start line of the view (for vertical scroll) + StartLine SLoc + + // Start column of the view (for horizontal scroll) // note that since the starting column of every line is different if the view // is scrolled, StartCol is a visual index (will be the same for every line) - StartLine, StartCol int + StartCol int } type Window interface { @@ -28,5 +31,6 @@ type Window interface { type BWindow interface { Window + SoftWrap SetBuffer(b *buffer.Buffer) } diff --git a/runtime/help/plugins.md b/runtime/help/plugins.md index 1818b96b..f671f776 100644 --- a/runtime/help/plugins.md +++ b/runtime/help/plugins.md @@ -259,6 +259,7 @@ The packages and functions are listed below (in Go type signatures): - `MTError` error message. - `Loc(x, y int) Loc`: creates a new location struct. + - `SLoc(line, row int) display.SLoc`: creates a new scrolling location struct. - `BTDefault`: default buffer type. - `BTLog`: log buffer type. From 1f73f8587c6b968393f884832c9bee352d7cb30d Mon Sep 17 00:00:00 2001 From: Dmitry Maluka Date: Wed, 7 Apr 2021 22:20:39 +0200 Subject: [PATCH 3/5] Add buffer.WordAt (#2070) Add buffer.WordAt function returning the word around a given location in the buffer. Useful for plugins. --- internal/buffer/buffer.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/internal/buffer/buffer.go b/internal/buffer/buffer.go index 7e0b808f..84ab7dc0 100644 --- a/internal/buffer/buffer.go +++ b/internal/buffer/buffer.go @@ -545,6 +545,27 @@ func (b *Buffer) RuneAt(loc Loc) rune { return '\n' } +// WordAt returns the word around a given location in the buffer +func (b *Buffer) WordAt(loc Loc) []byte { + if len(b.LineBytes(loc.Y)) == 0 || !util.IsWordChar(b.RuneAt(loc)) { + return []byte{} + } + + start := loc + end := loc.Move(1, b) + + for start.X > 0 && util.IsWordChar(b.RuneAt(start.Move(-1, b))) { + start.X-- + } + + lineLen := util.CharacterCount(b.LineBytes(loc.Y)) + for end.X < lineLen && util.IsWordChar(b.RuneAt(end)) { + end.X++ + } + + return b.Substr(start, end) +} + // Modified returns if this buffer has been modified since // being opened func (b *Buffer) Modified() bool { From 9ed35250764bba905d7ec6adaa70080684ba19cf Mon Sep 17 00:00:00 2001 From: Laszlo Gombos Date: Wed, 7 Apr 2021 16:20:57 -0400 Subject: [PATCH 4/5] Improve patch file detection by adding a header rule. (#1942) --- runtime/syntax/patch.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/syntax/patch.yaml b/runtime/syntax/patch.yaml index 452dd31c..996bdc38 100644 --- a/runtime/syntax/patch.yaml +++ b/runtime/syntax/patch.yaml @@ -1,7 +1,8 @@ filetype: patch -detect: +detect: filename: "\\.(patch|diff)$" + header: "^diff" rules: - brightgreen: "^\\+.*" From f1aaa307436f0ee3fbf0acc5b490cf328662de82 Mon Sep 17 00:00:00 2001 From: Alex Tsantilis Date: Wed, 7 Apr 2021 16:21:19 -0400 Subject: [PATCH 5/5] Update and rename perl6.yaml to raku.yaml (#1927) The language has been renamed but still aims to support the old file extensions for a time. --- runtime/syntax/{perl6.yaml => raku.yaml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename runtime/syntax/{perl6.yaml => raku.yaml} (94%) diff --git a/runtime/syntax/perl6.yaml b/runtime/syntax/raku.yaml similarity index 94% rename from runtime/syntax/perl6.yaml rename to runtime/syntax/raku.yaml index 6924f8df..c3d62017 100644 --- a/runtime/syntax/perl6.yaml +++ b/runtime/syntax/raku.yaml @@ -1,7 +1,7 @@ -filetype: perl6 +filetype: raku detect: - filename: "(\\.p6$|\\.pl6$|\\.pm6$)" + filename: "(\\.p6$|\\.pl6$|\\.pm6$|\\.raku$|\\.rakumod$|\\.rakudoc$)" rules: - type: "\\b(accept|alarm|atan2|bin(d|mode)|c(aller|h(dir|mod|op|own|root)|lose(dir)?|onnect|os|rypt)|d(bm(close|open)|efined|elete|ie|o|ump)|e(ach|of|val|x(ec|ists|it|p))|f(cntl|ileno|lock|ork)|get(c|login|peername|pgrp|ppid|priority|pwnam|(host|net|proto|serv)byname|pwuid|grgid|(host|net)byaddr|protobynumber|servbyport)|([gs]et|end)(pw|gr|host|net|proto|serv)ent|getsock(name|opt)|gmtime|goto|grep|hex|index|int|ioctl|join|keys|kill|last|length|link|listen|local(time)?|log|lstat|m|mkdir|msg(ctl|get|snd|rcv)|next|oct|open(dir)?|ord|pack|pipe|pop|printf?|push|q|qq|qx|rand|re(ad(dir|link)?|cv|do|name|quire|set|turn|verse|winddir)|rindex|rmdir|s|scalar|seek|seekdir|se(lect|mctl|mget|mop|nd|tpgrp|tpriority|tsockopt)|shift|shm(ctl|get|read|write)|shutdown|sin|sleep|socket(pair)?|sort|spli(ce|t)|sprintf|sqrt|srand|stat|study|substr|symlink|sys(call|read|tem|write)|tell(dir)?|time|tr|y|truncate|umask|un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\\b"