Skip to content

A plugin / OOP library for you to make your moves on treesitter and manipulate all the texts no matter the backend matcher

License

Notifications You must be signed in to change notification settings

litoj/manipulator.nvim

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

manipulator.nvim

Features

  • text ops: jump, select, move/paste, …, quickfix, mark, highlight
  • treesitter full traversal including injected languages
    • filtering by node type and lang
  • position sources: cursor, selection, mouse click/move (via vim.g.mousemoveevent)
  • vim motion support (5j…) (via CallPath:next_with_count)
  • Batch: collecting and selecting (native or fzf) found matches
  • extensive behaviour configuration with clever preset inheriting
  • CallPath: flexible & reusable keymapping function builder
  • extensive luadocs right in the code

TODOS

  • update docs & license, separate repo from the original but include references
    • add example under each feature
    • make features into ### headings, do
  • TODO: add info/gtdefinition for types in info windows - belongs to reform.nvim
  • TODO: matching by query syntax and catching into groups
  • TODO: add selection modification/filter (include trailing comma etc.)
    • possible, but currently no predefined list of filters, could use presets to add them
  • TODO: add wrapper for dot repeat
  • TODO: <num>gc<X> mappings (snf = select next function, gnn = jump next node)...
    • either callpath extension or type presets (f/a/s/c/l…) for the letters
  • TODO: region operators (text concat -> new region update etc.)
    • one method with str,R,str | R,str | str,R generating a correctly updated region

Syntax Tree Surfer is a plugin for Neovim that helps you surf through your document and move elements around using the nvim-treesitter API.

tree surfing cover

Table of Contents

  1. Version 1.0 Functionalities
  2. How do I install?
  3. Version 1.1 Update
  4. Version 2.0 Beta Update
  5. Version 2.2 Update

Version 1.0 Functionalities

Navigate around your document based on Treesitter's abstract Syntax Tree. Step into, step out, step over, step back.

syntax-tree-surfer.movement.showcase.mp4

Move / Swap elements around based on your visual selection

syntax-tree-surfer.swap.showcase.2.mp4

Swap in Normal Mode - Now supports Dot (.) Repeat

dot.repeat.mp4

How do I install?

Use your favorite Plugin Manager with the link ziontee113/syntax-tree-surfer

For Packer:

use "ziontee113/syntax-tree-surfer"

How do I set things up?

Here's my suggestion:

-- Syntax Tree Surfer
local opts = {noremap = true, silent = true}

-- Normal Mode Swapping:
-- Swap The Master Node relative to the cursor with it's siblings, Dot Repeatable
vim.keymap.set("n", "vU", function()
	vim.opt.opfunc = "v:lua.require'syntax-tree-surfer'.STSSwapUpNormal_Dot"
	return "g@l"
end, { silent = true, expr = true })
vim.keymap.set("n", "vD", function()
	vim.opt.opfunc = "v:lua.require'syntax-tree-surfer'.STSSwapDownNormal_Dot"
	return "g@l"
end, { silent = true, expr = true })

-- Swap Current Node at the Cursor with it's siblings, Dot Repeatable
vim.keymap.set("n", "vd", function()
	vim.opt.opfunc = "v:lua.require'syntax-tree-surfer'.STSSwapCurrentNodeNextNormal_Dot"
	return "g@l"
end, { silent = true, expr = true })
vim.keymap.set("n", "vu", function()
	vim.opt.opfunc = "v:lua.require'syntax-tree-surfer'.STSSwapCurrentNodePrevNormal_Dot"
	return "g@l"
end, { silent = true, expr = true })

--> If the mappings above don't work, use these instead (no dot repeatable)
-- vim.keymap.set("n", "vd", '<cmd>STSSwapCurrentNodeNextNormal<cr>', opts)
-- vim.keymap.set("n", "vu", '<cmd>STSSwapCurrentNodePrevNormal<cr>', opts)
-- vim.keymap.set("n", "vD", '<cmd>STSSwapDownNormal<cr>', opts)
-- vim.keymap.set("n", "vU", '<cmd>STSSwapUpNormal<cr>', opts)

-- Visual Selection from Normal Mode
vim.keymap.set("n", "vx", '<cmd>STSSelectMasterNode<cr>', opts)
vim.keymap.set("n", "vn", '<cmd>STSSelectCurrentNode<cr>', opts)

-- Select Nodes in Visual Mode
vim.keymap.set("x", "J", '<cmd>STSSelectNextSiblingNode<cr>', opts)
vim.keymap.set("x", "K", '<cmd>STSSelectPrevSiblingNode<cr>', opts)
vim.keymap.set("x", "H", '<cmd>STSSelectParentNode<cr>', opts)
vim.keymap.set("x", "L", '<cmd>STSSelectChildNode<cr>', opts)

-- Swapping Nodes in Visual Mode
vim.keymap.set("x", "<A-j>", '<cmd>STSSwapNextVisual<cr>', opts)
vim.keymap.set("x", "<A-k>", '<cmd>STSSwapPrevVisual<cr>', opts)

Now let's start Tree Surfing! πŸŒ²πŸ’¦

Version 1.1 update

This feature will help you save some keystrokes & brain power when you want to create some code at the top level node of your current cursor position.

lua require("syntax-tree-surfer").go_to_top_node_and_execute_commands(false, { "normal! O", "normal! O", "startinsert" })<cr>

The .go_to_top_node_and_execute_commands() method takes 2 arguments:

  1. boolean: if false then it will jump to the beginning of the node, if true it jumps to the end.

  2. lua table: a table that contains strings, each string is a vim command example: { "normal! O", "normal! O", "startinsert" }


Version 2.0 Beta Update

Targeted Jump with Virtual Text

STS.2.0.Beta.targeted.jump.-.converted.mp4

Filtered Jump through user-defined node types

STS.2.0.Beta.filtered.jump.-.converted.mp4

These are experimental features and I wish to expand them even further. If you have any suggestions, please feel free to let me know 😊

Example mappings for Version 2.0 Beta functionalities:

-- Syntax Tree Surfer V2 Mappings
-- Targeted Jump with virtual_text
local sts = require("syntax-tree-surfer")
vim.keymap.set("n", "gv", function() -- only jump to variable_declarations
	sts.targeted_jump({ "variable_declaration" })
end, opts)
vim.keymap.set("n", "gfu", function() -- only jump to functions
	sts.targeted_jump({ "function", "arrrow_function", "function_definition" })
  --> In this example, the Lua language schema uses "function",
  --  when the Python language uses "function_definition"
  --  we include both, so this keymap will work on both languages
end, opts)
vim.keymap.set("n", "gif", function() -- only jump to if_statements
	sts.targeted_jump({ "if_statement" })
end, opts)
vim.keymap.set("n", "gfo", function() -- only jump to for_statements
	sts.targeted_jump({ "for_statement" })
end, opts)
vim.keymap.set("n", "gj", function() -- jump to all that you specify
	sts.targeted_jump({
		"function",
	  "if_statement",
		"else_clause",
		"else_statement",
		"elseif_statement",
		"for_statement",
		"while_statement",
		"switch_statement",
	})
end, opts)

-------------------------------
-- filtered_jump --
-- "default" means that you jump to the default_desired_types or your lastest jump types
vim.keymap.set("n", "<A-n>", function()
	sts.filtered_jump("default", true) --> true means jump forward
end, opts)
vim.keymap.set("n", "<A-p>", function()
	sts.filtered_jump("default", false) --> false means jump backwards
end, opts)

-- non-default jump --> custom desired_types
vim.keymap.set("n", "your_keymap", function()
	sts.filtered_jump({
		"if_statement",
		"else_clause",
		"else_statement",
	}, true) --> true means jump forward
end, opts)
vim.keymap.set("n", "your_keymap", function()
	sts.filtered_jump({
		"if_statement",
		"else_clause",
		"else_statement",
	}, false) --> false means jump backwards
end, opts)

-------------------------------
-- jump with limited targets --
-- jump to sibling nodes only
vim.keymap.set("n", "-", function()
	sts.filtered_jump({
		"if_statement",
		"else_clause",
		"else_statement",
	}, false, { destination = "siblings" })
end, opts)
vim.keymap.set("n", "=", function()
	sts.filtered_jump({ "if_statement", "else_clause", "else_statement" }, true, { destination = "siblings" })
end, opts)

-- jump to parent or child nodes only
vim.keymap.set("n", "_", function()
	sts.filtered_jump({
		"if_statement",
		"else_clause",
		"else_statement",
	}, false, { destination = "parent" })
end, opts)
vim.keymap.set("n", "+", function()
	sts.filtered_jump({
		"if_statement",
		"else_clause",
		"else_statement",
	}, true, { destination = "children" })
end, opts)

-- Setup Function example:
-- These are the default options:
require("syntax-tree-surfer").setup({
	highlight_group = "STS_highlight",
	disable_no_instance_found_report = false,
	default_desired_types = {
		"function",
		"arrow_function",
		"function_definition",
		"if_statement",
		"else_clause",
		"else_statement",
		"elseif_statement",
		"for_statement",
		"while_statement",
		"switch_statement",
	},
	left_hand_side = "fdsawervcxqtzb",
	right_hand_side = "jkl;oiu.,mpy/n",
	icon_dictionary = {
		["if_statement"] = "",
		["else_clause"] = "οŠ‚",
		["else_statement"] = "οŠ‚",
		["elseif_statement"] = "",
		["for_statement"] = "ﭜ",
		["while_statement"] = "ο―©",
		["switch_statement"] = "ﳟ",
		["function"] = "οž”",
		["function_definition"] = "οž”",
		["variable_declaration"] = "",
	},
})

Because every languages have different schemas and node-types, you can check the node-types that you're interested in with https://github.com/nvim-treesitter/playground

You can also do a quick check using the command :STSPrintNodesAtCursor

Version 2.2 Update

Hold and swap nodes

2.2.lazy.demo.mp4

This feature allows marking a node and then swapping it with another node.

Example mapping:

-- Holds a node, or swaps the held node
vim.keymap.set("n", "gnh", "<cmd>STSSwapOrHold<cr>", opts)
-- Same for visual
vim.keymap.set("x", "gnh", "<cmd>STSSwapOrHoldVisual<cr>", opts)

The lower-level functionality can be accessed via:

require("syntax-tree-surfer").hold_or_swap(true) -- param is_visual boolean
require("syntax-tree-surfer").clear_held_node()

note that STSSwapOrHoldVisual will clear the visual selection, but hold_or_swap(true) will not.

Special Thanks To:

@lmburns for #9

@spiderforrest for #14

About

A plugin / OOP library for you to make your moves on treesitter and manipulate all the texts no matter the backend matcher

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Languages

  • Lua 100.0%