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>jccode_capture (Normal and Visual): capture the selection as a src block, with a link back to it and the git branch<prefix>jlcode_store_link: store acode:link to the symbol at the cursor<prefix>jpproject_open: open the repository's org file<prefix>jnproject_capture: capture a task into it<prefix>japroject_agenda: agenda of that file and the code TODOs<prefix>jtcode_todos: the repository's TODO comments in an agenda<prefix>jbcode_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 LANGblock ("" in Normal mode); lines starting with*or#+are escaped with a comma%(code-link)[[code:PATH::SYMBOL][SYMBOL (file.lua)]], or::LINEwhen 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, orfirst-lastof 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 (needsgit)%(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: links
[[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_templatetemplate ofcode_capture(target "project")template_keykey of its copy in the capture menu ("k")languagesfiletype -> Babel language ({})link_path"absolute" or "relative" ("absolute")store_linksstorecode:links in code buffers (true)store_symbollink to the symbol, not the line (true)lsp_timeoutms to wait for a language server (1000)exclude_filetypesfiletypes that are not code buffersproject_file".org/tasks.org"project_file_header"#+title: ${repo}\n"project_headline"Tasks"fallback_targetcode captures outside a repository (nil:default_notes_file)fallback_headlinetheir headline (nil: the end of the file)project_templatetemplate ofproject_captureproject_agenda_blocksblocks ofproject_agendabranch_clockclock in by branch (false)branch_clock_on_startalso for the branch at startup (false)branch_ticket_patternLua pattern of the ticket ("(%u+%-%d+)")todo_keywords{ "TODO", "FIXME", "HACK", "XXX", "BUG" }todo_link_pattern"^org:%s*(.-)%s*$"todo_require_commentonly after a comment marker (true)todo_excludeLua patterns of paths to skiptodo_scanner"auto", "git", "rg" or "lua"todo_max_files2000todo_max_items500todo_group_prefixbefore the category of grouped TODOs ("↳ ")