Searching inside text files directly from ranger is a fantastic idea, and there are several ways to accomplish this using tools like ripgrep, fzf, or grep. Below is a guide to integrate content-based searching into ranger.


ripgrep (rg) is fast and well-suited for searching inside text files.

  1. Install Ripgrep:

    • On Arch Linux: sudo pacman -S ripgrep
  2. Add a Custom Command in Ranger:

    • Edit your commands.py file (~/.config/ranger/commands.py) and add:
      from ranger.api.commands import Command
      
      class search_text(Command):
          """
          :search_text <pattern>
          Search for a pattern inside text files using ripgrep.
          """
      
          def execute(self):
              if not self.arg(1):
                  self.fm.notify("Usage: :search_text <pattern>", bad=True)
                  return
      
              import subprocess
              pattern = self.rest(1)
              command = f"rg --line-number --column {pattern}"
              process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
      
              stdout, stderr = process.communicate()
              if process.returncode == 0:
                  results = stdout.decode("utf-8").strip().split("\n")
                  if results:
                      self.fm.notify(f"Found {len(results)} result(s):")
                      for result in results:
                          self.fm.notify(result)
                  else:
                      self.fm.notify("No matches found.", bad=True)
              else:
                  self.fm.notify(stderr.decode("utf-8").strip(), bad=True)
      
  3. Usage:

    • Run :search_text <pattern> in ranger, where <pattern> is the text to search for.

To make results interactive, pipe them to fzf.

Steps:

  1. Modify the search_text Command:

    class search_fzf(Command):
        """
        :search_fzf <pattern>
        Search for a pattern inside files using ripgrep and select a result with fzf.
        """
    
        def execute(self):
            if not self.arg(1):
                self.fm.notify("Usage: :search_fzf <pattern>", bad=True)
                return
    
            import subprocess
            pattern = self.rest(1)
            command = f"rg --line-number --column {pattern} | fzf"
            process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
            stdout, stderr = process.communicate()
            if process.returncode == 0:
                result = stdout.decode("utf-8").strip()
                if result:
                    # Extract the file path and line number
                    file_path, line_number = result.split(":", 1)
                    self.fm.select_file(file_path)
                    self.fm.notify(f"Opening {file_path} at line {line_number}")
                else:
                    self.fm.notify("No matches found.", bad=True)
            else:
                self.fm.notify(stderr.decode("utf-8").strip(), bad=True)
    
  2. Usage:

    • Run :search_fzf <pattern> in ranger.
    • Select a result interactively with fzf, and ranger will open the file.

3. Search with Grep

If you prefer grep, the integration is similar:

  1. Custom Command:

    class grep_search(Command):
        """
        :grep_search <pattern>
        Search for a pattern inside text files using grep.
        """
    
        def execute(self):
            if not self.arg(1):
                self.fm.notify("Usage: :grep_search <pattern>", bad=True)
                return
    
            import subprocess
            pattern = self.rest(1)
            command = f"grep -rnw . -e '{pattern}'"
            process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
            stdout, stderr = process.communicate()
            if process.returncode == 0:
                results = stdout.decode("utf-8").strip().split("\n")
                if results:
                    self.fm.notify(f"Found {len(results)} result(s):")
                    for result in results:
                        self.fm.notify(result)
                else:
                    self.fm.notify("No matches found.", bad=True)
            else:
                self.fm.notify(stderr.decode("utf-8").strip(), bad=True)
    
  2. Usage:

    • Run :grep_search <pattern> to search inside text files using grep.

4. Advanced: Use bat for Highlighted Results

If you use bat as a previewer:

  1. Install bat:

    • On Arch Linux: sudo pacman -S bat
  2. Update the Command:

    class search_preview(Command):
        """
        :search_preview <pattern>
        Search for a pattern inside files using ripgrep and preview matches using bat.
        """
    
        def execute(self):
            if not self.arg(1):
                self.fm.notify("Usage: :search_preview <pattern>", bad=True)
                return
    
            import subprocess
            pattern = self.rest(1)
            command = f"rg --line-number --column {pattern} | fzf --preview 'bat --style=numbers --color=always --line-range :500 {}'"
            process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
            stdout, stderr = process.communicate()
            if process.returncode == 0:
                result = stdout.decode("utf-8").strip()
                if result:
                    file_path, line_number = result.split(":", 1)
                    self.fm.select_file(file_path)
            else:
                self.fm.notify(stderr.decode("utf-8").strip(), bad=True)
    
  3. Usage:

    • Run :search_preview <pattern> to search with previews.

Summary of Features

ToolDescriptionInteractiveFast
RipgrepUltra-fast text searchNoYes
FZFInteractive search with RipgrepYesYes
GrepTraditional text searchNoNo
BatSyntax-highlighted previewsYesYes