diff options
| author | Ralph Amissah <ralph.amissah@gmail.com> | 2026-05-10 00:04:45 -0400 |
|---|---|---|
| committer | Ralph Amissah <ralph.amissah@gmail.com> | 2026-05-10 00:04:45 -0400 |
| commit | 04bac0f37f652e59401e78a7533f29472bd034dd (patch) | |
| tree | 5c1534b255b4948328c9b8b098d67718719d1dca | |
| parent | grammar: promote editor-note channel marker to named node (diff) | |
queries: add textobjects.scm and indents.scm
textobjects.scm exposes the structure that hosts (Neovim's
nvim-treesitter-textobjects, Helix, Emacs treesit) can bind to keys
like af / if (footnote / inside footnote), ah / ih (heading section),
ab / ib (block body):
@class.outer / @class.inner headings (part and segment)
@function.outer / @function.inner block elements (code, poem,
block, group, table, quote,
pipe-table)
@comment.outer / @comment.inner footnotes and editor notes
@parameter.outer / @parameter.inner links and images
@block.outer / @block.inner paragraphs
@assignment.outer inline-formatting runs and
header fields
@attribute.outer book-index entries
indents.scm anchors top-level structures at column 0 and marks block
elements / header continuations as @indent.align. SiSU markup is
largely flat-indented, so this file is mostly defensive: it stops
auto-indent on <CR> from drifting away from column 0 inside paragraph
text, and lets editors line up subsequent header continuation lines
with the previous one.
(assisted by Claude-Code)
| -rw-r--r-- | queries/indents.scm | 48 | ||||
| -rw-r--r-- | queries/textobjects.scm | 140 |
2 files changed, 188 insertions, 0 deletions
diff --git a/queries/indents.scm b/queries/indents.scm new file mode 100644 index 0000000..aa73af8 --- /dev/null +++ b/queries/indents.scm @@ -0,0 +1,48 @@ +; Indentation queries for SiSU Spine markup. +; +; SiSU markup is largely flat: paragraphs and headings live at column 0, +; block bodies preserve their author-supplied indentation verbatim, and +; nesting is by markers rather than by indent. So indents.scm is mostly a +; no-op - the goal is to ensure that auto-indent on <CR> stays at column 0 +; for normal lines and respects existing indentation inside header +; continuations and blocks. + +; Tree-sitter indent semantics (per nvim-treesitter and treesit): +; @indent.begin - increases indent for the following line +; @indent.end - matches the @indent.begin and decreases indent +; @indent.zero - resets indent to column 0 +; @indent.align - aligns following lines with this node +; @indent.branch - same level as the parent (for else/elif-style joins) + +; Top-level structures live at column 0 - reset to zero on the next line. +(heading_part) @indent.zero +(heading_segment) @indent.zero +(paragraph) @indent.zero +(book_index) @indent.zero +(composite_include) @indent.zero +(page_break) @indent.zero +(horizontal_rule) @indent.zero +(ocn_suppress_open) @indent.zero +(ocn_suppress_close) @indent.zero +(body_comment) @indent.zero + +; Block elements: opening line increases indent for the body, closing +; line returns to zero. Editors that respect this will visually indent +; raw content one step from the delimiter line, which is conventional. +(code_block_curly) @indent.align +(code_block_tic) @indent.align +(poem_block_curly) @indent.align +(poem_block_tic) @indent.align +(block_block_curly) @indent.align +(block_block_tic) @indent.align +(group_block_curly) @indent.align +(group_block_tic) @indent.align +(table_block_curly) @indent.align +(table_block_tic) @indent.align +(quote_block_tic) @indent.align + +; Header continuation lines are indented by two spaces from column 0; +; mark continuations as align so a host that chooses to auto-indent the +; next continuation line matches the previous one. +(header_field) @indent.align +(header_continuation) @indent.align diff --git a/queries/textobjects.scm b/queries/textobjects.scm new file mode 100644 index 0000000..0a82481 --- /dev/null +++ b/queries/textobjects.scm @@ -0,0 +1,140 @@ +; Text-object queries for SiSU Spine markup. +; +; Capture conventions follow nvim-treesitter/textobjects: +; @<thing>.outer -> select including delimiters / surrounding whitespace +; @<thing>.inner -> select content only +; +; Hosts that consume these (Neovim's nvim-treesitter-textobjects, Helix, +; Emacs treesit) bind keys such as `af` / `if` to .outer / .inner. + +; ================================================================= +; Headings (sectioning units) +; ================================================================= +; A whole heading line is a "section header" object. Heading sections +; (the heading plus its body content up to the next heading of equal or +; higher level) are not directly expressible in tree-sitter without +; additional grammar work; hosts can synthesise that from these captures. + +(heading_part) @class.outer +(heading_part + content: (heading_content) @class.inner) + +(heading_segment) @class.outer +(heading_segment + content: (heading_content) @class.inner) + +; ================================================================= +; Block elements (code / poem / block / group / table / quote) +; ================================================================= +; Whole block including delimiters; raw_content is the inner. + +(code_block_curly) @function.outer +(code_block_curly + content: (raw_content) @function.inner) + +(code_block_tic) @function.outer +(code_block_tic + content: (raw_content) @function.inner) + +(poem_block_curly) @function.outer +(poem_block_curly + content: (raw_content) @function.inner) + +(poem_block_tic) @function.outer +(poem_block_tic + content: (raw_content) @function.inner) + +(block_block_curly) @function.outer +(block_block_curly + content: (raw_content) @function.inner) + +(block_block_tic) @function.outer +(block_block_tic + content: (raw_content) @function.inner) + +(group_block_curly) @function.outer +(group_block_curly + content: (raw_content) @function.inner) + +(group_block_tic) @function.outer +(group_block_tic + content: (raw_content) @function.inner) + +(table_block_curly) @function.outer +(table_block_curly + content: (raw_content) @function.inner) + +(table_block_tic) @function.outer +(table_block_tic + content: (raw_content) @function.inner) + +(quote_block_tic) @function.outer +(quote_block_tic + content: (raw_content) @function.inner) + +(pipe_table) @function.outer + +; ================================================================= +; Footnotes and editor notes +; ================================================================= +; Both share the same outer/inner shape; the inner skips the markers and +; closing delimiters. + +(footnote) @comment.outer +(footnote + (_)+ @comment.inner) + +(editor_note) @comment.outer +(editor_note + (_)+ @comment.inner) + +; ================================================================= +; Links and images +; ================================================================= + +(link) @parameter.outer +(link + text: (link_text) @parameter.inner) + +(image) @parameter.outer +(image + spec: (image_spec) @parameter.inner) + +; ================================================================= +; Paragraph / inline-formatting runs +; ================================================================= + +(paragraph) @block.outer +(paragraph + (_)+ @block.inner) + +; Inline formatting pairs - useful as fine-grained text objects. +; The same delimiter character pattern (e.g. `*{` / `}*`) opens and +; closes each, so .inner is everything between them. + +(emphasis) @assignment.outer +(bold) @assignment.outer +(italic) @assignment.outer +(underline) @assignment.outer +(citation_mark) @assignment.outer +(superscript) @assignment.outer +(subscript) @assignment.outer +(inserted) @assignment.outer +(strikethrough) @assignment.outer +(monospace_inline) @assignment.outer + +; ================================================================= +; Book index entries +; ================================================================= + +(book_index) @attribute.outer +(book_index + (index_content) @attribute.inner) + +; ================================================================= +; Header fields +; ================================================================= + +(header_field) @assignment.outer +(header_field + value: (header_value) @assignment.inner) |
