Awesome
Serpl
serpl
is a terminal user interface (TUI) application that allows users to search and replace keywords in an entire folder, similar to the functionality available in VS Code.
https://github.com/yassinebridi/serpl/assets/18403595/348506704-73336074-bfaf-4a9a-849c-bd4aa4e24afc
Table of Contents
- Features
- Installation
- Usage
- Panes
- Quick Hints
- Neovim Integration using toggleterm
- License
- Contributing
- Acknowledgements
- Similar Projects
Features
- Search for keywords across an entire project folder, with options for case sensitivity, AST Grep and more.
- Replace keywords with options for preserving case, AST Grep and more.
- Interactive preview of search results.
- Keyboard navigation for efficient workflow.
- Configurable key bindings and search modes.
Installation and Update
Prerequisites
- ripgrep installed on your system.
- (Optional) ast-grep installed on your system, if you want to use the AST Grep functionality.
Steps
- Install the application using Cargo:
cargo install serpl
- If you want to install the application with the AST Grep functionality, you can use the following command:
cargo install serpl --features ast_grep
- Run the application:
serpl
Binaries
Check the releases page for the latest binaries.
OS Specific Installation
Brew
serpl
can be installed using Homebrew:
brew install serpl
Arch Linux
serpl
can be installed from the official repositories using pacman
:
pacman -S serpl
Nix/NixOS
serpl
is included in nixpkgs since 24.11, and can be installed via Nix in different ways:
On standalone Nix setups:
nix profile install nixpkgs#serpl
On NixOS (via configuration.nix
or similar):
{pkgs, ...}: {
environment.systemPackages = [pkgs.serpl];
}
On Home-Manager:
{pkgs, ...}: {
home.packages = [pkgs.serpl];
}
Usage
Basic Commands
- Start the application in the current directory:
serpl
- Start the application and provide the project root path:
serpl --project-root /path/to/project
Key Bindings
Default key bindings can be customized through the config.json
file.
Default Key Bindings
Key Combination | Action |
---|---|
Ctrl + c | Quit |
Ctrl + b | Help |
Tab | Switch between tabs |
Backtab | Switch to previous tabs |
Ctrl + o | Process replace for all files |
r | Process replace for selected file or line |
Ctrl + n | Toggle search and replace modes |
Enter | Execute search (for large folders) |
g / Left / h | Go to top of the list |
G / Right / l | Go to bottom of the list |
j / Down | Move to the next item |
k / Up | Move to the previous item |
/ | Search results list |
d | Delete selected file or line |
Esc | Exit the current pane or dialog |
Enter (in dialogs) / y | Confirm action |
Esc (in dialogs) / n | Cancel action |
h , l , Tab (in dialogs) | Navigate dialog options |
Configuration
serpl
uses a configuration file to manage key bindings and other settings. By default, the path to the configuration file can be found by running serpl --version
. You can use various file formats for the configuration, such as JSON, JSON5, YAML, TOML, or INI.
Example Configurations
<details> <summary>JSON</summary>{
"keybindings": {
"<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit",
"<Tab>": "LoopOverTabs",
"<Backtab>": "BackLoopOverTabs",
"<Ctrl-o>": "ProcessReplace",
"<Ctrl-b>": "ShowHelp"
}
}
</details>
<details>
<summary>JSON5</summary>
{
keybindings: {
"<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit",
"<Tab>": "LoopOverTabs",
"<Backtab>": "BackLoopOverTabs",
"<Ctrl-o>": "ProcessReplace",
"<Ctrl-b>": "ShowHelp",
},
}
</details>
<details>
<summary>YAML</summary>
keybindings:
"<Ctrl-d>": "Quit"
"<Ctrl-c>": "Quit"
"<Tab>": "LoopOverTabs"
"<Backtab>": "BackLoopOverTabs"
"<Ctrl-o>": "ProcessReplace"
"<Ctrl-b>": "ShowHelp"
</details>
<details>
<summary>TOML</summary>
[keybindings]
"<Ctrl-d>" = "Quit"
"<Ctrl-c>" = "Quit"
"<Tab>" = "LoopOverTabs"
"<Backtab>" = "BackLoopOverTabs"
"<Ctrl-o>" = "ProcessReplace"
"<Ctrl-b>" = "ShowHelp"
</details>
<details>
<summary>INI</summary>
[keybindings]
<Ctrl-d> = Quit
<Ctrl-c> = Quit
<Tab> = LoopOverTabs
<Backtab> = BackLoopOverTabs
<Ctrl-o> = ProcessReplace
<Ctrl-b> = ShowHelp
</details>
You can customize the key bindings by modifying the configuration file in the format of your choice.
Panes
Search Input
- Input field for entering search keywords.
- Toggle search modes (Simple, Match Case, Match Whole Word, Match Case Whole Word, Regex, AST Grep).
- Simple: Search all occurrences of the keyword.
- Match Case: Search occurrences with the same case as the keyword.
- Match Whole Word: Search occurrences that match the keyword exactly.
- Match Case Whole Word: Search occurrences that match the keyword exactly with the same case.
- Regex: Search occurrences using a regular expression.
- AST Grep: Search occurrences using AST Grep.
[!TIP] If current directory is considerebly large, you have to click
Enter
to start the search.
Replace Input
- Input field for entering replacement text.
- Toggle replace modes (Simple, Preserve Case, AST Grep).
- Simple: Replace all occurrences of the keyword.
- Preserve Case: Replace occurrences while preserving the case of the keyword.
- AST Grep: Replace occurrences using AST Grep.
Search Results Pane
- List of files with search results.
- Navigation to select and view files.
- Option to delete files from the search results.
- Search results count and current file count.
- Ability to search the list using the
/
key.
Preview Pane
- Display of the selected file with highlighted search results, and context.
- Navigation to view different matches within the file.
- Option to delete individual lines containing matches.
Quick Hints
- Use the
Ctrl + b
key combination to display the help dialog. - Use the
Ctrl + o
key combination to process the replace for all files. - Use the
r
key to process the replace for the selected file or line. - Use the
Ctrl + n
key combination to toggle between search and replace modes. - Use the
g
,G
,j
, andk
keys to navigate through the search results. - Use the
d
key to delete the selected file or line.
Neovim Integration using toggleterm
Check out the toggleterm.nvim plugin for Neovim, which provides a terminal that can be toggled with a key binding. Or you can use the following configuration, if you are using AstroNvim:
return {
"akinsho/toggleterm.nvim",
cmd = { "ToggleTerm", "TermExec" },
dependencies = {
{
"AstroNvim/astrocore",
opts = function(_, opts)
local maps = opts.mappings
local astro = require "astrocore"
maps.n["<Leader>t"] = vim.tbl_get(opts, "_map_sections", "t")
local serpl = {
callback = function()
astro.toggle_term_cmd "serpl"
end,
desc = "ToggleTerm serpl",
}
maps.n["<Leader>sr"] = { serpl.callback, desc = serpl.desc }
maps.n["<Leader>tf"] = { "<Cmd>ToggleTerm direction=float<CR>", desc = "ToggleTerm float" }
maps.n["<Leader>th"] = { "<Cmd>ToggleTerm size=10 direction=horizontal<CR>", desc = "ToggleTerm horizontal split" }
maps.n["<Leader>tv"] = { "<Cmd>ToggleTerm size=80 direction=vertical<CR>", desc = "ToggleTerm vertical split" }
maps.n["<F7>"] = { '<Cmd>execute v:count . "ToggleTerm"<CR>', desc = "Toggle terminal" }
maps.t["<F7>"] = { "<Cmd>ToggleTerm<CR>", desc = "Toggle terminal" }
maps.i["<F7>"] = { "<Esc><Cmd>ToggleTerm<CR>", desc = "Toggle terminal" }
maps.n["<C-'>"] = { '<Cmd>execute v:count . "ToggleTerm"<CR>', desc = "Toggle terminal" }
maps.t["<C-'>"] = { "<Cmd>ToggleTerm<CR>", desc = "Toggle terminal" }
maps.i["<C-'>"] = { "<Esc><Cmd>ToggleTerm<CR>", desc = "Toggle terminal" }
end,
},
},
opts = {
highlights = {
Normal = { link = "Normal" },
NormalNC = { link = "NormalNC" },
NormalFloat = { link = "NormalFloat" },
FloatBorder = { link = "FloatBorder" },
StatusLine = { link = "StatusLine" },
StatusLineNC = { link = "StatusLineNC" },
WinBar = { link = "WinBar" },
WinBarNC = { link = "WinBarNC" },
},
size = 10,
---@param t Terminal
on_create = function(t)
vim.opt_local.foldcolumn = "0"
vim.opt_local.signcolumn = "no"
if t.hidden then
local toggle = function() t:toggle() end
vim.keymap.set({ "n", "t", "i" }, "<C-'>", toggle, { desc = "Toggle terminal", buffer = t.bufnr })
vim.keymap.set({ "n", "t", "i" }, "<F7>", toggle, { desc = "Toggle terminal", buffer = t.bufnr })
end
end,
shading_factor = 2,
direction = "float",
float_opts = { border = "rounded" },
},
}
License
This project is licensed under the MIT License. See the LICENSE file for details.
Contributing
(WIP)
Acknowledgements
- This project was inspired by the VS Code search and replace functionality.
- This project is built using the awesome ratatui.rs library, and build on top of their Component Template.
- Thanks to the ripgrep project for providing the search functionality.
- Thanks to the ast-grep project for providing the AST Grep functionality.
Similar Projects
- repgrep: An interactive replacer for ripgrep that makes it easy to find and replace across files on the command line.