fix: p and P differ in V mode, resolved and tested.
P should not replace the register, p should
This commit is contained in:
parent
78dc00a5e9
commit
1a98d3a4de
@ -240,9 +240,11 @@ func (a PasteBefore) WithCount(n int) Action {
|
|||||||
return PasteBefore{Count: n}
|
return PasteBefore{Count: n}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VisualPaste implements Action (p in visual mode) - replaces selection with register content
|
// VisualPaste implements Action (p/p in visual mode) - replaces selection with
|
||||||
|
// register content when Replace flag is set
|
||||||
type VisualPaste struct {
|
type VisualPaste struct {
|
||||||
Count int
|
Count int
|
||||||
|
Replace bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// VisualPaste.Execute: Replaces visual selection with register content (p in visual mode).
|
// VisualPaste.Execute: Replaces visual selection with register content (p in visual mode).
|
||||||
@ -266,11 +268,11 @@ func (a VisualPaste) Execute(m Model) tea.Cmd {
|
|||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case core.VisualMode:
|
case core.VisualMode:
|
||||||
visualCharPaste(m, reg, start, end)
|
visualCharPaste(m, reg, start, end, a.Replace)
|
||||||
case core.VisualBlockMode:
|
case core.VisualBlockMode:
|
||||||
visualBlockPaste(m, reg, start, end)
|
visualBlockPaste(m, reg, start, end, a.Replace)
|
||||||
case core.VisualLineMode:
|
case core.VisualLineMode:
|
||||||
visualLinePaste(m, reg, start, end)
|
visualLinePaste(m, reg, start, end, a.Replace)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit visual mode
|
// Exit visual mode
|
||||||
@ -295,7 +297,7 @@ func normalizeSelection(m Model) (core.Position, core.Position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// visualCharPaste: Handles paste operation in visual (character) mode.
|
// visualCharPaste: Handles paste operation in visual (character) mode.
|
||||||
func visualCharPaste(m Model, reg core.Register, start, end core.Position) {
|
func visualCharPaste(m Model, reg core.Register, start, end core.Position, replace bool) {
|
||||||
win := m.ActiveWindow()
|
win := m.ActiveWindow()
|
||||||
buf := m.ActiveBuffer()
|
buf := m.ActiveBuffer()
|
||||||
|
|
||||||
@ -344,11 +346,13 @@ func visualCharPaste(m Model, reg core.Register, start, end core.Position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update register with deleted text
|
// Update register with deleted text
|
||||||
|
if replace {
|
||||||
m.UpdateDefaultRegister(core.CharwiseRegister, []string{deletedText})
|
m.UpdateDefaultRegister(core.CharwiseRegister, []string{deletedText})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// visualBlockPaste: Handles paste operation in visual block mode.
|
// visualBlockPaste: Handles paste operation in visual block mode.
|
||||||
func visualBlockPaste(m Model, reg core.Register, start, end core.Position) {
|
func visualBlockPaste(m Model, reg core.Register, start, end core.Position, replace bool) {
|
||||||
win := m.ActiveWindow()
|
win := m.ActiveWindow()
|
||||||
buf := m.ActiveBuffer()
|
buf := m.ActiveBuffer()
|
||||||
|
|
||||||
@ -400,11 +404,13 @@ func visualBlockPaste(m Model, reg core.Register, start, end core.Position) {
|
|||||||
win.SetCursorCol(startCol)
|
win.SetCursorCol(startCol)
|
||||||
|
|
||||||
// Update register with deleted block text (joined)
|
// Update register with deleted block text (joined)
|
||||||
|
if replace {
|
||||||
m.UpdateDefaultRegister(core.CharwiseRegister, []string{strings.Join(deletedLines, "\n")})
|
m.UpdateDefaultRegister(core.CharwiseRegister, []string{strings.Join(deletedLines, "\n")})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// visualLinePaste: Handles paste operation in visual line mode.
|
// visualLinePaste: Handles paste operation in visual line mode.
|
||||||
func visualLinePaste(m Model, reg core.Register, start, end core.Position) {
|
func visualLinePaste(m Model, reg core.Register, start, end core.Position, replace bool) {
|
||||||
win := m.ActiveWindow()
|
win := m.ActiveWindow()
|
||||||
buf := m.ActiveBuffer()
|
buf := m.ActiveBuffer()
|
||||||
|
|
||||||
@ -451,7 +457,9 @@ func visualLinePaste(m Model, reg core.Register, start, end core.Position) {
|
|||||||
win.SetCursorCol(0)
|
win.SetCursorCol(0)
|
||||||
|
|
||||||
// Update register with deleted lines
|
// Update register with deleted lines
|
||||||
|
if replace {
|
||||||
m.UpdateDefaultRegister(core.LinewiseRegister, deletedLines)
|
m.UpdateDefaultRegister(core.LinewiseRegister, deletedLines)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractCharSelection: Extracts text from a character selection range.
|
// extractCharSelection: Extracts text from a character selection range.
|
||||||
@ -531,5 +539,5 @@ var _ Repeatable = VisualPaste{}
|
|||||||
|
|
||||||
// VisualPaste.WithCount: Returns a new VisualPaste with the given count.
|
// VisualPaste.WithCount: Returns a new VisualPaste with the given count.
|
||||||
func (a VisualPaste) WithCount(n int) Action {
|
func (a VisualPaste) WithCount(n int) Action {
|
||||||
return VisualPaste{Count: n}
|
return VisualPaste{Count: n, Replace: a.Replace}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1552,6 +1552,46 @@ func TestVisualPasteRegisterBehavior(t *testing.T) {
|
|||||||
t.Errorf("register type = %v, want LinewiseRegister", reg.Type)
|
t.Errorf("register type = %v, want LinewiseRegister", reg.Type)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("vP does not put deleted text into register", func(t *testing.T) {
|
||||||
|
tm := newTestModel(t,
|
||||||
|
WithLines([]string{"hello world"}),
|
||||||
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
||||||
|
WithRegister('"', core.CharwiseRegister, []string{"NEW"}),
|
||||||
|
)
|
||||||
|
sendKeys(tm, "v", "l", "l", "l", "l", "P") // select "hello", paste "NEW"
|
||||||
|
|
||||||
|
m := getFinalModel(t, tm)
|
||||||
|
// After visual paste, the deleted "hello" should be in the register
|
||||||
|
reg, ok := m.GetRegister('"')
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("unnamed register not found")
|
||||||
|
}
|
||||||
|
if len(reg.Content) != 1 || reg.Content[0] != "NEW" {
|
||||||
|
t.Errorf("register content = %q, want 'NEW'", reg.Content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("VP does not put deleted line into register", func(t *testing.T) {
|
||||||
|
tm := newTestModel(t,
|
||||||
|
WithLines([]string{"line one", "line two"}),
|
||||||
|
WithCursorPos(core.Position{Line: 0, Col: 0}),
|
||||||
|
WithRegister('"', core.LinewiseRegister, []string{"REPLACED"}),
|
||||||
|
)
|
||||||
|
sendKeys(tm, "V", "P")
|
||||||
|
|
||||||
|
m := getFinalModel(t, tm)
|
||||||
|
reg, ok := m.GetRegister('"')
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("unnamed register not found")
|
||||||
|
}
|
||||||
|
if len(reg.Content) != 1 || reg.Content[0] != "REPLACED" {
|
||||||
|
t.Errorf("register content = %q, want 'REPLACED'", reg.Content)
|
||||||
|
}
|
||||||
|
if reg.Type != core.LinewiseRegister {
|
||||||
|
t.Errorf("register type = %v, want LinewiseRegister", reg.Type)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVisualPasteEdgeCases(t *testing.T) {
|
func TestVisualPasteEdgeCases(t *testing.T) {
|
||||||
|
|||||||
@ -134,7 +134,8 @@ func NewVisualKeymap() *Keymap {
|
|||||||
"c": operator.ChangeOperator{},
|
"c": operator.ChangeOperator{},
|
||||||
},
|
},
|
||||||
actions: map[string]action.Action{
|
actions: map[string]action.Action{
|
||||||
"p": action.VisualPaste{Count: 1},
|
"p": action.VisualPaste{Count: 1, Replace: true},
|
||||||
|
"P": action.VisualPaste{Count: 1, Replace: false},
|
||||||
".": action.Repeat{Count: 1},
|
".": action.Repeat{Count: 1},
|
||||||
// ":": action.EnterComandMode{}, // Different OP
|
// ":": action.EnterComandMode{}, // Different OP
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user