From 97ee34426804783cdceda30c69faf677685190bb Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Mon, 20 Jan 2020 23:43:33 -0500 Subject: [PATCH] Fix some issues with syntax highlighting regions Fixes #1464 --- internal/buffer/buffer.go | 51 ++++++++++++++++++++++++++++++----- internal/display/bufwindow.go | 19 ++++++------- pkg/highlight/highlighter.go | 34 ++++++++++++----------- 3 files changed, 74 insertions(+), 30 deletions(-) diff --git a/internal/buffer/buffer.go b/internal/buffer/buffer.go index 296f6bf7..22680264 100644 --- a/internal/buffer/buffer.go +++ b/internal/buffer/buffer.go @@ -28,8 +28,11 @@ import ( const backupTime = 8000 var ( + // OpenBuffers is a list of the currently open buffers OpenBuffers []*Buffer - LogBuf *Buffer + // LogBuf is a reference to the log buffer which can be opened with the + // `> log` command + LogBuf *Buffer ) // The BufType defines what kind of buffer this is @@ -41,16 +44,26 @@ type BufType struct { } var ( + // BTDefault is a default buffer BTDefault = BufType{0, false, false, true} - BTHelp = BufType{1, true, true, true} - BTLog = BufType{2, true, true, false} + // BTHelp is a help buffer + BTHelp = BufType{1, true, true, true} + // BTLog is a log buffer + BTLog = BufType{2, true, true, false} + // BTScratch is a buffer that cannot be saved (for scratch work) BTScratch = BufType{3, false, true, false} - BTRaw = BufType{4, false, true, false} - BTInfo = BufType{5, false, true, false} + // BTRaw is is a buffer that shows raw terminal events + BTRaw = BufType{4, false, true, false} + // BTInfo is a buffer for inputting information + BTInfo = BufType{5, false, true, false} + // ErrFileTooLarge is returned when the file is too large to hash + // (fastdirty is automatically enabled) ErrFileTooLarge = errors.New("File is too large to hash") ) +// SharedBuffer is a struct containing info that is shared among buffers +// that have the same file open type SharedBuffer struct { *LineArray // Stores the last modification time of the file the buffer is pointing to @@ -97,8 +110,13 @@ type Buffer struct { // Name of the buffer on the status line name string - SyntaxDef *highlight.Def + // SyntaxDef represents the syntax highlighting definition being used + // This stores the highlighting rules and filetype detection info + SyntaxDef *highlight.Def + // The Highlighter struct actually performs the highlighting Highlighter *highlight.Highlighter + // Modifications is the list of modified regions for syntax highlighting + Modifications []Loc // Hash of the original buffer -- empty if fastdirty is on origHash [md5.Size]byte @@ -260,6 +278,8 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT screen.TermMessage(err) } + b.Modifications = make([]Loc, 10) + OpenBuffers = append(OpenBuffers, b) return b @@ -304,26 +324,44 @@ func (b *Buffer) SetName(s string) { b.name = s } +// Insert inserts the given string of text at the start location func (b *Buffer) Insert(start Loc, text string) { if !b.Type.Readonly { b.EventHandler.cursors = b.cursors b.EventHandler.active = b.curCursor b.EventHandler.Insert(start, text) + // b.Modifications is cleared every screen redraw so it's + // ok to append duplicates + b.Modifications = append(b.Modifications, Loc{start.Y, start.Y + strings.Count(text, "\n")}) + go b.Backup(true) } } +// Remove removes the characters between the start and end locations func (b *Buffer) Remove(start, end Loc) { if !b.Type.Readonly { b.EventHandler.cursors = b.cursors b.EventHandler.active = b.curCursor b.EventHandler.Remove(start, end) + b.Modifications = append(b.Modifications, Loc{start.Y, start.Y}) + go b.Backup(true) } } +// ClearModifications clears the list of modified lines in this buffer +// The list of modified lines is used for syntax highlighting so that +// we can selectively highlight only the necessary lines +// This function should be called every time this buffer is drawn to +// the screen +func (b *Buffer) ClearModifications() { + // clear slice without resetting the cap + b.Modifications = b.Modifications[:0] +} + // FileType returns the buffer's filetype func (b *Buffer) FileType() string { return b.Settings["filetype"].(string) @@ -372,6 +410,7 @@ func (b *Buffer) ReOpen() error { return err } +// RelocateCursors relocates all cursors (makes sure they are in the buffer) func (b *Buffer) RelocateCursors() { for _, c := range b.cursors { c.Relocate() diff --git a/internal/display/bufwindow.go b/internal/display/bufwindow.go index 7d23703e..47d9983a 100644 --- a/internal/display/bufwindow.go +++ b/internal/display/bufwindow.go @@ -370,17 +370,18 @@ func (w *BufWindow) displayBuffer() { } if b.Settings["syntax"].(bool) && b.SyntaxDef != nil { - for _, c := range b.GetCursors() { - // rehighlight starting from where the cursor is - start := c.Y - if start > 0 && b.Rehighlight(start-1) { - b.Highlighter.ReHighlightLine(b, start-1) - b.SetRehighlight(start-1, false) - } + for _, r := range b.Modifications { + for i := r.X; i <= r.Y; i++ { + if i > 0 && b.Rehighlight(i-1) { + b.Highlighter.ReHighlightLine(b, i-1) + b.SetRehighlight(i-1, false) + } - b.Highlighter.ReHighlightStates(b, start) - b.Highlighter.HighlightMatches(b, w.StartLine, w.StartLine+bufHeight) + b.Highlighter.ReHighlightStates(b, i) + } } + b.Highlighter.HighlightMatches(b, w.StartLine, w.StartLine+bufHeight) + b.ClearModifications() } var matchingBraces []buffer.Loc diff --git a/pkg/highlight/highlighter.go b/pkg/highlight/highlighter.go index e736abdb..7dc471ee 100644 --- a/pkg/highlight/highlighter.go +++ b/pkg/highlight/highlighter.go @@ -176,7 +176,7 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE return highlights } - if lineLen == 0 || statesOnly { + if lineLen == 0 { if canMatchEnd { h.lastRegion = curRegion } @@ -197,28 +197,32 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE } } if firstLoc[0] != lineLen { - highlights[start+firstLoc[0]] = firstRegion.limitGroup + if !statesOnly { + highlights[start+firstLoc[0]] = firstRegion.limitGroup + } h.highlightRegion(highlights, start, false, lineNum, sliceEnd(line, firstLoc[0]), curRegion, statesOnly) h.highlightRegion(highlights, start+firstLoc[1], canMatchEnd, lineNum, sliceStart(line, firstLoc[1]), firstRegion, statesOnly) return highlights } - fullHighlights := make([]Group, lineLen) - for i := 0; i < len(fullHighlights); i++ { - fullHighlights[i] = curRegion.group - } + if !statesOnly { + fullHighlights := make([]Group, lineLen) + for i := 0; i < len(fullHighlights); i++ { + fullHighlights[i] = curRegion.group + } - for _, p := range curRegion.rules.patterns { - matches := findAllIndex(p.regex, line, start == 0, canMatchEnd) - for _, m := range matches { - for i := m[0]; i < m[1]; i++ { - fullHighlights[i] = p.group + for _, p := range curRegion.rules.patterns { + matches := findAllIndex(p.regex, line, start == 0, canMatchEnd) + for _, m := range matches { + for i := m[0]; i < m[1]; i++ { + fullHighlights[i] = p.group + } } } - } - for i, h := range fullHighlights { - if i == 0 || h != fullHighlights[i-1] { - highlights[start+i] = h + for i, h := range fullHighlights { + if i == 0 || h != fullHighlights[i-1] { + highlights[start+i] = h + } } }