277 lines
8.0 KiB
Markdown
277 lines
8.0 KiB
Markdown
# Orkmode
|
|
|
|
A standalone org-mode implementation for KDE, built with Rust (parser/core) and C++ (Qt UI).
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
orkmode/
|
|
├── Cargo.toml # Workspace root
|
|
├── crates/
|
|
│ ├── org-ast/ # AST type definitions
|
|
│ │ └── src/
|
|
│ │ ├── lib.rs # Module exports
|
|
│ │ ├── document.rs # Document, settings
|
|
│ │ ├── headline.rs # Section, Headline, Planning
|
|
│ │ ├── elements.rs # Block elements (paragraph, list, table, etc.)
|
|
│ │ ├── objects.rs # Inline objects (markup, links, etc.)
|
|
│ │ ├── timestamp.rs # Timestamp types
|
|
│ │ └── span.rs # Source location tracking
|
|
│ ├── org-parser/ # Tree-sitter based parser
|
|
│ │ ├── build.rs # Compiles tree-sitter-org grammar
|
|
│ │ ├── tree-sitter-org/ # Git submodule: nvim-orgmode/tree-sitter-org
|
|
│ │ └── src/
|
|
│ │ ├── lib.rs # Parser API
|
|
│ │ ├── convert.rs # Tree-sitter → AST conversion
|
|
│ │ ├── ts.rs # Tree-sitter language bindings
|
|
│ │ └── error.rs # Error types
|
|
│ ├── ork-cli/ # Command-line interface (9P client)
|
|
│ │ └── src/
|
|
│ │ ├── main.rs # CLI commands (base + friendly verbs)
|
|
│ │ └── client.rs # 9P2000 client
|
|
│ └── ork-server/ # 9P server exposing org files
|
|
│ └── src/
|
|
│ ├── bin/ork-server.rs # Server entry point
|
|
│ ├── state.rs # Document state, section ops, ctl verbs
|
|
│ ├── namespace.rs # 9P namespace (docs, sections, agenda, query)
|
|
│ ├── p9.rs # 9P protocol handling
|
|
│ └── virtfs.rs # Virtual filesystem tree
|
|
└── ui/ # (future) Qt/QML UI
|
|
```
|
|
|
|
## Features
|
|
|
|
### Supported Org Syntax
|
|
|
|
- [x] Headlines with nesting levels
|
|
- [x] TODO keywords (customizable via #+TODO:)
|
|
- [x] Priority cookies ([#A], [#B], [#C])
|
|
- [x] Tags
|
|
- [x] Property drawers
|
|
- [x] Planning lines (SCHEDULED, DEADLINE, CLOSED)
|
|
- [x] Timestamps (active, inactive, ranges, repeaters)
|
|
- [x] Paragraphs
|
|
- [x] Plain lists (unordered, ordered, checkboxes)
|
|
- [x] Source blocks
|
|
- [x] Tables
|
|
- [x] Drawers
|
|
- [x] Keywords/directives
|
|
- [x] Comments
|
|
- [x] LaTeX environments
|
|
- [ ] Inline markup (bold, italic, etc.) - AST types ready, parsing TODO
|
|
- [ ] Links - AST types ready, parsing TODO
|
|
- [ ] Footnotes
|
|
- [ ] Citations
|
|
|
|
### Document Settings
|
|
|
|
Parses common #+KEYWORD directives:
|
|
- TITLE, AUTHOR, EMAIL, DATE
|
|
- TODO, SEQ_TODO, TYP_TODO
|
|
- PROPERTY, STARTUP, OPTIONS
|
|
- FILETAGS, CATEGORY, ARCHIVE
|
|
|
|
## Building
|
|
|
|
### Prerequisites
|
|
|
|
- Rust 1.70+
|
|
- C compiler (for tree-sitter grammar)
|
|
|
|
### Build
|
|
|
|
```bash
|
|
# Build all crates
|
|
cargo build
|
|
|
|
# Run tests
|
|
cargo test
|
|
|
|
# Build release
|
|
cargo build --release
|
|
```
|
|
|
|
## Usage
|
|
|
|
### CLI (ork)
|
|
|
|
The `ork` command operates on org directories or individual files. All commands default to
|
|
the current directory (`.`) when no path is specified. Use `-R` for recursive subdirectory scanning.
|
|
|
|
```bash
|
|
# List org files
|
|
ork files # Current directory
|
|
ork files ~/org # Specific directory
|
|
ork files -R . # Recursive
|
|
|
|
# Document info (aggregated across all files)
|
|
ork info # Current directory
|
|
ork info ~/org -R # Recursive
|
|
ork info file.org # Single file
|
|
ork info --json # JSON output
|
|
|
|
# List sections
|
|
ork sections # All sections in cwd
|
|
ork sections --todos-only # Only TODOs
|
|
ork sections --tag work # Filter by tag
|
|
ork sections --depth 2 # Max depth
|
|
ork sections -R -j # Recursive, JSON
|
|
|
|
# Query sections
|
|
ork query "tag:work" # By tag
|
|
ork query "todo:TODO" # By TODO state
|
|
ork query "priority:A" # By priority
|
|
ork query "title:pattern" # By title
|
|
ork query "file:notes" # By filename
|
|
ork query "done:false" -j # JSON output
|
|
|
|
# Get specific section by ID or title
|
|
ork get "my-task-id" # Searches all files
|
|
ork get "My Task" -j # JSON output
|
|
ork get "intro" ~/org -R # In specific path
|
|
|
|
# List tags with counts
|
|
ork tags # All tags in cwd
|
|
ork tags -R --json # Recursive, JSON
|
|
|
|
# Export as JSON
|
|
ork export --pretty # All documents
|
|
ork export --section my-id # Specific section
|
|
|
|
# Add section
|
|
ork add "New Task" file.org # To specific file
|
|
ork add "New Task" --file notes.org # When path is directory
|
|
ork add "New Task" file.org --todo TODO --priority A --tags "work,urgent"
|
|
ork add "New Task" file.org -n # Dry run
|
|
|
|
# Toggle TODO state
|
|
ork toggle "task-id" # Searches all files
|
|
ork toggle "task-id" -n # Dry run
|
|
|
|
# Agenda view
|
|
ork agenda # Current directory
|
|
ork agenda ~/org -R # Recursive
|
|
ork agenda --days 14 --json # 14 days, JSON
|
|
|
|
# Validate syntax
|
|
ork check # Current directory
|
|
ork check -R --json # Recursive, JSON
|
|
```
|
|
|
|
### Rust API
|
|
|
|
```rust
|
|
use org_parser::Parser;
|
|
|
|
let mut parser = Parser::new();
|
|
let doc = parser.parse(r#"
|
|
#+TITLE: My Document
|
|
#+TODO: TODO WAITING | DONE
|
|
|
|
* TODO [#A] First Task :work:
|
|
DEADLINE: <2025-01-15 Wed 14:00>
|
|
|
|
Some description.
|
|
|
|
** Subtask
|
|
- [ ] Item 1
|
|
- [X] Item 2
|
|
"#).unwrap();
|
|
|
|
// Access document properties
|
|
println!("Title: {:?}", doc.title());
|
|
println!("Sections: {}", doc.sections.len());
|
|
println!("TODOs: {}", doc.todos().count());
|
|
|
|
// Iterate sections
|
|
for section in doc.all_sections() {
|
|
println!("{} {}",
|
|
"*".repeat(section.level() as usize),
|
|
section.headline.title_text()
|
|
);
|
|
}
|
|
```
|
|
|
|
### 9P server & client
|
|
|
|
Serve a directory of org files over 9P, then act on it with `ork`:
|
|
|
|
```sh
|
|
# Start the server (unix socket or tcp)
|
|
ork-server -a unix:///tmp/ork.sock -d ~/org &
|
|
|
|
# Point the client at it (or pass -a on each call)
|
|
export ORK_ADDR=unix:///tmp/ork.sock
|
|
|
|
ork docs # list documents
|
|
ork tree work # outline of work.org
|
|
ork todos # TODO items across all docs
|
|
ork set work my-task keyword DONE
|
|
ork refile work my-task archive --parent done-items
|
|
ork query 'tag:work+todo:TODO'
|
|
```
|
|
|
|
Any 9P client works too (e.g. plan9port `9p`); the server owns all org
|
|
logic and reads/writes the real `.org` files.
|
|
|
|
## Architecture
|
|
|
|
### Parser Design
|
|
|
|
The parser uses [tree-sitter](https://tree-sitter.github.io/) with the
|
|
[nvim-orgmode/tree-sitter-org](https://github.com/nvim-orgmode/tree-sitter-org) grammar.
|
|
|
|
Benefits:
|
|
- **Incremental parsing**: Only re-parses changed regions
|
|
- **Error recovery**: Produces partial AST even with syntax errors
|
|
- **Performance**: O(n) parsing, minimal memory allocation
|
|
- **Battle-tested**: Used by Neovim's org-mode plugin
|
|
|
|
### AST Design
|
|
|
|
The AST follows the [org-element specification](https://orgmode.org/worg/dev/org-syntax.html):
|
|
|
|
- **Document**: Root node with settings and sections
|
|
- **Section**: Headline + content + children
|
|
- **Elements**: Block-level constructs (paragraphs, lists, blocks)
|
|
- **Objects**: Inline constructs (markup, links, timestamps)
|
|
|
|
All nodes include source span information for editor integration.
|
|
|
|
## Roadmap
|
|
|
|
### Phase 1: Parser (Current)
|
|
- [x] Tree-sitter integration
|
|
- [x] Core AST types
|
|
- [x] Headline/section parsing
|
|
- [x] Block element parsing
|
|
- [ ] Complete inline object parsing
|
|
|
|
### Phase 2: Qt UI
|
|
- [ ] Document rendering (read-only)
|
|
- [ ] Syntax highlighting
|
|
- [ ] Folding/cycling
|
|
- [ ] Navigation
|
|
|
|
### Phase 3: Editing
|
|
- [ ] Inline editing
|
|
- [ ] TODO state cycling
|
|
- [ ] Tag editing
|
|
- [ ] Property editing
|
|
|
|
### Phase 4: Agenda
|
|
- [ ] Basic agenda view
|
|
- [ ] Day/week/month views
|
|
- [ ] Filtering
|
|
- [ ] Custom views
|
|
|
|
### Phase 5: Advanced Features
|
|
- [ ] Code block execution
|
|
- [ ] Diagram rendering (mermaid, plantuml)
|
|
- [ ] Capture templates
|
|
- [ ] Clock/time tracking
|
|
|
|
## License
|
|
|
|
GPL-3.0-or-later
|