diff --git a/README.md b/README.md index 9933032..4e788a3 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,82 @@ # Binary-Swap: swap operands in binary expressions -Small plugin for neovim 0.8+ powered on [treesitter](https://github.com/nvim-treesitter/nvim-treesitter) for swapping operands and operators in binary expressions: `123 > 0` to `0 < 123` and `1 + 2` to `2 + 1` +Small plugin for neovim 0.11+ powered by [treesitter](https://github.com/nvim-treesitter/nvim-treesitter) +for swapping operands and operators in binary expressions: +`123 > 0` to `0 < 123` and `1 + 2` to `2 + 1` + - Comparison operations; - Mathematical operations; -https://user-images.githubusercontent.com/46977173/201508787-1b9604a1-1d0a-4feb-86d2-8b5417f4f679.mov +> [!NOTE] +> This is a maintained fork of +> [binary-swap](https://github.com/Wansmer/binary-swap.nvim) following its +> archivation. So far almost all code logic is the same with only minor refactoring. + + + +## Installation + +With [lazy.nvim](https://github.com/folke/lazy.nvim): + +```lua +{ + "Maneren/binary-swap.nvim", + dependencies = { + { "nvim-treesitter/nvim-treesitter" }, + }, + opts = {}, + keys = { + { + "KEY", + function () + require("binary-swap").swap_operands() + end + }, + { + "KEY", + function () + require("binary-swap").swap_operands_with_operator() + end + }, + } +} +``` -# Installation +With [packer.nvim](https://github.com/wbthomason/packer.nvim): -With [packer.nvim](): +> [!WARNING] +> Not officially supported anymore ```lua use({ - 'Wansmer/binary-swap.nvim', + "Maneren/binary-swap.nvim", setup = function () - vim.keymap.set('n', 'YOUR PREFER KEYS', function () - require('binary-swap').swap_operands() + vim.keymap.set("n", "KEY", function () + require("binary-swap").swap_operands() end) - vim.keymap.set('n', 'YOUR PREFER KEYS', function () - require('binary-swap').swap_operands_with_operator() + vim.keymap.set("n", "KEY", function () + require("binary-swap").swap_operands_with_operator() end) end }) ``` -Binary-wap doesn't set up keymaps by default and no required additional settings. +## Usage There are two methods available outside: 1. `require('binary-swap').swap_operands()` – swap only operands -(e.g., `MAX_VALUE >= getCurrentValue()` will transform to `getCurrentValue() >= MAX_VALUE`, here **operator** `>=` is not changed); + (e.g., `MAX_VALUE >= getCurrentValue()` will transform to + `getCurrentValue() >= MAX_VALUE`; **operator** `>=` is not changed); -2. `require('binary-swap').swap_operands_with_operator()` – swap operands and operator to opposite if possible. (e.g., `MAX_VALUE >= getCurrentValue()` transforms to `getCurrentValue() <= MAX_VALUE`) +2. `require('binary-swap').swap_operands_with_operator()` – swap operands and + operator to opposite if possible. (e.g., `MAX_VALUE >= getCurrentValue()` + transforms to `getCurrentValue() <= MAX_VALUE`) -## Note +## Languages -I considered a few different languages and at each of them node with binary expression node has type `binary_expression`. Plugin searches this type by default because have no reason to add additional options to set up. If in your favorite language, this type is different, feel free to open an issue. +Most languages have binary expression node with the type `binary_expression`, so +they should work out of the box. However, some languages may use different +node types, for example Python uses few `*_operator` node types. Those have to +be listed manually in the plugins, so they are being added when encountered. +Feel free to open an issue if your favorite language doesn't work. diff --git a/lua/binary-swap/swap.lua b/lua/binary-swap/swap.lua index fd5395b..02e1d9b 100644 --- a/lua/binary-swap/swap.lua +++ b/lua/binary-swap/swap.lua @@ -1,101 +1,108 @@ -local query = require('vim.treesitter.query') -local ts = require('vim.treesitter') --- `ts.get_node_text` for NVIM v0.9.0-dev-1275+gcbbf8bd66-dirty and newer --- see: https://github.com/neovim/neovim/pull/22761 -local get_node_text = ts.get_node_text or query.get_node_text -local ts_ok, ts_utils = pcall(require, 'nvim-treesitter.ts_utils') - -if not ts_ok then - return -end - local M = {} -local OPPOSITES = vim.tbl_add_reverse_lookup({ +local OPPOSITES = { ['>='] = '<=', + ['<='] = '>=', ['>'] = '<', + ['<'] = '>', ['<<'] = '>>', -}) + ['>>'] = '<<', +} -local BINARY = 'binary_expression' -local OPERATOR_INDEX = 2 +local BINARY = { + 'binary_expression', + 'binary_operator', + 'boolean_operator', + 'comparison_operator', +} ---Return TSNode with type 'binary_expression' or nil ----@param node userdata ----@return userdata|nil +---@param node TSNode +---@return TSNode|nil local function get_binary_node(node) - if not node then - return + if vim.tbl_contains(BINARY, node:type()) then + return node end - if node:type() ~= BINARY then - node = node:parent() - return get_binary_node(node) + local parent = node:parent() + + if not parent then + return end - return node + return get_binary_node(parent) end ---Returned list-like table with children of node ----This function is pretty much copied from 'nvim-treesitter' ----(TSRange:collect_children) ----@param node userdata TSNode instance ----@param filter? function Function for filtering output list ----@return table -local function collect_children(node, filter) +---@param node TSNode TSNode instance +---@return TSNode[] +local function collect_children(node) local children = {} for child in node:iter_children() do - if not filter or filter(child) then - table.insert(children, child) - end + table.insert(children, child) end return children end ---Returned swapped operands and opposite operator if it needs ----@param operands userdata[] +---@param operands TSNode[] ---@param swap_operator? boolean Swap operator to opposite or not ---@return table[] local function swap_operands(operands, swap_operator) local replacement = {} - for idx = #operands, 1, -1 do - local text = get_node_text(operands[idx], 0) - if type(text) == 'string' then - text = vim.split(text, '\n') - end + for idx = #operands, 1, -1 do + local text = vim.treesitter.get_node_text(operands[idx], 0) local reversed_idx = #operands - idx + 1 - if swap_operator and idx == OPERATOR_INDEX then - local operator = OPPOSITES[text[1]] - text = operator and { operator } or text + if swap_operator and idx == reversed_idx then + local operator = OPPOSITES[text] + + if operator then + text = operator + end end local range = { operands[reversed_idx]:range() } - table.insert(replacement, { text = text, range = range }) + table.insert(replacement, { text = vim.split(text, '\n'), range = range }) end + return replacement end ---Format and replace binary expression under cursor ---@param swap_operator? boolean Swap operator to opposite or not function M.format_and_replace(swap_operator) - local parser = vim.treesitter.get_parser(0) + local parser = vim.treesitter.get_parser() + + if not parser then + return + end + parser:parse() - local node = ts_utils.get_node_at_cursor(0) + local node = vim.treesitter.get_node() + + if not node then + return + end + local binary_expression = get_binary_node(node) - if binary_expression then - local operands = collect_children(binary_expression) - local replacement = swap_operands(operands, swap_operator) - for i = #replacement, 1, -1 do - local sr, sc, er, ec = unpack(replacement[i].range) - vim.api.nvim_buf_set_text(0, sr, sc, er, ec, replacement[i].text) - end + if not binary_expression then + return + end + + local operands = collect_children(binary_expression) + + local replacement = swap_operands(operands, swap_operator) + + for i = #replacement, 1, -1 do + local sr, sc, er, ec = unpack(replacement[i].range) + vim.api.nvim_buf_set_text(0, sr, sc, er, ec, replacement[i].text) end end