Skip to content

Editor Integration

sysid edited this page Oct 12, 2025 · 2 revisions

Editor Integration

bkmr integrates directly into your editor through its built-in Language Server Protocol (LSP) server and specialized plugins, providing intelligent snippet completion without leaving your development environment.

Overview

Instead of context-switching between editor and terminal to access snippets, bkmr brings your entire snippet library directly into your IDE with:

  • Automatic completion - Snippets appear in completion popup while typing
  • Language-aware filtering - Only relevant snippets shown based on file type
  • Universal snippets - Write once, automatically adapt to any language
  • Template interpolation - Dynamic content with Jinja2-style templates
  • CRUD operations - Manage snippets directly from your editor

Integration Options

bkmr provides three integration paths:

  1. Neovim Plugin (Recommended for Neovim) - Visual interface with zero configuration
  2. Built-in LSP Server - Universal integration for VS Code, Vim, Emacs, Sublime, etc.
  3. IntelliJ Platform Plugin - JetBrains IDEs with seamless LSP integration

Neovim Plugin (Recommended)

For Neovim users, bkmr-nvim provides the best integration experience with a visual interface and zero configuration.

Features

  • Visual snippet browser - Telescope/FZF integration for browsing and searching
  • In-editor editing - Create and edit snippets without leaving Neovim
  • Automatic LSP setup - Configures bkmr LSP server automatically
  • Custom commands - :Bkmr, :BkmrEdit, :BkmrSearch
  • Zero configuration - Works out of the box

Installation

With lazy.nvim:

{
  "sysid/bkmr-nvim",
  dependencies = { "nvim-lua/plenary.nvim" },
  config = function()
    require("bkmr").setup() -- Zero config required!
  end,
}

With packer.nvim:

use {
  'sysid/bkmr-nvim',
  requires = { 'nvim-lua/plenary.nvim' },
  config = function()
    require("bkmr").setup()
  end
}

With vim-plug:

Plug 'nvim-lua/plenary.nvim'
Plug 'sysid/bkmr-nvim'

" In your init.vim after plug#end():
lua require("bkmr").setup()

Custom Configuration

require("bkmr").setup({
  -- LSP configuration
  lsp = {
    cmd = { "bkmr", "lsp" },
    filetypes = { "*" }, -- Enable for all file types
  },

  -- Picker configuration (Telescope or FZF)
  picker = "telescope", -- or "fzf"

  -- Optional: Custom keymaps
  keymaps = {
    search = "<leader>bs",
    edit = "<leader>be",
    insert = "<leader>bi",
  }
})

Usage

Commands:

  • :Bkmr - Open snippet browser
  • :BkmrSearch [query] - Search snippets with optional query
  • :BkmrEdit <id> - Edit snippet by ID
  • :BkmrCreate - Create new snippet

Default Keymaps (customizable):

  • <leader>bs - Search snippets
  • <leader>be - Edit snippet
  • <leader>bi - Insert snippet at cursor

Built-in LSP Server

The bkmr binary includes a full Language Server Protocol implementation accessible via bkmr lsp, compatible with any LSP client.

Quick Start

# Start LSP server (usually configured in your editor)
bkmr lsp

# Start without template interpolation
bkmr lsp --no-interpolation

LSP Commands Reference

All commands are executed via workspace/executeCommand:

Command Parameters Returns Description
bkmr.createSnippet url (required)
title (required)
description (optional)
tags (optional)
Created snippet Creates new snippet with _snip_ tag
bkmr.updateSnippet id (required)
url (optional)
title (optional)
description (optional)
tags (optional)
Updated snippet Updates existing snippet
bkmr.getSnippet id (required) Snippet data Retrieves snippet by ID
bkmr.deleteSnippet id (required) Success status Deletes snippet
bkmr.listSnippets language (optional) {snippets: []} Lists snippets filtered by language
bkmr.insertFilepathComment Direct string URI WorkspaceEdit Inserts relative filepath as comment

Example Command Usage:

{
  "command": "bkmr.createSnippet",
  "arguments": [{
    "url": "console.log('test')",
    "title": "JS Log",
    "tags": ["javascript", "js"]
  }]
}

Manual Neovim Configuration

If you prefer manual configuration without the plugin:

-- ~/.config/nvim/lua/lsp/bkmr.lua
local lspconfig = require('lspconfig')

-- Define the bkmr LSP configuration
lspconfig.configs.bkmr_lsp = {
  default_config = {
    cmd = { "bkmr", "lsp" },
    filetypes = {
      "rust", "python", "javascript", "typescript", "go",
      "java", "c", "cpp", "html", "css", "shell", "yaml",
      "json", "markdown", "vim"
    },
    root_dir = function(fname)
      return lspconfig.util.find_git_ancestor(fname)
        or vim.fn.getcwd()
    end,
    settings = {},
  },
}

-- Setup with capabilities and custom commands
lspconfig.bkmr_lsp.setup{
  capabilities = require('cmp_nvim_lsp').default_capabilities(),
  on_attach = function(client, bufnr)
    -- Custom command for filepath insertion
    vim.api.nvim_buf_create_user_command(bufnr, 'BkmrInsertPath',
      function()
        vim.lsp.buf.execute_command({
          command = "bkmr.insertFilepathComment",
          arguments = { vim.uri_from_bufnr(bufnr) }
        })
      end,
      { desc = "Insert filepath comment" }
    )

    -- List snippets command
    vim.api.nvim_buf_create_user_command(bufnr, 'BkmrListSnippets',
      function()
        local filetype = vim.bo[bufnr].filetype
        vim.lsp.buf.execute_command({
          command = "bkmr.listSnippets",
          arguments = { { language = filetype } }
        })
      end,
      { desc = "List snippets for current language" }
    )
  end
}

VS Code Configuration

Add to your settings.json:

{
  "languageServerExample.servers": {
    "bkmr-lsp": {
      "command": "bkmr",
      "args": ["lsp"],
      "filetypes": ["*"]
    }
  }
}

Note: Requires an LSP extension like "LSP" or "Language Server Client" to connect custom LSP servers.

Vim Configuration (vim-lsp)

if executable('bkmr')
  augroup LspBkmr
    autocmd!
    autocmd User lsp_setup call lsp#register_server({
      \ 'name': 'bkmr-lsp',
      \ 'cmd': {server_info->['bkmr', 'lsp']},
      \ 'allowlist': [
      \   'rust', 'javascript', 'typescript', 'python', 'go',
      \   'java', 'c', 'cpp', 'html', 'css', 'scss', 'ruby',
      \   'php', 'swift', 'kotlin', 'shell', 'yaml', 'json',
      \   'markdown', 'xml', 'vim'
      \ ],
      \ })
  augroup END
endif

Emacs Configuration (eglot)

;; Add to your init.el or .emacs
(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
               '((rust-mode python-mode js-mode typescript-mode go-mode) . ("bkmr" "lsp"))))

Sublime Text Configuration (LSP)

Create a configuration in LSP settings:

{
  "clients": {
    "bkmr-lsp": {
      "enabled": true,
      "command": ["bkmr", "lsp"],
      "selector": "source"
    }
  }
}

IntelliJ Platform Plugin

bkmr-intellij-plugin brings bkmr integration to all JetBrains IDEs through the JetBrains Marketplace.

Features

  • Seamless LSP integration - Automatic snippet completion with no manual triggers
  • Tab navigation - Full snippet placeholder support with Tab/Shift+Tab navigation
  • Cross-platform - Works in all JetBrains IDEs:
    • IntelliJ IDEA
    • PyCharm
    • WebStorm
    • CLion
    • RustRover
    • GoLand
    • PhpStorm
    • Android Studio
  • Filepath comments - Insert relative filepaths as comments with smart language detection

Installation

  1. Open your JetBrains IDE
  2. Go to Settings/PreferencesPlugins
  3. Search for "bkmr" in Marketplace
  4. Click Install
  5. Restart IDE

Or install manually:

  1. Download from JetBrains Marketplace
  2. Settings/PreferencesPlugins → ⚙️ → Install Plugin from Disk

Configuration

The plugin automatically detects the bkmr binary in your PATH. For custom locations:

  1. Settings/PreferencesToolsbkmr
  2. Set bkmr Executable Path to custom location
  3. Optionally enable/disable template interpolation

Language Support and Filtering

The LSP server automatically detects file types and filters snippets accordingly.

Language Mapping

Language Extensions LSP ID Tag Aliases Comment Style
Rust .rs rust rust //, /* */
Python .py python python, py #
JavaScript .js, .mjs javascript javascript, js //, /* */
TypeScript .ts, .tsx typescript typescript, ts //, /* */
Go .go go go, golang //, /* */
Shell .sh, .bash shell, sh shell, sh, bash #
Java .java java java //, /* */
C/C++ .c, .cpp, .h c, cpp c, cpp //, /* */
HTML .html html html <!-- -->
CSS .css css css /* */
Ruby .rb ruby ruby #
PHP .php php php //, /* */
YAML .yaml, .yml yaml yaml, yml #
JSON .json json json N/A
Markdown .md markdown markdown, md <!-- -->

Creating Language-Specific Snippets

# Language-specific snippets
bkmr add 'println!("{}", ${1:value})' rust,_snip_ --title "Rust Print"
bkmr add 'console.log(${1:message})' javascript,_snip_ --title "JS Log"
bkmr add 'print(f"{${1:var}}")' python,_snip_ --title "Python F-String"

# Multi-language snippets
bkmr add 'if ${1:condition} {\n    ${2:body}\n}' rust,go,_snip_ --title "If Statement"

# Universal snippets (work in any language)
bkmr add '// TODO: ${1:description}' universal,_snip_ --title "TODO Comment"

Universal Snippet System

Universal snippets are bkmr's most powerful feature - write once in Rust syntax, automatically adapt to any language.

How It Works

Universal snippets stored in Rust syntax are automatically translated to the target language's conventions:

Original Snippet (Rust syntax):

// Function: ${1:name}
// Description: ${2:description}
fn ${1:name}() {
    ${3:// Implementation}
}

Automatically Becomes in Python:

# Function: ${1:name}
# Description: ${2:description}
def ${1:name}():
    ${3:# Implementation}

Automatically Becomes in JavaScript:

// Function: ${1:name}
// Description: ${2:description}
function ${1:name}() {
    ${3:// Implementation}
}

Comment Translation

Single-line comments:

  • Rust/JS/C: // comment (original)
  • Python/Shell/YAML: # comment
  • HTML/XML: <!-- comment -->
  • CSS: /* comment */

Block comments:

  • Rust/JS/C: /* block */ (original)
  • Python: """block""" or '''block'''
  • HTML: <!-- block -->
  • Ruby: =begin\nblock\n=end

Indentation Adaptation

  • Rust/Python/Java: 4 spaces (default)
  • JavaScript/TypeScript: 2 spaces
  • Go: Tabs
  • HTML/CSS/YAML: 2 spaces
  • Makefile: Tabs (required)

Universal Snippet Examples

Function Template:

bkmr add '// ${1:Function name}
// ${2:Description}
fn ${1}() {
    ${3:// Implementation}
}' universal,_snip_ --title "Function Template"

Error Handling:

bkmr add '// Handle ${1:error type}
match ${1} {
    Ok(val) => ${2:// Success},
    Err(e) => ${3:// Error handling},
}' universal,rust,_snip_ --title "Error Handler"

Documentation Block:

bkmr add '// ${1:Component name}
//
// ${2:Description}
//
// Args:
//     ${3:arguments}
//
// Returns:
//     ${4:return type}' universal,_snip_ --title "Doc Block"

Template Interpolation

The LSP server processes Jinja2-style templates before serving snippets.

Enabling/Disabling Interpolation

# With interpolation (default)
bkmr lsp

# Without interpolation (raw snippets)
bkmr lsp --no-interpolation

Template Examples

Date-Stamped Comments:

bkmr add '// Generated on {{ current_date | strftime("%Y-%m-%d") }}
// By: {{ env("USER") }}
// Host: {{ "hostname" | shell }}' _snip_ --title "Header"

Dynamic Imports:

bkmr add 'import {{ "git branch --show-current" | shell | replace("-", "_") }}' python,_snip_ --title "Branch Import"

Environment-Aware Configuration:

bkmr add 'API_URL={{ env("API_URL", "http://localhost:8000") }}
USER={{ env("USER") }}
TIMESTAMP={{ current_date | strftime("%s") }}' _snip_ --title "Config"

See Template Interpolation for complete template documentation.

Plain Text Snippets

Tag snippets with plain to prevent LSP placeholder interpretation:

# Plain text - literal insertion, no ${} processing
bkmr add 'DATABASE_URL=${DATABASE_URL}' plain,_snip_ --title "Env Template"

# LSP snippet - with placeholders
bkmr add 'DATABASE_URL=${1:localhost}' _snip_ --title "Env Config"

Use Cases for Plain Snippets:

  • Shell scripts with literal ${} variable syntax
  • Configuration templates with environment variables
  • Docker Compose files with ${VAR} syntax
  • Helm templates with Go template syntax

Workflow Examples

Daily Development Workflow

1. Quick Snippet Access:

# In your editor, start typing...
Type: "log"
↓
Completion popup shows:
- JS Log: console.log(${1:message})
- Python Log: print(f"{${1:var}}")
- Rust Log: println!("{}", ${1:value})

2. Insert and Fill Placeholders:

Select "JS Log"
↓
Inserts: console.log(█)
↓
Type your message
↓
Press Tab to move to next placeholder (if any)

3. Create Snippets On-The-Fly:

# In Neovim with bkmr-nvim:
:BkmrCreate
↓
Enter snippet content
↓
Tag with language
↓
Immediately available in completion

Code Review Workflow

# Create review comment snippet
bkmr add '// REVIEW: ${1:issue}
// Suggested fix: ${2:solution}
// Priority: ${3:high|medium|low}' review,_snip_ --title "Review Comment"

# Now available in any code file
# Type "review" → automatic completion → fill placeholders

Documentation Workflow

# Create function documentation template
bkmr add '/// ${1:Brief description}
///
/// # Arguments
/// * `${2:arg}` - ${3:arg description}
///
/// # Returns
/// ${4:return description}' rust,doc,_snip_ --title "Rust Doc"

# Use in editor:
# Type "doc" above function → select template → fill in details

Troubleshooting

No Completions Appearing

Check snippet existence:

bkmr search -t _snip_
bkmr search -t _snip_,rust  # Language-specific

Test LSP server:

# Basic connectivity test
echo '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{}}' | bkmr lsp

# Should return JSON response with capabilities

Verify bkmr version:

bkmr --version
# Requires 4.31.0 or later for full LSP support

Check editor LSP client:

  • Neovim: :LspInfo - Verify bkmr_lsp is attached
  • VS Code: Check "Output" → "Language Server" logs
  • Vim: :LspStatus or check vim-lsp logs

Snippets Not Filtered by Language

Issue: All snippets appear regardless of file type

Solutions:

  1. Tag snippets with language names:
# Ensure snippets have language tags
bkmr add 'content' python,_snip_ --title "Python Snippet"
  1. Verify file type detection:
# In Neovim
:set filetype?

# Should match language ID (python, rust, javascript, etc.)
  1. Check LSP language filtering:
# Test language-specific filtering
python3 scripts/lsp/list_snippets.py --language rust --debug

Template Interpolation Issues

Templates not resolving:

# Start LSP with interpolation explicitly enabled (default)
bkmr lsp

# Or disable if templates conflict with snippet content
bkmr lsp --no-interpolation

Environment variables not expanding:

# Check variable exists
echo $VARIABLE_NAME

# Use fallback values in templates
{{ env("VARIABLE", "default-value") }}

Debug Mode

Enable detailed logging:

# Start LSP with debug logging
RUST_LOG=debug bkmr lsp 2>/tmp/bkmr-lsp.log

# In another terminal, watch logs
tail -f /tmp/bkmr-lsp.log

# Trigger completion in editor, check logs for errors

Common log messages:

  • Received completion request - LSP client requesting completions
  • Filtering snippets for language: rust - Language detection working
  • Found N snippets for language - Snippets being served
  • Template rendering failed - Template syntax errors

Testing LSP Commands

Use the provided test scripts in scripts/lsp/:

# List all snippets
python3 scripts/lsp/list_snippets.py

# List by language
python3 scripts/lsp/list_snippets.py --language rust --debug

# Get specific snippet
python3 scripts/lsp/get_snippet.py 123

# Show available commands
python3 scripts/lsp/show_commands.py

# Test LSP client communication
python3 scripts/lsp/test_lsp_client.py

IntelliJ Plugin Issues

Plugin not connecting:

  1. Verify bkmr is in PATH:
which bkmr
bkmr --version
  1. Check plugin settings:

    • SettingsToolsbkmr
    • Verify executable path
    • Test connection button
  2. Check IDE logs:

    • HelpShow Log in Finder/Explorer
    • Search for "bkmr" or "LSP" errors

Snippets not appearing:

  1. Ensure snippets have _snip_ system tag:
bkmr search -t _snip_ --json | jq '.[] | {id, title, tags}'
  1. Verify language tags match file type:
# For a .rs file, snippets should have "rust" tag
bkmr search -t _snip_,rust

Performance Considerations

Server-Side Filtering

The LSP server performs all filtering server-side for efficiency:

  • Network traffic: Only relevant snippets transmitted to editor
  • Client performance: Minimal processing in editor/IDE
  • Latency: Fast completions even with thousands of snippets

Connection Pooling

  • Database access: Efficient connection pooling via r2d2
  • Stateless operation: Each request independent for reliability
  • Concurrent access: Supports multiple editor instances

Optimization Tips

1. Tag snippets appropriately:

# Good: Specific language tags
bkmr add 'content' rust,async,_snip_ --title "Async Code"

# Avoid: Generic tags that match everything
bkmr add 'content' code,_snip_ --title "Code"

2. Use plain tag when appropriate:

# For literals, use plain to skip LSP processing
bkmr add '${VAR}' plain,_snip_ --title "Literal Variable"

3. Limit snippet size:

  • Keep snippets focused and concise
  • Large snippets slow down completion popup
  • Break complex templates into smaller pieces

Related Projects and Resources

Official Plugins

Testing and Development

LSP Resources

Architecture Notes

The bkmr LSP server is built into the binary and provides:

  • Protocol implementation: tower-lsp for LSP 3.17 specification
  • Service integration: Direct integration with bkmr's service layer
  • Server-side filtering: Efficient language-aware snippet filtering
  • Async operations: tokio runtime for non-blocking I/O
  • Stateless design: Reliable operation across editor restarts
  • Dependency injection: Clean architecture with explicit dependencies

Key Design Decisions:

  • Single binary includes both CLI and LSP functionality
  • No separate server process installation required
  • Same snippet database used by CLI and LSP
  • Templates processed server-side for consistency

Related Pages

Clone this wiki locally