Skip to content

Commit ca60120

Browse files
authored
Merge pull request zyedidia#3335 from dmaluka/line-actions-cleanup
Improve and unify `CopyLine`, `CutLine`, `DeleteLine`, `DuplicateLine` actions
2 parents 1539da7 + bf65847 commit ca60120

File tree

5 files changed

+152
-67
lines changed

5 files changed

+152
-67
lines changed

internal/action/actions.go

Lines changed: 125 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,101 +1238,179 @@ func (h *BufPane) Redo() bool {
12381238
return true
12391239
}
12401240

1241+
func (h *BufPane) selectLines() int {
1242+
if h.Cursor.HasSelection() {
1243+
start := h.Cursor.CurSelection[0]
1244+
end := h.Cursor.CurSelection[1]
1245+
if start.GreaterThan(end) {
1246+
start, end = end, start
1247+
}
1248+
if end.X == 0 {
1249+
end = end.Move(-1, h.Buf)
1250+
}
1251+
1252+
h.Cursor.Deselect(true)
1253+
h.Cursor.SetSelectionStart(buffer.Loc{0, start.Y})
1254+
h.Cursor.SetSelectionEnd(buffer.Loc{0, end.Y + 1})
1255+
} else {
1256+
h.Cursor.SelectLine()
1257+
}
1258+
return h.Cursor.CurSelection[1].Y - h.Cursor.CurSelection[0].Y
1259+
}
1260+
12411261
// Copy the selection to the system clipboard
12421262
func (h *BufPane) Copy() bool {
1243-
if h.Cursor.HasSelection() {
1244-
h.Cursor.CopySelection(clipboard.ClipboardReg)
1245-
h.freshClip = true
1246-
InfoBar.Message("Copied selection")
1263+
if !h.Cursor.HasSelection() {
1264+
return false
12471265
}
1266+
h.Cursor.CopySelection(clipboard.ClipboardReg)
1267+
h.freshClip = false
1268+
InfoBar.Message("Copied selection")
12481269
h.Relocate()
12491270
return true
12501271
}
12511272

1252-
// CopyLine copies the current line to the clipboard
1273+
// CopyLine copies the current line to the clipboard. If there is a selection,
1274+
// CopyLine copies all the lines that are (fully or partially) in the selection.
12531275
func (h *BufPane) CopyLine() bool {
1254-
if h.Cursor.HasSelection() {
1276+
origLoc := h.Cursor.Loc
1277+
origLastVisualX := h.Cursor.LastVisualX
1278+
origSelection := h.Cursor.CurSelection
1279+
1280+
nlines := h.selectLines()
1281+
if nlines == 0 {
12551282
return false
12561283
}
1257-
origLoc := h.Cursor.Loc
1258-
h.Cursor.SelectLine()
12591284
h.Cursor.CopySelection(clipboard.ClipboardReg)
1260-
h.freshClip = true
1261-
InfoBar.Message("Copied line")
1285+
h.freshClip = false
1286+
if nlines > 1 {
1287+
InfoBar.Message(fmt.Sprintf("Copied %d lines", nlines))
1288+
} else {
1289+
InfoBar.Message("Copied line")
1290+
}
12621291

1263-
h.Cursor.Deselect(true)
12641292
h.Cursor.Loc = origLoc
1293+
h.Cursor.LastVisualX = origLastVisualX
1294+
h.Cursor.CurSelection = origSelection
12651295
h.Relocate()
12661296
return true
12671297
}
12681298

1269-
// CutLine cuts the current line to the clipboard
1270-
func (h *BufPane) CutLine() bool {
1271-
h.Cursor.SelectLine()
1299+
// Cut the selection to the system clipboard
1300+
func (h *BufPane) Cut() bool {
12721301
if !h.Cursor.HasSelection() {
12731302
return false
12741303
}
1304+
h.Cursor.CopySelection(clipboard.ClipboardReg)
1305+
h.Cursor.DeleteSelection()
1306+
h.Cursor.ResetSelection()
1307+
h.freshClip = false
1308+
InfoBar.Message("Cut selection")
1309+
1310+
h.Relocate()
1311+
return true
1312+
}
1313+
1314+
// CutLine cuts the current line to the clipboard. If there is a selection,
1315+
// CutLine cuts all the lines that are (fully or partially) in the selection.
1316+
func (h *BufPane) CutLine() bool {
1317+
nlines := h.selectLines()
1318+
if nlines == 0 {
1319+
return false
1320+
}
1321+
totalLines := nlines
12751322
if h.freshClip {
1276-
if h.Cursor.HasSelection() {
1277-
if clip, err := clipboard.Read(clipboard.ClipboardReg); err != nil {
1278-
InfoBar.Error(err)
1279-
} else {
1280-
clipboard.WriteMulti(clip+string(h.Cursor.GetSelection()), clipboard.ClipboardReg, h.Cursor.Num, h.Buf.NumCursors())
1281-
}
1323+
if clip, err := clipboard.Read(clipboard.ClipboardReg); err != nil {
1324+
InfoBar.Error(err)
1325+
return false
1326+
} else {
1327+
clipboard.WriteMulti(clip+string(h.Cursor.GetSelection()), clipboard.ClipboardReg, h.Cursor.Num, h.Buf.NumCursors())
1328+
totalLines = strings.Count(clip, "\n") + nlines
12821329
}
1283-
} else if time.Since(h.lastCutTime)/time.Second > 10*time.Second || !h.freshClip {
1284-
h.Copy()
1330+
} else {
1331+
h.Cursor.CopySelection(clipboard.ClipboardReg)
12851332
}
12861333
h.freshClip = true
1287-
h.lastCutTime = time.Now()
12881334
h.Cursor.DeleteSelection()
12891335
h.Cursor.ResetSelection()
1290-
InfoBar.Message("Cut line")
1336+
h.Cursor.StoreVisualX()
1337+
if totalLines > 1 {
1338+
InfoBar.Message(fmt.Sprintf("Cut %d lines", totalLines))
1339+
} else {
1340+
InfoBar.Message("Cut line")
1341+
}
12911342
h.Relocate()
12921343
return true
12931344
}
12941345

1295-
// Cut the selection to the system clipboard
1296-
func (h *BufPane) Cut() bool {
1297-
if h.Cursor.HasSelection() {
1298-
h.Cursor.CopySelection(clipboard.ClipboardReg)
1299-
h.Cursor.DeleteSelection()
1300-
h.Cursor.ResetSelection()
1301-
h.freshClip = true
1302-
InfoBar.Message("Cut selection")
1303-
1304-
h.Relocate()
1305-
return true
1346+
// Duplicate the selection
1347+
func (h *BufPane) Duplicate() bool {
1348+
if !h.Cursor.HasSelection() {
1349+
return false
13061350
}
1307-
return h.CutLine()
1351+
h.Buf.Insert(h.Cursor.CurSelection[1], string(h.Cursor.GetSelection()))
1352+
InfoBar.Message("Duplicated selection")
1353+
h.Relocate()
1354+
return true
13081355
}
13091356

1310-
// DuplicateLine duplicates the current line or selection
1357+
// DuplicateLine duplicates the current line. If there is a selection, DuplicateLine
1358+
// duplicates all the lines that are (fully or partially) in the selection.
13111359
func (h *BufPane) DuplicateLine() bool {
1312-
var infoMessage = "Duplicated line"
13131360
if h.Cursor.HasSelection() {
1314-
infoMessage = "Duplicated selection"
1315-
h.Buf.Insert(h.Cursor.CurSelection[1], string(h.Cursor.GetSelection()))
1361+
origLoc := h.Cursor.Loc
1362+
origLastVisualX := h.Cursor.LastVisualX
1363+
origSelection := h.Cursor.CurSelection
1364+
1365+
start := h.Cursor.CurSelection[0]
1366+
end := h.Cursor.CurSelection[1]
1367+
if start.GreaterThan(end) {
1368+
start, end = end, start
1369+
}
1370+
if end.X == 0 {
1371+
end = end.Move(-1, h.Buf)
1372+
}
1373+
1374+
h.Cursor.Deselect(true)
1375+
h.Cursor.Loc = end
1376+
h.Cursor.End()
1377+
for y := start.Y; y <= end.Y; y++ {
1378+
h.Buf.Insert(h.Cursor.Loc, "\n"+string(h.Buf.LineBytes(y)))
1379+
}
1380+
1381+
h.Cursor.Loc = origLoc
1382+
h.Cursor.LastVisualX = origLastVisualX
1383+
h.Cursor.CurSelection = origSelection
1384+
1385+
if start.Y < end.Y {
1386+
InfoBar.Message(fmt.Sprintf("Duplicated %d lines", end.Y-start.Y+1))
1387+
} else {
1388+
InfoBar.Message("Duplicated line")
1389+
}
13161390
} else {
13171391
h.Cursor.End()
13181392
h.Buf.Insert(h.Cursor.Loc, "\n"+string(h.Buf.LineBytes(h.Cursor.Y)))
1319-
// h.Cursor.Right()
1393+
InfoBar.Message("Duplicated line")
13201394
}
1321-
1322-
InfoBar.Message(infoMessage)
13231395
h.Relocate()
13241396
return true
13251397
}
13261398

1327-
// DeleteLine deletes the current line
1399+
// DeleteLine deletes the current line. If there is a selection, DeleteLine
1400+
// deletes all the lines that are (fully or partially) in the selection.
13281401
func (h *BufPane) DeleteLine() bool {
1329-
h.Cursor.SelectLine()
1330-
if !h.Cursor.HasSelection() {
1402+
nlines := h.selectLines()
1403+
if nlines == 0 {
13311404
return false
13321405
}
13331406
h.Cursor.DeleteSelection()
13341407
h.Cursor.ResetSelection()
1335-
InfoBar.Message("Deleted line")
1408+
h.Cursor.StoreVisualX()
1409+
if nlines > 1 {
1410+
InfoBar.Message(fmt.Sprintf("Deleted %d lines", nlines))
1411+
} else {
1412+
InfoBar.Message("Deleted line")
1413+
}
13361414
h.Relocate()
13371415
return true
13381416
}

internal/action/bufpane.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,8 @@ type BufPane struct {
233233
lastClickTime time.Time
234234
lastLoc buffer.Loc
235235

236-
// lastCutTime stores when the last ctrl+k was issued.
237-
// It is used for clearing the clipboard to replace it with fresh cut lines.
238-
lastCutTime time.Time
239-
240-
// freshClip returns true if the clipboard has never been pasted.
236+
// freshClip returns true if one or more lines have been cut to the clipboard
237+
// and have never been pasted yet.
241238
freshClip bool
242239

243240
// Was the last mouse event actually a double click?
@@ -780,6 +777,7 @@ var BufKeyActions = map[string]BufKeyAction{
780777
"CopyLine": (*BufPane).CopyLine,
781778
"Cut": (*BufPane).Cut,
782779
"CutLine": (*BufPane).CutLine,
780+
"Duplicate": (*BufPane).Duplicate,
783781
"DuplicateLine": (*BufPane).DuplicateLine,
784782
"DeleteLine": (*BufPane).DeleteLine,
785783
"MoveLinesUp": (*BufPane).MoveLinesUp,
@@ -907,6 +905,7 @@ var MultiActions = map[string]bool{
907905
"Copy": true,
908906
"Cut": true,
909907
"CutLine": true,
908+
"Duplicate": true,
910909
"DuplicateLine": true,
911910
"DeleteLine": true,
912911
"MoveLinesUp": true,

internal/action/defaults_darwin.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ var bufdefaults = map[string]string{
4545
"Alt-]": "DiffNext|CursorEnd",
4646
"Ctrl-z": "Undo",
4747
"Ctrl-y": "Redo",
48-
"Ctrl-c": "CopyLine|Copy",
49-
"Ctrl-x": "Cut",
48+
"Ctrl-c": "Copy|CopyLine",
49+
"Ctrl-x": "Cut|CutLine",
5050
"Ctrl-k": "CutLine",
51-
"Ctrl-d": "DuplicateLine",
51+
"Ctrl-d": "Duplicate|DuplicateLine",
5252
"Ctrl-v": "Paste",
5353
"Ctrl-a": "SelectAll",
5454
"Ctrl-t": "AddTab",
@@ -146,8 +146,8 @@ var infodefaults = map[string]string{
146146
"Backtab": "CycleAutocompleteBack",
147147
"Ctrl-z": "Undo",
148148
"Ctrl-y": "Redo",
149-
"Ctrl-c": "CopyLine|Copy",
150-
"Ctrl-x": "Cut",
149+
"Ctrl-c": "Copy|CopyLine",
150+
"Ctrl-x": "Cut|CutLine",
151151
"Ctrl-k": "CutLine",
152152
"Ctrl-v": "Paste",
153153
"Home": "StartOfTextToggle",

internal/action/defaults_other.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ var bufdefaults = map[string]string{
4848
"Alt-]": "DiffNext|CursorEnd",
4949
"Ctrl-z": "Undo",
5050
"Ctrl-y": "Redo",
51-
"Ctrl-c": "CopyLine|Copy",
52-
"Ctrl-x": "Cut",
51+
"Ctrl-c": "Copy|CopyLine",
52+
"Ctrl-x": "Cut|CutLine",
5353
"Ctrl-k": "CutLine",
54-
"Ctrl-d": "DuplicateLine",
54+
"Ctrl-d": "Duplicate|DuplicateLine",
5555
"Ctrl-v": "Paste",
5656
"Ctrl-a": "SelectAll",
5757
"Ctrl-t": "AddTab",
@@ -149,8 +149,8 @@ var infodefaults = map[string]string{
149149
"Backtab": "CycleAutocompleteBack",
150150
"Ctrl-z": "Undo",
151151
"Ctrl-y": "Redo",
152-
"Ctrl-c": "CopyLine|Copy",
153-
"Ctrl-x": "Cut",
152+
"Ctrl-c": "Copy|CopyLine",
153+
"Ctrl-x": "Cut|CutLine",
154154
"Ctrl-k": "CutLine",
155155
"Ctrl-v": "Paste",
156156
"Home": "StartOfTextToggle",

runtime/help/keybindings.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,14 @@ Autocomplete
278278
The `StartOfTextToggle` and `SelectToStartOfTextToggle` actions toggle between
279279
jumping to the start of the text (first) and start of the line.
280280

281+
The `CutLine` action cuts the current line and adds it to the previously cut
282+
lines in the clipboard since the last paste (rather than just replaces the
283+
clipboard contents with this line). So you can cut multiple, not necessarily
284+
consecutive lines to the clipboard just by pressing `Ctrl-k` multiple times,
285+
without selecting them. If you want the more traditional behavior i.e. just
286+
rewrite the clipboard every time, you can use `CopyLine,DeleteLine` action
287+
instead of `CutLine`.
288+
281289
You can also bind some mouse actions (these must be bound to mouse buttons)
282290

283291
```
@@ -495,10 +503,10 @@ conventions for text editing defaults.
495503
"Alt-]": "DiffNext|CursorEnd",
496504
"Ctrl-z": "Undo",
497505
"Ctrl-y": "Redo",
498-
"Ctrl-c": "CopyLine|Copy",
499-
"Ctrl-x": "Cut",
506+
"Ctrl-c": "Copy|CopyLine",
507+
"Ctrl-x": "Cut|CutLine",
500508
"Ctrl-k": "CutLine",
501-
"Ctrl-d": "DuplicateLine",
509+
"Ctrl-d": "Duplicate|DuplicateLine",
502510
"Ctrl-v": "Paste",
503511
"Ctrl-a": "SelectAll",
504512
"Ctrl-t": "AddTab",
@@ -621,8 +629,8 @@ are given below:
621629
"Backtab": "CycleAutocompleteBack",
622630
"Ctrl-z": "Undo",
623631
"Ctrl-y": "Redo",
624-
"Ctrl-c": "CopyLine|Copy",
625-
"Ctrl-x": "Cut",
632+
"Ctrl-c": "Copy|CopyLine",
633+
"Ctrl-x": "Cut|CutLine",
626634
"Ctrl-k": "CutLine",
627635
"Ctrl-v": "Paste",
628636
"Home": "StartOfTextToggle",

0 commit comments

Comments
 (0)