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

mini.move: less intrusive blockwise movement #838

Closed
2 tasks done
matu3ba opened this issue Apr 24, 2024 · 2 comments
Closed
2 tasks done

mini.move: less intrusive blockwise movement #838

matu3ba opened this issue Apr 24, 2024 · 2 comments
Labels

Comments

@matu3ba
Copy link

matu3ba commented Apr 24, 2024

Contributing guidelines

Module(s)

mini.move

Description

Use case
Drawing block selections up and down breaks right-wards indentation, which can make virtual edits for adjusting graphics unnecessary painful. I hope below drawings show the problem. Any kind of ideas would be appreciated.

Problem Description

got

                           ┌──────────────┐
                           │              │
                           │              │
          ┌────────────┐   │              │
          │    upsii   │   │              │
          │     123    │   │              │
          │            │   └──────────────┘
          │            │
          │            │
          └────────────┘

want without redundant steps
1 box selection copy
2 selection replace whitespace
3 selection wanted position and paste

                           ┌──────────────┐
                           │              │
                           │              │
          ┌────────────┐   │              │
          │            │   │              │
          │            │   │              │
          │    upsii   │   └──────────────┘
          │     123    │
          │            │
          └────────────┘

current behavior:
1 line selection movements dont work for multiple lines
2 minimove.move_selection up/down breaks right-hand intendation

                           ┌──────────────┐
                           │              │
                           │              │
          ┌────────────┐   │              │
          │       │   │              │
          │       │   │              │
          │    upsii        │   └──────────────┘
          │     123         │
          │            │
          └────────────┘

It would be nice, if move_selection would have another mode
so that a move is an actual swap of text elements freely
into the buffer positions, so moving 1 upwards does

                           ┌──────────────┐
                           │              │
                           │              │
          ┌────upsii───┐   │              │
          │     123    │   │              │
          │    ─────   │   │              │
          │            │   └──────────────┘
          │            │
          │            │
          └────────────┘

and moving 1 more upwards does

                           ┌──────────────┐
                           │              │
               upsii       │              │
          ┌──── 123 ───┐   │              │
          │    ─────   │   │              │
          │            │   │              │
          │            │   └──────────────┘
          │            │
          │            │
          └────────────┘

until

               upsii       ┌──────────────┐
                123        │              │
                           │              │
          ┌────────────┐   │              │
          │            │   │              │
          │            │   │              │
          │            │   └──────────────┘
          │            │
          │            │
          └────────────┘

with the internal position of the collision range be kept.
To me it looks like you have implemented collision detection,
but nothing to keep track of correct fixups after nothig collides.
For example, left and right movements have the same behavior:

                           ┌──────────────┐
                           │              │
                           │              │
          ┌────────────┐   │              │
  upsii        │       │   │              │
   123         │       │   │              │
          │            │   └──────────────┘
          │            │
          │            │
          └────────────┘
local selmove_hint = [[
 Arrow^^^^^^
 ^ ^ _k_ ^ ^
 _h_ ^ ^ _l_
 ^ ^ _j_ ^ ^                      _<C-c>_
]]

local ok_minimove, minimove = pcall(require, 'mini.move')
assert(ok_minimove)
if ok_minimove == true then
  local opts = {
    mappings = {
      left = '',
      right = '',
      down = '',
      up = '',
      line_left = '',
      line_right = '',
      line_down = '',
      line_up = '',
    },
  }
  minimove.setup(opts)
  -- setup here prevents needless global vars for opts required by `move_selection()/moveline()`
  M.minimove_box_hydra = Hydra {
    name = 'Move Box Selection',
    hint = selmove_hint,
    config = {
      color = 'pink',
      invoke_on_body = true,
    },
    mode = 'v',
    body = '<leader>vb',
    heads = {
      {
        'h',
        function() minimove.move_selection('left', opts) end,
      },
      {
        'j',
        function() minimove.move_selection('down', opts) end,
      },
      {
        'k',
        function() minimove.move_selection('up', opts) end,
      },
      {
        'l',
        function() minimove.move_selection('right', opts) end,
      },
      { '<C-c>', nil, { exit = true } },
    },
  }
  M.minimove_line_hydra = Hydra {
    name = 'Move Line Selection',
    hint = selmove_hint,
    config = {
      color = 'pink',
      invoke_on_body = true,
    },
    mode = 'n',
    body = '<leader>vl',
    heads = {
      {
        'h',
        function() minimove.move_line('left', opts) end,
      },
      {
        'j',
        function() minimove.move_line('down', opts) end,
      },
      {
        'k',
        function() minimove.move_line('up', opts) end,
      },
      {
        'l',
        function() minimove.move_line('right', opts) end,
      },
      { '<C-c>', nil, { exit = true } },
    },
  }
end
@matu3ba matu3ba added the feature-request Feature request label Apr 24, 2024
@echasnovski echasnovski changed the title mini.move: Movements and block movements as virtual edits with collision detection and fixups mini.move: less intrusive blockwise movement Apr 24, 2024
@echasnovski
Copy link
Owner

Thanks for the suggestion!

I can reproduce. At first glance, it is indeed not a very good behavior. However, blockwise movement is kind of lesser tier of support for 'mini.move' because of how complicated it is to implement.

I'll look more into why this happens.

@echasnovski
Copy link
Owner

I've taken another look at this and it seems to work as expected.

It would be nice, if move_selection would have another mode
so that a move is an actual swap of text elements freely
into the buffer positions, so moving 1 upwards does

This is the core of the issue: 'mini.move' actually moves selection/line, not swaps. It may look like swapping when moving to the nearest neighboor (i.e. with v:count 1), but it actually is not. Here moving is more or less equivalent to "cut current selection -> move to target position -> paste selection". And provided diagrams show exactly this behavior.

My best suggestion is to use exchange gx operator from 'mini.operators'. So in this case it would be something like:

  • Select blockwise the area.
  • Press gx. The selection gets highlighted with MiniOperatorsExchangeFrom highlight group (by default linked to IncSearch).
  • Navigate to the target position.
  • Reselect same region with 1v. Important: make sure that new region and original "from" region do not intersect, as this will not get the result you'd expect.
  • Press gx again.

Closing as not planned.

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

No branches or pull requests

2 participants