Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature request]: Duplicate lazy line selection #3110

Closed
Gavin-Holt opened this issue Jan 4, 2024 · 7 comments · Fixed by #3335
Closed

[Feature request]: Duplicate lazy line selection #3110

Gavin-Holt opened this issue Jan 4, 2024 · 7 comments · Fixed by #3335

Comments

@Gavin-Holt
Copy link

Gavin-Holt commented Jan 4, 2024

Hi

My name is Gavin, and I am a lazy line selector.

I have tried to go straight, but lack the willpower to make pinpoint accurate line selections.

I blame a troubled relationship with the mouse, we never bonded, and I have no love for the little rodent.

My condition is so bad, that sometimes my lazy line selection is backwards - from bottom up!

Thankfully, there is a near perfect solution, the micro editor.

Micro will act upon partially selected lines:

  • MoveLinesDown
  • MoveLinesUp
  • IndentSelection
  • OutSelection
  • lua:comment.comment
  • Textfilter
  • SpawnMultiCursorSelect

The only irregular command I have discovered so far is DuplicateLine, and it trips me up hourly. With no selection, the current line is duplicated. However, with any text selected - only that text is duplicated (see #416).

To duplicate a set of lines, I have to make a pinpoint selection, remembering to include the end-of-line character (not selected with a simple shift end!).

I guess many users will be accustomed to the default behaviour, and it would be unfair for my affliction to inconvenience the majority of law-abiding precision line selectors.

My ideal solution would be a pair of functions DuplicateLinesBelow and DuplicateLinesAbove, which would make copies of the current lazy line selections above or below AND leave my selection (and clipboard) unchanged. e.g.

Before DuplicateLinesBelow :

image

After DuplicateLinesBelow :

image

I am not good at manipulating bp.Cursor.Loc, so a Lua solution is beyond my abilities.

Any help for an incurable lazy line selector would be welcome.

Kind Regards Gavin Holt

OS: Windows 10
Version: 2.0.13
Commit hash: 68d88b5
Compiled on December 12, 2023

@Gavin-Holt Gavin-Holt changed the title [Feature request]: Solution for inconsistent lazy line selection [Feature request]: Duplicate lazy line selection Jan 4, 2024
@dmaluka
Copy link
Collaborator

dmaluka commented Jan 6, 2024

I'm using the following in my init.lua for lazy line selection:

function selectLineUp(bp)
    if not bp.Cursor:HasSelection() then
        bp:EndOfLine()
        bp:SelectToStartOfLine()
    else
        bp:SelectUp()
        bp:SelectToStartOfLine()
    end
end

function selectLineDown(bp)
    if not bp.Cursor:HasSelection() then
        bp:StartOfLine()
        bp:SelectDown()
    else
        bp:SelectDown()
    end
end

I have Ctrl-Shift-Up and Ctrl-Shift-Down bound to these functions:

    "CtrlShiftDown": "lua:initlua.selectLineDown",
    "CtrlShiftUp": "lua:initlua.selectLineUp",

So in particular you could use these to quickly select a few whole lines via Ctrl-Shift-Up or Ctrl-Shift-Down (i.e. in whatever direction you like) and then duplicate them via Ctrl-D.

@Gavin-Holt
Copy link
Author

Gavin-Holt commented Jan 11, 2024

Hi,

Many thanks for sharing your functions - taking this as inspiration - and combining with #2583, I have written a SelectBlock function:

function editor_SelectBlock(Current)                  
-- Extend the selection to include the whole of any line selected
    if editor.HasSelection(Current)  then
        if Current.Cursor.CurSelection[1]:LessThan(-Current.Cursor.CurSelection[2]) then
            -- forward -> backward
            -- Extend selection to end
            Current:SelectToEndOfLine()
            -- Get EOL Character
            Current:SelectRight()
            -- Swap to end of selection
            editor_SwapAnchor(Current)
            -- Extend selection to begining of line
            Current:SelectToStartOfLine()
        else
            -- backward -> forward
            -- Extend selection to begining of line
            Current:SelectToStartOfLine()
            -- Swap to end of selection
            editor_SwapAnchor(Current)
            -- Extend selection to end
            Current:SelectToEndOfLine()
            -- Get EOL Character
            Current:SelectRight()
        end
    else
        Current.Cursor:SelectLine()
    end
    return true
end

This can be called before duplicateline to achieve block duplication:

    "CtrlD": "lua:initlua.editor_GetBlock, duplicateline"

Unfortunately these manipulations change the current selection (so you can't make multiple copies).

I will leave this open as I still feel duplicateline is an irregular command and better solution should be found.

Kind Regards Gavin Holt

@Gavin-Holt
Copy link
Author

I guess we can close this now as there is a work-around.

Kind Regards Gavin Holt

@Gavin-Holt
Copy link
Author

Gavin-Holt commented May 7, 2024

Hi,

After downloading the new binary today, my selectblock function no longer works. I suspect this is a result of Fix Deselect() after mouse selection 3f810c2 or Fix cursor moving down when selection exist #3091

This function is only used to extend the selection before using the DuplicateLine action, to cope with with partly selected lines and I have it bound to Ctrl+D:

    "CtrlD": "lua:initlua.selectblock,DuplicateLine,EndOfLine",

It fails only when the lazy selection if from the bottom upwards!

Generally Micro will act upon partly selected lines (top down or bottom up) for all the following :

  • MoveLinesDown
  • MoveLinesUp
  • IndentSelection
  • OutdentSelection
  • lua:comment.comment
  • Textfilter
  • SpawnMultiCursorSelect

DuplicateLine remains an outlier, and my work-around no longer works!

Not wanting to upset the current users of DuplicateLine could we have a DuplicateLines action which acts on all partly selected lines?

Kind Regards Gavin Holt

PS. Naming things is hard: given a free hand I would rename DuplicateLine action to Duplicate (a method to duplicate selection, or current line if no selection, without using the clipboard), and that would allow a more logical use for DuplicateLine (duplicate all partly selected lines or current line if no selection, without using the clipboard).

@dmaluka
Copy link
Collaborator

dmaluka commented Jun 7, 2024

After downloading the new binary today, my selectblock function no longer works. I suspect this is a result of Fix Deselect() after mouse selection 3f810c2 or Fix cursor moving down when selection exist #3091

Yes, we changed the behavior to always ignore the direction of selection. First, to make it at least consistent (and to fix bugs caused by its inconsistency), second, because apparently some users even prefer this ignore-direction-of-selection behavior.

You can use something like this now:

function selectblock(bp)
    if bp.Cursor:HasSelection() then
        local lastY = bp.Cursor.CurSelection[2].Y
        bp:StartOfLine()
        while bp.Cursor.Loc.Y <= lastY do
            bp:SelectDown()
        end
    end
end

PS. Naming things is hard: given a free hand I would rename DuplicateLine action to Duplicate (a method to duplicate selection, or current line if no selection, without using the clipboard), and that would allow a more logical use for DuplicateLine (duplicate all partly selected lines or current line if no selection, without using the clipboard).

I'm thinking about the same. We may clean up and unify things by changing behavior of some action without changing behavior of the default keys bound to them (so that the change would not affect most users): bind Ctrl-c to Copy|CopyLine, Ctrl-x to Cut|CutLine, Ctrl-d to Duplicate|DuplicateLine. The Copy, Cut and Duplicate actions would return false if there is no selection, and the *Line variants would operate on whole lines, not just the selection.

@dmaluka
Copy link
Collaborator

dmaluka commented Jun 9, 2024

I'm thinking about the same. We may clean up and unify things by changing behavior of some action without changing behavior of the default keys bound to them (so that the change would not affect most users): bind Ctrl-c to Copy|CopyLine, Ctrl-x to Cut|CutLine, Ctrl-d to Duplicate|DuplicateLine. The Copy, Cut and Duplicate actions would return false if there is no selection, and the *Line variants would operate on whole lines, not just the selection.

Implemented in #3335. With this PR you will probably not need your selectblock any longer.

@Gavin-Holt
Copy link
Author

Hi

I have been giving more thought to duplicate and have written a function to deal with three situations:

  1. Duplicate the current line (if no selection)
  2. Duplicate selected word/phrase (on one line)
  3. Duplicate the whole of any partly selected lines (lazy multi-line selection)

All of these situations leave the cursor position/selection unchanged, and therefore can be repeated for multiple duplication.

My Lua function satisfies my needs, but would be better as a built in action - to work with multiple cursors.

function duplicate(Current)
    if Current.Cursor:HasSelection() then
        if Current.Cursor.CurSelection[1].Y == Current.Cursor.CurSelection[2].Y then
            -- Selection on a single line - therefore:
                -- Duplicate selection with padding
                -- Insert after last character
                -- Maintain original selection
            local sel = Current.Cursor:GetSelection()   -- BYTES
            sel = util.String(sel)                      -- String
            sel = sel.." "                              -- Add right padding as likely a word
            local nextcharLOC = buffer.Loc(Current.Cursor.CurSelection[2].X+1, Current.Cursor.CurSelection[2].Y)
            -- If I don't increment X the inserted text expands the current selection!
            Current.Buf:Insert(nextcharLOC, sel)
            return true
        else
            -- Multiline selection - therefore:
                -- Get all part selected lines
                -- Insert after last line
                -- Maintain original selection
            local block  =  ""
            local top    = math.min(Current.Cursor.CurSelection[1].Y,Current.Cursor.CurSelection[2].Y)
            local bottom = math.max(Current.Cursor.CurSelection[1].Y,Current.Cursor.CurSelection[2].Y)
            for i=top, bottom,1 do
                block = block..Current.Buf:Line(i).."\n"
            end
            local nextlineLOC = buffer.Loc(0,bottom+1)
            Current.Buf:Insert(nextlineLOC, block)
            return true
        end
    else
            -- Nil selection  - therefore:
                -- Duplicate current line
                -- Insert after current line
                -- Maintain cursor position
            local sel = Current.Buf:Line(Current.Cursor.Y).."\n"
            local nextlineLOC = buffer.Loc(0,Current.Cursor.Y+1)
            Current.Buf:Insert(nextlineLOC, sel)
            return true
    end
end

Kind Regards Gavin Holt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants