Skip to content

Lightning-fast LSP using ctags with smart triggers (@files .classes #methods) for universal code navigation

License

Notifications You must be signed in to change notification settings

ruykin/triggerfish

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Triggerfish LSP

Lightning-fast LSP server with smart trigger-based completions for files, classes, and functions.

Features

  • @ trigger for file completions with fuzzy search
  • . trigger for class completions with fuzzy search
  • # trigger for method/function completions with fuzzy search
  • Works in .txt files by default
  • Automatic workspace indexing with universal-ctags integration
  • Shows all project files and code symbols
  • Intelligent directory filtering (ignores .git, node_modules, etc.)
  • Optional Go core subprocess for graph queries

Requirements

  • 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

Installation

# From source
pip install -e lsp

# From PyPI (future)
pip install triggerfish

Quick Start (VSCode)

The 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

Usage

# Start server
cd lsp
python -m triggerfish

# With debug logging
python -m triggerfish --log-level DEBUG

Editor Configuration

VSCode

Option 1: Use the Included Extension (Recommended)

The repository includes a ready-to-use VSCode extension at .vscode/extensions/triggerfish-lsp/.

  1. Install dependencies:

    cd .vscode/extensions/triggerfish-lsp
    npm install
  2. Open the workspace in VSCode - the extension is automatically discovered

  3. The extension will automatically use lsp/.venv/bin/python if available, otherwise falls back to system python

  4. Open a .txt file 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"
        }
      }
    }
  }
}
  1. 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 };

Neovim

Modern Setup (Neovim 0.10+)

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,
})

Using nvim-lspconfig

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,
})

Lazy.nvim Plugin Manager

{
  '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,
}

Zed

  1. 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
    }
  }
}
  1. Create ~/.config/zed/languages/text.json (optional):
{
  "name": "Plain Text",
  "path_suffixes": ["txt"],
  "line_comments": [],
  "language_servers": ["triggerfish"]
}

Generic LSP Client

For any LSP client:

  • Command: python3 -m triggerfish
  • Trigger characters: @, ., #
  • Filetypes: text, txt, plaintext
  • Root markers: .git, .hg, .svn

Examples

File Completions with @

In a .txt file, type:

@myfi

Shows completions for all matching files in your project:

  • my_file.py
  • my_first.js
  • my_filter.ts
  • my_file.md
  • my_config.json

Class Completions with .

In a .txt file, type:

.UserAuth

Shows completions for all matching classes:

  • UserAuthentication
  • UserAuthProvider
  • UserAuthManager

Method/Function Completions with #

In a .txt file, type:

#get_user

Shows completions for all matching methods and functions:

  • get_user_by_id
  • get_user_profile
  • get_user_settings

Real-World Usage

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 method

Note: 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.

Configuration

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)

Examples

# 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

Development

# 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 triggerfish

Architecture

  • lsp/ - Python LSP server (editor-facing)
  • core/ - Go graph engine (subprocess)
  • Communication: STDIO newline-delimited JSON
  • Graph v0: Definition graph (files, classes, functions)

Troubleshooting

No completions appearing

  • Logs location: ~/.triggerfish/logs/triggerfish.log
  • Make sure you're in a .txt file 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

Class/Method completions not working

  • Ensure universal-ctags is installed: ctags --version
  • Check if ctags is in your PATH: which ctags (Unix) or where ctags (Windows)
  • Look for ctags errors in the log file
  • Some files may not be parseable by ctags (check language support)

Completions showing wrong symbols

  • 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

Performance issues

  • Check log file for ctags timeout errors
  • Large projects may take longer to index initially
  • Increase ctags timeout: export TRIGGERFISH_CTAGS_TIMEOUT=60

Testing the server

# 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.log

License

Apache 2.0

About

Lightning-fast LSP using ctags with smart triggers (@files .classes #methods) for universal code navigation

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published