#ast-grep #key-bindings #replace #tui #applications #search #folder

app serpl

A simple terminal UI for search and replace, ala VS Code

25 releases

0.3.3 Aug 2, 2024
0.3.2 Aug 2, 2024
0.3.1 Jul 17, 2024
0.2.1 Jul 13, 2024
0.1.29 Jun 28, 2024

#244 in Text processing

MIT license

190KB
4.5K SLoC

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

  1. Features
  2. Installation
  3. Usage
  4. Panes
  5. Quick Hints
  6. Neovim Integration using toggleterm
  7. License
  8. Contributing
  9. Acknowledgements
  10. 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

  1. 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
    
  1. 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

JSON
{
  "keybindings": {
    "<Ctrl-d>": "Quit",
    "<Ctrl-c>": "Quit",
    "<Tab>": "LoopOverTabs",
    "<Backtab>": "BackLoopOverTabs",
    "<Ctrl-o>": "ProcessReplace"
  }
}
JSON5
{
  keybindings: {
    "<Ctrl-d>": "Quit",
    "<Ctrl-c>": "Quit",
    "<Tab>": "LoopOverTabs",
    "<Backtab>": "BackLoopOverTabs",
    "<Ctrl-o>": "ProcessReplace",
  },
}
YAML
keybindings:
  "<Ctrl-d>": "Quit"
  "<Ctrl-c>": "Quit"
  "<Tab>": "LoopOverTabs"
  "<Backtab>": "BackLoopOverTabs"
  "<Ctrl-o>": "ProcessReplace"
TOML
[keybindings]
"<Ctrl-d>" = "Quit"
"<Ctrl-c>" = "Quit"
"<Tab>" = "LoopOverTabs"
"<Backtab>" = "BackLoopOverTabs"
"<Ctrl-o>" = "ProcessReplace"
INI
[keybindings]
<Ctrl-d> = Quit
<Ctrl-c> = Quit
<Tab> = LoopOverTabs
<Backtab> = BackLoopOverTabs
<Ctrl-o> = ProcessReplace

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, and k 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.

Dependencies

~23–37MB
~578K SLoC