org.nvim

Code and notes (code)

Stability: experimental (org-extensions-stability)

code connects the code you edit with your org notes: capture a selection with a link back to it, link to functions by name, keep an org file per git repository, clock in by git branch and see the repository's TODO comments in the agenda.

require("org").setup({ extensions = { code = {} } })

Keys (global, under <prefix>j)

  <prefix>jc  code_capture (Normal and Visual): capture the selection as a
              src block, with a link back to it and the git branch
  <prefix>jl  code_store_link: store a code: link to the symbol at the
              cursor
  <prefix>jp  project_open: open the repository's org file
  <prefix>jn  project_capture: capture a task into it
  <prefix>ja  project_agenda: agenda of that file and the code TODOs
  <prefix>jt  code_todos: the repository's TODO comments in an agenda
  <prefix>jb  code_link_branch (org buffers): set the heading's :BRANCH:
              to the current git branch

Capturing code

code_capture works in any file that is not org. In Visual mode the selection becomes a src block in the language of the buffer's filetype (lua, python, js for javascript, C++ for cpp...; languages adds or changes names); in Normal mode there is no block. The note goes to the repository's project file (below), under project_headline. Outside a repository it goes to fallback_target (default: default_notes_file), under fallback_headline (default: none, at the end of the file); the copy in the capture menu does the same. The template is capture_template; these expansions fill it:

  %(code-block)      the selection as a #+begin_src LANG block ("" in
                     Normal mode); lines starting with * or #+ are
                     escaped with a comma
  %(code-link)       [[code:PATH::SYMBOL][SYMBOL (file.lua)]], or
                     ::LINE when no symbol is found
  %(code-file-link)  [[file:PATH::LINE][file.lua:LINE]]
  %(code-file)       the path relative to the repository
  %(code-line)       the line, or first-last of a selection
  %(code-lang)       the Babel language
  %(code-symbol)     the function or class around the cursor
  %(git-repo)        the repository's directory name
  %(git-branch)      the checked out branch ("" when detached)
  %(git-commit)      the short hash of HEAD (needs git)
  %(git-info)        repo on branch @ commit

They work in every capture template started from a code buffer, so you can use them in your own capture.templates. The extension also adds a copy of capture_template to the capture menu under template_key ("k") when that key is free (template_key = false to not add it).

Put %(code-block) at the start of a line: an entry's body is not indented for it. (The expansions are registered in require("org.capture").expansions, name -> function(ctx), which other code can add to the same way.)

[[code:src/app.lua::M.setup]] opens the file and jumps to the symbol, found with the language server's document symbols (textDocument/ documentSymbol; a server that is still starting for the file, from vim.lsp.enable() or a FileType autocmd, or a running one that serves the filetype is waited for up to lsp_timeout ms, then up to lsp_timeout ms for its answer), else treesitter definitions, else a text search for function NAME, def NAME, NAME =, then the word. A dotted name (Class.method) matches the symbol in its container. code:PATH::42 jumps to line 42. A relative path is looked up from the git root of the org file, then of the working directory, then from the org file's directory, the working directory and the repositories of recent code buffers.

store_link (and code_store_link) in a code buffer stores a code: link to the function or class around the cursor, from the language server or treesitter, else to the line. link_path = "relative" writes the path relative to the git root. Exported, a code: link is its description in code style.

With the lsp extension (org-extensions-lsp), go to definition on a code: link jumps to its target, and with transclusion (org-extensions-transclusion) #+transclude: [[code:src/app.lua::M.setup]] shows the whole definition in a src block. Both find the symbol with treesitter, else the text search, without asking a language server (and without loading the file into a buffer).

The treesitter lookup knows functions, methods, classes, structs, enums, interfaces, traits, modules and type definitions in the usual grammars (checked with Lua, Python, JavaScript, TypeScript, Rust and C), and names bound to a function (const f = () => {}, M.f = function() end). A method is linked as Class.method (Type.method in a Rust impl). Go methods are linked by their name only.

Per-project org files

project_file names the org file of a repository: a path relative to its root (default .org/tasks.org), or one with <org_directory>, ${repo} and ${root} replaced, such as "<org_directory>/projects/${repo}.org" to keep notes out of the repository, or a function (root, repo) -> path. The file is created with project_file_header when first used. The repository is the one of the current buffer, else of the last code buffer you were in, else of the working directory.

project_agenda shows project_agenda_blocks restricted to the project file: by default the week, its TODOs and the code TODOs.

Code TODOs

The code_todos agenda block type lists comments with todo_keywords (TODO, FIXME, HACK, XXX, BUG) after a comment marker (--, //, #, /*, ;...):

-- TODO: handle errors
-- FIXME(bob): wrong value
-- TODO(org:3f2a...): translate the greetings

They are found with git grep (tracked and untracked files, binary files skipped), else rg, else by reading at most todo_max_files files; org files are skipped (todo_exclude). The search runs in the background: the first time the block shows "(scanning...)" and the agenda is redrawn when it is done; later the block shows the last result at once and is redrawn if a new search finds something else. It stops after todo_max_items comments (the count then reads "500+"). <CR>, <Tab> and <Space> on an item open the code line; agenda commands that change org entries (todo state, schedule, tags, clock, refile, archive...) say that they don't apply to a code TODO. A comment naming a heading's ID (TODO(org:ID), see todo_link_pattern) is listed under an item for that heading. Use the block in custom commands too:

agenda = { custom_commands = {
  c = { description = "Code TODOs", type = "code_todos", root = "~/app" },
} }

Without root the block uses the current repository.

Clocking by branch

With branch_clock = true, switching the git branch of the file you work in (noticed on BufEnter, FocusGained and DirChanged) clocks into the heading whose :BRANCH: property is the branch name, or else whose ID, CUSTOM_ID or TICKET property, or title, holds the ticket that branch_ticket_pattern (default "(%u+%-%d+)", ABC-123) takes from the name. Headings are looked for in the project file and the agenda files. The branch found at startup only clocks in with branch_clock_on_start. code_link_branch sets :BRANCH: on the heading at point. The branch is read from .git/HEAD, so no process runs on BufEnter.

Options

  capture_template      template of code_capture (target "project")
  template_key          key of its copy in the capture menu ("k")
  languages             filetype -> Babel language ({})
  link_path             "absolute" or "relative" ("absolute")
  store_links           store code: links in code buffers (true)
  store_symbol          link to the symbol, not the line (true)
  lsp_timeout           ms to wait for a language server (1000)
  exclude_filetypes     filetypes that are not code buffers
  project_file          ".org/tasks.org"
  project_file_header   "#+title: ${repo}\n"
  project_headline      "Tasks"
  fallback_target       code captures outside a repository (nil:
                        default_notes_file)
  fallback_headline     their headline (nil: the end of the file)
  project_template      template of project_capture
  project_agenda_blocks blocks of project_agenda
  branch_clock          clock in by branch (false)
  branch_clock_on_start also for the branch at startup (false)
  branch_ticket_pattern Lua pattern of the ticket ("(%u+%-%d+)")
  todo_keywords         { "TODO", "FIXME", "HACK", "XXX", "BUG" }
  todo_link_pattern     "^org:%s*(.-)%s*$"
  todo_require_comment  only after a comment marker (true)
  todo_exclude          Lua patterns of paths to skip
  todo_scanner          "auto", "git", "rg" or "lua"
  todo_max_files        2000
  todo_max_items        500
  todo_group_prefix     before the category of grouped TODOs ("↳ ")