Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions internal/app/commands_editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,23 @@ func registerEditorCommands(app *App) {
Handler: func() { app.EditorGroup.TitleCase() },
})

reg.Register(command.Command{
ID: "bookmark.toggle", Title: "Toggle Bookmark",
Handler: func() { app.EditorGroup.ToggleBookmark() },
})
reg.Register(command.Command{
ID: "bookmark.next", Title: "Next Bookmark",
Handler: func() { app.EditorGroup.NextBookmark() },
})
reg.Register(command.Command{
ID: "bookmark.prev", Title: "Previous Bookmark",
Handler: func() { app.EditorGroup.PrevBookmark() },
})
reg.Register(command.Command{
ID: "bookmark.clearAll", Title: "Clear All Bookmarks",
Handler: func() { app.EditorGroup.ClearBookmarks() },
})

reg.Register(command.Command{
ID: "editor.quit", Title: "Quit",
Handler: app.Quit,
Expand Down
1 change: 1 addition & 0 deletions internal/app/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func BuildStyleMap(theme config.ThemeConfig) term.StyleMap {
applyStyleDef(&m, term.StyleSearchMatch, theme.Editor.SearchMatch)
applyStyleDef(&m, term.StyleSearchActive, theme.Editor.SearchActive)
applyStyleDef(&m, term.StyleBracketMatch, theme.Editor.BracketMatch)
applyStyleDef(&m, term.StyleBookmark, theme.Editor.Bookmark)
applyStyleDef(&m, term.StyleDiffAdded, theme.Diff.Added)
applyStyleDef(&m, term.StyleDiffDeleted, theme.Diff.Deleted)
applyStyleDef(&m, term.StyleDiffModified, theme.Diff.Modified)
Expand Down
3 changes: 3 additions & 0 deletions internal/config/keybindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ func DefaultKeybindings() []KeyBinding {
{Key: "ctrl+k 9", Command: "fold.expandAll"},
{Key: "ctrl+k b", Command: "panel.toggle"},
{Key: "ctrl+k j", Command: "editor.joinLines"},
{Key: "ctrl+k m", Command: "bookmark.toggle"},
{Key: "ctrl+k n", Command: "bookmark.next"},
{Key: "ctrl+k ,", Command: "bookmark.prev"},
{Key: "ctrl+t", Command: "terminal.toggle"},
{Key: "alt+t", Command: "terminal.fullscreen"},
{Key: "f10", Command: "menu.file"},
Expand Down
48 changes: 25 additions & 23 deletions internal/config/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type EditorStyles struct {
SearchMatch StyleDef `json:"searchMatch"`
SearchActive StyleDef `json:"searchActive"`
BracketMatch StyleDef `json:"bracketMatch"`
Bookmark StyleDef `json:"bookmark"`
Diagnostics DiagnosticStyles `json:"diagnostics"`
}

Expand Down Expand Up @@ -144,37 +145,37 @@ type HoverStyles struct {
}

type ThemeConfig struct {
Default StyleDef `json:"default"`
Muted StyleDef `json:"muted"`
Success StyleDef `json:"success"`
Danger StyleDef `json:"danger"`
Warning StyleDef `json:"warning"`
StatusBar StyleDef `json:"statusBar"`
Tabs TabStyles `json:"tabs"`
Sidebar SidebarStyles `json:"sidebar"`
Dialog DialogStyles `json:"dialog"`
Editor EditorStyles `json:"editor"`
Menu MenuStyles `json:"menu"`
Input InputStyles `json:"input"`
Hover HoverStyles `json:"hover"`
Border StyleDef `json:"border"`
Diff DiffStyles `json:"diff"`
Scrollbar StyleDef `json:"scrollbar"`
Syntax SyntaxStyles `json:"syntax"`
Borders BorderChars `json:"borders"`
Terminal TerminalColors `json:"terminal,omitempty"`
Default StyleDef `json:"default"`
Muted StyleDef `json:"muted"`
Success StyleDef `json:"success"`
Danger StyleDef `json:"danger"`
Warning StyleDef `json:"warning"`
StatusBar StyleDef `json:"statusBar"`
Tabs TabStyles `json:"tabs"`
Sidebar SidebarStyles `json:"sidebar"`
Dialog DialogStyles `json:"dialog"`
Editor EditorStyles `json:"editor"`
Menu MenuStyles `json:"menu"`
Input InputStyles `json:"input"`
Hover HoverStyles `json:"hover"`
Border StyleDef `json:"border"`
Diff DiffStyles `json:"diff"`
Scrollbar StyleDef `json:"scrollbar"`
Syntax SyntaxStyles `json:"syntax"`
Borders BorderChars `json:"borders"`
Terminal TerminalColors `json:"terminal,omitempty"`
}

func DefaultTheme() ThemeConfig {
t := ThemeConfig{
Terminal: DefaultTerminalColors(),
Default: StyleDef{Fg: "#fafafa", Bg: "#1f1f1f"},
Muted: StyleDef{Fg: "#888888"},
Default: StyleDef{Fg: "#fafafa", Bg: "#1f1f1f"},
Muted: StyleDef{Fg: "#888888"},

Menu: MenuStyles{
Active: StyleDef{Fg: "#ffffff", Bg: "#505050", Bold: true},
},
StatusBar: StyleDef{},
StatusBar: StyleDef{},

Tabs: TabStyles{
Active: StyleDef{Fg: "#ffffff", Bold: true},
Expand Down Expand Up @@ -254,6 +255,7 @@ func (t *ThemeConfig) ResolveColors() {
}
fillFg(&t.Hover.Bold, t.Default.Fg)
fillFg(&t.Hover.Code, t.Syntax.String.Fg)
fillFg(&t.Editor.Bookmark, "#d7ba7d")
}

func fillFg(s *StyleDef, color string) {
Expand All @@ -266,4 +268,4 @@ func fillBg(s *StyleDef, color string) {
if s.Bg == "" {
s.Bg = color
}
}
}
21 changes: 11 additions & 10 deletions internal/term/screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const (
StyleInputAction
StyleHoverBold
StyleHoverCode
StyleBookmark
)

// DirectColor holds an RGBA color for terminal emulator output.
Expand All @@ -62,7 +63,7 @@ type DirectColor struct {
type CellAttr byte

const (
CellAttrBold CellAttr = 1 << iota
CellAttrBold CellAttr = 1 << iota
CellAttrUnderline
CellAttrItalic
CellAttrReverse
Expand All @@ -79,17 +80,17 @@ type Cell struct {

// Direct-style fields for terminal emulator cells.
// When Direct is true, Fg/Bg/Attrs are used instead of Style.
Direct bool
Fg DirectColor
Bg DirectColor
Attrs CellAttr
Direct bool
Fg DirectColor
Bg DirectColor
Attrs CellAttr
}

// CursorStyle represents the shape of the text cursor.
type CursorStyle int

const (
CursorStyleBlinkingBar CursorStyle = iota // default
CursorStyleBlinkingBar CursorStyle = iota // default
CursorStyleSteadyBar
CursorStyleBlinkingBlock
CursorStyleSteadyBlock
Expand Down Expand Up @@ -149,8 +150,8 @@ func (m *MockScreen) SetCell(x, y int, c Cell) {
m.Cells[[2]int{x, y}] = c
}

func (m *MockScreen) Show() {}
func (m *MockScreen) Clear() { m.Cells = make(map[[2]int]Cell) }
func (m *MockScreen) ShowCursor(x, y int) {}
func (m *MockScreen) HideCursor() {}
func (m *MockScreen) Show() {}
func (m *MockScreen) Clear() { m.Cells = make(map[[2]int]Cell) }
func (m *MockScreen) ShowCursor(x, y int) {}
func (m *MockScreen) HideCursor() {}
func (m *MockScreen) SetCursorStyle(CursorStyle) {}
4 changes: 2 additions & 2 deletions internal/term/tcell_screen.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"github.com/gdamore/tcell/v2"
)

const StyleCount = 48
const StyleCount = 49

type StyleMap [StyleCount]tcell.Style

Expand Down Expand Up @@ -120,7 +120,7 @@ func (t *TcellScreen) HideCursor() {

var cursorStyleMap = map[CursorStyle]tcell.CursorStyle{
CursorStyleBlinkingBar: tcell.CursorStyleBlinkingBar,
CursorStyleSteadyBar: tcell.CursorStyleSteadyBar,
CursorStyleSteadyBar: tcell.CursorStyleSteadyBar,
CursorStyleBlinkingBlock: tcell.CursorStyleBlinkingBlock,
CursorStyleSteadyBlock: tcell.CursorStyleSteadyBlock,
CursorStyleBlinkingUnderline: tcell.CursorStyleBlinkingUnderline,
Expand Down
29 changes: 29 additions & 0 deletions internal/ui/editor_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type editorTab struct {
TabSize int
Content Widget
Pinned bool
Bookmarks map[int]bool
}

type EditorGroupWidget struct {
Expand Down Expand Up @@ -384,6 +385,7 @@ func (g *EditorGroupWidget) SwitchTab(idx int) {
func (g *EditorGroupWidget) saveMultiState() {
if t := g.activeTab(); t != nil && t.Content == nil {
t.Multi = g.Editor.Multi
t.Bookmarks = g.Editor.Bookmarks
}
}

Expand Down Expand Up @@ -842,6 +844,32 @@ func (g *EditorGroupWidget) TitleCase() {
}
}

func (g *EditorGroupWidget) ToggleBookmark() {
if g.IsEditorActive() {
g.Editor.ToggleBookmark()
g.saveMultiState()
}
}

func (g *EditorGroupWidget) NextBookmark() {
if g.IsEditorActive() {
g.Editor.NextBookmark()
}
}

func (g *EditorGroupWidget) PrevBookmark() {
if g.IsEditorActive() {
g.Editor.PrevBookmark()
}
}

func (g *EditorGroupWidget) ClearBookmarks() {
if g.IsEditorActive() {
g.Editor.ClearBookmarks()
g.saveMultiState()
}
}

func (g *EditorGroupWidget) IsMultiCursorActive() bool {
return g.IsEditorActive() && g.Editor.isMultiActive()
}
Expand Down Expand Up @@ -914,6 +942,7 @@ func (g *EditorGroupWidget) syncTabs() {
g.Editor.Highlighter = t.Highlighter
g.Editor.Diagnostics = t.Diagnostics
g.Editor.Folds = t.Folds
g.Editor.Bookmarks = t.Bookmarks
g.Editor.buildDiagIndex()
g.Editor.InvalidateMaxLineWidth()
if t.TabSize > 0 {
Expand Down
81 changes: 80 additions & 1 deletion internal/ui/editor_widget.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type EditorPaneWidget struct {
cachedVisibleLines []int
searchByLine map[int][]int
diagByLine map[int][]int
Bookmarks map[int]bool
}

func NewEditorPaneWidget(buf *buffer.Buffer, cur *cursor.Cursor, vp *view.Viewport) *EditorPaneWidget {
Expand Down Expand Up @@ -253,6 +254,9 @@ func (e *EditorPaneWidget) Render(surface *RenderSurface) {
}
}
}
if lineIdx < totalLines && e.Bookmarks != nil && e.Bookmarks[lineIdx] {
surface.SetCell(0, y, term.Cell{Ch: '●', Style: term.StyleBookmark})
}
}

if lineIdx < totalLines {
Expand Down Expand Up @@ -2177,7 +2181,6 @@ func (e *EditorPaneWidget) SplitSelectionToLines() {
e.scrollViewport()
}


// transformSelection replaces the selected text with the result of applying fn.
// It preserves the selection after transformation.
func (e *EditorPaneWidget) transformSelection(fn func(string) string) {
Expand Down Expand Up @@ -2268,3 +2271,79 @@ func (e *EditorPaneWidget) TitleCase() {
return string(runes)
})
}

func (e *EditorPaneWidget) ToggleBookmark() {
if e.Bookmarks == nil {
e.Bookmarks = make(map[int]bool)
}
line := e.Cursor.Line
if e.Bookmarks[line] {
delete(e.Bookmarks, line)
} else {
e.Bookmarks[line] = true
}
}

func (e *EditorPaneWidget) sortedBookmarks() []int {
if len(e.Bookmarks) == 0 {
return nil
}
lines := make([]int, 0, len(e.Bookmarks))
for line := range e.Bookmarks {
lines = append(lines, line)
}
sort.Ints(lines)
return lines
}

func (e *EditorPaneWidget) NextBookmark() {
sorted := e.sortedBookmarks()
if len(sorted) == 0 {
return
}
cur := e.Cursor.Line
for _, line := range sorted {
if line > cur {
e.Cursor.Line = line
e.Cursor.Col = 0
e.clampCursor()
e.scrollViewport()
return
}
}
// Wrap around to first bookmark
e.Cursor.Line = sorted[0]
e.Cursor.Col = 0
e.clampCursor()
e.scrollViewport()
}

func (e *EditorPaneWidget) PrevBookmark() {
sorted := e.sortedBookmarks()
if len(sorted) == 0 {
return
}
cur := e.Cursor.Line
for i := len(sorted) - 1; i >= 0; i-- {
if sorted[i] < cur {
e.Cursor.Line = sorted[i]
e.Cursor.Col = 0
e.clampCursor()
e.scrollViewport()
return
}
}
// Wrap around to last bookmark
e.Cursor.Line = sorted[len(sorted)-1]
e.Cursor.Col = 0
e.clampCursor()
e.scrollViewport()
}

func (e *EditorPaneWidget) ClearBookmarks() {
e.Bookmarks = nil
}

func (e *EditorPaneWidget) HasBookmarks() bool {
return len(e.Bookmarks) > 0
}
Loading
Loading