Merge: Simple bookmark system

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-05-20 03:03:15 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-05-20 03:10:52 +0200
Commit 3569bae895dbe1b0c424a2a09678933c2126c11b (patch)
-rw-r--r-- editor.go 93
-rw-r--r-- kevent.go 40
-rw-r--r-- theme.go 12
3 files changed, 140 insertions, 5 deletions
diff --git a/editor.go b/editor.go
...
43
	FuzzyModeFile FuzzyType = iota
43
	FuzzyModeFile FuzzyType = iota
44
	FuzzyModeBuffer
44
	FuzzyModeBuffer
45
	FuzzyModeWarning
45
	FuzzyModeWarning
  
46
	FuzzyModeBookmark
46
)
47
)
47
  
48
  
48
type Jump struct {
49
type Jump struct {
...
60
}
61
}
61
  
62
  
62
// MatchRange represents a span of text matched by search or replace.
63
// MatchRange represents a span of text matched by search or replace.
  
64
  
63
type MatchRange struct {
65
type MatchRange struct {
64
	startLine int
66
	startLine int
65
	startCol  int
67
	startCol  int
...
89
	fuzzyCandidates    []string         // Raw list of all possible items (files/buffers/etc.).
91
	fuzzyCandidates    []string         // Raw list of all possible items (files/buffers/etc.).
90
	fuzzyType          FuzzyType        // What the fuzzy finder is searching for.
92
	fuzzyType          FuzzyType        // What the fuzzy finder is searching for.
91
	fuzzyDiagnostics   []DiagnosticItem // Diagnostics from all buffers (accessible via finder).
93
	fuzzyDiagnostics   []DiagnosticItem // Diagnostics from all buffers (accessible via finder).
  
94
	bookmarks          [5]*Jump         // Quick bookmarks (F1-F5).
92
	mouseEnabled       bool             // Toggle for mouse support.
95
	mouseEnabled       bool             // Toggle for mouse support.
93
	visualStartX       int              // Starting anchor for visual selection.
96
	visualStartX       int              // Starting anchor for visual selection.
94
	visualStartY       int              // Starting anchor for visual selection.
97
	visualStartY       int              // Starting anchor for visual selection.
...
237
		showDebugLog:      false,
240
		showDebugLog:      false,
238
		jumplist:          []Jump{},
241
		jumplist:          []Jump{},
239
		jumpIndex:         -1,
242
		jumpIndex:         -1,
  
243
		bookmarks:         [5]*Jump{},
240
		devMode:           devMode,
244
		devMode:           devMode,
241
		ollamaClient:      NewOllamaClient(),
245
		ollamaClient:      NewOllamaClient(),
242
	}
246
	}
...
722
	e.mode = ModeFuzzy
726
	e.mode = ModeFuzzy
723
}
727
}
724
  
728
  
  
729
func (e *Editor) SetBookmark(index int) {
  
730
	if index < 0 || index >= len(e.bookmarks) {
  
731
		return
  
732
	}
  
733
	b := e.activeBuffer()
  
734
	if b == nil {
  
735
		return
  
736
	}
  
737
	e.bookmarks[index] = &Jump{
  
738
		filename: b.filename,
  
739
	}
  
740
	e.message = fmt.Sprintf("Bookmark F%d set", index+1)
  
741
}
  
742
  
  
743
func (e *Editor) GotoBookmark(index int) {
  
744
	if index < 0 || index >= len(e.bookmarks) {
  
745
		return
  
746
	}
  
747
	bookmark := e.bookmarks[index]
  
748
	if bookmark == nil {
  
749
		e.message = fmt.Sprintf("Bookmark F%d not set", index+1)
  
750
		return
  
751
	}
  
752
  
  
753
	// Find if buffer is already open
  
754
	bufferIndex := -1
  
755
	for i, b := range e.buffers {
  
756
		if b.filename == bookmark.filename {
  
757
			bufferIndex = i
  
758
			break
  
759
		}
  
760
	}
  
761
  
  
762
	// If buffer not found, try to load it
  
763
	if bufferIndex == -1 && bookmark.filename != "" {
  
764
		err := e.LoadFile(bookmark.filename)
  
765
		if err == nil {
  
766
			bufferIndex = e.activeBufferIndex
  
767
		}
  
768
	}
  
769
  
  
770
	if bufferIndex != -1 {
  
771
		e.activeBufferIndex = bufferIndex
  
772
		e.centerScreen()
  
773
		e.mode = ModeNormal
  
774
	} else if bookmark.filename == "" {
  
775
		// Maybe it was an empty [No Name] buffer
  
776
		e.message = fmt.Sprintf("Bookmark F%d: switching to [No Name] buffer", index+1)
  
777
		// We already found the first empty buffer if it exists.
  
778
	} else {
  
779
		e.message = fmt.Sprintf("Could not open bookmarked file: %s", bookmark.filename)
  
780
	}
  
781
}
  
782
  
  
783
func (e *Editor) startBookmarkFuzzyFinder() {
  
784
	e.fuzzyCandidates = []string{}
  
785
  
  
786
	for i, bookmark := range e.bookmarks {
  
787
		if bookmark == nil {
  
788
			e.fuzzyCandidates = append(e.fuzzyCandidates, fmt.Sprintf("F%d: [Not Set]", i+1))
  
789
			continue
  
790
		}
  
791
		filename := bookmark.filename
  
792
		if filename == "" {
  
793
			filename = "[No Name]"
  
794
		} else {
  
795
			filename = filepath.Base(filename)
  
796
		}
  
797
		e.fuzzyCandidates = append(e.fuzzyCandidates, fmt.Sprintf("F%d: %s", i+1, filename))
  
798
	}
  
799
  
  
800
	e.fuzzyBuffer = []rune{}
  
801
	e.fuzzyIndex = 0
  
802
	e.fuzzyType = FuzzyModeBookmark
  
803
	e.updateFuzzyResults()
  
804
	e.mode = ModeFuzzy
  
805
}
  
806
  
725
func fuzzyMatch(query, target string) (int, bool) {
807
func fuzzyMatch(query, target string) (int, bool) {
726
	if query == "" {
808
	if query == "" {
727
		return 0, true
809
		return 0, true
...
890
			}
972
			}
891
			e.mode = ModeNormal
973
			e.mode = ModeNormal
892
		}
974
		}
  
975
	} else if e.fuzzyType == FuzzyModeBookmark {
  
976
		if e.fuzzyIndex >= len(e.fuzzyResults) || e.fuzzyIndex >= len(e.fuzzyResultIndices) {
  
977
			return
  
978
		}
  
979
		// In startBookmarkFuzzyFinder, we didn't filter, so indices match results
  
980
		// but if we did filter, e.fuzzyResultIndices would hold the original bookmark index
  
981
		bookmarkIdx := e.fuzzyResultIndices[e.fuzzyIndex]
  
982
		e.GotoBookmark(bookmarkIdx)
893
	}
983
	}
894
}
984
}
895
  
985
  
...
3995
		case FuzzyModeWarning:
4085
		case FuzzyModeWarning:
3996
			modeStr = "WARNINGS"
4086
			modeStr = "WARNINGS"
3997
			fg, bg = GetThemeColor(ColorFuzzyModeWarnings)
4087
			fg, bg = GetThemeColor(ColorFuzzyModeWarnings)
  
4088
		case FuzzyModeBookmark:
  
4089
			modeStr = "BOOKMARKS"
  
4090
			fg, bg = GetThemeColor(ColorFuzzyModeBookmarks)
3998
		default:
4091
		default:
3999
			modeStr = "FUZZY"
4092
			modeStr = "FUZZY"
4000
			fg, bg = GetThemeColor(ColorNormalMode)
4093
			fg, bg = GetThemeColor(ColorNormalMode)
...
diff --git a/kevent.go b/kevent.go
...
243
		e.ModifyNumberUnderCursor(1)
243
		e.ModifyNumberUnderCursor(1)
244
	case termbox.KeyCtrlZ:
244
	case termbox.KeyCtrlZ:
245
		e.ModifyNumberUnderCursor(-1)
245
		e.ModifyNumberUnderCursor(-1)
  
246
	case termbox.KeyF1:
  
247
		if e.pendingKey == Config.LeaderKey {
  
248
			e.SetBookmark(0)
  
249
			e.pendingKey = 0
  
250
		} else {
  
251
			e.GotoBookmark(0)
  
252
		}
  
253
	case termbox.KeyF2:
  
254
		if e.pendingKey == Config.LeaderKey {
  
255
			e.SetBookmark(1)
  
256
			e.pendingKey = 0
  
257
		} else {
  
258
			e.GotoBookmark(1)
  
259
		}
  
260
	case termbox.KeyF3:
  
261
		if e.pendingKey == Config.LeaderKey {
  
262
			e.SetBookmark(2)
  
263
			e.pendingKey = 0
  
264
		} else {
  
265
			e.GotoBookmark(2)
  
266
		}
  
267
	case termbox.KeyF4:
  
268
		if e.pendingKey == Config.LeaderKey {
  
269
			e.SetBookmark(3)
  
270
			e.pendingKey = 0
  
271
		} else {
  
272
			e.GotoBookmark(3)
  
273
		}
  
274
	case termbox.KeyF5:
  
275
		if e.pendingKey == Config.LeaderKey {
  
276
			e.SetBookmark(4)
  
277
			e.pendingKey = 0
  
278
		} else {
  
279
			e.GotoBookmark(4)
  
280
		}
246
	}
281
	}
247
  
282
  
248
	// Prevent key event fallthrough.
283
	// Prevent key event fallthrough.
...
535
	case 'b':
570
	case 'b':
536
		if e.pendingKey == Config.LeaderKey {
571
		if e.pendingKey == Config.LeaderKey {
537
			e.startBufferFuzzyFinder()
572
			e.startBufferFuzzyFinder()
  
573
			e.pendingKey = 0
  
574
		}
  
575
	case 'm':
  
576
		if e.pendingKey == Config.LeaderKey {
  
577
			e.startBookmarkFuzzyFinder()
538
			e.pendingKey = 0
578
			e.pendingKey = 0
539
		}
579
		}
540
	case 'P':
580
	case 'P':
...
diff --git a/theme.go b/theme.go
...
53
	ColorFuzzyModeBuffers   // Indicator that fuzzy finder is searching buffers.
53
	ColorFuzzyModeBuffers   // Indicator that fuzzy finder is searching buffers.
54
	ColorFuzzyModeFiles     // Indicator that fuzzy finder is searching files.
54
	ColorFuzzyModeFiles     // Indicator that fuzzy finder is searching files.
55
	ColorFuzzyModeWarnings  // Indicator that fuzzy finder is searching diagnostics.
55
	ColorFuzzyModeWarnings  // Indicator that fuzzy finder is searching diagnostics.
  
56
	ColorFuzzyModeBookmarks // Indicator that fuzzy finder is searching bookmarks.
56
  
57
  
57
	// Colors for Tree-sitter syntax highlighting.
58
	// Colors for Tree-sitter syntax highlighting.
58
	ColorTSFunction
59
	ColorTSFunction
...
110
	ColorGutterSignInfo:    {Background: termbox.Attribute(221), Foreground: termbox.Attribute(1)},
111
	ColorGutterSignInfo:    {Background: termbox.Attribute(221), Foreground: termbox.Attribute(1)},
111
	ColorGutterSignHint:    {Background: termbox.Attribute(221), Foreground: termbox.Attribute(1)},
112
	ColorGutterSignHint:    {Background: termbox.Attribute(221), Foreground: termbox.Attribute(1)},
112
  
113
  
113
	ColorFuzzyResult:       {Background: termbox.ColorDefault, Foreground: termbox.Attribute(254)},
114
	ColorFuzzyResult:        {Background: termbox.ColorDefault, Foreground: termbox.Attribute(254)},
114
	ColorFuzzySelected:     {Background: termbox.Attribute(236), Foreground: termbox.Attribute(254)},
115
	ColorFuzzySelected:      {Background: termbox.Attribute(236), Foreground: termbox.Attribute(254)},
115
	ColorFuzzyModeBuffers:  {Background: termbox.Attribute(125), Foreground: termbox.Attribute(255)},
116
	ColorFuzzyModeBuffers:   {Background: termbox.Attribute(125), Foreground: termbox.Attribute(255)},
116
	ColorFuzzyModeFiles:    {Background: termbox.Attribute(125), Foreground: termbox.Attribute(255)},
117
	ColorFuzzyModeFiles:     {Background: termbox.Attribute(125), Foreground: termbox.Attribute(255)},
117
	ColorFuzzyModeWarnings: {Background: termbox.Attribute(33), Foreground: termbox.Attribute(255)},
118
	ColorFuzzyModeWarnings:  {Background: termbox.Attribute(33), Foreground: termbox.Attribute(255)},
  
119
	ColorFuzzyModeBookmarks: {Background: termbox.Attribute(93), Foreground: termbox.Attribute(255)},
118
  
120
  
119
	ColorEmptyLineMarker: {Background: termbox.ColorDefault, Foreground: termbox.Attribute(244)},
121
	ColorEmptyLineMarker: {Background: termbox.ColorDefault, Foreground: termbox.Attribute(244)},
120
  
122
  
...