Skip to content

Sarctiann/mojo.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

194 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ”ฅ mojo.nvim

Neovim integration for Mojo.

Centralizes filetype detection, Treesitter, LSP, formatting, and environment activationโ€”designed so each piece can be swapped when Modular ships official tooling.

mojo.nvim demo

Index

What it provides

  • .mojo, .mojoc, and ๐Ÿ”ฅ filetype detection
  • Treesitter parser registration for Mojo
  • Environment helpers for Pixi, virtualenv, and manual SDK paths
  • LSP and formatter integration (native vim.lsp.config / conform.nvim)
  • Terminal environment auto-activation
  • Completion support (nvim-cmp / blink.cmp) with keywords, builtins, types, and snippets
  • lualine.nvim statusline integration with SDK version display
  • MojoVersion component for non-lualine statuslines
  • Debugging support โ€” nvim-dap (mojo-lldb-dap) + native terminal (mojo-lldb)
  • Run current Mojo file with :Mojo run / :Mojo dedicated
  • LSP lifecycle management (:Mojo menu, :Mojo restart, :Mojo stop, :Mojo refresh)
  • 4-space indentation for Mojo files
  • LazyVim, AstroNvim, NvChad, and kickstart.nvim adapter helpers
  • EmmyLua type annotations (module: Mojo-lang)
  • Adapter pattern for every integration โ€” swap any backend without changing config

Features

Filetype

.mojo, .mojoc, and ๐Ÿ”ฅ files are automatically recognized as mojo filetype. The plugin adds these to Neovim's filetype detection and triggers environment activation for each Mojo buffer.

Environment

Detects Pixi (pixi.toml / .pixi/) and virtualenv (.venv/) projects and activates them for LSP, formatting, and terminal buffers transparently. Also supports manual SDK path override via config.sdk_path or the $MOJO_SDK_PATH environment variable.

Treesitter

Registers the self-hosted Mojo parser grammar with nvim-treesitter. The grammar files live in tree-sitter/mojo/ โ€” no external parser repo required. Automatically checks for grammar updates and recompiles when needed, with :Mojo rebuild (or :MojoRebuildParser when commands.spread = true) for manual rebuilds.

LSP

Configures mojo-lsp-server via Neovim's native vim.lsp.config / vim.lsp.enable (0.11+) with environment-aware binary resolution (finds the binary in the active Pixi/venv environment). Supports custom root markers for project detection. No nvim-lspconfig dependency.

Format

Configures mojo format via conform.nvim with environment-aware binary resolution. Provides consistent formatting across the editor.

Terminal

Auto-activates the project environment in new shell terminal buffers. Detects shell terminals and applies the correct activation command before they start.

Completion

Provides keyword autocompletion for Mojo-specific keywords (54), builtin functions (42), standard library types (46), and snippets (13). Integrates with nvim-cmp and blink.cmp via dedicated adapters. Completion is context-aware โ€” it defers to LSP completions after . and :.

Run

Execute the current Mojo file with :Mojo run (opens a terminal split) or :Mojo dedicated (opens or reuses a dedicated terminal buffer). Both commands resolve the mojo binary through the active environment and display a winbar with close instructions. If commands.spread = true, the individual :MojoRun and :MojoRunDedicated commands are also available.

Run Mojo programs

Debug

Debug the current Mojo file with :Mojo debug (auto-selects backend), :Mojo debug-native (terminal via mojo-lldb), or :Mojo debug-dap (nvim-dap). The auto backend prefers DAP when mojo-lldb-dap is available, falling back to native mojo-lldb. Works with both pixi and uv projects.

Native debug (:MojoDebugNative) opens a terminal with LLDB keymaps: r (run), n (next), s (step), c (continue), v (frame variable), b (sync breakpoints), q/<Esc>/<CR> (close). Editor breakpoints placed via nvim-dap (<leader>db) or the :Mojo debug toggle_bp command are synced to LLDB on launch, save, and manual sync.

Debug Mojo programs

DAP debug (:MojoDebugDap) requires nvim-dap and provides four launch configurations: Debug Mojo File, Debug Mojo File (with args), Debug Binary, and Attach to Process.

macOS note: The plugin automatically removes quarantine and re-signs debug adapter binaries (mojo-lldb-dap / lldb-dap) on macOS. Both native and DAP debugging work on macOS with pixi and uv projects.

Indentation

Sets 4-space indentation for Mojo files (matching Python-style conventions) via ftplugin/mojo.lua.

Commands

Commands are configured via the commands option. By default only the master :Mojo command is created (commands.spread = false). Set commands.spread = true to also register the individual commands.

Command Description
:Mojo {subcommand} Master command with tab-completion (see below)
:MojoMenu Open floating actions menu (restart/stop LSP, refresh SDK)
:MojoRefreshSDK Clear SDK cache and re-detect environment
:MojoRestartLSP Restart Mojo LSP server
:MojoStopLSP Stop Mojo LSP server
:MojoRun Run current .mojo file in a terminal split
:MojoRunDedicated Run current .mojo file in a dedicated terminal buffer
:MojoDebug Debug current .mojo file (auto-selects best backend)
:MojoDebugNative Debug via mojo-lldb in terminal (dbg_native)
:MojoDebugDap Debug via nvim-dap + mojo-lldb-dap (dbg_dap)
:MojoRebuildParser Manually rebuild the self-hosted tree-sitter Mojo parser

:Mojo subcommands: menu, run, dedicated, debug, debug-native, debug-dap, restart, stop, refresh, rebuild, keymaps, help. Press <Tab> after :Mojo to cycle through them.

Keymaps

Default keymaps for Mojo buffers. These can be overridden or disabled per-keymap via the keymaps config section โ€” set any entry to false to disable it, or change the LHS string to rebind.

Keymap Modes Context Description
K Normal FileType mojo Signature help inside parens, hover otherwise
<leader>ca Normal, Visual FileType mojo Code action (vim.lsp.buf.code_action)
q, <Esc>, <CR> Normal, Terminal :MojoRun buffer Close run terminal
r n s c v Normal Debug terminal LLDB: run, next, step, continue, frame var
b Normal Debug terminal Re-sync breakpoints from editor signs

The :MojoMenu floating window also has numbered keymaps 1, 2, 3 mapped to each action, and q / <Esc> to close.

Statusline

Shows environment name, SDK version, and tool status indicators (LSP, formatter, debugger, diagnostics). Indicators are clickable โ€” click on any to open the :MojoMenu floating window with restart/stop LSP and refresh SDK actions.

Status icons: ๓ฐ„ฌ (active/green), โ—‹ (inactive/yellow), ๓ฐ…– (error/red).

Highlight groups: MojoIcon, MojoText, MojoSep, MojoGood, MojoNeutral, MojoWarn, MojoErr.

Configure per-indicator with statusline options or use require("mojo.status").display() for non-lualine statuslines.

Lualine integration

The lualine adapter auto-injects the Mojo component into lualine_x. If lualine loads after mojo.nvim, the adapter wraps lualine.setup to inject on first call.

require("lualine").setup({
  sections = {
    lualine_x = {
      -- The Mojo component is injected here automatically.
      -- Add your own sections around it.
    },
  },
})

Icon and color options:

require("mojo").setup({
  statusline = {
    enabled = true,
    icon = "๐Ÿ”ฅ",              -- change the icon shown in the statusline
    color = "#ff9e64",        -- MojoText / MojoSep color
    icon_color = "#ff6f00",   -- MojoIcon color
    show_env_name = true,     -- show pixi/venv environment name
    show_sdk_version = true,  -- show SDK version (e.g. "1.0.0b2")
    show_lsp = true,          -- show LSP status indicator
    show_fmt = true,          -- show formatter status indicator
    show_dbg = true,          -- show debugger status indicator
    show_diag = true,         -- show diagnostic count
    clickable = true,         -- click to open :MojoMenu
  },
})

You can override the icon with any string, e.g. icon = "๓ฑตฎ" for a Nerd Font alternative.

Installation

lazy.nvim
{
  "Sarctiann/mojo.nvim",
  main = "mojo",
  opts = {},
}
packer.nvim
use {
  "Sarctiann/mojo.nvim",
  config = function()
    require("mojo").setup({})
  end,
}
mini.deps
local add = MiniDeps.add

add({
  source = "Sarctiann/mojo.nvim",
  depends = {},
})

require("mojo").setup({})
vim-plug
Plug 'Sarctiann/mojo.nvim'

lua << EOF
require("mojo").setup({})
EOF
rocks.nvim

Run :Rocks install mojo.nvim then add to your init.lua:

require("mojo").setup({})

Setup

require("mojo").setup({
  verbose = true, -- writes mojo-debug.log to cwd
})

All features are enabled by default. Pass enabled = false to disable any feature.

Note: opts (lazy.nvim) and setup() accept the same config table.

Configuration

All options and their defaults
{
  filetype = { enabled = true },
  terminal = {
    enabled = true,
    auto_activate = true,
    delay_ms = 200,
  },
  treesitter = {
    enabled = true,
    adapter = nil, -- custom adapter function
  },
  lsp = {
    enabled = true,
    root_markers = { "pixi.toml", "pyproject.toml", ".pixi", ".venv" },
    include_dirs = nil, -- extra include directories
    filter_docstring_diagnostics = nil, -- filter diagnostics in docstrings
    adapter = nil, -- custom adapter function
  },
  format = {
    enabled = true,
    formatter_name = "mojo",
    adapter = nil, -- custom adapter function
  },
  completion = {
    enabled = true,
    adapter = nil, -- custom adapter function
  },
  keymaps = {
    enabled = true,
    signature_help = "K", -- LHS string, or false to disable
    code_action = "<leader>ca", -- LHS string, or false to disable
  },
  commands = {
    master = true, -- creates :Mojo {subcommand} with tab-completion
    spread = false, -- creates individual :MojoMenu, :MojoRun, etc.
  },
  sdk_path = nil, -- or "/path/to/mojo/sdk"
  statusline = {
    enabled = true,
    icon = "๐Ÿ”ฅ",
    show_env_name = true,
    show_sdk_version = true,
    show_lsp = true,
    show_dbg = true,
    show_fmt = true,
    show_diag = true,
    clickable = true,
    colored = true,
    color = "#ff9e64",
    icon_color = "#ff6f00",
    adapter = nil, -- custom adapter function
  },
  debug = {
    enabled = true,
    auto_scroll = true,
    auto_backend = nil, -- nil = auto, "native", "dap"
    keymaps = {
      toggle_breakpoint = "<leader>db",
      clear_breakpoints = "<leader>dB",
      start = "<leader>dr",
      continue = "<leader>dc",
      step_into = "<leader>ds",
      step_over = "<leader>dn",
      step_out = "<leader>do",
      stop = "<leader>dt",
    },
    search_for = { -- searched in order; user can extend for custom envs
      { name = "lldb-dap",       role = "dap" },
      { name = "_mojo-lldb-dap", role = "dap" },
      { name = "mojo-lldb-dap",  role = "dap" },
      { name = "mojo-lldb",      role = "native" },
      { name = "lldb",           role = "native" },
    },
    adapter = nil, -- custom adapter function
  },
  verbose = false,
  hooks = {},
}

Integrations

All integrations are optional โ€” the plugin degrades gracefully if the backend is not installed.

Integration Backend Adapter
LSP native vim.lsp.config lua/mojo/adapters/lspconfig.lua
Formatting conform.nvim lua/mojo/adapters/conform.lua
Treesitter nvim-treesitter lua/mojo/adapters/treesitter.lua
Completion nvim-cmp / blink.cmp lua/mojo/adapters/nvim-cmp.lua / lua/mojo/adapters/blink.lua
Debugging nvim-dap lua/mojo/adapters/dap.lua
Statusline lualine.nvim lua/mojo/adapters/lualine.lua
Distribution LazyVim lua/mojo/adapters/lazyvim.lua

AstroNvim, NvChad, and kickstart.nvim work by just adding { "Sarctiann/mojo.nvim" } to your plugins โ€” no adapter needed.

Each adapter can be replaced via its feature's adapter config field for custom behavior.

Notes

  • The plugin does not ship the Mojo LSP binary or official toolchain
  • Debugging is enabled by default; both native and DAP backends degrade gracefully when binaries not found. On macOS, the plugin automatically handles quarantine and code-signing for debug adapter binaries
  • When verbose = true, logs are written to mojo-debug.log in the current working directory
  • The plugin auto-activates Pixi or venv project environments before Mojo LSP startup and in terminal buffers
  • Treesitter is isolated behind lua/mojo/treesitter.lua. The parser grammar is self-hosted in tree-sitter/mojo/. The plugin auto-rebuilds the parser when the grammar source changes; :MojoRebuildParser is available for manual rebuilds
  • Mojo files use 4-space indentation (configured via ftplugin/mojo.lua)

Environment detection chain

  1. Manual SDK path โ€” config.sdk_path or $MOJO_SDK_PATH env var
  2. Derived environment โ€” .derived/bin/ directory
  3. Pixi environment โ€” pixi.toml or .pixi/ directory
  4. Virtual environment โ€” .venv/bin/
  5. No environment โ€” falls back to system PATH

Detection results are cached per project root. Clear cache with :MojoRefreshSDK.

LSP crash recovery

The plugin tracks LSP server exits and applies exponential backoff on restart (0s โ†’ 5s โ†’ 30s โ†’ 60s) with a cap at 3 restarts. The counter resets after 30 seconds of stable running.

Hooks

The plugin exposes a hooks system for extensibility. Currently available hook:

  • resolve_root(path, markers) โ€” override root directory detection (defaults to vim.fs.root())

Adapter overrides

Every feature accepts an optional adapter field in its config table. When set, the plugin calls adapter(opts) instead of its default adapter, allowing complete swapping of any backend without changing the rest of the config.

Tools that work without Mojo-specific config

  • telescope.nvim โ€” picks up .mojo/.๐Ÿ”ฅ files in standard pickers
  • trouble.nvim โ€” displays diagnostics from mojo-lsp-server automatically
  • nvim-cmp / blink.cmp โ€” receives LSP completions from mojo-lsp-server via the nvim_lsp source
  • which-key.nvim โ€” discovers any Mojo-related keymaps you define

Adapter-based integration for other tools is tracked in docs/TODO.md.

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

10 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors