@@ -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
12421262func (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.
12531275func (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.
13111359func (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.
13281401func (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}
0 commit comments