diff --git a/editor.go b/editor.go index 8dc2a5071282da7d16921a1af1a118572f6069d7..bb28aebb10baaad2c99911d4f8f5a10e0370b825 100644 --- a/editor.go +++ b/editor.go @@ -43,6 +43,7 @@ const ( FuzzyModeFile FuzzyType = iota FuzzyModeBuffer FuzzyModeWarning + FuzzyModeBookmark ) type Jump struct { @@ -60,6 +61,7 @@ severity int } // MatchRange represents a span of text matched by search or replace. + type MatchRange struct { startLine int startCol int @@ -89,6 +91,7 @@ fuzzyScroll int // Viewport offset for the result list. fuzzyCandidates []string // Raw list of all possible items (files/buffers/etc.). fuzzyType FuzzyType // What the fuzzy finder is searching for. fuzzyDiagnostics []DiagnosticItem // Diagnostics from all buffers (accessible via finder). + bookmarks [5]*Jump // Quick bookmarks (F1-F5). mouseEnabled bool // Toggle for mouse support. visualStartX int // Starting anchor for visual selection. visualStartY int // Starting anchor for visual selection. @@ -237,6 +240,7 @@ maxLogMessages: 50, showDebugLog: false, jumplist: []Jump{}, jumpIndex: -1, + bookmarks: [5]*Jump{}, devMode: devMode, ollamaClient: NewOllamaClient(), } @@ -722,6 +726,84 @@ e.updateFuzzyResults() e.mode = ModeFuzzy } +func (e *Editor) SetBookmark(index int) { + if index < 0 || index >= len(e.bookmarks) { + return + } + b := e.activeBuffer() + if b == nil { + return + } + e.bookmarks[index] = &Jump{ + filename: b.filename, + } + e.message = fmt.Sprintf("Bookmark F%d set", index+1) +} + +func (e *Editor) GotoBookmark(index int) { + if index < 0 || index >= len(e.bookmarks) { + return + } + bookmark := e.bookmarks[index] + if bookmark == nil { + e.message = fmt.Sprintf("Bookmark F%d not set", index+1) + return + } + + // Find if buffer is already open + bufferIndex := -1 + for i, b := range e.buffers { + if b.filename == bookmark.filename { + bufferIndex = i + break + } + } + + // If buffer not found, try to load it + if bufferIndex == -1 && bookmark.filename != "" { + err := e.LoadFile(bookmark.filename) + if err == nil { + bufferIndex = e.activeBufferIndex + } + } + + if bufferIndex != -1 { + e.activeBufferIndex = bufferIndex + e.centerScreen() + e.mode = ModeNormal + } else if bookmark.filename == "" { + // Maybe it was an empty [No Name] buffer + e.message = fmt.Sprintf("Bookmark F%d: switching to [No Name] buffer", index+1) + // We already found the first empty buffer if it exists. + } else { + e.message = fmt.Sprintf("Could not open bookmarked file: %s", bookmark.filename) + } +} + +func (e *Editor) startBookmarkFuzzyFinder() { + e.fuzzyCandidates = []string{} + + for i, bookmark := range e.bookmarks { + if bookmark == nil { + e.fuzzyCandidates = append(e.fuzzyCandidates, fmt.Sprintf("F%d: [Not Set]", i+1)) + continue + } + filename := bookmark.filename + if filename == "" { + filename = "[No Name]" + } else { + filename = filepath.Base(filename) + } + e.fuzzyCandidates = append(e.fuzzyCandidates, fmt.Sprintf("F%d: %s", i+1, filename)) + } + + e.fuzzyBuffer = []rune{} + e.fuzzyIndex = 0 + e.fuzzyType = FuzzyModeBookmark + e.updateFuzzyResults() + e.mode = ModeFuzzy +} + func fuzzyMatch(query, target string) (int, bool) { if query == "" { return 0, true @@ -890,6 +972,14 @@ e.centerScreen() } e.mode = ModeNormal } + } else if e.fuzzyType == FuzzyModeBookmark { + if e.fuzzyIndex >= len(e.fuzzyResults) || e.fuzzyIndex >= len(e.fuzzyResultIndices) { + return + } + // In startBookmarkFuzzyFinder, we didn't filter, so indices match results + // but if we did filter, e.fuzzyResultIndices would hold the original bookmark index + bookmarkIdx := e.fuzzyResultIndices[e.fuzzyIndex] + e.GotoBookmark(bookmarkIdx) } } @@ -3995,6 +4085,9 @@ fg, bg = GetThemeColor(ColorFuzzyModeBuffers) case FuzzyModeWarning: modeStr = "WARNINGS" fg, bg = GetThemeColor(ColorFuzzyModeWarnings) + case FuzzyModeBookmark: + modeStr = "BOOKMARKS" + fg, bg = GetThemeColor(ColorFuzzyModeBookmarks) default: modeStr = "FUZZY" fg, bg = GetThemeColor(ColorNormalMode) diff --git a/kevent.go b/kevent.go index 4b6611f36161084f30cffa8760f0dbbcd86e714d..c3847c11a30ddc4206728d25ddcaa54884375eda 100644 --- a/kevent.go +++ b/kevent.go @@ -243,6 +243,41 @@ case termbox.KeyCtrlA: e.ModifyNumberUnderCursor(1) case termbox.KeyCtrlZ: e.ModifyNumberUnderCursor(-1) + case termbox.KeyF1: + if e.pendingKey == Config.LeaderKey { + e.SetBookmark(0) + e.pendingKey = 0 + } else { + e.GotoBookmark(0) + } + case termbox.KeyF2: + if e.pendingKey == Config.LeaderKey { + e.SetBookmark(1) + e.pendingKey = 0 + } else { + e.GotoBookmark(1) + } + case termbox.KeyF3: + if e.pendingKey == Config.LeaderKey { + e.SetBookmark(2) + e.pendingKey = 0 + } else { + e.GotoBookmark(2) + } + case termbox.KeyF4: + if e.pendingKey == Config.LeaderKey { + e.SetBookmark(3) + e.pendingKey = 0 + } else { + e.GotoBookmark(3) + } + case termbox.KeyF5: + if e.pendingKey == Config.LeaderKey { + e.SetBookmark(4) + e.pendingKey = 0 + } else { + e.GotoBookmark(4) + } } // Prevent key event fallthrough. @@ -535,6 +570,11 @@ } case 'b': if e.pendingKey == Config.LeaderKey { e.startBufferFuzzyFinder() + e.pendingKey = 0 + } + case 'm': + if e.pendingKey == Config.LeaderKey { + e.startBookmarkFuzzyFinder() e.pendingKey = 0 } case 'P': diff --git a/theme.go b/theme.go index 544af8a538220252ced9103496fa4c2eb6d6c5b9..4c33387c88f00cffcb2126f99830cd9c8ebc7721 100644 --- a/theme.go +++ b/theme.go @@ -53,6 +53,7 @@ ColorDiagSummaryWarning // Warning count in the status bar. ColorFuzzyModeBuffers // Indicator that fuzzy finder is searching buffers. ColorFuzzyModeFiles // Indicator that fuzzy finder is searching files. ColorFuzzyModeWarnings // Indicator that fuzzy finder is searching diagnostics. + ColorFuzzyModeBookmarks // Indicator that fuzzy finder is searching bookmarks. // Colors for Tree-sitter syntax highlighting. ColorTSFunction @@ -110,11 +111,12 @@ ColorGutterSignWarning: {Background: termbox.Attribute(221), Foreground: termbox.Attribute(1)}, ColorGutterSignInfo: {Background: termbox.Attribute(221), Foreground: termbox.Attribute(1)}, ColorGutterSignHint: {Background: termbox.Attribute(221), Foreground: termbox.Attribute(1)}, - ColorFuzzyResult: {Background: termbox.ColorDefault, Foreground: termbox.Attribute(254)}, - ColorFuzzySelected: {Background: termbox.Attribute(236), Foreground: termbox.Attribute(254)}, - ColorFuzzyModeBuffers: {Background: termbox.Attribute(125), Foreground: termbox.Attribute(255)}, - ColorFuzzyModeFiles: {Background: termbox.Attribute(125), Foreground: termbox.Attribute(255)}, - ColorFuzzyModeWarnings: {Background: termbox.Attribute(33), Foreground: termbox.Attribute(255)}, + ColorFuzzyResult: {Background: termbox.ColorDefault, Foreground: termbox.Attribute(254)}, + ColorFuzzySelected: {Background: termbox.Attribute(236), Foreground: termbox.Attribute(254)}, + ColorFuzzyModeBuffers: {Background: termbox.Attribute(125), Foreground: termbox.Attribute(255)}, + ColorFuzzyModeFiles: {Background: termbox.Attribute(125), Foreground: termbox.Attribute(255)}, + ColorFuzzyModeWarnings: {Background: termbox.Attribute(33), Foreground: termbox.Attribute(255)}, + ColorFuzzyModeBookmarks: {Background: termbox.Attribute(93), Foreground: termbox.Attribute(255)}, ColorEmptyLineMarker: {Background: termbox.ColorDefault, Foreground: termbox.Attribute(244)},