A structural code editor for Neovim. View, reorder, rename, duplicate, delete, and annotate code declarations from a floating window. Powered by Treesitter and LSP.
max_depth)dd + p) — saves back to the source file on :w (children are protected from being moved outside their parent)yy + p) — auto-generates suffixed names (_1, _2)// comment above any entry to add it to the source file in the language's native syntax (# for Python, -- for Lua, // for JS/TS/Go/Rust/C/C++)<CR>) — jump to the declaration in the source, cursor lands on the symbol namegd) — center and flash the declaration in the source window without leaving the floatK) — show LSP hover info (type signature, docs) for the code pointfunction greet/1)transparent.nvim and winblend| Language | Filetypes | Highlights |
|---|---|---|
| TypeScript | typescript, typescriptreact |
const/let/var, export, declare, abstract class, interface, type, enum, namespace, decorators (skipped), all class/interface/enum members |
| JavaScript | javascript, javascriptreact |
const/let/var, export, generator functions, class with methods/fields/static blocks |
| Python | python |
def, async def, class with methods, decorators, type alias (3.12), augmented assignment, if/while/for/try/with |
| Lua | lua |
function, local function, local variables, assignments, if/while/for/do/repeat |
| Go | go |
func, method (Receiver.Method), type (struct/interface with fields/methods), var, const, embedded types, go/defer/select |
| Rust | rust |
fn, struct (with fields), enum (with variants), impl, trait, union, mod (nestable), macro_rules!, macro invocations, extern, pub detection |
| C | c |
function, declaration, typedef, struct/enum/union (nestable with fields/enumerators), #define, forward declaration arity |
| C++ | cpp |
Everything in C plus: class, namespace (nestable), template, using alias, concept (C++20), friend, static_assert, constructors/destructors, operator overloads, scoped enums (enum class), C++20 modules |
:TSInstall <lang>){
"Sang-it/fluoride",
config = function()
require("fluoride").setup()
end,
}
set runtimepath+=~/path/to/fluoride
:Fluoride
Map to a keybinding:
vim.keymap.set("n", "<leader>cp", "<cmd>Fluoride<cr>", { desc = "Fluoride" })
All options are optional. Defaults shown below:
require("fluoride").setup({
window = {
title = "Fluoride", -- string or false to hide
border = "single", -- any nvim_open_win border format
winblend = 15, -- transparency (0-100)
footer = true, -- show keybinding hints at bottom
center_breakpoint = 80, -- switch to centered layout below this width
sidebar = { -- right-side floating window (wide terminals)
width = 0.3, -- proportion of terminal width (0-1)
height = 0.85, -- proportion of terminal height (0-1)
row = 2, -- rows from top edge
col = 2, -- cols from right edge
},
centered = { -- centered float (narrow terminals)
width = 0.6, -- proportion of terminal width (0-1)
height = 0.6, -- proportion of terminal height (0-1)
},
},
keymaps = {
close = "q", -- close the window
close_alt = "<C-c>", -- alternative close (false to disable)
jump = "<CR>", -- jump to code point (focus moves to source)
peek = "gd", -- peek at code point (center + flash)
hover = "K", -- LSP hover on code point
toggle_children = "<Tab>", -- toggle nested members on/off
yank = "gy", -- peek + copy code block to clipboard
},
max_depth = 1, -- nesting depth for children (0=none, 1=direct children, 2+=deeper)
yank_comments = true, -- include attached comments in yank (default: true)
confirm_delete = true, -- prompt before deleting code points (false to skip)
highlight = {
peek_duration = 200, -- ms for gd peek flash
rename_duration = 130, -- ms for rename flash per entry
},
})
The border option accepts any format supported by nvim_open_win:
border = "single" -- preset
border = "rounded" -- preset
border = { "┌", "─", "┐", "│", "┘", "─", "└", "│" } -- custom chars
border = { "", "", "", "", "", "", "", "│" } -- left border only
Inside the Fluoride window:
| Key | Action |
|---|---|
q / <C-c> |
Close the window |
<CR> |
Jump to code point (cursor on symbol name, focus moves to source) |
gd |
Peek — center and flash the code point in source (focus stays in float) |
K |
LSP hover — show type signature and docs |
:w |
Apply all changes (reorder, rename, duplicate, delete, comments, format) |
dd + p |
Reorder a code point |
yy + p |
Duplicate a code point (auto-suffixed name) |
dd |
Delete a code point (confirmed on :w) |
<Tab> |
Cycle nested members depth (0 → 1 → ... → max_depth → 0) |
gy |
Peek + copy code block to clipboard (vim register and system clipboard) |
:Fluoridemax_depth):const MAX_CONNECTIONS
interface ServerConfig
• property host
• property port
• property debug
function createLogger/1
namespace Services -- with max_depth = 2
• class ConnectionPool
• method add/1 -- depth 2 children visible
• method remove/1
• method getActive/0
export function startServer/1
export const DEFAULT_CONFIG
dd + pyy + p to copy a code point (gets _1 suffix)dd to remove a code point// my comment above any entry:w applies all changes, formats via LSP, and refreshes the list<CR> to jump, gd to peek, K to hoverCreate lua/fluoride/langs/<filetype>.lua:
local M = {}
M.filetypes = { "mylang" }
M.parsers = { mylang = "mylang" }
M.comment_types = { comment = true }
M.comment_prefix = "//"
M.highlights = {
["function"] = { prefix = "Keyword", name = "Function" },
["class"] = { prefix = "Type", name = "Type" },
}
function M.is_declaration(node)
return node:type() ~= "comment"
end
function M.get_name(node, bufnr) -- extract symbol name
end
function M.get_display_type(node, bufnr) -- return display prefix
end
function M.get_arity(node, bufnr) -- return param count or nil
end
-- Optional: for nested declarations
function M.is_nestable(node) -- true if node contains children
end
function M.get_body_node(node) -- return the body node to iterate
end
function M.is_child_declaration(node) -- true if child should be shown
end
return M
The module is auto-discovered on first use — no registration needed.
MIT