Coder Social home page Coder Social logo

wansmer / sibling-swap.nvim Goto Github PK

View Code? Open in Web Editor NEW
154.0 3.0 1.0 25 KB

Neovim plugin for swaps closest siblings with Tree-Sitter

License: MIT License

Makefile 1.04% Lua 94.50% HTML 1.04% JavaScript 3.42%
formating neovim neovim-plugin treesitter editing-support

sibling-swap.nvim's Introduction

Sibling-swap.nvim: swaps closest siblings with Tree-Sitter

Neovim plugin to swap places of siblings, e.g., arguments, parameters, attributes, pairs in objects, array's items e.t.c., which located near and separated by allowed_separators or space.

  • Zero-config (almost): No need to setup specific language – should works from scratch with all languages supported by Tree-Sitter;
  • Simple: Just grab this node and move;
  • Sticky-cursor: The cursor follows the text on which it was called;
  • Smart: Able to replace operand in binary expressions and Mathematical operations to opposite1.
  • Dot-repeat: Dot-repeat and v:count for all keymaps
demo_sibling-swap.mov

Requirements

  1. Neovim 0.8+
  2. nvim-treesitter
  3. Read the WARNING

Installation

With packer.nvim:

use({
  'Wansmer/sibling-swap.nvim',
  requires = { 'nvim-treesitter' },
  config = function()
    require('sibling-swap').setup({--[[ your config ]]})
  end,
})

Configuration

Default config

local DEFAULT_SETTINGS = {
  allowed_separators = {
    ',',
    ';',
    'and',
    'or',
    '&&',
    '&',
    '||',
    '|',
    '==',
    '===',
    '!=',
    '!==',
    '-',
    '+',
    ['<'] = '>',
    ['<='] = '>=',
    ['>'] = '<',
    ['>='] = '<=',
  },
  use_default_keymaps = true,
  -- Highlight recently swapped node. Can be boolean or table
  -- If table: { ms = 500, hl_opts = { link = 'IncSearch' } }
  -- `hl_opts` is a `val` from `nvim_set_hl()`
  highlight_node_at_cursor = false,
  -- keybinding for movements to right or left (and up or down, if `allow_interline_swaps` is true)
  -- (`<C-,>` and `<C-.>` may not map to control chars at system level, so are sent by certain terminals as just `,` and `.`. In this case, just add the mappings you want.)
  keymaps = {
    ['<C-.>'] = 'swap_with_right',
    ['<C-,>'] = 'swap_with_left',
    ['<space>.'] = 'swap_with_right_with_opp',
    ['<space>,'] = 'swap_with_left_with_opp',
  },
  ignore_injected_langs = false,
  -- allow swaps across lines
  allow_interline_swaps = true,
  -- swaps interline siblings without separators (no recommended, helpful for swaps html-like attributes)
  interline_swaps_without_separator = false,
  -- Fallbacs for tiny settings for langs and nodes. See #fallback
  fallback = {},
}

Options

Separators

allowed_separators: list of separators for detecting suitable siblings. 'Separators' meaning unnamed treesitter node. If you need to change separator to the opposite value (e.g., in binary expressions), set it like key = value. If you want to disable something separator - set it to false.

Example:

require('sibling-swap').setup({
  allowed_separators = {
    -- standart
    '=',
    -- with opposite value
    ['>>'] = '<<',
    ['<<'] = '>>',
    -- disable
    ['&'] = false,
  }
})

Keymaps

use_default_keymaps - use default keymaps or not. keymaps - keymaps by default.

If you want to change it, here is two way to do it:

  1. Change it in options (like above). Be sure what use_default_keymaps is 'true';
  2. Add vim.keymap.set('n', 'YOUR_PREFER_KEYS', require('sibling-swap').swap_with_left) anywhere in your config. Be sure what use_default_keymaps is 'false';

Injected languagas

ignore_injected_langs: 'true' is not recommended. If set to 'true', plugin will not to recognize injected languages, e.g. blocks of code in markdown, js in html or js in vue.

Here is two reason to set it 'true':

If you no work with filetypes allowing injected languages; If you want to be able to swap node with injected language when cursor is placed on injected, e.g.:

<template>
  <app-item @click="clic | kHandler" class="class" />
  |
  <!-- The 'clickHandler' is a javascript and it have not any -->
  <!-- siblings. If 'ignore_injected_langs' is 'false', the plugin will do nothing. -->
  <!-- If 'ignore_injected_langs' is 'true', attribute '@click="clickHandler"' will -->
  <!-- swap. But in section 'script' or 'stile' the plugin will not working. -->
</template>

<script setup>
const one = { tw|o: 'two', one: 'one' }
                |
    // If 'ignore_injected_langs' is 'true', Tree-Sitter recognize
    // all <script> section as injected language and it will be
    // ignored.
</script>

Fallback

You can pass control to a third-party plugin or custom function if the node search process finds matches with the nodes listed here.

---@class FallbackItem
---@field enable boolean|function(node: TSNode): boolean
---@field action function(node: TSNode, side: string): void

require('sibling-swap').setup({
  ---@field fallback table<string, table<string, FallbackItem>>
  fallback = {
    ['javascript'] = {
      string = {
        enable = function(node)
          -- some condition
          return true
        end,
        action = function(node, side)
          -- Do something instead of swapping
        end,
      },
    },
  },
})

Warning

Plugin work with SIBLINGS. Its meaning what any siblings which located near, has ‘allowed’ separator or space between each other and placed in same level in ‘treesitter’ tree - are suitable for swaps. It allows no setup each language by separate. It supposed, what you understand it before using.

Examples:

function test (a) { return a; }
//        |
//  cursor here and you trigger 'swap_with_right', code will transform to
function (a) test { return a; }
// because 'test' and '(a)' on same line, on one level in tree and has space between each other
<p class="swap" is="left">Swap me</p>
<!--   |         -->
<!-- cursor here and you trigger 'swap_with_left', code will transform to -->
<class="swap" p is="left">Swap me</p>
<!-- because 'class="swap"' and 'p' on same line, on one level in tree and has space between each other -->

Footnotes

  1. If you want to swap operand and operators with by one key from anywhere in binary expressions, look at binary-swap.nvim

sibling-swap.nvim's People

Contributors

hungryjoe avatar wansmer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

hungryjoe

sibling-swap.nvim's Issues

Ability to dot `.` repeat a swap

First off, thanks a lot for this really neat plugin! I have been a long time vim-exchange user, and this tree-sitter based approach will definitely be a super convenient addition!

However, I am missing the ability to repeat a swap with a .. For example, instead of hitting <C-,> 3 times in a row, I would hit it once, and then . twice.

In the case of the default mapping, such a dot repeat is probably not very useful, but I prefer to create mappings that do not interfere too much with basic shortcuts such as <C-,> and <C-.> (which, for me, are already taken partly by other vim config, and party by my terminal). The dot repeat would allow me to conveniently keep swapping without the overhead of a longer mapping!

Change default keymaps

<C-,> and <C-.> do not commonly map to control chars at system level, so are sent by many terminals as just , and . so the default keymaps don't work.

Suggest <M-,> and <M-.> ? Or perhaps mention this in the README?

Thanks for the great plugin!

(PS. also typo in the README local DEFAUTL_SETTINGSlocal DEFAULT_SETTINGS)

swap didn't work

nvim version

$ nvim -v
NVIM v0.10.0
Build type: Release
LuaJIT 2.1.1713773202
Run "nvim -V1 -v" for more info

plugin version

image

plugin configuration

i use lazy.nvim plugin manager

	{
		"Wansmer/sibling-swap.nvim",
		dependencies = { "nvim-treesitter" },
		config = function()
			require("sibling-swap").setup({
				allowed_separators = {
					",",
					":",
					";",
					"and",
					"or",
					"&&",
					"&",
					"||",
					"|",
					"==",
					"===",
					"!=",
					"!==",
					"-",
					"+",
					["<"] = ">",
					["<="] = ">=",
					[">"] = "<",
					[">="] = "<=",
				},
				use_default_keymaps = true,
				-- Highlight recently swapped node. Can be boolean or table
				-- If table: { ms = 500, hl_opts = { link = 'IncSearch' } }
				-- `hl_opts` is a `val` from `nvim_set_hl()`
				highlight_node_at_cursor = true,
				-- keybinding for movements to right or left (and up or down, if `allow_interline_swaps` is true)
				-- (`<C-,>` and `<C-.>` may not map to control chars at system level, so are sent by certain terminals as just `,` and `.`. In this case, just add the mappings you want.)
				keymaps = {
					["<C-.>"] = "swap_with_right",
					["<C-,>"] = "swap_with_left",
					["<space>."] = "swap_with_right_with_opp",
					["<space>,"] = "swap_with_left_with_opp",
				},
				ignore_injected_langs = false,
				-- allow swaps across lines
				allow_interline_swaps = true,
				-- swaps interline siblings without separators (no recommended, helpful for swaps html-like attributes)
				interline_swaps_without_separator = false,
				-- Fallbacs for tiny settings for langs and nodes. See #fallback
				fallback = {},
			})
		end,
	},

Swap doesn't work

2024-05-30.11.54.22.mov

keymaps have no `desc`

I use WhichKey and there doesn't seem to be a way to use sibling-swap's keymaps to set the desc field which is normally set when using vim.keymap.set.

This is what I see in WhichKey for those keys (e.g., following <Leader>s):
image

It'd be perfectly fine to set them by default to the values of the functions which are called, e.g., l -> swap_with_right and so on. I just want a hint as to what those keys do in that context.

FR: Option to highlight the node that moved

Thanks for this plugin, it's actually much better than other node-swapping plugins I tried.

I think an option to highlight the node that has just been moved could be useful. Reason being that sometimes, a different node than I expect gets moved, and then I am always a bit confused which one exactly moved.

Swapping doesn't work when JSX props are on separate lines

Hi, thank you for the plugin! It feels really good, however the swapping of JSX props doesn't work when they're on separate lines, even with allow_interline_swaps enabled:

// Page.tsx

export const Component = () => {
  return (
    <div
      className="123"
      style={{
        |w|idth: 100,
//      | it correctly swaps width and height when the cursor is on 'w'
        height: 100,
      }}
      |d|ata-one=""
//    | however when the cursor is on 'd' there, I cannot swap 'data-one' and 'data-two' attributes
      data-two=""
    ></div>
  )
}

Edit: for clarity, it works perfectly when the attributes are on the same line.

My packer and treesitter setup:

```lua use { 'Wansmer/sibling-swap.nvim', requires = { 'nvim-treesitter' }, config = function () require'sibling-swap'.setup {} end } use { 'Wansmer/sibling-swap.nvim', requires = { 'nvim-treesitter' }, config = function () require'sibling-swap'.setup {} end }

-- in a separate file
local status, ts = pcall(require, 'nvim-treesitter.configs')
if (not status) then return end

ts.setup {
highlight = {
enable = true,
disable = {}
},
indent = {
enable = true,
disable = {}
},
ensure_installed = {
"markdown",
"markdown_inline",
"lua",
"tsx",
"json",
"yaml",
"css",
"html",
},
autotag = {
enable = true
},
context_commentstring = {
config = {
javascript = {
__default = '// %s',
jsx_element = '{/%s/}',
jsx_fragment = '{/%s/}',
jsx_attribute = '// %s',
comment = '// %s'
},
typescript = {
__default = '// %s',
__multiline = '/* %s */'
}
}
}
}

local parser_config = require 'nvim-treesitter.parsers'.get_parser_configs()
parser_config.tsx.filetype_to_parsername = { "javascript", "typescript.tsx" }

</p>
</details> 

Feature Request: Limit swap to node context

Example:

{
  "name": "Project A",
  "details": {
    "started": "2021-01-01",
    "completed": "2023-01-01",
    "tasks": ["Task 1", "Task 2", "^Task 3", "Task 4"] <-- cursor on Task 3
  }
}

When the cursor is on Task 3, the swap should be limited to the items in array, and should stop when Task 3 is at the beginning or end of the array.

Instead the swap operation is allowed to continue after Task 3 is at the beginning of the list, and the swap then performs on the next outer node, which then swaps the tasks and completed objects.

{
  "name": "Project A",
  "details": {
    "started": "2021-01-01",
    "tasks": ["^Task 3", "Task 1", "Task 2", "Task 4"],
    "completed": "2023-01-01"
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.