diff --git a/editor/document/row.go b/editor/document/row.go index fae1cb4..07be2b2 100644 --- a/editor/document/row.go +++ b/editor/document/row.go @@ -106,12 +106,17 @@ func (r *Row) update() { } func (r *Row) updateSyntax() { - for _, ch := range r.render { + i := 0 + + for i < r.RenderSize() { + ch := r.render[i] if unicode.IsDigit(ch) { r.Hl = append(r.Hl, highlight.Number) } else { r.Hl = append(r.Hl, highlight.Normal) } + + i++ } } diff --git a/editor/draw.go b/editor/draw.go index 84b99be..d84faef 100644 --- a/editor/draw.go +++ b/editor/draw.go @@ -74,35 +74,39 @@ func (e *editor) drawRows(ab *gilo.Buffer) { continue } - currentColor := terminal.DefaultFGColor - row := e.document.GetRow(fileRow) - - for i, ch := range row.Render(e.offset) { - if row.Hl[i] == highlight.Normal { - if currentColor != terminal.DefaultFGColor { - ab.Append(terminal.DefaultFGColor) - currentColor = terminal.DefaultFGColor - } - - ab.AppendRune(ch) - } else { - color := highlight.SyntaxToColor(row.Hl[i]) - if color != currentColor { - currentColor = color - ab.Append(color) - } - - ab.AppendRune(ch) - } - } - - ab.Append(terminal.DefaultFGColor) + e.drawFileRow(fileRow, ab) } ab.AppendLn(terminal.ClearLine) } } +func (e *editor) drawFileRow(fileRow int, ab *gilo.Buffer) { + currentColor := terminal.DefaultFGColor + row := e.document.GetRow(fileRow) + + for i, ch := range row.Render(e.offset) { + if row.Hl[i] == highlight.Normal { + if currentColor != terminal.DefaultFGColor { + ab.Append(terminal.DefaultFGColor) + currentColor = terminal.DefaultFGColor + } + + ab.AppendRune(ch) + } else { + color := highlight.SyntaxToColor(row.Hl[i]) + if color != currentColor { + currentColor = color + ab.Append(color) + } + + ab.AppendRune(ch) + } + } + + ab.Append(terminal.DefaultFGColor) +} + func (e *editor) drawPlaceholderRow(y int, ab *gilo.Buffer) { if e.document.RowCount() == 0 && y == e.screen.Rows/3 { welcome := fmt.Sprintf("Gilo editor -- version %s", gilo.Version) diff --git a/editor/editor.go b/editor/editor.go index ed8186a..259dfdf 100644 --- a/editor/editor.go +++ b/editor/editor.go @@ -25,6 +25,7 @@ type editor struct { offset *gilo.Point document *doc.Document status *statusMsg + search *search quitTimes uint8 renderX int } @@ -49,6 +50,7 @@ func NewEditor() *editor { offset, document, status, + newSearch(), gilo.QuitTimes, 0, } @@ -138,61 +140,6 @@ func (e *editor) prompt(prompt string, callback func(string, string)) string { } } -func (e *editor) find() { - savedCursor := e.cursor.Clone() - savedOffset := e.cursor.Clone() - - lastMatch := -1 - direction := 1 - query := e.prompt("Search: %s (Use ESC/Arrows/Enter)", func(query string, ch string) { - if ch == string(key.Enter) || ch == string(key.Esc) { - lastMatch = -1 - direction = 1 - return - } else if ch == keyRight || ch == keyDown { - direction = 1 - } else if ch == keyLeft || ch == keyUp { - direction = -1 - } else { - lastMatch = -1 - direction = 1 - } - - if lastMatch == -1 { - direction = 1 - } - - current := lastMatch - - for i := 0; i < e.document.RowCount(); i++ { - current += direction - if current == -1 { - current = e.document.RowCount() - } else if current == e.document.RowCount() { - current = 0 - } - - row := e.document.GetRow(current) - matchIndex := row.Search(query) - if matchIndex != -1 { - lastMatch = current - e.cursor.Y = current - e.cursor.X = row.RenderXtoCursorX(matchIndex) - e.offset.Y = e.document.RowCount() - - break - } - } - }) - - if query == "" { - e.cursor = savedCursor.Clone() - e.offset = savedOffset.Clone() - - return - } -} - func (e *editor) insertChar(ch rune) { if e.cursor.Y == e.document.RowCount() { e.document.AppendRow("") diff --git a/editor/highlight/constants.go b/editor/highlight/constants.go index 8d91e6d..b09f4c5 100644 --- a/editor/highlight/constants.go +++ b/editor/highlight/constants.go @@ -6,4 +6,5 @@ package highlight const ( Normal = iota Number + Match ) diff --git a/editor/highlight/fn.go b/editor/highlight/fn.go index 3e016ab..08eb7a7 100644 --- a/editor/highlight/fn.go +++ b/editor/highlight/fn.go @@ -2,11 +2,19 @@ package highlight import "timshome.page/gilo/terminal" -func SyntaxToColor(hl int) string { - switch hl { - case Number: - return terminal.FGRed - default: - return terminal.DefaultFGColor - } +var syntaxColorMap = map[int]string{ + Number: terminal.FGRed, + Match: terminal.FGBlue, + Normal: terminal.DefaultFGColor, +} + +// Take a highlighting type and map it to +// an ANSI color escape code for display +func SyntaxToColor(hl int) string { + color := syntaxColorMap[hl] + if len(color) == 0 { + color = terminal.DefaultFGColor + } + + return color } diff --git a/editor/search.go b/editor/search.go new file mode 100644 index 0000000..d5418e4 --- /dev/null +++ b/editor/search.go @@ -0,0 +1,112 @@ +package editor + +import ( + "timshome.page/gilo/editor/highlight" + "timshome.page/gilo/gilo" + "timshome.page/gilo/key" +) + +type search struct { + cursor *gilo.Point + offset *gilo.Point + hlLine int + hl []int + direction int + lastMatch int +} + +func newSearch() *search { + return &search{ + cursor: gilo.DefaultPoint(), + offset: gilo.DefaultPoint(), + hlLine: -1, + hl: []int{}, + lastMatch: -1, + direction: 1, + } +} + +func (e *editor) find() { + e.search.cursor.X = e.cursor.X + e.search.cursor.Y = e.cursor.Y + e.search.offset.X = e.offset.X + e.search.offset.Y = e.offset.Y + + query := e.prompt("Search: %s (Use ESC/Arrows/Enter)", e.findCallback) + + if query == "" { + e.cursor.X = e.search.cursor.X + e.cursor.Y = e.search.cursor.Y + e.offset.X = e.search.offset.X + e.offset.Y = e.search.offset.Y + + return + } +} + +func (e *editor) findCallback(query string, ch string) { + if e.search.hlLine != -1 && e.search.hl != nil { + staleRow := e.document.GetRow(e.search.hlLine) + for i, val := range e.search.hl { + staleRow.Hl[i] = val + } + e.search.hl = nil + e.search.hlLine = -1 + } + + if ch == string(key.Enter) || ch == string(key.Esc) { + e.search.lastMatch = -1 + e.search.direction = 1 + return + } else if ch == keyRight || ch == keyDown { + e.search.direction = 1 + } else if ch == keyLeft || ch == keyUp { + e.search.direction = -1 + } else { + e.search.lastMatch = -1 + e.search.direction = 1 + } + + if e.search.lastMatch == -1 { + e.search.direction = 1 + } + + if len(query) == 0 { + return + } + + current := e.search.lastMatch + + for i := 0; i < e.document.RowCount(); i++ { + current += e.search.direction + + if current == -1 { + current = e.document.RowCount() - 1 + } else if current == e.document.RowCount() { + current = 0 + } + + row := e.document.GetRow(current) + matchIndex := row.Search(query) + if matchIndex == -1 { + continue + } + + e.search.lastMatch = current + e.cursor.Y = current + e.cursor.X = row.RenderXtoCursorX(matchIndex) + e.offset.Y = e.document.RowCount() + + // Update highlighting of search result + e.search.hlLine = current + e.search.hl = make([]int, row.RenderSize()) + for i, val := range row.Hl { + e.search.hl[i] = val + } + for x := matchIndex; x < matchIndex+len(query); x++ { + row.Hl[x] = highlight.Match + } + + break + } +} diff --git a/gilo.go b/gilo.go index 1770bdf..adc1ad8 100644 --- a/gilo.go +++ b/gilo.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "golang.org/x/term" "os" "timshome.page/gilo/editor" diff --git a/key/key.go b/key/key.go index e843f0e..e198ef6 100644 --- a/key/key.go +++ b/key/key.go @@ -1,5 +1,7 @@ package key +import "unicode" + // ---------------------------------------------------------------------------- // !Terminal Input Escape Code Sequences // ---------------------------------------------------------------------------- @@ -12,7 +14,7 @@ const ( // Is this an ASCII character? func IsAscii(char rune) bool { - return char < 0x80 + return char <= unicode.MaxASCII } // Is this an ASCII ctrl character? @@ -34,3 +36,8 @@ func Ctrl(char rune) rune { return ch } + +// Is the character a general token separator type? +func IsSeparator(char rune) bool { + return unicode.IsPunct(char) || unicode.IsSpace(char) +} \ No newline at end of file