• Stars
    star
    386
  • Rank 110,478 (Top 3 %)
  • Language
    Emacs Lisp
  • Created over 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A modern, easy-to-expand fuzzy search framework

This project has been replaced by blink-search, thank you!

What is snails?

Snails is a modern, easy-to-expand fuzzy search framework.

The goal of this project is to minimize the development threshold for fuzzy search plugins. If you know how to write a filter function, you can write a new search plugin in 5 minutes, regardless of how complex the search framework is.

Installation

  1. Clone or download this repository (path of the folder is the <path-to-snails> used below).
  2. If you are using Mac, install exec-path-from-shell dependency.
  3. In your ~/.emacs, add the following two lines:
(add-to-list 'load-path "<path-to-snails>") ; add snails to your load-path
(require 'snails)

Usage

M-x snails to launch snails

  • Default snails search input in backends: awesome-tab group, buffer name, recently files or bookmark
  • Search command if prefix start with >
  • Search variable or function define if prefix start with @
  • Search current buffer content if prefix start with #
  • Search project file content if prefix start with !
  • Search file if prefix start with ?
  • Enter "!color-rg@/Users/andy/color-rg" to search color-rg keywrod in directory /Users/andy/color-rg recursively.
  • Enter "?color-rg@" to search color-rg file in HOME directory.
  • Enter "?color-rg@/home/andy/color-rg" to search color-rg file in /home/andy/color-rg directory.

You can use M-x snails-search-point to launch snails with symbol around point.

You can customize snails-default-backends and snails-prefix-backends with your own prefix rule.

Use Snails without popup frame.

Default snails popup new frame to search candidates. If you don't like popup style, you can change to helm style with below code:

(setq snails-show-with-frame nil)

Use Snails With Custom Backends

You can also customize the search backends you want to use! (similar to Ivy)

You can either write your own backend (see below) or assemble all available backends like the following:

Just search opened buffers (use snails with 1 backend only!)

(snails '(snails-backend-buffer))

Search opened buffers and recently opened files (use snails with 2 backends!)

(snails '(snails-backend-buffer snails-backend-recentf))

Search symbol at point of opened buffers and recently opened files (use snails with 2 backends!)

(snails '(snails-backend-buffer snails-backend-recentf) t)

Search customize string hello

(snails nil "hello")

When you customize the search backends, snails won't filter search result with input prefix.

Currently Available Backends

Backend Description
snails-backend-awesome-tab-group Switch group of awesome-tab, need install plugin awesome-tab
snails-backend-buffer Search buffer list
snails-backend-recentf Search recently files
snails-backend-bookmark Switch bookmark
snails-backend-imenu Jump to function or variable definition
snails-backend-current-buffer Search current buffer content
snails-backend-rg Use ripgrep search content in current project, need install ripgrep
snails-backend-search-pdf Use rga search pdf in current directory or supplied directory with @, need install ripgrep-all
snails-backend-fd Use fd search files in current project, need install fd
snails-backend-mdfind Use mdfind search files in local disk, only Mac
snails-backend-everything Use everything search files in local disk, only Windows, need install everything
snails-backend-fasd Use fasd to search most visit directory , need install fasd
snails-backend-command Search command
snails-backend-eaf-pdf-table Search table of contents of PDF file, united EAF technology
snails-backend-eaf-browser-history Search history of browser, united EAF technology, need install fzf
snails-backend-eaf-browser-open Open url in browser, united EAF technology
snails-backend-eaf-browser-search Open keyword in browser, united EAF technology
snails-backend-eaf-github-search Search keyword in github, united EAF technology
snails-backend-kv-store Read/Set/Update/Del key-value pair in sqlite3, need install sqlite3 and required emacsql

Fuzz match

Snails use normal match algorithm default.

Snails will use fuzz match algorithm once you install fuz and add fuz in load-path.

To install fuz.el , please follow below steps:

  1. Install Rust
  2. Download fuz.el repo: git clone https://github.com/cireu/fuz.el
  3. Build fuz-core.so: cargo build --release
  4. Rename target/release/libfuz_core.so or target/release/libfuz_core.dylib to fuz-core.so
  5. Make sure fuz-core.so and all files in https://github.com/cireu/fuz.el add to your load-path

Keymap

Key Description
C-n Select next candidate
C-p Select previous candidate
M-n Select next candidate
M-p Select previous candidate
M-, Select next candidate
M-. Select previous candidate
C-v Select next backend
M-v Select previous backend
M-j Select next backend
M-k Select previous backend
C-m Confirm candiate
RET Confirm candiate
M-w Copy candidate
C-g Quit snails
ESC ESC ESC Quit snails
M-h Quit snails

Architecture Design of Snails

snails-core.el is framework code, it only do:

  1. Monitor user input, generate input ticker and send a search request to the backend.
  2. Check backend's search result with input ticker.
  3. Render search result if input ticker is newest.

Sync backend search action is trigger by framework when user type new character. Async backend search action only trigger by framework when user release keyboard key.

Input ticker is the label of the input event,backend's input ticker will expired when user type new character in input buffer.

When backend search finish, framework will drop search result if input ticker is expired.

How to Write a New Plugin?

Writing a plugin for snails is very simple. As long as you have basic knowledge of elisp, you can write a plugin in 5 minutes!

Snails plugins fall into two categories: sync plugins and asynchronous plugins.

Sync plugins are plugins that get the completion results immediately, such as buffers, recent files, etc.

Asynchronous plugins are plugins that take time to get completion results and are usually placed in child processes for calculation, such as find file, grep file, etc.

Writing a Sync Plugin

Let's take the example of snails-backend-recentf plugin:

(require 'snails-core)
(require 'recentf)

(recentf-mode 1)

(snails-create-sync-backend
 :name
 "RECENTF"

 :candidate-filter
 (lambda (input)
   (let (candidates)
     (dolist (file recentf-list)
       (when (or
              (string-equal input "")
              (snails-match-input-p input file))
         (snails-add-candiate 'candidates file file)))
     (snails-sort-candidates input candidates 1 1)
     candidates))

 :candidate-icon
 (lambda (candidate)
   (snails-render-file-icon candidate))

 :candidate-do
 (lambda (candidate)
   (find-file candidate)))

(provide 'snails-backend-recentf)
  • :name parameter is the name of your plugin, and it must be unique. Snails distinguishes the results of different plugins based on the plugin name.

  • :candidate-filter is the filter function. input is user input content, and you need to return a candidate list to the snails framework, where elements of the candidate list are formatted as (list display-name candidate-content). The first candidate element display-name is the string presented to the user, and the second candidate element candidate-content is the string passed to the candidate-do callback below. If nothing was found, please return nil, and snails will hide the backend result.

  • :candidate-icon is function to render candidate icon, if you don't need icon, just ignore this.

  • :candidate-do is the function to confirm the candidate, and it can be any code you want.

Taking the above plug-in as an example, when the user does not input anything, all the recently viewed files are displayed, and when the user does input something, the recently viewed files are filtered according to the input content. When the user confirms, use the find-file command to open the file.

Writing an Async Plugin

Let's take the example of snails-backend-mdfind plugin:

(require 'snails-core)

(snails-create-async-backend
 :name
 "MDFIND"

 :build-command
 (lambda (input)
   (when (and (featurep 'cocoa)
              (> (length input) 3))
     (list "mdfind" "-name" (format "'%s'" input))))

 :candidate-filter
 (lambda (candidate-list)
   (let (candidates)
     (dolist (candidate candidate-list)
       (snails-add-candiate 'candidates candidate candidate))
     candidates))

 :candidate-icon
 (lambda (candidate)
   (snails-render-file-icon candidate))

 :candidate-do
 (lambda (candidate)
   (find-file candidate)))

(provide 'snails-backend-mdfind)
  • :name parameter is the name of your plugin, and it must be unique. Snails distinguishes the results of different plugins based on the plugin name.

  • :build-command is the function to build a command with user input. input is the user input content, and you need to return a list of strings where the first string is a shell command, and the rest of the strings are arguments to pass to the shell command. If you don't want the search to continue, please return nil.

  • :candidate-filter is the filter function, candidate-list is a list of strings returned by the shell command, and you need to return a candidate list to the snails framework, where elements of the candidate list are formatted as (list display-name candidate-content). The first candidate element display-name is the string presented to the user, and the second candidate element candidate-content is the string passed to the candidate-do callback below. If nothing was found, please return nil, and snails will hide the backend result.

  • :candidate-icon is function to render candidate icon, if you don't need icon, just ignore this.

  • :candidate-do is the function to confirm the candiate, and it can be any code you want.

Taking the above plug-in as an example, when the user inputs "multi-term", build-command will check the input length, and the search will only start after more than 5 characters have been entered. Then, the build-command function will build commands (list "mdfind" "'multi-term'") to pass to the async subprocess. When the async subprocess finishes, it will return a list of strings to the candidate-fitler callback, and the candiate-filter function will wrap the shell result as a candidate list. When the user confirms, use the find-file command to open the file.

Snails is very smart; it will manage subprocesses of the async backend. When the user modifies the input, the snails framework automatically creates a new subprocess to search for the results, while automatically killing the old running process. No matter how fast the user enters, it won't block Emacs.

FAQ

Why doesn't snails frame work when I open a fullscreen Emacs on Mac?

Mac will force the fullscreen Emacs window to a separate workspace, and then any new frame created by make-frame will not float above the Emacs window as expected.

If you start Emacs with fullscreen mode, you can use my workaround code to fix this problem:

(if (featurep 'cocoa)
    (progn
      (setq ns-use-native-fullscreen nil)
      (setq ns-use-fullscreen-animation nil)

      (set-frame-parameter (selected-frame) 'fullscreen 'maximized)

      (run-at-time "2sec" nil
                   (lambda ()
                     (toggle-frame-fullscreen)
                     )))
  (require 'fullscreen)
  (fullscreen))

Why not support match highlight?

Search tools such as fd and rg has --color option. It's easy use `ansi-color' library to render match color.

But the reason why Snails doesn't show highlights is because rendering colors can cause very large performance problems, causing Emacs to get stuck.

The biggest goal of Snails project is fast, although I know that highlighting is very meaningful, so I am willing to sacrifice this feature for fluency.

If you know how to keep fluency when adding highlights, please contribute your patch. ;)

Manage Evil state

I personally never use the evil plugin, if you want manage evil state in input buffer of snails. You should customize your own code with `snails-mode-hook'

More Repositories

1

lazycat-emacs

Andy Stewart's emacs
Emacs Lisp
425
star
2

aweshell

Awesome shell extension base on eshell with wonderful features!
Emacs Lisp
395
star
3

awesome-tab

Emacs package to provide out-of-the-box configuration to use tabs.
Emacs Lisp
364
star
4

deepin-terminal

Deepin Terminal written by vala
Vala
257
star
5

awesome-tray

Hide mode-line, display necessary information at right of minibuffer.
Emacs Lisp
233
star
6

nox

Nox is a lightweight, high-performance LSP client for Emacs
Emacs Lisp
206
star
7

color-rg

Search and refactoring tool based on ripgrep.
Emacs Lisp
149
star
8

popweb

Show popup web window for Emacs
JavaScript
142
star
9

mind-wave

Emacs AI plugin based on ChatGPT API
Emacs Lisp
137
star
10

company-english-helper

English helper base on Emacs company-mode
Emacs Lisp
99
star
11

insert-translated-name

Insert translated string as variable or function name
Emacs Lisp
87
star
12

awesome-pair

Auto parenthesis pairing with syntax table
Emacs Lisp
76
star
13

auto-save

Automatically save files without temporary files to protect your finger. ;)
Emacs Lisp
73
star
14

blink-search

In the blink of an eye, the search is complete
Emacs Lisp
72
star
15

sdcv

Emacs interface for sdcv (Stardict console version)
Emacs Lisp
64
star
16

deno-bridge

Build bridge between Emacs and Deno, execution of JavaScript and Typescript within Emacs.
Emacs Lisp
59
star
17

deepin-software-center

Software center for linux deepin.
Python
51
star
18

manateelazycat.github.io

My personal blog
HTML
50
star
19

deepin-screen-recorder

Deepin screen recorder
C++
50
star
20

holo-layer

HoloLayer is a multimedia layer plugin designed specifically for Emacs
Emacs Lisp
48
star
21

grammatical-edit

Grammatical edit base on tree-sitter
Emacs Lisp
47
star
22

hammerspoon-config

My config for Hammerspoon Window Manager
Lua
46
star
23

thing-edit

Copy and paste anything under cursor.
Emacs Lisp
46
star
24

sort-tab

Smarter tab solution for Emacs, sort tab with using frequency.
Emacs Lisp
44
star
25

deepin-system-monitor

System monitor for deepin
C++
41
star
26

instant-rename-tag

Instant rename tag
Emacs Lisp
32
star
27

python-bridge

Write Emacs Plugin by Python, split code from EAF.
Emacs Lisp
32
star
28

lazy-load

Lazy load keys for speed ​​up Emacs startup.
Emacs Lisp
30
star
29

deepin-pinyin-assistant

Deepin pinyin assistant
C
27
star
30

fingertip

Fingertip is struct edit plugin that base on treesit
Emacs Lisp
26
star
31

deepin-editor

Simple note application for deepin
C++
24
star
32

markmacro

Keyboard macro for marked regions
Emacs Lisp
21
star
33

one-key

Many commands share one key.
Emacs Lisp
20
star
34

mrkeyboard

Mr. Keyboard
Vala
17
star
35

grep-dired

Find name with given regexp, and show in dired.
Emacs Lisp
16
star
36

lazycat-theme

Cool hacker's emacs theme, but won't hurt your eye
Emacs Lisp
16
star
37

nova

Nova is a multi-threaded remote access plugin designed specifically for Emacs, with outstanding file synchronization performance.
Emacs Lisp
16
star
38

multi-term

Managing multiple terminal buffers in Emacs.
Emacs Lisp
15
star
39

deepin-voice-recorder

Voice recorder application for deepin
C++
15
star
40

corfu-english-helper

English helper for Emacs, base on corfu-mode
Emacs Lisp
15
star
41

lazy-search

Mark current symbol and jump in all matching symbols.
Emacs Lisp
15
star
42

lsp-bridge

Fastest LSP client for Emacs, work in progress...
Python
14
star
43

deepin-picker

Color picker tool for deepin
C++
13
star
44

css-sort

An Emacs extension you can sort CSS attributables automatically.
Emacs Lisp
13
star
45

tower-ng

Use rails write web todo-list tool like https://tower.im, this project is just a learning project
Ruby
12
star
46

smart-align

Smart align block around cursor
Emacs Lisp
11
star
47

delete-block

Delete block effectively
Emacs Lisp
10
star
48

recursive-search-references

Find function references in directory
Emacs Lisp
9
star
49

deepin-desktop-monitor

Deepin Desktop Monitor
C++
8
star
50

key-echo

Key-Echo is an Emacs plugin that uses XRecord technology to listen to system key events
Emacs Lisp
8
star
51

highlight-matching-tag

This plugin will highlight matching tag instantaneously.
Emacs Lisp
8
star
52

bison

Mode to editing bision source code in Emacs
Emacs Lisp
8
star
53

wraplish

Wraplish 是一个在 Unicode 与英文之间加上空格的Emacs插件,
Emacs Lisp
7
star
54

duplicate-line

Duplicate line or region, don't need move cursor.
Emacs Lisp
7
star
55

flex

It's a mode for flex files that provide better syntax highlight than flex-mode.el
Emacs Lisp
7
star
56

watch-other-window

Scroll other window and keep current window's position.
Emacs Lisp
7
star
57

find-define

Jump to the definition of a function or variable
Emacs Lisp
6
star
58

deepin-gnome-shell-3.4.1

Deepin gnome shell 3.4.1
C
6
star
59

move-text

Move current line or region
Emacs Lisp
6
star
60

deepin-translate-tools

Translate tools for deepin linux.
Python
6
star
61

vi-navigate

Navigate read-only buffer like vi behavior.
Emacs Lisp
6
star
62

html-to-word

This is a HTML to Word conversion library for Rails.
Ruby
6
star
63

manateelazycat

Github profile repo
5
star
64

cache-path-from-shell

Provide a chache mechanism make sure exec-path-from-shell just execute once.
Emacs Lisp
5
star
65

find-orphan

Find orphan function that need remove
Emacs Lisp
5
star
66

open-newline

Open newline like vi.
Emacs Lisp
4
star
67

toggle-one-window

Toggle between window layout and one window.
Emacs Lisp
4
star
68

lazycat-gs-theme

Gnome shell for my own use
Shell
3
star
69

manatee

Manatee Integrate Live Environment
Haskell
3
star
70

deb2po

Convert format between *.debian and *.po file.
Python
3
star
71

effortless-indent

Indent paste code without additional selection operations
Emacs Lisp
3
star
72

lazycat-emacs-time-machine

The elisp code that has been tossed, no longer used, archived to commemorate
Emacs Lisp
3
star
73

deno-bridge-ts

Build bridge between Emacs and Deno, execution of JavaScript and Typescript within Emacs, this repo is TypeScript part for deno-bridge
TypeScript
1
star