phase 0: scaffold workspace, plugin layout, and CI
Lay down the empty repo skeleton defined in SPEC.md §5 so subsequent phases have somewhere to land: - Cargo workspace (resolver v2, edition 2021, MSRV 1.83) with nuwiki-core, nuwiki-lsp, nuwiki-ls — dep direction matches §6.2. - Vim/Neovim plugin directory layout (plugin, lua, autoload, ftdetect, ftplugin, syntax, doc, scripts) with header-only stubs. - .gitea/workflows/ci.yaml running fmt, clippy -D warnings, and tests pinned to Rust 1.83. - README, dual MIT/Apache-2.0 license texts, .gitignore. Verified: cargo check / fmt --check / clippy / test all clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
RUST_BACKTRACE: short
|
||||||
|
RUSTFLAGS: -D warnings
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fmt:
|
||||||
|
name: cargo fmt --check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@1.83
|
||||||
|
with:
|
||||||
|
components: rustfmt
|
||||||
|
- run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: cargo clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@1.83
|
||||||
|
with:
|
||||||
|
components: clippy
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-clippy-
|
||||||
|
- run: cargo clippy --workspace --all-targets -- -D warnings
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: cargo test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@1.83
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-test-
|
||||||
|
- run: cargo test --workspace --all-targets
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# Rust build artifacts
|
||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# Plugin runtime: downloaded LSP binary lives here (see SPEC.md §7.2)
|
||||||
|
/bin
|
||||||
|
|
||||||
|
# Editor / OS noise
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Vim help tags (regenerated by :helptags)
|
||||||
|
doc/tags
|
||||||
Generated
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nuwiki-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nuwiki-ls"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nuwiki-lsp",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nuwiki-lsp"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nuwiki-core",
|
||||||
|
]
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = [
|
||||||
|
"crates/nuwiki-core",
|
||||||
|
"crates/nuwiki-lsp",
|
||||||
|
"crates/nuwiki-ls",
|
||||||
|
]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.83"
|
||||||
|
authors = ["Gabriel Fróes Franco <gffranco@gmail.com>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
repository = "https://code.gfran.co/gffranco/nuwiki"
|
||||||
|
homepage = "https://code.gfran.co/gffranco/nuwiki"
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
unsafe_code = "forbid"
|
||||||
|
|
||||||
|
[workspace.lints.clippy]
|
||||||
|
all = { level = "warn", priority = -1 }
|
||||||
+201
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Support. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or support.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2026 Gabriel Fróes Franco
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Gabriel Fróes Franco
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# nuwiki
|
||||||
|
|
||||||
|
A Vim/Neovim plugin providing full vimwiki syntax support, implemented as a
|
||||||
|
Rust-based language server (LSP).
|
||||||
|
|
||||||
|
> **Status:** Phase 0 — scaffolding. Not yet functional.
|
||||||
|
|
||||||
|
See [`SPEC.md`](./SPEC.md) for the full project specification.
|
||||||
|
|
||||||
|
## Repository layout
|
||||||
|
|
||||||
|
```
|
||||||
|
nuwiki/
|
||||||
|
├── Cargo.toml workspace root
|
||||||
|
├── crates/
|
||||||
|
│ ├── nuwiki-core/ parser, AST, renderer (no editor deps)
|
||||||
|
│ ├── nuwiki-lsp/ LSP protocol bridge
|
||||||
|
│ └── nuwiki-ls/ binary that speaks LSP over stdio
|
||||||
|
├── plugin/ universal Vim/Neovim entry point
|
||||||
|
├── lua/nuwiki/ Neovim Lua glue
|
||||||
|
├── autoload/nuwiki/ Vim VimL glue
|
||||||
|
├── ftdetect/ filetype detection for .wiki
|
||||||
|
├── ftplugin/ per-buffer filetype settings
|
||||||
|
├── syntax/ static fallback highlighting
|
||||||
|
├── doc/ `:h nuwiki`
|
||||||
|
├── scripts/ build-time helpers
|
||||||
|
└── .gitea/workflows/ CI / release pipelines
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --workspace
|
||||||
|
cargo test --workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
MSRV: Rust 1.83.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Dual-licensed under either of:
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([`LICENSE-APACHE`](./LICENSE-APACHE))
|
||||||
|
- MIT license ([`LICENSE-MIT`](./LICENSE-MIT))
|
||||||
|
|
||||||
|
at your option. Unless you explicitly state otherwise, any contribution
|
||||||
|
intentionally submitted for inclusion in this work shall be dual licensed as
|
||||||
|
above, without any additional terms or conditions.
|
||||||
|
|||||||
@@ -0,0 +1,596 @@
|
|||||||
|
# nuwiki — Project Specification
|
||||||
|
|
||||||
|
> Last updated: 2026-05-08
|
||||||
|
> Status: In design — Phase 0 not yet started
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
nuwiki is a Vim/Neovim plugin that provides full vimwiki syntax support, implemented as a Rust-based Language Server (LSP). It is a spiritual successor to vimwiki, architecturally distinct from it, designed for correctness, performance, and extensibility.
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
- Full vimwiki syntax support (highlighting, navigation, link following, diagnostics)
|
||||||
|
- First-class support for both Vim 9+ and Neovim 0.5+
|
||||||
|
- Installable via standard plugin managers (lazy.nvim, vim-plug, Dein)
|
||||||
|
- Extensible to Markdown syntax without changes to core architecture
|
||||||
|
- Independently testable core library with no editor dependencies
|
||||||
|
|
||||||
|
**Non-goals (v1.0):**
|
||||||
|
- MediaWiki syntax support
|
||||||
|
- Multi-wiki configurations
|
||||||
|
- Incremental / tree-sitter parsing
|
||||||
|
- Browser-based or WASM build
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Repository
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|---|---|
|
||||||
|
| License | Dual MIT/Apache-2.0 |
|
||||||
|
| MSRV | Rust 1.83 (stable-2) |
|
||||||
|
|---|---|
|
||||||
|
| URL | `https://code.gfran.co/gffranco/nuwiki` |
|
||||||
|
| Version control | Gitea |
|
||||||
|
| CI/CD | Gitea Actions |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Naming Conventions
|
||||||
|
|
||||||
|
| Context | Name |
|
||||||
|
|---|---|
|
||||||
|
| GitHub/Gitea repo | `nuwiki` |
|
||||||
|
| Cargo workspace | `nuwiki` |
|
||||||
|
| Core library crate | `nuwiki-core` |
|
||||||
|
| LSP bridge crate | `nuwiki-lsp` |
|
||||||
|
| Binary crate | `nuwiki-ls` |
|
||||||
|
| Vim plugin entry | `plugin/nuwiki.vim` |
|
||||||
|
| Lua module | `require('nuwiki')` |
|
||||||
|
| VimL autoload | `nuwiki#lsp#start()` |
|
||||||
|
| Vim help file | `doc/nuwiki.txt` → `:h nuwiki` |
|
||||||
|
| Release binary | `nuwiki-ls-{version}-{target}.tar.gz` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Technology Stack
|
||||||
|
|
||||||
|
| Concern | Choice | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| Implementation language | Rust | Performance, type safety, single binary distribution, ideal for AST modelling |
|
||||||
|
| Editor integration | LSP over stdio | Works in both Vim and Neovim without a shared scripting language |
|
||||||
|
| LSP server library | `tower-lsp` | Async, tokio-based, higher-level than `lsp-server`; easier to start with |
|
||||||
|
| Parser library | `winnow` | Successor to `nom`, designed for resilient/partial parsing; strong error recovery |
|
||||||
|
| Cargo edition | 2021 | Current standard; required for resolver v2 |
|
||||||
|
| Cargo resolver | v2 | Required for edition 2021 workspaces |
|
||||||
|
| Vim glue layer | VimL (`plugin/nuwiki.vim`, `autoload/`) | Universal entry point for all plugin managers |
|
||||||
|
| Neovim glue layer | Lua (`lua/nuwiki/`) | First-class Neovim API access via `vim.lsp.start()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Repository Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
nuwiki/
|
||||||
|
│
|
||||||
|
├── Cargo.toml # Rust workspace root
|
||||||
|
├── Cargo.lock
|
||||||
|
│
|
||||||
|
├── crates/
|
||||||
|
│ ├── nuwiki-ls/ # Binary crate — thin main.rs, starts stdio LSP server
|
||||||
|
│ │ ├── Cargo.toml
|
||||||
|
│ │ └── src/main.rs
|
||||||
|
│ │
|
||||||
|
│ ├── nuwiki-core/ # Library crate — parser, AST, renderer (no editor deps)
|
||||||
|
│ │ ├── Cargo.toml
|
||||||
|
│ │ └── src/
|
||||||
|
│ │ ├── lib.rs
|
||||||
|
│ │ ├── syntax/
|
||||||
|
│ │ │ ├── mod.rs
|
||||||
|
│ │ │ ├── registry.rs
|
||||||
|
│ │ │ └── vimwiki/
|
||||||
|
│ │ │ ├── mod.rs
|
||||||
|
│ │ │ ├── lexer.rs
|
||||||
|
│ │ │ └── parser.rs
|
||||||
|
│ │ ├── ast/
|
||||||
|
│ │ │ ├── mod.rs
|
||||||
|
│ │ │ ├── block.rs
|
||||||
|
│ │ │ ├── inline.rs
|
||||||
|
│ │ │ └── link.rs
|
||||||
|
│ │ └── render/
|
||||||
|
│ │ ├── mod.rs
|
||||||
|
│ │ └── html.rs
|
||||||
|
│ │
|
||||||
|
│ └── nuwiki-lsp/ # Library crate — LSP protocol bridge
|
||||||
|
│ ├── Cargo.toml
|
||||||
|
│ └── src/lib.rs
|
||||||
|
│
|
||||||
|
├── plugin/ # Universal Vim/Neovim entry point
|
||||||
|
│ └── nuwiki.vim
|
||||||
|
│
|
||||||
|
├── lua/ # Neovim-specific Lua layer
|
||||||
|
│ └── nuwiki/
|
||||||
|
│ ├── init.lua
|
||||||
|
│ ├── config.lua
|
||||||
|
│ ├── lsp.lua
|
||||||
|
│ └── install.lua # Binary download / build-from-source logic
|
||||||
|
│
|
||||||
|
├── autoload/ # Lazy-loaded VimL (Vim compat layer)
|
||||||
|
│ └── nuwiki/
|
||||||
|
│ └── lsp.vim
|
||||||
|
│
|
||||||
|
├── ftdetect/ # Filetype detection for .wiki files
|
||||||
|
│ └── nuwiki.vim
|
||||||
|
│
|
||||||
|
├── ftplugin/ # Per-filetype buffer settings
|
||||||
|
│ └── nuwiki.vim
|
||||||
|
│
|
||||||
|
├── syntax/ # Static fallback syntax highlighting (no LSP required)
|
||||||
|
│ └── nuwiki.vim
|
||||||
|
│
|
||||||
|
├── doc/ # Vim help documentation
|
||||||
|
│ └── nuwiki.txt
|
||||||
|
│
|
||||||
|
├── scripts/
|
||||||
|
│ └── download_bin.vim # VimL binary download (used by Dein/vim-plug build hooks)
|
||||||
|
│
|
||||||
|
└── .gitea/
|
||||||
|
└── workflows/
|
||||||
|
├── ci.yaml # Lint, test, fmt check on every push/PR
|
||||||
|
└── release.yaml # Cross-compile + release on v* tag
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Architecture
|
||||||
|
|
||||||
|
### 6.1 Layer Model
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Consumer Layer │
|
||||||
|
│ (editor highlight, HTML export, TOC, …) │
|
||||||
|
└────────────────────────┬────────────────────────────┘
|
||||||
|
│ operates on
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ AST (nuwiki-core, shared) │
|
||||||
|
│ Document > Block nodes > Inline nodes │
|
||||||
|
└────────────────────────┬────────────────────────────┘
|
||||||
|
│ produced by
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Parser — syntax-specific (nuwiki-core) │
|
||||||
|
│ Registered per syntax; receives TokenStream │
|
||||||
|
└────────────────────────┬────────────────────────────┘
|
||||||
|
│ consumes
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Lexer — syntax-specific (nuwiki-core) │
|
||||||
|
│ Registered per syntax; receives raw text │
|
||||||
|
└────────────────────────┬────────────────────────────┘
|
||||||
|
│ reads
|
||||||
|
▼
|
||||||
|
Raw text input
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Crate Dependency Rules
|
||||||
|
|
||||||
|
```
|
||||||
|
nuwiki-ls → nuwiki-lsp → nuwiki-core
|
||||||
|
```
|
||||||
|
|
||||||
|
- `nuwiki-core` must never depend on `nuwiki-lsp` or `nuwiki-ls`
|
||||||
|
- `nuwiki-lsp` must never depend on `nuwiki-ls`
|
||||||
|
- The VimL and Lua editor layers must never contain logic — pure wiring only
|
||||||
|
|
||||||
|
### 6.3 Syntax Plugin Interface
|
||||||
|
|
||||||
|
Every syntax (vimwiki, future markdown) implements:
|
||||||
|
|
||||||
|
```
|
||||||
|
SyntaxPlugin
|
||||||
|
id: string -- "vimwiki" | "markdown"
|
||||||
|
display_name: string
|
||||||
|
file_extensions: string[] -- [".wiki"] | [".md"]
|
||||||
|
lexer: fn(text) → TokenStream
|
||||||
|
parser: fn(TokenStream) → DocumentNode
|
||||||
|
```
|
||||||
|
|
||||||
|
Registry:
|
||||||
|
```
|
||||||
|
SyntaxRegistry
|
||||||
|
register(plugin: SyntaxPlugin)
|
||||||
|
get(id: &str) → Option<&SyntaxPlugin>
|
||||||
|
detect_from_extension(ext: &str) → Option<&SyntaxPlugin>
|
||||||
|
```
|
||||||
|
|
||||||
|
All plugins must be `Send + Sync` (held in the LSP server registry).
|
||||||
|
|
||||||
|
### 6.4 AST Node Types
|
||||||
|
|
||||||
|
#### Document
|
||||||
|
```
|
||||||
|
DocumentNode
|
||||||
|
children: Vec<BlockNode>
|
||||||
|
metadata: PageMetadata -- title, nohtml, template, date
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Block Nodes
|
||||||
|
```
|
||||||
|
HeadingNode level: 1–6 | children: Vec<InlineNode> | centered: bool
|
||||||
|
ParagraphNode children: Vec<InlineNode>
|
||||||
|
HorizontalRuleNode
|
||||||
|
BlockquoteNode children: Vec<BlockNode>
|
||||||
|
PreformattedNode content: String | language: Option<String>
|
||||||
|
MathBlockNode content: String | environment: Option<String>
|
||||||
|
ListNode ordered: bool | symbol: ListSymbol | items: Vec<ListItemNode>
|
||||||
|
DefinitionListNode items: Vec<DefinitionItemNode>
|
||||||
|
TableNode rows: Vec<TableRowNode> | has_header: bool
|
||||||
|
CommentNode content: String
|
||||||
|
ErrorNode raw: String | message: String -- resilient parse failure
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Inline Nodes
|
||||||
|
```
|
||||||
|
TextNode content: String
|
||||||
|
BoldNode children: Vec<InlineNode>
|
||||||
|
ItalicNode children: Vec<InlineNode>
|
||||||
|
BoldItalicNode children: Vec<InlineNode>
|
||||||
|
StrikethroughNode children: Vec<InlineNode>
|
||||||
|
CodeNode content: String
|
||||||
|
SuperscriptNode children: Vec<InlineNode>
|
||||||
|
SubscriptNode children: Vec<InlineNode>
|
||||||
|
MathInlineNode content: String
|
||||||
|
KeywordNode keyword: Keyword -- TODO|DONE|STARTED|FIXME|FIXED|XXX
|
||||||
|
ColorNode color: String | children: Vec<InlineNode>
|
||||||
|
WikiLinkNode target: LinkTarget | description: Option<Vec<InlineNode>>
|
||||||
|
ExternalLinkNode url: String | description: Option<Vec<InlineNode>>
|
||||||
|
TransclusionNode url: String | alt: Option<String> | attrs: HashMap<String,String>
|
||||||
|
RawUrlNode url: String
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Supporting Types
|
||||||
|
```
|
||||||
|
ListItemNode
|
||||||
|
symbol: ListSymbol
|
||||||
|
level: usize
|
||||||
|
checkbox: Option<CheckboxState>
|
||||||
|
children: Vec<InlineNode>
|
||||||
|
sublist: Option<ListNode>
|
||||||
|
|
||||||
|
DefinitionItemNode
|
||||||
|
term: Option<Vec<InlineNode>>
|
||||||
|
definitions: Vec<Vec<InlineNode>>
|
||||||
|
|
||||||
|
TableRowNode cells: Vec<TableCellNode> | is_header: bool
|
||||||
|
TableCellNode children: Vec<InlineNode> | col_span: bool | row_span: bool
|
||||||
|
|
||||||
|
LinkTarget
|
||||||
|
kind: LinkKind -- Wiki|Interwiki|Diary|File|Local|Raw|AnchorOnly
|
||||||
|
path: Option<String>
|
||||||
|
wiki_index: Option<usize>
|
||||||
|
wiki_name: Option<String>
|
||||||
|
anchor: Option<String>
|
||||||
|
is_absolute: bool
|
||||||
|
is_directory: bool
|
||||||
|
|
||||||
|
ListSymbol = Dash | Star | Hash | Numeric | NumericParen
|
||||||
|
| AlphaParen | AlphaUpperParen | RomanParen | RomanUpperParen
|
||||||
|
CheckboxState = Empty | Quarter | Half | ThreeQuarters | Done | Rejected
|
||||||
|
Keyword = Todo | Done | Started | Fixme | Fixed | Xxx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.5 Span / Source Location
|
||||||
|
|
||||||
|
Every AST node carries a `Span`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Span {
|
||||||
|
start: Position,
|
||||||
|
end: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Position {
|
||||||
|
line: u32, -- 0-indexed
|
||||||
|
column: u32, -- 0-indexed, byte offset within line
|
||||||
|
offset: usize, -- absolute byte offset from document start
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Spans are required for LSP diagnostics, semantic tokens, and go-to-definition.
|
||||||
|
|
||||||
|
### 6.6 Lexer Strategy
|
||||||
|
|
||||||
|
- **Two-pass:** block-level pass first, then inline pass within each block
|
||||||
|
- **Token types:** syntax-specific (`VimwikiToken`) — not shared across syntaxes
|
||||||
|
- **TokenStream:** eager `Vec<Token>` wrapped in a `TokenStream` newtype (changeable to lazy later)
|
||||||
|
- Operates on Rust `char`s for correctness; spans stored as byte offsets
|
||||||
|
|
||||||
|
### 6.7 Parser Strategy
|
||||||
|
|
||||||
|
- Uses `winnow` parser combinator library
|
||||||
|
- Resilient: on malformed input, emits `ErrorNode` and continues — never fails the whole document
|
||||||
|
- Inline marker precedence rules documented explicitly in code and tests
|
||||||
|
|
||||||
|
### 6.8 Renderer
|
||||||
|
|
||||||
|
- `Renderer` trait: writer-based (`fn render(&self, doc: &DocumentNode, w: &mut dyn Write)`)
|
||||||
|
- `HtmlRenderer` is the first implementation
|
||||||
|
- Link resolution injected as a callback at construction time
|
||||||
|
- Template support: minimal `{{content}}` / `{{title}}` token substitution
|
||||||
|
- CSS: linked external stylesheet (matches vimwiki convention)
|
||||||
|
|
||||||
|
### 6.9 LSP Features
|
||||||
|
|
||||||
|
| Feature | LSP Method |
|
||||||
|
|---|---|
|
||||||
|
| Syntax highlighting | `textDocument/semanticTokens/full` + `/range` |
|
||||||
|
| Diagnostics (broken links, parse errors) | `textDocument/publishDiagnostics` |
|
||||||
|
| Follow link / go to definition | `textDocument/definition` |
|
||||||
|
| Backlinks | `textDocument/references` |
|
||||||
|
| TOC / outline | `textDocument/documentSymbol` |
|
||||||
|
| Link preview | `textDocument/hover` |
|
||||||
|
| Link autocomplete | `textDocument/completion` (trigger: `[[`) |
|
||||||
|
| Page rename | `workspace/rename` |
|
||||||
|
| Workspace search | `workspace/symbol` |
|
||||||
|
|
||||||
|
### 6.10 Document Store
|
||||||
|
|
||||||
|
```
|
||||||
|
DocumentStore: Arc<DashMap<Url, DocumentState>>
|
||||||
|
|
||||||
|
DocumentState
|
||||||
|
text: String
|
||||||
|
ast: DocumentNode
|
||||||
|
version: i32
|
||||||
|
```
|
||||||
|
|
||||||
|
Full re-parse on every `didOpen` / `didChange`. Incremental parsing deferred post-v1.
|
||||||
|
|
||||||
|
### 6.11 UTF-16 / Position Encoding
|
||||||
|
|
||||||
|
LSP positions negotiated as UTF-8 (`positionEncoding = "utf-8"`) during `initialize` where the client supports LSP 3.17+. UTF-16 conversion fallback implemented in the LSP layer for older clients. `nuwiki-core` is always UTF-8 / byte-offset internally.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Editor Integration
|
||||||
|
|
||||||
|
### 7.1 Plugin Manager Installation
|
||||||
|
|
||||||
|
**lazy.nvim:**
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
"gffranco/nuwiki",
|
||||||
|
build = "lua require('nuwiki').install()",
|
||||||
|
ft = { "vimwiki" },
|
||||||
|
opts = {},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**vim-plug:**
|
||||||
|
```vim
|
||||||
|
Plug 'gffranco/nuwiki', { 'do': 'vim -e -s -c "source scripts/download_bin.vim" -c "q"' }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dein:**
|
||||||
|
```vim
|
||||||
|
call dein#add('gffranco/nuwiki', {
|
||||||
|
\ 'build': 'vim -e -s -c "source scripts/download_bin.vim" -c "q"'
|
||||||
|
\ })
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Binary Distribution
|
||||||
|
|
||||||
|
- Pre-built binaries published as Gitea release assets
|
||||||
|
- Named: `nuwiki-ls-{version}-{target}.tar.gz`
|
||||||
|
- Downloaded at install time by `lua/nuwiki/install.lua` or `scripts/download_bin.vim`
|
||||||
|
- Fallback: `cargo build --release` if download fails or `g:nuwiki_build_from_source = 1`
|
||||||
|
- Binary installed to `{plugin_dir}/bin/nuwiki-ls[.exe]`
|
||||||
|
|
||||||
|
### 7.3 Editor Detection
|
||||||
|
|
||||||
|
```vim
|
||||||
|
" plugin/nuwiki.vim
|
||||||
|
if has('nvim')
|
||||||
|
lua require('nuwiki').setup()
|
||||||
|
else
|
||||||
|
call nuwiki#lsp#start()
|
||||||
|
endif
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 Vim LSP Client Preference Order (Vim only)
|
||||||
|
|
||||||
|
1. `vim-lsp` (matoto/vim-lsp)
|
||||||
|
2. `coc.nvim`
|
||||||
|
3. Error message if neither found
|
||||||
|
|
||||||
|
### 7.5 User Config Schema
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('nuwiki').setup({
|
||||||
|
wiki_root = "~/vimwiki", -- root directory of the wiki
|
||||||
|
file_extension = ".wiki", -- file extension to associate
|
||||||
|
syntax = "vimwiki", -- "vimwiki" | "markdown" (future)
|
||||||
|
log_level = "warn", -- "error"|"warn"|"info"|"debug"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.6 Health Check
|
||||||
|
|
||||||
|
`:checkhealth nuwiki` verifies:
|
||||||
|
- Binary exists at `{plugin_dir}/bin/nuwiki-ls`
|
||||||
|
- Binary is executable and responds to `--version`
|
||||||
|
- LSP client is running
|
||||||
|
- Filetype detection works for `.wiki` files
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. CI/CD
|
||||||
|
|
||||||
|
### 8.1 Runner Setup
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|---|---|
|
||||||
|
| System | Gitea Actions (`act_runner`) |
|
||||||
|
| Mode | Docker (jobs run in containers) |
|
||||||
|
| Docker access | Host socket mounted (`/var/run/docker.sock`) |
|
||||||
|
| Internet access | Yes |
|
||||||
|
|
||||||
|
### 8.2 Workflow Files
|
||||||
|
|
||||||
|
**`.gitea/workflows/ci.yaml`** — triggers on every push and PR:
|
||||||
|
- `cargo fmt --check`
|
||||||
|
- `cargo clippy -- -D warnings`
|
||||||
|
- `cargo test --workspace`
|
||||||
|
|
||||||
|
**`.gitea/workflows/release.yaml`** — triggers on `v*` tag push:
|
||||||
|
- Cross-compile for all 4 Linux targets using `cross`
|
||||||
|
- Package binaries as `.tar.gz`
|
||||||
|
- Create Gitea release and upload assets via REST API using `RELEASE_TOKEN` secret
|
||||||
|
- Publish `nuwiki-core` to crates.io using `CARGO_REGISTRY_TOKEN` secret
|
||||||
|
|
||||||
|
### 8.3 Build Targets
|
||||||
|
|
||||||
|
| Target | Built by |
|
||||||
|
|---|---|
|
||||||
|
| `x86_64-unknown-linux-gnu` | CI (`cross`) |
|
||||||
|
| `aarch64-unknown-linux-gnu` | CI (`cross`) |
|
||||||
|
| `x86_64-unknown-linux-musl` | CI (`cross`) |
|
||||||
|
| `aarch64-unknown-linux-musl` | CI (`cross`) |
|
||||||
|
| `x86_64-apple-darwin` | Manual |
|
||||||
|
| `aarch64-apple-darwin` | Manual |
|
||||||
|
| `x86_64-pc-windows-msvc` | Manual |
|
||||||
|
|
||||||
|
### 8.4 Release Secrets
|
||||||
|
|
||||||
|
| Secret | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `RELEASE_TOKEN` | Gitea personal access token for creating releases and uploading assets |
|
||||||
|
| `CARGO_REGISTRY_TOKEN` | crates.io API token for publishing `nuwiki-core` |
|
||||||
|
|
||||||
|
### 8.5 `actions/cache` Configuration
|
||||||
|
|
||||||
|
Requires `act_runner`'s `config.yaml` to have the cache server host set to the runner host's LAN IP so job containers can reach it.
|
||||||
|
|
||||||
|
### 8.6 Breaking Change Policy
|
||||||
|
|
||||||
|
A semver-breaking change (`v0.x` → `v0.x+1` or `v1.x` → `v2.0`) is defined as any of:
|
||||||
|
- AST node type additions, removals, or field changes in `nuwiki-core`
|
||||||
|
- `SyntaxPlugin` or `Renderer` trait signature changes
|
||||||
|
- User config schema key removals or type changes
|
||||||
|
- Removed LSP capabilities
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Vimwiki Syntax Feature Checklist
|
||||||
|
|
||||||
|
### Typefaces
|
||||||
|
- [ ] Bold: `*text*`
|
||||||
|
- [ ] Italic: `_text_`
|
||||||
|
- [ ] Bold italic: `_*text*_` / `*_text_*`
|
||||||
|
- [ ] Strikethrough: `~~text~~`
|
||||||
|
- [ ] Inline code: `` `text` ``
|
||||||
|
- [ ] Superscript: `super^script^`
|
||||||
|
- [ ] Subscript: `sub,,script,,`
|
||||||
|
- [ ] Keywords: `TODO` `DONE` `STARTED` `FIXME` `FIXED` `XXX`
|
||||||
|
|
||||||
|
### Links
|
||||||
|
- [ ] Plain wikilink: `[[Target]]`
|
||||||
|
- [ ] Described wikilink: `[[Target|Description]]`
|
||||||
|
- [ ] Subdirectory wikilink: `[[dir/Page]]`
|
||||||
|
- [ ] Root-relative wikilink: `[[/Page]]`
|
||||||
|
- [ ] Filesystem-absolute wikilink: `[[//path]]`
|
||||||
|
- [ ] Subdirectory link: `[[dir/]]`
|
||||||
|
- [ ] Interwiki numbered: `[[wiki1:Page]]`
|
||||||
|
- [ ] Interwiki named: `[[wn.Name:Page]]`
|
||||||
|
- [ ] Diary link: `[[diary:YYYY-MM-DD]]`
|
||||||
|
- [ ] Anchor-only link: `[[#Anchor]]`
|
||||||
|
- [ ] Wikilink with anchor: `[[Page#Anchor]]`
|
||||||
|
- [ ] Raw URLs: `https://…` `mailto:…` `ftp://…`
|
||||||
|
- [ ] External file link: `[[file:path]]` / `[[local:path]]`
|
||||||
|
- [ ] Transclusion: `{{URL}}` with optional alt and attrs
|
||||||
|
- [ ] Thumbnail link: `[[imgURL|{{thumbURL}}]]`
|
||||||
|
|
||||||
|
### Headers
|
||||||
|
- [ ] Levels 1–6: `= H1 =` … `====== H6 ======`
|
||||||
|
- [ ] Centered header (leading whitespace before `=`)
|
||||||
|
|
||||||
|
### Lists
|
||||||
|
- [ ] Unordered: `-` and `*`
|
||||||
|
- [ ] Ordered: `1.` `1)` `a)` `A)` `i)` `I)` `#`
|
||||||
|
- [ ] Nested and mixed types
|
||||||
|
- [ ] Multi-line items (indentation continuation)
|
||||||
|
- [ ] Definition lists: `Term:: Definition`
|
||||||
|
- [ ] Checkboxes: `[ ]` `[.]` `[o]` `[O]` `[X]` `[-]`
|
||||||
|
|
||||||
|
### Tables
|
||||||
|
- [ ] Basic `|`-delimited table
|
||||||
|
- [ ] Header row separator: `|---|`
|
||||||
|
- [ ] Column span: `>`
|
||||||
|
- [ ] Row span: `\/`
|
||||||
|
- [ ] Inline formatting inside cells
|
||||||
|
|
||||||
|
### Preformatted Text
|
||||||
|
- [ ] Fenced block: `{{{ … }}}`
|
||||||
|
- [ ] Optional language/class on opening fence
|
||||||
|
|
||||||
|
### Mathematical Formulae
|
||||||
|
- [ ] Inline math: `$ … $`
|
||||||
|
- [ ] Block display math: `{{$ … }}$`
|
||||||
|
- [ ] Block environment math: `{{$%env% … }}$`
|
||||||
|
|
||||||
|
### Blockquotes
|
||||||
|
- [ ] 4-space indent blockquote
|
||||||
|
- [ ] `>` prefix blockquote
|
||||||
|
|
||||||
|
### Comments
|
||||||
|
- [ ] Single-line: `%% …`
|
||||||
|
- [ ] Multi-line: `%%+ … +%%`
|
||||||
|
|
||||||
|
### Horizontal Rule
|
||||||
|
- [ ] Four or more dashes: `----`
|
||||||
|
|
||||||
|
### Placeholders
|
||||||
|
- [ ] `%title <text>`
|
||||||
|
- [ ] `%nohtml`
|
||||||
|
- [ ] `%template <name>`
|
||||||
|
- [ ] `%date <date>`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Implementation Phases
|
||||||
|
|
||||||
|
| Phase | Name | Key Output |
|
||||||
|
|---|---|---|
|
||||||
|
| 0 | Scaffolding | Compiling empty workspace + CI skeleton |
|
||||||
|
| 1 | Core AST | All node types, spans, Visitor trait |
|
||||||
|
| 2 | Syntax Plugin Interface | `Lexer`/`Parser`/`SyntaxPlugin` traits, `SyntaxRegistry` |
|
||||||
|
| 3 | Vimwiki Lexer | `VimwikiToken`, two-pass lexer with spans |
|
||||||
|
| 4 | Vimwiki Parser | Full AST from token stream, error recovery |
|
||||||
|
| 5 | Renderer | `Renderer` trait + `HtmlRenderer` |
|
||||||
|
| 6 | LSP Foundation | `didOpen`/`didChange`, diagnostics, document symbols |
|
||||||
|
| 7 | Semantic Tokens | AST → LSP semantic token stream |
|
||||||
|
| 8 | Navigation | Definition, references, hover, completion, workspace index |
|
||||||
|
| 9 | Editor Glue | Installable plugin, health check, binary download |
|
||||||
|
| 10 | CI/CD | Automated lint/test/release pipeline |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Pending Decisions
|
||||||
|
|
||||||
|
These decisions are required before or during the phase indicated.
|
||||||
|
|
||||||
|
| # | Decision | Needed by | Options | Notes |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| ~~P1~~ | ~~**License**~~ | ~~Phase 0~~ | ✅ **Dual MIT/Apache-2.0** | |
|
||||||
|
| ~~P2~~ | ~~**MSRV**~~ | ~~Phase 0~~ | ✅ **stable-2 (Rust 1.83)** | |
|
||||||
|
| P3 | **String representation in AST** | Phase 1 | `String` (owned) · `Cow<'a, str>` · `Arc<str>` | `String` simplest; `Cow` enables zero-copy parsing later |
|
||||||
|
| P4 | **Visitor pattern** | Phase 1 | Define trait now · Defer to Phase 5 | Recommend defining interface now; avoids refactor when renderer arrives |
|
||||||
|
| P5 | **Minimum Neovim version** | Phase 9 | 0.8 (`vim.lsp.start`) · 0.11 (`vim.lsp.config`) | 0.8 = broader compat; 0.11 = cleaner Lua API |
|
||||||
|
| P6 | **Minimum Vim version** | Phase 9 | Vim 8.2 · Vim 9.0 · Vim 9.1 | LSP client plugins work best on 9.0+; recommend documenting 9.1 as minimum |
|
||||||
|
| P7 | **Semantic token type mapping** | Phase 7 | Standard LSP types only · Custom token types | Custom types need client-side highlight group definitions; more flexible but more setup for users |
|
||||||
|
| P8 | **Workspace indexing strategy** | Phase 8 | Eager on startup · Lazy + background | Eager blocks startup on large wikis; recommend lazy with progress notification |
|
||||||
|
| P9 | **macOS runner** | Phase 10 | Stay manual · Register macOS runner
|
||||||
|
| Revisit if a Mac is available; no action needed now |
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
" autoload/nuwiki/lsp.vim — Vim (non-Neovim) LSP client wiring.
|
||||||
|
" Implementation deferred to Phase 9 (Editor Glue).
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "nuwiki-core"
|
||||||
|
description = "Core parser, AST, and renderer for nuwiki — editor-independent."
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
//! Core parser, AST, and renderer for nuwiki.
|
||||||
|
//!
|
||||||
|
//! This crate is editor-independent and must never depend on `nuwiki-lsp` or
|
||||||
|
//! `nuwiki-ls`. See SPEC.md §6.2 for the dependency rules.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "nuwiki-ls"
|
||||||
|
description = "nuwiki language server binary — speaks LSP over stdio."
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nuwiki-ls"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nuwiki-lsp = { path = "../nuwiki-lsp", version = "0.1.0" }
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fn main() {}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "nuwiki-lsp"
|
||||||
|
description = "LSP protocol bridge for nuwiki, built on top of nuwiki-core."
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nuwiki-core = { path = "../nuwiki-core", version = "0.1.0" }
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
//! LSP protocol bridge for nuwiki.
|
||||||
|
//!
|
||||||
|
//! Translates between LSP requests/notifications and `nuwiki-core` AST
|
||||||
|
//! operations. Must never depend on `nuwiki-ls` (see SPEC.md §6.2).
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
*nuwiki.txt* LSP-backed vimwiki support for Vim and Neovim.
|
||||||
|
|
||||||
|
Documentation deferred to Phase 9 (Editor Glue).
|
||||||
|
|
||||||
|
vim:tw=78:ts=8:ft=help:norl:
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
" ftdetect/nuwiki.vim — filetype detection for .wiki files.
|
||||||
|
" Implementation deferred to Phase 9 (Editor Glue).
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
" ftplugin/nuwiki.vim — per-buffer filetype settings for .wiki buffers.
|
||||||
|
" Implementation deferred to Phase 9 (Editor Glue).
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
-- lua/nuwiki/config.lua — user configuration schema and defaults.
|
||||||
|
-- Implementation deferred to Phase 9 (Editor Glue).
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
-- lua/nuwiki/init.lua — Neovim entry point. Exposes setup() and install().
|
||||||
|
-- Implementation deferred to Phase 9 (Editor Glue).
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
-- lua/nuwiki/install.lua — binary download / build-from-source.
|
||||||
|
-- Implementation deferred to Phase 9 (Editor Glue).
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
-- lua/nuwiki/lsp.lua — vim.lsp.start wiring for the nuwiki-ls binary.
|
||||||
|
-- Implementation deferred to Phase 9 (Editor Glue).
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
" plugin/nuwiki.vim — universal entry point for Vim and Neovim.
|
||||||
|
" Loaded once per session by all plugin managers.
|
||||||
|
" Implementation deferred to Phase 9 (Editor Glue).
|
||||||
|
|
||||||
|
if exists('g:loaded_nuwiki')
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
let g:loaded_nuwiki = 1
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
" scripts/download_bin.vim — VimL binary download for Dein/vim-plug build hooks.
|
||||||
|
" Implementation deferred to Phase 9 (Editor Glue).
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
" syntax/nuwiki.vim — static fallback syntax highlighting (no LSP required).
|
||||||
|
" Implementation deferred to Phase 9 (Editor Glue).
|
||||||
Reference in New Issue
Block a user