package editor import ( "testing" tea "github.com/charmbracelet/bubbletea" ) // sel sets a selection from (ar,ac) to (cr,cc) (anchor → cursor). func sel(e *Editor, ar, ac, cr, cc int) { a := Position{Row: ar, Col: ac} e.anchor = &a e.Cursor = Position{Row: cr, Col: cc} } func TestWrapBoldNoSelectionInsertsPair(t *testing.T) { e := newEditorWith("") e.WrapBold() if e.Lines[0] != "****" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "****") } if e.Cursor.Col != 2 { t.Fatalf("Cursor.Col = %d, want 2 (between markers)", e.Cursor.Col) } } func TestWrapBoldWrapsSelection(t *testing.T) { e := newEditorWith("word") sel(e, 0, 0, 0, 4) e.WrapBold() if e.Lines[0] != "**word**" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "**word**") } if got := e.SelectedText(); got != "word" { t.Fatalf("selection = %q, want inner %q", got, "word") } } func TestWrapBoldTogglesOffMarkersInsideSelection(t *testing.T) { e := newEditorWith("**word**") sel(e, 0, 0, 0, 8) e.WrapBold() if e.Lines[0] != "word" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "word") } if got := e.SelectedText(); got != "word" { t.Fatalf("selection = %q, want %q", got, "word") } } func TestWrapBoldTogglesOffMarkersOutsideSelection(t *testing.T) { e := newEditorWith("**word**") sel(e, 0, 2, 0, 6) // select just "word", markers sit outside e.WrapBold() if e.Lines[0] != "word" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "word") } if got := e.SelectedText(); got != "word" { t.Fatalf("selection = %q, want %q", got, "word") } } func TestWrapItalicUsesUnderscore(t *testing.T) { e := newEditorWith("hi") sel(e, 0, 0, 0, 2) e.WrapItalic() if e.Lines[0] != "_hi_" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "_hi_") } } func TestWrapCodeUsesBacktick(t *testing.T) { e := newEditorWith("x") sel(e, 0, 0, 0, 1) e.WrapCode() if e.Lines[0] != "`x`" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "`x`") } } func TestWrapLinkWrapsSelectionCursorInParens(t *testing.T) { e := newEditorWith("text") sel(e, 0, 0, 0, 4) e.WrapLink() if e.Lines[0] != "[text]()" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "[text]()") } // cursor parked inside the () — just before the closing paren if e.Cursor.Col != 7 { t.Fatalf("Cursor.Col = %d, want 7 (inside parens)", e.Cursor.Col) } if e.HasSelection() { t.Fatal("link wrap should collapse selection") } } func TestWrapLinkNoSelectionCursorInBrackets(t *testing.T) { e := newEditorWith("") e.WrapLink() if e.Lines[0] != "[]()" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "[]()") } if e.Cursor.Col != 1 { t.Fatalf("Cursor.Col = %d, want 1 (inside brackets)", e.Cursor.Col) } } func TestWrapLinkTogglesOff(t *testing.T) { e := newEditorWith("[text](url)") sel(e, 0, 0, 0, 11) e.WrapLink() if e.Lines[0] != "text" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "text") } } func TestAltSKeyBoldsAsOneUndoGroup(t *testing.T) { e := newEditorWith("word") sel(e, 0, 0, 0, 4) e.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("s"), Alt: true}) if e.Lines[0] != "**word**" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "**word**") } e.Undo() if e.Lines[0] != "word" { t.Fatalf("after undo Lines[0] = %q, want %q", e.Lines[0], "word") } } func TestWordCountWholeBuffer(t *testing.T) { e := newEditorWith("one two", "three") if n := e.WordCount(); n != 3 { t.Fatalf("WordCount = %d, want 3", n) } } func TestSelectionStats(t *testing.T) { e := newEditorWith("one two three") sel(e, 0, 0, 0, 7) // "one two" c, w, ok := e.SelectionStats() if !ok || c != 7 || w != 2 { t.Fatalf("SelectionStats = (%d,%d,%v), want (7,2,true)", c, w, ok) } } func TestSelectionStatsNoSelection(t *testing.T) { e := newEditorWith("x") if _, _, ok := e.SelectionStats(); ok { t.Fatal("SelectionStats ok should be false with no selection") } } func TestTypingStarWrapsSelection(t *testing.T) { e := newEditorWith("word") sel(e, 0, 0, 0, 4) e.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("*")}) if e.Lines[0] != "*word*" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "*word*") } if e.SelectedText() != "word" { t.Fatalf("selection = %q, want inner %q", e.SelectedText(), "word") } } func TestTypingStarTwiceNestsToBold(t *testing.T) { e := newEditorWith("word") sel(e, 0, 0, 0, 4) e.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("*")}) e.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("*")}) if e.Lines[0] != "**word**" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "**word**") } } func TestTypingBracketWrapsSelection(t *testing.T) { e := newEditorWith("word") sel(e, 0, 0, 0, 4) e.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("[")}) if e.Lines[0] != "[word]" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "[word]") } } func TestTypingParenWrapsSelection(t *testing.T) { e := newEditorWith("word") sel(e, 0, 0, 0, 4) e.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("(")}) if e.Lines[0] != "(word)" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "(word)") } } func TestTypingWrapCharNoSelectionInsertsNormally(t *testing.T) { e := newEditorWith("ab") e.Cursor = Position{Row: 0, Col: 1} e.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("*")}) if e.Lines[0] != "a*b" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "a*b") } } func TestTypingNonWrapCharReplacesSelection(t *testing.T) { e := newEditorWith("word") sel(e, 0, 0, 0, 4) e.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("x")}) if e.Lines[0] != "x" { t.Fatalf("Lines[0] = %q, want %q", e.Lines[0], "x") } }