Lightning-fast LSP server with smart trigger-based completions for files, classes, and functions.
@trigger for file completions with fuzzy search.trigger for class completions with fuzzy search#trigger for method/function completions with fuzzy search- Works in
.txtfiles by default - Automatic workspace indexing with
universal-ctagsintegration - Shows all project files and code symbols
- Intelligent directory filtering (ignores
.git,node_modules, etc.) - Optional Go core subprocess for graph queries
- Python 3.9+
universal-ctags(for class/method/function parsing)- macOS:
brew install universal-ctags - Ubuntu/Debian:
apt install universal-ctags - Arch:
pacman -S ctags - Windows: Download from ctags.io
- macOS:
# From source
pip install -e lsp
# From PyPI (future)
pip install triggerfishThe fastest way to get started with VSCode:
# 1. Install Triggerfish
pip install -e lsp
# 2. Install extension dependencies
cd .vscode/extensions/triggerfish-lsp
npm install
# 3. Open workspace in VSCode
# The extension will auto-activate for .txt files
# 4. Try it out in a .txt file:
# - Type @ to see file completions
# - Type . to see class completions
# - Type # to see method/function completions# Start server
cd lsp
python -m triggerfish
# With debug logging
python -m triggerfish --log-level DEBUGOption 1: Use the Included Extension (Recommended)
The repository includes a ready-to-use VSCode extension at .vscode/extensions/triggerfish-lsp/.
-
Install dependencies:
cd .vscode/extensions/triggerfish-lsp npm install -
Open the workspace in VSCode - the extension is automatically discovered
-
The extension will automatically use
lsp/.venv/bin/pythonif available, otherwise falls back to systempython -
Open a
.txtfile and start using@,., or#triggers
To customize the Python path, add to your workspace settings (.vscode/settings.json):
{
"triggerfish.pythonPath": "/path/to/your/python"
}Option 2: Manual LSP Client Setup
Install a generic LSP client extension like vscode-lsp-client
Add to your settings.json:
{
"lsp.languages": {
"plaintext": {
"command": "python",
"args": ["-m", "triggerfish"],
"filetypes": ["plaintext", "txt"],
"triggerCharacters": ["@", ".", "#"]
}
}
}Option 3: Create Your Own Extension
Create .vscode/extensions/triggerfish/package.json:
{
"name": "triggerfish-lsp",
"displayName": "Triggerfish LSP",
"version": "0.1.0",
"engines": { "vscode": "^1.75.0" },
"activationEvents": ["onLanguage:plaintext"],
"main": "./extension.js",
"contributes": {
"configuration": {
"title": "Triggerfish",
"properties": {
"triggerfish.pythonPath": {
"type": "string",
"default": "python",
"description": "Path to Python executable"
}
}
}
}
}- Create
.vscode/extensions/triggerfish/extension.js:
const vscode = require('vscode');
const { LanguageClient } = require('vscode-languageclient/node');
let client;
function activate(context) {
const config = vscode.workspace.getConfiguration('triggerfish');
const workspaceFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
const defaultPythonPath = workspaceFolder
? `${workspaceFolder}/lsp/.venv/bin/python`
: 'python';
const pythonPath = config.get('pythonPath') || defaultPythonPath;
const serverOptions = {
command: pythonPath,
args: ['-m', 'triggerfish']
};
const clientOptions = {
documentSelector: [{ scheme: 'file', language: 'plaintext' }],
synchronize: {
fileEvents: vscode.workspace.createFileSystemWatcher('**/*')
}
};
client = new LanguageClient(
'triggerfish',
'Triggerfish LSP',
serverOptions,
clientOptions
);
client.start();
}
function deactivate() {
if (client) return client.stop();
}
module.exports = { activate, deactivate };Add to your init.lua:
-- Triggerfish LSP setup using vim.lsp.config
vim.lsp.config.triggerfish = {
cmd = { 'python3', '-m', 'triggerfish' },
filetypes = { 'text', 'txt' },
root_markers = { '.git', '.hg', '.svn' },
settings = {},
}
-- Enable for text files
vim.api.nvim_create_autocmd('FileType', {
pattern = { 'text', 'txt' },
callback = function(args)
vim.lsp.enable('triggerfish', args.buf)
end,
})local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
-- Register triggerfish if not already registered
if not configs.triggerfish then
configs.triggerfish = {
default_config = {
cmd = { 'python3', '-m', 'triggerfish' },
filetypes = { 'text', 'txt' },
root_dir = lspconfig.util.root_pattern('.git', '.hg', '.svn'),
settings = {},
},
}
end
-- Setup triggerfish
lspconfig.triggerfish.setup({
on_attach = function(client, bufnr)
-- Optional: Add custom keymaps
local opts = { buffer = bufnr, noremap = true, silent = true }
vim.keymap.set('i', '@', '@', opts) -- Trigger file completion
vim.keymap.set('i', '.', '.', opts) -- Trigger class completion
vim.keymap.set('i', '#', '#', opts) -- Trigger method completion
end,
}){
'neovim/nvim-lspconfig',
config = function()
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
if not configs.triggerfish then
configs.triggerfish = {
default_config = {
cmd = { 'python3', '-m', 'triggerfish' },
filetypes = { 'text', 'txt' },
root_dir = lspconfig.util.root_pattern('.git'),
settings = {},
},
}
end
lspconfig.triggerfish.setup({})
end,
}- Create or edit
~/.config/zed/settings.json:
{
"lsp": {
"triggerfish": {
"binary": {
"path": "python3",
"arguments": ["-m", "triggerfish"]
},
"settings": {},
"initialization_options": {}
}
},
"languages": {
"Plain Text": {
"language_servers": ["triggerfish"],
"format_on_save": "off",
"tab_size": 2
}
}
}- Create
~/.config/zed/languages/text.json(optional):
{
"name": "Plain Text",
"path_suffixes": ["txt"],
"line_comments": [],
"language_servers": ["triggerfish"]
}For any LSP client:
- Command:
python3 -m triggerfish - Trigger characters:
@,.,# - Filetypes:
text,txt,plaintext - Root markers:
.git,.hg,.svn
In a .txt file, type:
@myfi
Shows completions for all matching files in your project:
my_file.pymy_first.jsmy_filter.tsmy_file.mdmy_config.json
In a .txt file, type:
.UserAuth
Shows completions for all matching classes:
UserAuthenticationUserAuthProviderUserAuthManager
In a .txt file, type:
#get_user
Shows completions for all matching methods and functions:
get_user_by_idget_user_profileget_user_settings
Notes.txt:
TODO: Check the implementation in @src/auth/login.py
The .UserAuthentication class handles this
Call #validate_credentials to verify the user
Bug in @components/Button.tsx
The .Button class needs updating
Fix #handleClick methodNote: All triggers (., #, @) currently only work in .txt files. This allows you to reference files, classes, and functions from text documents without triggering completions in your code files.
Triggerfish can be configured via environment variables or .env file:
| Variable | Default | Description |
|---|---|---|
TRIGGERFISH_LOG_FILE |
~/.triggerfish/logs/triggerfish.log |
Log file location |
TRIGGERFISH_LOG_LEVEL |
INFO |
Logging level (DEBUG, INFO, WARNING, ERROR) |
TRIGGERFISH_CTAGS_EXECUTABLE |
ctags |
Path to ctags executable |
TRIGGERFISH_CTAGS_TIMEOUT |
30 |
Timeout for ctags execution (seconds) |
TRIGGERFISH_MIN_FUZZY_SCORE |
60 |
Minimum fuzzy match score (0-100) |
TRIGGERFISH_MAX_COMPLETION_ITEMS |
50 |
Maximum completion items to return |
TRIGGERFISH_CORE_ENABLED |
1 |
Enable Go core subprocess |
TRIGGERFISH_CORE_EXECUTABLE |
triggerfish-core |
Path to Go core binary |
TRIGGERFISH_CORE_TIMEOUT |
10 |
Core request timeout (seconds) |
# Use custom ctags
export TRIGGERFISH_CTAGS_EXECUTABLE=/usr/local/bin/ctags
# Increase timeout for large files
export TRIGGERFISH_CTAGS_TIMEOUT=60
# Show more completions
export TRIGGERFISH_MAX_COMPLETION_ITEMS=100
# More lenient fuzzy matching
export TRIGGERFISH_MIN_FUZZY_SCORE=40
# Debug logging
export TRIGGERFISH_LOG_LEVEL=DEBUG
# Disable core subprocess
export TRIGGERFISH_CORE_ENABLED=0
# Start server with custom config
cd lsp
python -m triggerfish# Enter dev environment (Nix)
nix develop
# Configure (optional)
cp .env.example .env
# Build Go core
make core-build
# Run smoke test
make smoke
# Install dev dependencies
pip install -e lsp[dev]
# Run tests
cd lsp
pytest
# Check coverage
pytest --cov=triggerfish --cov-report=html
# Format code
black triggerfish tests
# Type check
mypy triggerfish
# Lint
ruff check triggerfish tests
pylint triggerfishlsp/- Python LSP server (editor-facing)core/- Go graph engine (subprocess)- Communication: STDIO newline-delimited JSON
- Graph v0: Definition graph (files, classes, functions)
- Logs location:
~/.triggerfish/logs/triggerfish.log - Make sure you're in a
.txtfile when testing completions - Check that the LSP client is configured with filetype
'text'or'txt' - Verify the server is running:
- Neovim: Check
:LspInfo - VSCode: Check "Output" panel → "Triggerfish LSP"
- Zed: Check debug panel
- Neovim: Check
- Ensure
universal-ctagsis installed:ctags --version - Check if ctags is in your PATH:
which ctags(Unix) orwhere ctags(Windows) - Look for ctags errors in the log file
- Some files may not be parseable by ctags (check language support)
- The workspace index is built on LSP initialization
- Restart the LSP server to rebuild the index:
- Neovim:
:LspRestart triggerfish - VSCode: Reload window (Cmd/Ctrl+R)
- Zed: Restart editor
- Neovim:
- Check log file for ctags timeout errors
- Large projects may take longer to index initially
- Increase ctags timeout:
export TRIGGERFISH_CTAGS_TIMEOUT=60
# Check if ctags is working
ctags --version
# Test ctags on a file
ctags --output-format=json --fields=* --excmd=pattern your_file.py
# Start server with debug logging
cd lsp
python -m triggerfish --log-level DEBUG
# Check logs
tail -f ~/.triggerfish/logs/triggerfish.logApache 2.0