diff --git a/gilo.go b/gilo.go index 1fa661c..81609b4 100644 --- a/gilo.go +++ b/gilo.go @@ -29,7 +29,7 @@ func main() { e.Open(os.Args[1]) } - e.SetStatusMessage("HELP: Ctrl-S = save | Ctrl-Q = quit") + e.SetStatusMessage("HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find") // The input loop for { diff --git a/internal/editor/document/row.go b/internal/editor/document/row.go index 4f56391..eaeee3f 100644 --- a/internal/editor/document/row.go +++ b/internal/editor/document/row.go @@ -34,6 +34,10 @@ func (r *Row) Render(at *gilo.Point) string { return string(r.render[at.X:]) } +func (r *Row) Search(query string) int { + return strings.Index(string(r.render), query) +} + func (r *Row) insertRune(ch rune, at int) { // If insertion index is invalid, just // append the rune to the end of the array @@ -114,3 +118,22 @@ func (r *Row) CursorXToRenderX(cursorX int) (renderX int) { return renderX } + +func (r *Row) RenderXtoCursorX(renderX int) (cursorX int) { + currentRenderX := 0 + cursorX = 0 + + for cursorX = 0; cursorX < r.Size(); cursorX++ { + if r.chars[cursorX] == '\t' { + currentRenderX += (gilo.TabSize - 1) - (currentRenderX % gilo.TabSize) + } else { + currentRenderX += 1 + } + + if currentRenderX > renderX { + return cursorX + } + } + + return cursorX +} \ No newline at end of file diff --git a/internal/editor/editor.go b/internal/editor/editor.go index 9dc4930..433fe57 100644 --- a/internal/editor/editor.go +++ b/internal/editor/editor.go @@ -58,9 +58,22 @@ func (e *editor) Open(filename string) { e.document.Open(filename) } -func (e *editor) Save() { +func (e *editor) SetStatusMessage(template string, a ...interface{}) { + e.status = &statusMsg{ + fmt.Sprintf(template, a...), + time.Now(), + } +} + +func (e *editor) ProcessKeypress() bool { + ch, _ := terminal.ReadKey() + + return e.processKeypressChar(ch) +} + +func (e *editor) save() { if e.document.Filename == "" { - e.document.Filename = e.Prompt("Save as: %s (ESC to cancel)") + e.document.Filename = e.prompt("Save as: %s (ESC to cancel)") if e.document.Filename == "" { e.SetStatusMessage("Save aborted") } @@ -75,20 +88,7 @@ func (e *editor) Save() { } } -func (e *editor) SetStatusMessage(template string, a ...interface{}) { - e.status = &statusMsg{ - fmt.Sprintf(template, a...), - time.Now(), - } -} - -func (e *editor) ProcessKeypress() bool { - ch, _ := terminal.ReadKey() - - return e.processKeypressChar(ch) -} - -func (e *editor) Prompt(prompt string) string { +func (e *editor) prompt(prompt string) string { buf := gilo.NewBuffer() // Show the prompt message @@ -124,6 +124,25 @@ func (e *editor) Prompt(prompt string) string { } } +func (e *editor) find() { + query := e.prompt("Search: %s (ESC to cancel)") + if query == "" { + return + } + + for i := 0; i < e.document.RowCount(); i++ { + row := e.document.GetRow(i) + matchIndex := row.Search(query) + if matchIndex != -1 { + e.cursor.Y = i + e.cursor.X = row.RenderXtoCursorX(matchIndex) + e.offset.Y = e.document.RowCount() + + break + } + } +} + func (e *editor) insertChar(ch rune) { if e.cursor.Y == e.document.RowCount() { e.document.AppendRow("") diff --git a/internal/editor/input.go b/internal/editor/input.go index 5592161..dfe6885 100644 --- a/internal/editor/input.go +++ b/internal/editor/input.go @@ -42,7 +42,10 @@ func (e *editor) processKeypressChar(ch rune) bool { return false case key.Ctrl('s'): - e.Save() + e.save() + + case key.Ctrl('f'): + e.find() case key.Enter: e.document.InsertNewline(e.cursor)