├── .cargo └── config ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .rustfmt.toml ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── scripts └── release.sh ├── vimwiki-cli ├── .gitignore ├── Cargo.toml ├── README.md └── src │ ├── ast.rs │ ├── css.rs │ ├── lib.rs │ ├── main.rs │ ├── opt.rs │ ├── subcommand │ ├── convert.rs │ ├── format.rs │ ├── inspect.rs │ ├── mod.rs │ └── serve.rs │ └── utils.rs ├── vimwiki-core ├── .gitignore ├── Cargo.toml ├── Makefile ├── README.md ├── benches │ ├── fixtures │ │ └── vimwiki │ │ │ └── specification.wiki │ └── vimwiki_parser.rs ├── src │ ├── lang │ │ ├── elements │ │ │ ├── blocks │ │ │ │ ├── blockquotes.rs │ │ │ │ ├── code.rs │ │ │ │ ├── definitions.rs │ │ │ │ ├── dividers.rs │ │ │ │ ├── headers.rs │ │ │ │ ├── inline │ │ │ │ │ ├── code.rs │ │ │ │ │ ├── comments.rs │ │ │ │ │ ├── links │ │ │ │ │ │ ├── anchor.rs │ │ │ │ │ │ ├── data.rs │ │ │ │ │ │ ├── description.rs │ │ │ │ │ │ └── mod.rs │ │ │ │ │ ├── math.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── tags.rs │ │ │ │ │ └── typefaces.rs │ │ │ │ ├── lists │ │ │ │ │ ├── item.rs │ │ │ │ │ └── mod.rs │ │ │ │ ├── math.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── paragraphs.rs │ │ │ │ ├── placeholders.rs │ │ │ │ └── tables.rs │ │ │ ├── mod.rs │ │ │ └── utils │ │ │ │ ├── mod.rs │ │ │ │ └── region.rs │ │ ├── mod.rs │ │ ├── output │ │ │ ├── html │ │ │ │ ├── config.rs │ │ │ │ ├── convert.rs │ │ │ │ ├── error.rs │ │ │ │ ├── formatter.rs │ │ │ │ ├── mod.rs │ │ │ │ └── utils.rs │ │ │ ├── mod.rs │ │ │ └── vimwiki │ │ │ │ ├── config.rs │ │ │ │ ├── convert.rs │ │ │ │ ├── error.rs │ │ │ │ ├── formatter.rs │ │ │ │ └── mod.rs │ │ └── parsers │ │ │ ├── errors.rs │ │ │ ├── mod.rs │ │ │ ├── span.rs │ │ │ ├── utils │ │ │ ├── bytes.rs │ │ │ ├── character.rs │ │ │ ├── convert.rs │ │ │ ├── error.rs │ │ │ ├── line.rs │ │ │ ├── mod.rs │ │ │ └── whitespace.rs │ │ │ └── vimwiki │ │ │ ├── blocks │ │ │ ├── blockquotes.rs │ │ │ ├── code.rs │ │ │ ├── definitions.rs │ │ │ ├── dividers.rs │ │ │ ├── headers.rs │ │ │ ├── inline │ │ │ │ ├── code.rs │ │ │ │ ├── comments.rs │ │ │ │ ├── links │ │ │ │ │ ├── diary.rs │ │ │ │ │ ├── interwiki.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── raw.rs │ │ │ │ │ ├── transclusion.rs │ │ │ │ │ └── wiki.rs │ │ │ │ ├── math.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── tags.rs │ │ │ │ └── typefaces.rs │ │ │ ├── lists.rs │ │ │ ├── math.rs │ │ │ ├── mod.rs │ │ │ ├── paragraphs.rs │ │ │ ├── placeholders.rs │ │ │ └── tables.rs │ │ │ └── mod.rs │ ├── lib.rs │ ├── timekeeper.rs │ └── utils.rs └── tests │ ├── lib.rs │ ├── output │ ├── html │ │ ├── lists-with-hard-line-wrap.out.html │ │ ├── lists-with-hard-line-wrap.wiki │ │ ├── standalone-anchor-link.out.html │ │ └── standalone-anchor-link.wiki │ ├── mod.rs │ └── vimwiki │ │ ├── blockquotes.out.wiki │ │ ├── blockquotes.wiki │ │ ├── code.out.wiki │ │ ├── code.wiki │ │ ├── headers.out.wiki │ │ ├── headers.wiki │ │ ├── links.out.wiki │ │ ├── links.wiki │ │ ├── lists.out.wiki │ │ ├── lists.wiki │ │ ├── paragraphs.out.wiki │ │ └── paragraphs.wiki │ └── parser │ ├── fixtures │ ├── issue │ │ ├── 119.wiki │ │ ├── 120.wiki │ │ └── 122.wiki │ ├── misc │ │ ├── comment-in-definition-list.wiki │ │ ├── depth-check.wiki │ │ └── windows-support.wiki │ ├── mod.rs │ ├── pandoc │ │ └── vimwiki-reader.wiki │ └── vimwikiwiki │ │ ├── Related Tools.wiki │ │ ├── Tips and Snips.wiki │ │ ├── Troubleshooting.wiki │ │ └── index.wiki │ ├── integration │ ├── issues.rs │ ├── misc │ │ ├── comment_in_definition_list.rs │ │ ├── depth_check.rs │ │ ├── mod.rs │ │ └── windows_support.rs │ ├── mod.rs │ ├── pandoc │ │ ├── mod.rs │ │ └── vimwiki_reader.rs │ └── vimwiki_wiki │ │ ├── index.rs │ │ ├── mod.rs │ │ ├── related_tools.rs │ │ ├── tips_and_snips.rs │ │ └── troubleshooting.rs │ ├── mod.rs │ └── utils.rs ├── vimwiki-wasm ├── .gitignore ├── Cargo.toml ├── PUBLISH.md ├── README.md ├── package.json ├── publish.sh └── src │ ├── elements.rs │ ├── lib.rs │ └── utils.rs ├── vimwiki ├── .gitignore ├── Cargo.toml ├── README.md └── src │ └── lib.rs └── vimwiki_macros ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Makefile ├── README.md ├── src ├── args.rs ├── error.rs ├── formatter.rs ├── lib.rs ├── tokens │ ├── elements │ │ ├── blocks │ │ │ ├── blockquotes.rs │ │ │ ├── code.rs │ │ │ ├── definitions.rs │ │ │ ├── dividers.rs │ │ │ ├── headers.rs │ │ │ ├── inline │ │ │ │ ├── code.rs │ │ │ │ ├── comments.rs │ │ │ │ ├── links.rs │ │ │ │ ├── math.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── tags.rs │ │ │ │ └── typefaces.rs │ │ │ ├── lists.rs │ │ │ ├── math.rs │ │ │ ├── mod.rs │ │ │ ├── paragraphs.rs │ │ │ ├── placeholders.rs │ │ │ └── tables.rs │ │ ├── location.rs │ │ └── mod.rs │ ├── mod.rs │ ├── primitives.rs │ └── utils.rs └── utils.rs └── tests ├── functions ├── mod.rs ├── vimwiki.rs └── vimwiki_format.rs ├── hygiene.rs └── lib.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-linux-musl] 2 | linker = "x86_64-linux-musl-gcc" 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | env: 10 | RUST_BACKTRACE: 1 11 | 12 | jobs: 13 | test: 14 | name: Test Rust ${{ matrix.rust }} on ${{ matrix.os }} 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | include: 20 | - { rust: stable, os: ubuntu-latest } 21 | - { rust: stable, os: macos-latest } 22 | - { rust: stable, os: windows-latest } 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions/cache@v2 26 | with: 27 | path: | 28 | ~/.cargo/registry 29 | ~/.cargo/git 30 | target 31 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 32 | - uses: hecrj/setup-rust-action@v1 33 | with: 34 | rust-version: ${{ matrix.rust }} 35 | - name: Check Cargo availability 36 | run: cargo --version 37 | - run: cargo test --verbose --workspace 38 | - run: cargo test --verbose --workspace --all-features 39 | 40 | clippy: 41 | name: Lint with clippy 42 | runs-on: ubuntu-latest 43 | env: 44 | RUSTFLAGS: -Dwarnings 45 | steps: 46 | - uses: actions/checkout@v2 47 | - uses: actions/cache@v2 48 | with: 49 | path: | 50 | ~/.cargo/registry 51 | ~/.cargo/git 52 | target 53 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 54 | - uses: hecrj/setup-rust-action@v1 55 | with: 56 | components: clippy 57 | - name: Check Cargo availability 58 | run: cargo --version 59 | - run: cargo clippy --workspace --all-targets --verbose 60 | - run: cargo clippy --workspace --all-targets --verbose --all-features 61 | 62 | rustfmt: 63 | name: Verify code formatting 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v2 67 | - uses: actions/cache@v2 68 | with: 69 | path: | 70 | ~/.cargo/registry 71 | ~/.cargo/git 72 | target 73 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 74 | - uses: hecrj/setup-rust-action@v1 75 | with: 76 | components: rustfmt 77 | - name: Check Cargo availability 78 | run: cargo --version 79 | - run: cargo fmt --all -- --check 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | 5 | ## [Unreleased] - ReleaseDate 6 | 7 | ### Added 8 | 9 | - `vimwiki-cli` now includes a **format** subcommand to format vimwiki text 10 | - `vimwiki-core` now supports converting an ast into vimwiki text 11 | 12 | ### Changed 13 | 14 | - Moved `iter::*` to root level of `vimwiki-core` crate 15 | - `ListItemContents` now contains a `Vec` and the associated 16 | parser now supports other types such as `CodeBlock`, `MathBlock`, 17 | `Blockquote`, and `Table` as options for being included 18 | - List items no longer keep raw text, but instead paragraphs of text as one 19 | of the potential elements 20 | - HTML output of list items with text now yields `
  • ...

  • ` instead 21 | of the previous `
  • ...
  • ` 22 | - Refactor `DefinitionList` to use new `DefinitionBundle` type for definitions 23 | 24 | ### Fixed 25 | 26 | - Local anchor links were adding `index.html` in front of the anchor 27 | regardless of the page's name 28 | - Bump to `0.3.0` of `vimvar` dependency to support `init.lua` when searching 29 | for wiki paths 30 | 31 | ### Removed 32 | 33 | - Deleted `vimwiki-server` as no longer going to support a GraphQL server of 34 | documents since `AsyncGraphql` dependency keeps breaking. Instead, will 35 | focus on expanding the `vimwiki-cli` offering to provide features to 36 | query wiki documents 37 | 38 | ### Performance 39 | 40 | - Refactored text parser to yield a 5x speedup on local testing of wikis that 41 | previously took ~30s now finishing in ~6s for parsing and output 42 | 43 | ## [0.1.0] - 2021-06-06 44 | 45 | ### Added 46 | 47 | - `vimwiki-cli` crate that exposes a cli interface to convert vimwiki to html 48 | and inspect vimwiki using **jsonpath** queries 49 | - `vimwiki-wasm` crate that exposes a wasm version of `vimwiki` to enable 50 | parsing and manipulation in the browser 51 | - `vimvar` dependency to support loading **g:vimwiki_list** directly from 52 | neovim/vim config files, used both by `vimwiki-cli` and `vimwiki-server` 53 | - `--quiet` option added to `vimwiki-server` to support only showing 54 | error-level log messages 55 | - `vimwiki` now supports optional feature **html** to render vimwiki as html 56 | 57 | ### Changed 58 | 59 | - `vimwiki` crate renamed to `vimwiki-core` in order to provide a `vimwiki` 60 | crate that contains both core and macros to simplify usage 61 | - `vimwiki-server` now supports colored output of logging and defaults to 62 | info-level logging 63 | - Move `vimwiki-cli` logic into *lib.rs* that is imported within *main.rs* 64 | - Transclusion links now use spaces between properties instead of pipe 65 | symbols (`{{img.png|desc|prop1="value" prop2="value"}}`) 66 | 67 | ### Fixed 68 | 69 | - Raw links weren't captured if preceded by text on a line ([#119](https://github.com/chipsenkbeil/vimwiki-rs/issues/119)) 70 | 71 | ## [0.1.0-alpha.6] - 2021-05-28 72 | 73 | ### Added 74 | 75 | - This `CHANGELOG.md` file to keep track of future changes 76 | - `scripts/release.sh` to keep track of all version changes and update multiple 77 | `Cargo.toml` as well as other files like this changelog 78 | - `vimwiki__format` and `vimwiki__raw_format` support into 79 | the `vimwiki_macros` crate to support injecting content into vimwiki macros 80 | at compile-time 81 | ([#102](https://github.com/chipsenkbeil/vimwiki-rs/issues/102)) 82 | 83 | ### Changed 84 | 85 | - `vimwiki_macros` is now more hygienic through proper testing 86 | - `vimwiki_macros` now uses `syn` for some parsing and `proc-macro-crate` 87 | to detect and get the root of the `vimwiki` crate 88 | ([#92](https://github.com/chipsenkbeil/vimwiki-rs/issues/92)) 89 | - `vimwiki` now exports all items at the top level including items 90 | found under the `elements` module 91 | 92 | ### Bugs 93 | 94 | - `vimwiki_list_format!(...)` and `vimwiki_definition_list_format!(...)` and 95 | their raw counterparts do not support `{}` injection without explicit number 96 | or names as the generated code may not maintain the same order of list items. 97 | It is recommended to use `{0}` or `{name}` instead when working with those 98 | two types 99 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "vimwiki", 4 | "vimwiki-cli", 5 | "vimwiki-core", 6 | "vimwiki_macros", 7 | "vimwiki-wasm", 8 | ] 9 | 10 | [profile.release] 11 | opt-level = 'z' 12 | lto = true 13 | codegen-units = 1 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vimwiki-rs: Rust libraries and tooling for vimwiki 2 | 3 | [![CI](https://github.com/chipsenkbeil/vimwiki-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/chipsenkbeil/vimwiki-rs/actions/workflows/ci.yml) 4 | 5 | Welcome to the primary repository for all things Rust + vimwiki! This 6 | repository houses several Rust crates alongside binaries like `vimwiki-server` 7 | that enable parsing, querying, modifying, and generating vimwiki content. 8 | 9 | ## [vimwiki][vimwiki_readme] [![Crates.io][vimwiki_crates_img]][vimwiki_crates_lnk] [![Docs.rs][vimwiki_doc_img]][vimwiki_doc_lnk] 10 | 11 | [vimwiki_readme]: ./vimwiki/README.md 12 | [vimwiki_crates_img]: https://img.shields.io/crates/v/vimwiki.svg 13 | [vimwiki_crates_lnk]: https://crates.io/crates/vimwiki 14 | [vimwiki_doc_img]: https://docs.rs/vimwiki/badge.svg 15 | [vimwiki_doc_lnk]: https://docs.rs/vimwiki 16 | 17 | Represents the language definition and parsing support for the vimwiki language. 18 | 19 | ## [vimwiki-cli][vimwiki_cli_readme] [![Crates.io][vimwiki_cli_crates_img]][vimwiki_cli_crates_lnk] [![Docs.rs][vimwiki_cli_doc_img]][vimwiki_cli_doc_lnk] 20 | 21 | [vimwiki_cli_readme]: ./vimwiki-cli/README.md 22 | [vimwiki_cli_crates_img]: https://img.shields.io/crates/v/vimwiki-cli.svg 23 | [vimwiki_cli_crates_lnk]: https://crates.io/crates/vimwiki-cli 24 | [vimwiki_cli_doc_img]: https://docs.rs/vimwiki-cli/badge.svg 25 | [vimwiki_cli_doc_lnk]: https://docs.rs/vimwiki-cli 26 | 27 | Provides tiny command-line interface on top of the vimwiki parser and HTML 28 | output functionality of the vimwiki library. 29 | 30 | ## [vimwiki-core][vimwiki_core_readme] [![Crates.io][vimwiki_core_crates_img]][vimwiki_core_crates_lnk] [![Docs.rs][vimwiki_core_doc_img]][vimwiki_core_doc_lnk] 31 | 32 | [vimwiki_core_readme]: ./vimwiki-core/README.md 33 | [vimwiki_core_crates_img]: https://img.shields.io/crates/v/vimwiki-core.svg 34 | [vimwiki_core_crates_lnk]: https://crates.io/crates/vimwiki-core 35 | [vimwiki_core_doc_img]: https://docs.rs/vimwiki-core/badge.svg 36 | [vimwiki_core_doc_lnk]: https://docs.rs/vimwiki-core 37 | 38 | Provides the core vimwiki elements, parsing, and other features that are 39 | exposed through the primary vimwiki crate. 40 | 41 | ## [vimwiki_macros][vimwiki_macros_readme] [![Crates.io][vimwiki_macros_crates_img]][vimwiki_macros_crates_lnk] [![Docs.rs][vimwiki_macros_doc_img]][vimwiki_macros_doc_lnk] 42 | 43 | [vimwiki_macros_readme]: ./vimwiki-macros/README.md 44 | [vimwiki_macros_crates_img]: https://img.shields.io/crates/v/vimwiki_macros.svg 45 | [vimwiki_macros_crates_lnk]: https://crates.io/crates/vimwiki_macros 46 | [vimwiki_macros_doc_img]: https://docs.rs/vimwiki_macros/badge.svg 47 | [vimwiki_macros_doc_lnk]: https://docs.rs/vimwiki_macros 48 | 49 | Contains macros to generate vimwiki components in Rust at compile time. 50 | 51 | ## [vimwiki-server][vimwiki_server_readme] [![Crates.io][vimwiki_server_crates_img]][vimwiki_server_crates_lnk] [![Docs.rs][vimwiki_server_doc_img]][vimwiki_server_doc_lnk] 52 | 53 | [vimwiki_server_readme]: ./vimwiki-server/README.md 54 | [vimwiki_server_crates_img]: https://img.shields.io/crates/v/vimwiki-server.svg 55 | [vimwiki_server_crates_lnk]: https://crates.io/crates/vimwiki-server 56 | [vimwiki_server_doc_img]: https://docs.rs/vimwiki-server/badge.svg 57 | [vimwiki_server_doc_lnk]: https://docs.rs/vimwiki-server 58 | 59 | Provides graphql server to inspect and manipulate vimwiki files. 60 | 61 | ## [vimwiki-wasm][vimwiki_wasm_readme] [![Crates.io][vimwiki_wasm_crates_img]][vimwiki_wasm_crates_lnk] [![Docs.rs][vimwiki_wasm_doc_img]][vimwiki_wasm_doc_lnk] 62 | 63 | [vimwiki_wasm_readme]: ./vimwiki-wasm/README.md 64 | [vimwiki_wasm_crates_img]: https://img.shields.io/crates/v/vimwiki-wasm.svg 65 | [vimwiki_wasm_crates_lnk]: https://crates.io/crates/vimwiki-wasm 66 | [vimwiki_wasm_doc_img]: https://docs.rs/vimwiki-wasm/badge.svg 67 | [vimwiki_wasm_doc_lnk]: https://docs.rs/vimwiki-wasm 68 | 69 | Provides a Web Assembly (wasm) binding to the vimwiki library, enabling parsing 70 | of vimwiki text within a browser (or NodeJS) and outputting in HTML. 71 | 72 | # Sister Projects 73 | 74 | Alongside this repository are several other projects 75 | 76 | ## vimwiki 77 | 78 | [Link to project](https://github.com/vimwiki/vimwiki) 79 | 80 | Main project and plugin for vim dedicated to the vimwiki language. This is 81 | what started it all and is your main source for vim functionality and support 82 | for the language. 83 | 84 | ## vimwiki-server.nvim 85 | 86 | [Link to project](https://github.com/chipsenkbeil/vimwiki-server.nvim) 87 | 88 | Represents a sister project that offers enhanced and alternative functionality 89 | in the neovim editor for the vimwiki language. This project uses 90 | `vimwiki-server` to power its functionality in combination with the neovim Lua 91 | engine to provide all of its vimwiki goodness. 92 | -------------------------------------------------------------------------------- /vimwiki-cli/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /vimwiki-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vimwiki-cli" 3 | description = "Comand-line interface for vimwiki operations" 4 | categories = ["command-line-utilities"] 5 | version = "0.1.1" 6 | authors = ["Chip Senkbeil "] 7 | edition = "2018" 8 | homepage = "https://github.com/chipsenkbeil/vimwiki-rs" 9 | repository = "https://github.com/chipsenkbeil/vimwiki-rs" 10 | readme = "README.md" 11 | license = "MIT OR Apache-2.0" 12 | 13 | [features] 14 | default = [] 15 | timekeeper = ["vimwiki/timekeeper"] 16 | 17 | [[bin]] 18 | name = "vimwiki" 19 | path = "src/main.rs" 20 | 21 | [dependencies] 22 | directories = "3.0.2" 23 | jsonpath_lib = "0.2.6" 24 | lazy_static = "1.4.0" 25 | log = "0.4.14" 26 | serde = "1.0.126" 27 | serde_json = "1.0.64" 28 | sha-1 = "0.9.6" 29 | shellexpand = "2.1.0" 30 | stderrlog = "0.5.1" 31 | structopt = "0.3.21" 32 | tantivy = "0.17.0" 33 | toml = "0.5.8" 34 | walkdir = "2.3.2" 35 | vimvar = "0.3" 36 | vimwiki = { version = "=0.1.1", path = "../vimwiki", features = ["html"] } 37 | 38 | [dev-dependencies] 39 | indoc = "1.0.4" 40 | tempfile = "3.3.0" 41 | -------------------------------------------------------------------------------- /vimwiki-cli/README.md: -------------------------------------------------------------------------------- 1 | # vimwiki cli 2 | 3 | Provides command-line interface to expose parts of vimwiki library. 4 | 5 | ## Usage 6 | 7 | TODO - publish npm package and provide guidance 8 | 9 | ## Building from source 10 | 11 | TODO - provide build instructions 12 | 13 | ## License 14 | 15 | This project is licensed under either of 16 | 17 | Apache License, Version 2.0, (LICENSE-APACHE or 18 | [apache-license][apache-license]) MIT license (LICENSE-MIT or 19 | [mit-license][mit-license]) at your option. 20 | 21 | [apache-license]: http://www.apache.org/licenses/LICENSE-2.0 22 | [mit-license]: http://opensource.org/licenses/MIT 23 | -------------------------------------------------------------------------------- /vimwiki-cli/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod ast; 2 | mod css; 3 | mod opt; 4 | mod subcommand; 5 | mod utils; 6 | 7 | use ast::Ast; 8 | use log::*; 9 | use std::path::PathBuf; 10 | use structopt::StructOpt; 11 | use vimwiki::{HtmlConfig, VimwikiConfig}; 12 | 13 | pub use opt::*; 14 | 15 | pub enum ExitCodes { 16 | FailedToLoadConfig = 1, 17 | FailedToLoadData = 2, 18 | SubcommandFailed = 3, 19 | } 20 | 21 | impl ExitCodes { 22 | pub fn exit(self) -> ! { 23 | std::process::exit(self as i32); 24 | } 25 | } 26 | 27 | /// Loads CLI options from CLI arguments 28 | pub fn load_opt_from_args() -> Opt { 29 | Opt::from_args() 30 | } 31 | 32 | /// Runs the CLI using the provided options, returning success if completed 33 | /// or an error containing the appropriate exit code to return via [`Exitcodes::exit`] 34 | pub fn run(opt: Opt) -> Result<(), ExitCodes> { 35 | #[cfg(feature = "timekeeper")] 36 | let timekeeper = opt.common.timekeeper; 37 | 38 | #[cfg(feature = "timekeeper")] 39 | if timekeeper { 40 | vimwiki::timekeeper::enable(); 41 | } 42 | 43 | let res = match opt.subcommand { 44 | Subcommand::Convert(cmd) => { 45 | let (config, ast) = 46 | load_html_config_and_ast(&opt.common, &cmd.extra_paths)?; 47 | subcommand::convert(cmd, opt.common, config, ast) 48 | } 49 | Subcommand::Format(cmd) => { 50 | let config = load_format_config(&opt.common)?; 51 | subcommand::format(cmd, opt.common, config) 52 | } 53 | Subcommand::Serve(cmd) => { 54 | let (config, ast) = 55 | load_html_config_and_ast(&opt.common, &cmd.extra_paths)?; 56 | subcommand::serve(cmd, opt.common, config, ast) 57 | } 58 | Subcommand::Inspect(cmd) => { 59 | let (config, ast) = 60 | load_html_config_and_ast(&opt.common, &cmd.extra_paths)?; 61 | subcommand::inspect(cmd, opt.common, config, ast) 62 | } 63 | }; 64 | 65 | #[cfg(feature = "timekeeper")] 66 | if timekeeper { 67 | vimwiki::timekeeper::disable(); 68 | vimwiki::timekeeper::print_report(true); 69 | } 70 | 71 | if let Err(x) = res { 72 | error!("Subcommand failed: {}", x); 73 | return Err(ExitCodes::SubcommandFailed); 74 | } 75 | 76 | Ok(()) 77 | } 78 | 79 | fn load_format_config(opt: &CommonOpt) -> Result { 80 | if let Some(path) = opt.config.as_ref() { 81 | utils::load_format_config(path).map_err(|x| { 82 | error!("Failed to load config: {}", x); 83 | ExitCodes::FailedToLoadConfig 84 | }) 85 | } else { 86 | Ok(VimwikiConfig::default()) 87 | } 88 | } 89 | 90 | fn load_html_config_and_ast( 91 | opt: &CommonOpt, 92 | extra_paths: &[PathBuf], 93 | ) -> Result<(HtmlConfig, Ast), ExitCodes> { 94 | let config = match utils::load_html_config(opt, extra_paths) { 95 | Ok(config) => config, 96 | Err(x) => { 97 | error!("Failed to load config: {}", x); 98 | return Err(ExitCodes::FailedToLoadConfig); 99 | } 100 | }; 101 | 102 | let ast = match Ast::load( 103 | &config, 104 | &opt.include, 105 | &opt.cache, 106 | opt.no_cache, 107 | opt.no_prune_cache, 108 | ) { 109 | Ok(ast) => ast, 110 | Err(x) => { 111 | error!("Failed to load data: {}", x); 112 | return Err(ExitCodes::FailedToLoadData); 113 | } 114 | }; 115 | 116 | Ok((config, ast)) 117 | } 118 | -------------------------------------------------------------------------------- /vimwiki-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use vimwiki_cli as cli; 2 | 3 | fn main() { 4 | let opt = cli::load_opt_from_args(); 5 | init_logging(&opt.common); 6 | if let Err(x) = cli::run(opt) { 7 | x.exit(); 8 | } 9 | } 10 | 11 | fn init_logging(opt: &cli::CommonOpt) { 12 | stderrlog::new() 13 | .module("vimwiki_cli") 14 | .quiet(opt.quiet) 15 | .verbosity(opt.verbose) 16 | .timestamp(opt.timestamp.unwrap_or(stderrlog::Timestamp::Off)) 17 | .init() 18 | .unwrap(); 19 | } 20 | -------------------------------------------------------------------------------- /vimwiki-cli/src/subcommand/format.rs: -------------------------------------------------------------------------------- 1 | use crate::{CommonOpt, FormatSubcommand}; 2 | use log::*; 3 | use std::{collections::HashSet, ffi::OsStr, io, path::Path}; 4 | use vimwiki::*; 5 | use walkdir::WalkDir; 6 | 7 | pub fn format( 8 | cmd: FormatSubcommand, 9 | _opt: CommonOpt, 10 | config: VimwikiConfig, 11 | ) -> io::Result<()> { 12 | let extensions: HashSet = cmd.extensions.into_iter().collect(); 13 | 14 | for path in cmd.paths { 15 | // Need to make sure the path is legit 16 | let path = match path.canonicalize() { 17 | Ok(path) => path, 18 | Err(x) => { 19 | error!("{:?} failed to canonicalize: {}", path, x); 20 | return Err(x); 21 | } 22 | }; 23 | 24 | // If path is to a file, we want to process it directly regardless of 25 | // the extension 26 | if path.is_file() { 27 | process_file(config.clone(), path.as_path(), cmd.inline)?; 28 | 29 | // Otherwise, we walk the directory 30 | } else { 31 | for entry in WalkDir::new(path) 32 | .into_iter() 33 | .filter_map(|e| e.ok()) 34 | .filter(|e| e.file_type().is_file()) 35 | { 36 | if let Some(ext) = 37 | entry.path().extension().and_then(OsStr::to_str) 38 | { 39 | if extensions.contains(ext) { 40 | process_file(config.clone(), entry.path(), cmd.inline)?; 41 | } else { 42 | warn!( 43 | "{:?} :: skipped due to unrecognized extension ({})!", 44 | entry.path(), 45 | ext 46 | ); 47 | } 48 | } else { 49 | warn!( 50 | "{:?} :: skipped due to lack of extension!", 51 | entry.path(), 52 | ); 53 | } 54 | } 55 | } 56 | } 57 | 58 | Ok(()) 59 | } 60 | 61 | fn process_file( 62 | config: VimwikiConfig, 63 | input_path: &Path, 64 | inplace: bool, 65 | ) -> io::Result<()> { 66 | trace!( 67 | "process_file(_, input_path = {:?}, inplace = {})", 68 | input_path, 69 | inplace 70 | ); 71 | 72 | // Load the file's text 73 | let text = std::fs::read_to_string(input_path)?; 74 | 75 | debug!("{:?} :: file loaded!", input_path); 76 | 77 | // Convert file to a vimwiki page ast 78 | let page = 79 | Language::from_vimwiki_str(&text) 80 | .parse::() 81 | .map_err(|x| { 82 | io::Error::new(io::ErrorKind::InvalidData, x.to_string()) 83 | })?; 84 | 85 | debug!("{:?} :: page parsed!", input_path); 86 | 87 | // Convert page back to vimwiki text 88 | let text = page.to_vimwiki_string(config).map_err(|x| { 89 | io::Error::new(io::ErrorKind::InvalidData, x.to_string()) 90 | })?; 91 | 92 | debug!("{:?} :: vimwiki generated!", input_path); 93 | 94 | // If indicated, we replace the file's contents inline 95 | if inplace { 96 | info!("Writing to {:?}", input_path); 97 | std::fs::write(input_path, text)?; 98 | 99 | // Otherwise, print to stdout 100 | } else { 101 | println!("{}", text); 102 | } 103 | 104 | Ok(()) 105 | } 106 | -------------------------------------------------------------------------------- /vimwiki-cli/src/subcommand/inspect.rs: -------------------------------------------------------------------------------- 1 | use crate::{Ast, CommonOpt, InspectSubcommand}; 2 | use jsonpath_lib as jsonpath; 3 | use std::{ 4 | fs, 5 | io::{self, Write}, 6 | }; 7 | use vimwiki::HtmlConfig; 8 | 9 | pub fn inspect( 10 | cmd: InspectSubcommand, 11 | _opt: CommonOpt, 12 | _config: HtmlConfig, 13 | ast: Ast, 14 | ) -> io::Result<()> { 15 | let InspectSubcommand { 16 | output, json_path, .. 17 | } = cmd; 18 | 19 | let ast_json = serde_json::to_value(ast).map_err(io::Error::from)?; 20 | let values = 21 | jsonpath::select(&ast_json, json_path.as_str()).map_err(|x| { 22 | io::Error::new(io::ErrorKind::InvalidData, x.to_string()) 23 | })?; 24 | 25 | if let Some(path) = output { 26 | let file = fs::File::create(path)?; 27 | let mut writer = io::BufWriter::new(file); 28 | serde_json::to_writer_pretty(&mut writer, &values) 29 | .map_err(io::Error::from)?; 30 | writer.flush()?; 31 | Ok(()) 32 | } else { 33 | let stdout = io::stdout(); 34 | serde_json::to_writer_pretty(stdout, &values).map_err(io::Error::from) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vimwiki-cli/src/subcommand/mod.rs: -------------------------------------------------------------------------------- 1 | mod convert; 2 | mod format; 3 | mod inspect; 4 | mod serve; 5 | 6 | pub use convert::convert; 7 | pub use format::format; 8 | pub use inspect::inspect; 9 | pub use serve::serve; 10 | -------------------------------------------------------------------------------- /vimwiki-cli/src/subcommand/serve.rs: -------------------------------------------------------------------------------- 1 | use crate::{Ast, CommonOpt, ServeSubcommand}; 2 | use std::io; 3 | use vimwiki::HtmlConfig; 4 | 5 | pub fn serve( 6 | _cmd: ServeSubcommand, 7 | _opt: CommonOpt, 8 | _config: HtmlConfig, 9 | _ast: Ast, 10 | ) -> io::Result<()> { 11 | Ok(()) 12 | } 13 | -------------------------------------------------------------------------------- /vimwiki-cli/src/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::CommonOpt; 2 | use log::*; 3 | use std::{ 4 | io, 5 | path::{Path, PathBuf}, 6 | }; 7 | use vimwiki::{HtmlConfig, HtmlWikiConfig, VimwikiConfig}; 8 | 9 | /// Attempts to load a vimwiki format config from a file 10 | pub fn load_format_config(path: &Path) -> io::Result { 11 | trace!("load_format_config(path = {:?})", path); 12 | 13 | let config_string = std::fs::read_to_string(path)?; 14 | let config: VimwikiConfig = toml::from_str(config_string.as_str())?; 15 | 16 | Ok(config) 17 | } 18 | 19 | /// Attempts to load an html config from a file, attempting to load wikis from 20 | /// vim/neovim if no wikis are defined or if merge = true 21 | pub fn load_html_config( 22 | opt: &CommonOpt, 23 | extra_paths: &[PathBuf], 24 | ) -> io::Result { 25 | let CommonOpt { 26 | config, 27 | merge, 28 | include, 29 | .. 30 | } = opt; 31 | 32 | trace!( 33 | "load_html_config(path = {:?}, include = {:?}, merge = {}, extra_paths = {:?})", 34 | config, 35 | include, 36 | merge, 37 | extra_paths 38 | ); 39 | 40 | let mut config: HtmlConfig = if let Some(path) = config { 41 | let config_string = std::fs::read_to_string(path)?; 42 | toml::from_str(config_string.as_str())? 43 | } else { 44 | HtmlConfig::default() 45 | }; 46 | 47 | // Attempt to load wikis from vim if html config has no wikis or if 48 | // we are explicitly told to merge 49 | if config.wikis.is_empty() || *merge { 50 | // We attempt to load and parse our wiki content now, and if it fails 51 | // then we report over stderr and continue 52 | match load_vimwiki_list() { 53 | Ok(mut wikis) => { 54 | // Add our config wikis AFTER the vimwiki list to maintain order 55 | wikis.extend(config.wikis); 56 | config.wikis = wikis; 57 | } 58 | Err(x) => { 59 | warn!("Failed to load vimwiki_list from vim/neovim: {}", x) 60 | } 61 | } 62 | } 63 | 64 | // Add temporary wikis for standalone directories and files we are given 65 | for path in extra_paths.iter() { 66 | let path = match path.canonicalize() { 67 | Ok(path) => path, 68 | Err(x) => { 69 | error!("{:?} failed to canonicalize: {}", path, x); 70 | return Err(x); 71 | } 72 | }; 73 | debug!("Creating temporary wiki for {:?}", path); 74 | if path.is_dir() { 75 | config.wikis.push(HtmlWikiConfig { 76 | path: path.to_path_buf(), 77 | ..Default::default() 78 | }); 79 | } else if let Some(parent) = path.parent() { 80 | config.wikis.push(HtmlWikiConfig { 81 | path: parent.to_path_buf(), 82 | ..Default::default() 83 | }); 84 | } 85 | } 86 | 87 | // Finally, filter out wikis based on include logic 88 | config.wikis = config 89 | .wikis 90 | .into_iter() 91 | .enumerate() 92 | .filter(|(idx, wiki)| { 93 | opt.filter_by_wiki_idx_and_name(*idx, wiki.name.as_deref()) 94 | }) 95 | .map(|(_, wiki)| wiki) 96 | .collect(); 97 | 98 | Ok(config) 99 | } 100 | 101 | /// Loads g:vimwiki_list from vim/neovim and then attempts to convert it into 102 | /// a structured html wiki config 103 | fn load_vimwiki_list() -> std::io::Result> { 104 | trace!("load_vimwiki_list()"); 105 | 106 | let vimwiki_list_json = vimvar::load_global_var("vimwiki_list", false)?; 107 | trace!( 108 | "g:vimwiki_list == {:?}", 109 | serde_json::to_string_pretty(&vimwiki_list_json) 110 | ); 111 | 112 | if let Some(json) = vimwiki_list_json { 113 | serde_json::from_value(json).map_err(Into::into) 114 | } else { 115 | Ok(Vec::new()) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /vimwiki-core/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /vimwiki-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vimwiki-core" 3 | description = "Core library elements for vimwiki data structures, parsing, and more" 4 | categories = ["parser-implementations", "template-engine"] 5 | version = "0.1.1" 6 | authors = ["Chip Senkbeil "] 7 | edition = "2018" 8 | homepage = "https://github.com/chipsenkbeil/vimwiki-rs" 9 | repository = "https://github.com/chipsenkbeil/vimwiki-rs" 10 | readme = "README.md" 11 | license = "MIT OR Apache-2.0" 12 | 13 | [features] 14 | default = [] 15 | html = ["dirs", "relative-path", "shellexpand", "syntect", "voca_rs"] 16 | timekeeper = [] 17 | 18 | [[bench]] 19 | name = "vimwiki_parser" 20 | harness = false 21 | 22 | [dependencies.derive_more] 23 | version = "0.99.14" 24 | default-features = false 25 | features = [ 26 | "as_mut", 27 | "as_ref", 28 | "constructor", 29 | "deref", 30 | "deref_mut", 31 | "display", 32 | "error", 33 | "from", 34 | "index", 35 | "index_mut", 36 | "into", 37 | "into_iterator", 38 | "is_variant", 39 | "try_into", 40 | ] 41 | 42 | [dependencies] 43 | bytecount = "0.6.0" 44 | chrono = { version = "0.4.15", features = ["serde"] } 45 | lazy_static = "1.4" 46 | memchr = "2.3.3" 47 | nom = "6.1.2" 48 | numerals = "0.1.4" 49 | percent-encoding = "2.1.0" 50 | serde = { version = "1.0.115", features = ["derive"] } 51 | serde_with = "1.9.1" 52 | uriparse = { version = "0.6.3", features = ["serde"] } 53 | 54 | ### HTML-only features ### 55 | 56 | # For acquiring the home directory 57 | dirs = { version = "3.0.2", optional = true } 58 | 59 | # For support in calculating relative link urls 60 | relative-path = { version = "1.4.0", optional = true } 61 | 62 | # For translating ~/blah/blah and other paths into complete paths 63 | shellexpand = { version = "2.1.0", optional = true } 64 | 65 | # For server-side rendering of code blocks in HTML 66 | # NOTE: Using default-fancy so we can compile via webassembly elsewhere 67 | syntect = { version = "4.5.0", optional = true, default-features = false, features = ["default-fancy"] } 68 | 69 | # For safe HTML escaping 70 | voca_rs = { version = "1.13.0", optional = true } 71 | 72 | [dev-dependencies] 73 | criterion = "0.3.3" 74 | indoc = "1.0.2" 75 | similar-asserts = "1.1.0" 76 | vimwiki = { version = "=0.1.1", path = "../vimwiki", features = ["macros"] } 77 | walkdir = "2.3.2" 78 | -------------------------------------------------------------------------------- /vimwiki-core/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help build clean test 2 | 3 | help: ## Display help information 4 | @printf 'usage: make [target] ...\n\ntargets:\n' 5 | @egrep '^(.+)\:\ .*##\ (.+)' ${MAKEFILE_LIST} | sed 's/:.*##/#/' | column -t -c 2 -s '#' 6 | 7 | build: ## Build debug version 8 | @cargo build 9 | 10 | clean: ## Cleans build resources 11 | @cargo clean 12 | 13 | test: ## Run all tests 14 | @cargo test 15 | -------------------------------------------------------------------------------- /vimwiki-core/README.md: -------------------------------------------------------------------------------- 1 | # vimwiki-core 2 | 3 | This crate provides the core elements and logic for working with vimwiki. 4 | 5 | It should NOT be imported directly. Instead, when importing `vimwiki`, this 6 | crate is automatically brought in. 7 | 8 | ## License 9 | 10 | This project is licensed under either of 11 | 12 | Apache License, Version 2.0, (LICENSE-APACHE or 13 | [apache-license][apache-license]) MIT license (LICENSE-MIT or 14 | [mit-license][mit-license]) at your option. 15 | 16 | [apache-license]: http://www.apache.org/licenses/LICENSE-2.0 17 | [mit-license]: http://opensource.org/licenses/MIT 18 | -------------------------------------------------------------------------------- /vimwiki-core/benches/vimwiki_parser.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 2 | use std::{fs, path::PathBuf, time::Duration}; 3 | use vimwiki::{Language, Page}; 4 | 5 | fn parse_page_benchmark(c: &mut Criterion) { 6 | let base = 7 | PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("benches/fixtures"); 8 | let target = PathBuf::from("vimwiki/specification.wiki"); 9 | let path = base.join(target); 10 | let file_contents = 11 | fs::read_to_string(path).expect("Failed to load fixture"); 12 | 13 | c.bench_with_input( 14 | BenchmarkId::new("parse page", "specification.wiki"), 15 | &file_contents, 16 | |b, s| { 17 | let language = Language::from_vimwiki_str(s); 18 | b.iter(|| language.parse::().expect("Failed to parse")) 19 | }, 20 | ); 21 | } 22 | 23 | criterion_group! { 24 | name = benches; 25 | config = Criterion::default().measurement_time(Duration::new(15, 0)); 26 | targets = parse_page_benchmark 27 | } 28 | criterion_main!(benches); 29 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/blockquotes.rs: -------------------------------------------------------------------------------- 1 | use crate::{ElementLike, StrictEq}; 2 | use derive_more::{Constructor, Index, IndexMut, IntoIterator}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::{borrow::Cow, fmt, iter::FromIterator}; 5 | 6 | #[derive( 7 | Constructor, 8 | Clone, 9 | Debug, 10 | Eq, 11 | PartialEq, 12 | Hash, 13 | Index, 14 | IndexMut, 15 | IntoIterator, 16 | Serialize, 17 | Deserialize, 18 | )] 19 | pub struct Blockquote<'a> { 20 | /// Represents the lines of text contained within the blockquote include 21 | /// potential blank lines 22 | #[index] 23 | #[index_mut] 24 | #[into_iterator(owned, ref, ref_mut)] 25 | pub lines: Vec>, 26 | } 27 | 28 | impl ElementLike for Blockquote<'_> {} 29 | 30 | impl<'a> Blockquote<'a> { 31 | /// Returns total line groups available 32 | pub fn line_group_cnt(&self) -> usize { 33 | self.line_groups().count() 34 | } 35 | 36 | /// Returns an iterator over slices of lines where each item is a slice 37 | /// of lines representing a group of lines 38 | pub fn line_groups(&self) -> impl Iterator]> { 39 | self.lines 40 | .split(|line| line.is_empty()) 41 | .filter(|lines| !lines.is_empty()) 42 | } 43 | 44 | /// Returns an iterator over mutable slices of lines where each item is a slice 45 | /// of lines representing a group of lines 46 | pub fn mut_line_groups(&self) -> impl Iterator]> { 47 | self.lines 48 | .split(|line| line.is_empty()) 49 | .filter(|lines| !lines.is_empty()) 50 | } 51 | } 52 | 53 | impl Blockquote<'_> { 54 | pub fn to_borrowed(&self) -> Blockquote { 55 | use self::Cow::*; 56 | 57 | self.lines 58 | .iter() 59 | .map(|x| { 60 | Cow::Borrowed(match x { 61 | Borrowed(x) => *x, 62 | Owned(x) => x.as_str(), 63 | }) 64 | }) 65 | .collect() 66 | } 67 | 68 | pub fn into_owned(self) -> Blockquote<'static> { 69 | self.into_iter() 70 | .map(|x| Cow::from(x.into_owned())) 71 | .collect() 72 | } 73 | } 74 | 75 | impl<'a> fmt::Display for Blockquote<'a> { 76 | /// Writes out the blockquote by writing out each of its lines 77 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 78 | for line in self { 79 | writeln!(f, "{}", line)?; 80 | } 81 | 82 | Ok(()) 83 | } 84 | } 85 | 86 | impl<'a> FromIterator<&'a str> for Blockquote<'a> { 87 | fn from_iter>(iter: I) -> Self { 88 | Self { 89 | lines: iter.into_iter().map(Cow::Borrowed).collect(), 90 | } 91 | } 92 | } 93 | 94 | impl FromIterator for Blockquote<'static> { 95 | fn from_iter>(iter: I) -> Self { 96 | Self { 97 | lines: iter.into_iter().map(Cow::Owned).collect(), 98 | } 99 | } 100 | } 101 | 102 | impl<'a> FromIterator> for Blockquote<'a> { 103 | fn from_iter>>(iter: I) -> Self { 104 | Self { 105 | lines: iter.into_iter().collect(), 106 | } 107 | } 108 | } 109 | 110 | impl<'a> StrictEq for Blockquote<'a> { 111 | /// Same as PartialEq 112 | #[inline] 113 | fn strict_eq(&self, other: &Self) -> bool { 114 | self == other 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/code.rs: -------------------------------------------------------------------------------- 1 | use crate::{ElementLike, StrictEq}; 2 | use derive_more::{Constructor, Index, IndexMut, IntoIterator}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::{borrow::Cow, collections::HashMap, fmt, iter::FromIterator}; 5 | 6 | #[derive( 7 | Constructor, 8 | Clone, 9 | Debug, 10 | Eq, 11 | PartialEq, 12 | Index, 13 | IndexMut, 14 | IntoIterator, 15 | Serialize, 16 | Deserialize, 17 | )] 18 | pub struct CodeBlock<'a> { 19 | /// Represents the language associated with the code block if it has one 20 | pub language: Option>, 21 | 22 | /// Represents metadata associated with the code block in the form of 23 | /// key/value pairs 24 | pub metadata: HashMap, Cow<'a, str>>, 25 | 26 | /// Represents the lines of text contained within the code block 27 | #[index] 28 | #[index_mut] 29 | #[into_iterator(owned, ref, ref_mut)] 30 | pub lines: Vec>, 31 | } 32 | 33 | impl ElementLike for CodeBlock<'_> {} 34 | 35 | impl<'a> CodeBlock<'a> { 36 | /// Constructs a code block with the provided lines using no language or metadata 37 | pub fn from_lines, L: Into>>( 38 | iter: I, 39 | ) -> Self { 40 | Self { 41 | language: None, 42 | metadata: HashMap::new(), 43 | lines: iter.into_iter().map(Into::into).collect(), 44 | } 45 | } 46 | } 47 | 48 | impl CodeBlock<'_> { 49 | pub fn to_borrowed(&self) -> CodeBlock { 50 | use self::Cow::*; 51 | 52 | CodeBlock { 53 | language: self.language.as_ref().map(|x| { 54 | Cow::Borrowed(match x { 55 | Borrowed(x) => *x, 56 | Owned(x) => x.as_str(), 57 | }) 58 | }), 59 | metadata: self 60 | .metadata 61 | .iter() 62 | .map(|(key, value)| { 63 | let key = Cow::Borrowed(match key { 64 | Borrowed(x) => *x, 65 | Owned(x) => x.as_str(), 66 | }); 67 | let value = Cow::Borrowed(match value { 68 | Borrowed(x) => *x, 69 | Owned(x) => x.as_str(), 70 | }); 71 | 72 | (key, value) 73 | }) 74 | .collect(), 75 | lines: self 76 | .lines 77 | .iter() 78 | .map(|x| { 79 | Cow::Borrowed(match x { 80 | Borrowed(x) => *x, 81 | Owned(x) => x.as_str(), 82 | }) 83 | }) 84 | .collect(), 85 | } 86 | } 87 | 88 | pub fn into_owned(self) -> CodeBlock<'static> { 89 | CodeBlock { 90 | language: self.language.map(|x| Cow::from(x.into_owned())), 91 | metadata: self 92 | .metadata 93 | .into_iter() 94 | .map(|(key, value)| { 95 | (Cow::from(key.into_owned()), Cow::from(value.into_owned())) 96 | }) 97 | .collect(), 98 | lines: self 99 | .lines 100 | .into_iter() 101 | .map(|x| Cow::from(x.into_owned())) 102 | .collect(), 103 | } 104 | } 105 | } 106 | 107 | impl<'a> fmt::Display for CodeBlock<'a> { 108 | /// Writes out the code block by writing out each of its lines, separated 109 | /// by line feed 110 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 111 | write!(f, "{}", self.lines.join("\n")) 112 | } 113 | } 114 | 115 | impl<'a> FromIterator<&'a str> for CodeBlock<'a> { 116 | /// Produces a new code block using the given iterator as the 117 | /// code block's lines 118 | fn from_iter>(iter: I) -> Self { 119 | Self::from_lines(iter) 120 | } 121 | } 122 | 123 | impl FromIterator for CodeBlock<'static> { 124 | /// Produces a new code block using the given iterator as the 125 | /// code block's lines 126 | fn from_iter>(iter: I) -> Self { 127 | Self::from_lines(iter) 128 | } 129 | } 130 | 131 | impl<'a> FromIterator> for CodeBlock<'a> { 132 | /// Produces a new code block using the given iterator as the 133 | /// code block's lines 134 | fn from_iter>>(iter: I) -> Self { 135 | Self::from_lines(iter) 136 | } 137 | } 138 | 139 | impl<'a> StrictEq for CodeBlock<'a> { 140 | /// Same as PartialEq 141 | fn strict_eq(&self, other: &Self) -> bool { 142 | self == other 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/dividers.rs: -------------------------------------------------------------------------------- 1 | use crate::{ElementLike, StrictEq}; 2 | use derive_more::Constructor; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive( 6 | Constructor, Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, 7 | )] 8 | pub struct Divider; 9 | 10 | impl ElementLike for Divider {} 11 | 12 | impl StrictEq for Divider { 13 | /// Same as PartialEq 14 | #[inline] 15 | fn strict_eq(&self, other: &Self) -> bool { 16 | self == other 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/headers.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | lang::elements::{ 3 | InlineElement, InlineElementContainer, IntoChildren, Located, 4 | }, 5 | ElementLike, StrictEq, 6 | }; 7 | use derive_more::{Constructor, Index, IndexMut, IntoIterator}; 8 | use serde::{Deserialize, Serialize}; 9 | use std::fmt; 10 | 11 | #[derive( 12 | Constructor, 13 | Clone, 14 | Debug, 15 | Eq, 16 | PartialEq, 17 | Hash, 18 | Index, 19 | IndexMut, 20 | IntoIterator, 21 | Serialize, 22 | Deserialize, 23 | )] 24 | pub struct Header<'a> { 25 | /// Represents the content contained within the header 26 | #[index] 27 | #[index_mut] 28 | #[into_iterator(owned, ref, ref_mut)] 29 | pub content: InlineElementContainer<'a>, 30 | 31 | /// Represents the level of the header (1, 2, 3, etc) 32 | pub level: usize, 33 | 34 | /// Represents whether or not the header is centered 35 | pub centered: bool, 36 | } 37 | 38 | impl ElementLike for Header<'_> {} 39 | 40 | impl<'a> Header<'a> { 41 | /// Represents the smallest a header's level can be 42 | pub const MIN_LEVEL: usize = 1; 43 | 44 | /// Represents teh largest a header's level can be 45 | pub const MAX_LEVEL: usize = 6; 46 | } 47 | 48 | impl Header<'_> { 49 | pub fn to_borrowed(&self) -> Header { 50 | Header { 51 | level: self.level, 52 | content: self.content.to_borrowed(), 53 | centered: self.centered, 54 | } 55 | } 56 | 57 | pub fn into_owned(self) -> Header<'static> { 58 | Header { 59 | level: self.level, 60 | content: self.content.into_owned(), 61 | centered: self.centered, 62 | } 63 | } 64 | } 65 | 66 | impl<'a> fmt::Display for Header<'a> { 67 | /// Writes out the header by writing out its content using the underlying 68 | /// display impl 69 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 70 | write!(f, "{}", self.content) 71 | } 72 | } 73 | 74 | impl<'a> IntoChildren for Header<'a> { 75 | type Child = Located>; 76 | 77 | fn into_children(self) -> Vec { 78 | self.content.into_children() 79 | } 80 | } 81 | 82 | impl<'a> StrictEq for Header<'a> { 83 | /// Performs strict_eq on level, centered status, and content 84 | fn strict_eq(&self, other: &Self) -> bool { 85 | self.level == other.level 86 | && self.centered == other.centered 87 | && self.content.strict_eq(&other.content) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/inline/code.rs: -------------------------------------------------------------------------------- 1 | use crate::{ElementLike, StrictEq}; 2 | use derive_more::{AsRef, Constructor, Display, Into}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::borrow::Cow; 5 | 6 | #[derive( 7 | AsRef, 8 | Constructor, 9 | Clone, 10 | Debug, 11 | Display, 12 | Into, 13 | Eq, 14 | PartialEq, 15 | Hash, 16 | Serialize, 17 | Deserialize, 18 | )] 19 | #[as_ref(forward)] 20 | pub struct CodeInline<'a>( 21 | /// Represents the text contained within the inline code snippet 22 | Cow<'a, str>, 23 | ); 24 | 25 | impl ElementLike for CodeInline<'_> {} 26 | 27 | impl<'a> CodeInline<'a> { 28 | /// Extracts a string slice containing the entire code snippet 29 | /// 30 | /// # Examples 31 | /// 32 | /// Basic usage: 33 | /// 34 | /// ``` 35 | /// # use std::borrow::Cow; 36 | /// # use vimwiki_core::CodeInline; 37 | /// let code = CodeInline::new(Cow::Borrowed("some code")); 38 | /// assert_eq!(code.as_str(), "some code"); 39 | /// ``` 40 | pub fn as_str(&self) -> &str { 41 | self.0.as_ref() 42 | } 43 | } 44 | 45 | impl CodeInline<'_> { 46 | pub fn as_borrowed(&self) -> CodeInline { 47 | use self::Cow::*; 48 | 49 | let code = Cow::Borrowed(match &self.0 { 50 | Borrowed(x) => *x, 51 | Owned(x) => x.as_str(), 52 | }); 53 | 54 | CodeInline::new(code) 55 | } 56 | 57 | pub fn into_owned(self) -> CodeInline<'static> { 58 | let code = Cow::from(self.0.into_owned()); 59 | 60 | CodeInline::new(code) 61 | } 62 | } 63 | 64 | impl<'a> From<&'a str> for CodeInline<'a> { 65 | fn from(s: &'a str) -> Self { 66 | Self::new(Cow::Borrowed(s)) 67 | } 68 | } 69 | 70 | impl From for CodeInline<'static> { 71 | fn from(s: String) -> Self { 72 | Self::new(Cow::Owned(s)) 73 | } 74 | } 75 | 76 | impl<'a> StrictEq for CodeInline<'a> { 77 | /// Same as PartialEq 78 | #[inline] 79 | fn strict_eq(&self, other: &Self) -> bool { 80 | self == other 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/inline/links/anchor.rs: -------------------------------------------------------------------------------- 1 | use crate::StrictEq; 2 | use derive_more::{ 3 | Constructor, Deref, DerefMut, Index, IndexMut, IntoIterator, 4 | }; 5 | use serde::{Deserialize, Serialize}; 6 | use std::{borrow::Cow, convert::TryFrom, fmt}; 7 | use uriparse::{Fragment, FragmentError}; 8 | 9 | /// Represents an anchor 10 | #[derive( 11 | Constructor, 12 | Clone, 13 | Debug, 14 | Deref, 15 | DerefMut, 16 | Eq, 17 | PartialEq, 18 | Hash, 19 | Index, 20 | IndexMut, 21 | IntoIterator, 22 | Serialize, 23 | Deserialize, 24 | )] 25 | #[into_iterator(owned, ref, ref_mut)] 26 | pub struct Anchor<'a>( 27 | /// Represents the individual parts of the anchor 28 | Vec>, 29 | ); 30 | 31 | impl Anchor<'_> { 32 | pub fn to_borrowed(&self) -> Anchor { 33 | use self::Cow::*; 34 | 35 | let elements = self 36 | .into_iter() 37 | .map(|x| { 38 | Cow::Borrowed(match x { 39 | Borrowed(x) => *x, 40 | Owned(x) => x.as_str(), 41 | }) 42 | }) 43 | .collect(); 44 | 45 | Anchor::new(elements) 46 | } 47 | 48 | pub fn into_owned(self) -> Anchor<'static> { 49 | let elements = self 50 | .into_iter() 51 | .map(|x| Cow::from(x.into_owned())) 52 | .collect(); 53 | 54 | Anchor::new(elements) 55 | } 56 | } 57 | 58 | impl<'a> Anchor<'a> { 59 | /// Produces an encoded URI fragment in the form of #my%23fragment 60 | /// if the anchor has any elements, otherwise yields an empty string 61 | pub fn to_encoded_uri_fragment(&self) -> String { 62 | use std::fmt::Write; 63 | let mut fragment = String::new(); 64 | if !self.is_empty() { 65 | write!(&mut fragment, "#{}", self.0.join("%23")).expect( 66 | "Anchor encoded_uri_fragment returned error unexpectedly", 67 | ); 68 | } 69 | fragment 70 | } 71 | 72 | // NOTE: Cannot use FromStr due to conflicting lifetimes of impl and trait 73 | // method's input str 74 | /// Parses a uri fragment in the form of `#one#two#three` with a starting 75 | /// pound sign 76 | pub fn from_uri_fragment(s: &'a str) -> Option { 77 | let mut it = s.split('#'); 78 | 79 | if let Some(piece) = it.next() { 80 | if piece.is_empty() { 81 | return Some(it.collect()); 82 | } 83 | } 84 | 85 | None 86 | } 87 | } 88 | 89 | impl<'a> fmt::Display for Anchor<'a> { 90 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 91 | if self.is_empty() { 92 | Ok(()) 93 | } else { 94 | write!(f, "#{}", self.0.join("#")) 95 | } 96 | } 97 | } 98 | 99 | impl<'a> std::iter::FromIterator<&'a str> for Anchor<'a> { 100 | fn from_iter>(iter: I) -> Self { 101 | Self::new(iter.into_iter().map(Cow::from).collect()) 102 | } 103 | } 104 | 105 | impl std::iter::FromIterator for Anchor<'static> { 106 | fn from_iter>(iter: I) -> Self { 107 | Self::new(iter.into_iter().map(Cow::from).collect()) 108 | } 109 | } 110 | 111 | impl From for Anchor<'static> { 112 | fn from(s: String) -> Self { 113 | Self::new(vec![Cow::from(s)]) 114 | } 115 | } 116 | 117 | impl<'a> From<&'a str> for Anchor<'a> { 118 | fn from(s: &'a str) -> Self { 119 | Self::new(vec![Cow::from(s)]) 120 | } 121 | } 122 | 123 | impl<'a> TryFrom> for Fragment<'static> { 124 | type Error = FragmentError; 125 | 126 | /// Consumes an anchor, producing a newly-allocated fragment 127 | fn try_from(anchor: Anchor<'a>) -> Result { 128 | Fragment::try_from(anchor.0.join("-").as_str()) 129 | .map(Fragment::into_owned) 130 | } 131 | } 132 | 133 | impl<'a> StrictEq for Anchor<'a> { 134 | /// Same as PartialEq 135 | #[inline] 136 | fn strict_eq(&self, other: &Self) -> bool { 137 | self == other 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/inline/links/description.rs: -------------------------------------------------------------------------------- 1 | use super::LinkData; 2 | use crate::StrictEq; 3 | use derive_more::{Display, From, IsVariant, TryInto}; 4 | use serde::{Deserialize, Serialize}; 5 | use std::{borrow::Cow, convert::TryFrom}; 6 | use uriparse::{URIReference, URIReferenceError}; 7 | 8 | /// Represents a description for a link 9 | #[derive( 10 | Clone, 11 | Debug, 12 | Display, 13 | From, 14 | TryInto, 15 | Eq, 16 | PartialEq, 17 | Hash, 18 | IsVariant, 19 | Serialize, 20 | Deserialize, 21 | )] 22 | #[serde(rename_all = "snake_case", tag = "type", content = "data")] 23 | pub enum Description<'a> { 24 | Text(Cow<'a, str>), 25 | TransclusionLink(Box>), 26 | } 27 | 28 | impl<'a> Description<'a> { 29 | pub fn into_uri_ref(self) -> Option> { 30 | match self { 31 | Self::TransclusionLink(x) => Some(x.uri_ref), 32 | _ => None, 33 | } 34 | } 35 | 36 | pub fn try_from_uri_ref_str( 37 | s: &'a str, 38 | ) -> Result, URIReferenceError> { 39 | Ok(Description::TransclusionLink(Box::new(LinkData::try_from( 40 | s, 41 | )?))) 42 | } 43 | } 44 | 45 | impl Description<'_> { 46 | pub fn to_borrowed(&self) -> Description { 47 | use self::Cow::*; 48 | 49 | match self { 50 | Self::Text(ref x) => Description::from(Cow::Borrowed(match x { 51 | Borrowed(x) => *x, 52 | Owned(x) => x.as_str(), 53 | })), 54 | Self::TransclusionLink(ref x) => Description::from(x.to_borrowed()), 55 | } 56 | } 57 | 58 | pub fn into_owned(self) -> Description<'static> { 59 | match self { 60 | Self::Text(x) => Description::from(Cow::from(x.into_owned())), 61 | Self::TransclusionLink(x) => Description::from(x.into_owned()), 62 | } 63 | } 64 | } 65 | 66 | impl<'a> From<&'a str> for Description<'a> { 67 | fn from(s: &'a str) -> Self { 68 | Self::from(Cow::Borrowed(s)) 69 | } 70 | } 71 | 72 | impl From for Description<'static> { 73 | fn from(s: String) -> Self { 74 | Self::from(Cow::Owned(s)) 75 | } 76 | } 77 | 78 | impl<'a> From> for Description<'a> { 79 | fn from(uri_ref: URIReference<'a>) -> Self { 80 | Self::from(LinkData::from(uri_ref)) 81 | } 82 | } 83 | 84 | impl<'a> From> for Description<'a> { 85 | fn from(link_data: LinkData<'a>) -> Self { 86 | Self::TransclusionLink(Box::new(link_data)) 87 | } 88 | } 89 | 90 | impl<'a> StrictEq for Description<'a> { 91 | /// Same as PartialEq 92 | #[inline] 93 | fn strict_eq(&self, other: &Self) -> bool { 94 | self == other 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/inline/math.rs: -------------------------------------------------------------------------------- 1 | use crate::{ElementLike, StrictEq}; 2 | use derive_more::{AsRef, Constructor, Display}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::borrow::Cow; 5 | 6 | #[derive( 7 | AsRef, 8 | Constructor, 9 | Clone, 10 | Debug, 11 | Display, 12 | Eq, 13 | PartialEq, 14 | Hash, 15 | Serialize, 16 | Deserialize, 17 | )] 18 | #[as_ref(forward)] 19 | pub struct MathInline<'a>( 20 | /// Represents the text contained within the inline math snippet 21 | Cow<'a, str>, 22 | ); 23 | 24 | impl ElementLike for MathInline<'_> {} 25 | 26 | impl<'a> MathInline<'a> { 27 | /// Extracts a string slice containing the entire math snippet 28 | /// 29 | /// # Examples 30 | /// 31 | /// Basic usage: 32 | /// 33 | /// ``` 34 | /// # use std::borrow::Cow; 35 | /// # use vimwiki_core::MathInline; 36 | /// let math = MathInline::new(Cow::Borrowed("some math")); 37 | /// assert_eq!(math.as_str(), "some math"); 38 | /// ``` 39 | pub fn as_str(&self) -> &str { 40 | self.0.as_ref() 41 | } 42 | } 43 | 44 | impl MathInline<'_> { 45 | pub fn as_borrowed(&self) -> MathInline { 46 | use self::Cow::*; 47 | 48 | let formula = Cow::Borrowed(match &self.0 { 49 | Borrowed(x) => *x, 50 | Owned(x) => x.as_str(), 51 | }); 52 | 53 | MathInline::new(formula) 54 | } 55 | 56 | pub fn into_owned(self) -> MathInline<'static> { 57 | let formula = Cow::Owned(self.0.into_owned()); 58 | 59 | MathInline::new(formula) 60 | } 61 | } 62 | 63 | impl<'a> From<&'a str> for MathInline<'a> { 64 | fn from(s: &'a str) -> Self { 65 | Self::new(Cow::Borrowed(s)) 66 | } 67 | } 68 | 69 | impl From for MathInline<'static> { 70 | fn from(s: String) -> Self { 71 | Self::new(Cow::Owned(s)) 72 | } 73 | } 74 | 75 | impl<'a> StrictEq for MathInline<'a> { 76 | /// Same as PartialEq 77 | #[inline] 78 | fn strict_eq(&self, other: &Self) -> bool { 79 | self == other 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/math.rs: -------------------------------------------------------------------------------- 1 | use crate::{ElementLike, StrictEq}; 2 | use derive_more::{Constructor, Index, IndexMut, IntoIterator}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::{borrow::Cow, fmt, iter::FromIterator}; 5 | 6 | #[derive( 7 | Constructor, 8 | Clone, 9 | Debug, 10 | Eq, 11 | PartialEq, 12 | Hash, 13 | Index, 14 | IndexMut, 15 | IntoIterator, 16 | Serialize, 17 | Deserialize, 18 | )] 19 | pub struct MathBlock<'a> { 20 | /// Represents the lines of text contained within the math block 21 | #[index] 22 | #[index_mut] 23 | #[into_iterator(owned, ref, ref_mut)] 24 | pub lines: Vec>, 25 | 26 | /// Represents the environment associated with the math block if it has one 27 | pub environment: Option>, 28 | } 29 | 30 | impl ElementLike for MathBlock<'_> {} 31 | 32 | impl<'a> MathBlock<'a> { 33 | /// Constructs a math block with the provided lines using no environment 34 | pub fn from_lines, L: Into>>( 35 | iter: I, 36 | ) -> Self { 37 | Self { 38 | lines: iter.into_iter().map(Into::into).collect(), 39 | environment: None, 40 | } 41 | } 42 | } 43 | 44 | impl MathBlock<'_> { 45 | pub fn to_borrowed(&self) -> MathBlock { 46 | use self::Cow::*; 47 | 48 | MathBlock { 49 | lines: self 50 | .lines 51 | .iter() 52 | .map(|x| { 53 | Cow::Borrowed(match x { 54 | Borrowed(x) => *x, 55 | Owned(x) => x.as_str(), 56 | }) 57 | }) 58 | .collect(), 59 | environment: self.environment.as_ref().map(|x| { 60 | Cow::Borrowed(match &x { 61 | Borrowed(x) => *x, 62 | Owned(x) => x.as_str(), 63 | }) 64 | }), 65 | } 66 | } 67 | 68 | pub fn into_owned(self) -> MathBlock<'static> { 69 | MathBlock { 70 | lines: self 71 | .lines 72 | .into_iter() 73 | .map(|x| Cow::from(x.into_owned())) 74 | .collect(), 75 | environment: self.environment.map(|x| Cow::from(x.into_owned())), 76 | } 77 | } 78 | } 79 | 80 | impl<'a> fmt::Display for MathBlock<'a> { 81 | /// Writes out the math block by writing out each of its lines, separated 82 | /// by line feed 83 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 84 | write!(f, "{}", self.lines.join("\n")) 85 | } 86 | } 87 | 88 | impl<'a> FromIterator<&'a str> for MathBlock<'a> { 89 | /// Produces a new math block using the given iterator as the 90 | /// math block's lines 91 | fn from_iter>(iter: I) -> Self { 92 | Self::from_lines(iter) 93 | } 94 | } 95 | 96 | impl FromIterator for MathBlock<'static> { 97 | /// Produces a new math block using the given iterator as the 98 | /// math block's lines 99 | fn from_iter>(iter: I) -> Self { 100 | Self::from_lines(iter) 101 | } 102 | } 103 | 104 | impl<'a> FromIterator> for MathBlock<'a> { 105 | /// Produces a new math block using the given iterator as the 106 | /// math block's lines 107 | fn from_iter>>(iter: I) -> Self { 108 | Self::from_lines(iter) 109 | } 110 | } 111 | 112 | impl<'a> StrictEq for MathBlock<'a> { 113 | /// Same as PartialEq 114 | fn strict_eq(&self, other: &Self) -> bool { 115 | self == other 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/paragraphs.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | lang::elements::{ 3 | InlineElement, InlineElementContainer, IntoChildren, Located, 4 | }, 5 | ElementLike, StrictEq, 6 | }; 7 | use derive_more::{Constructor, Index, IndexMut, IntoIterator}; 8 | use serde::{Deserialize, Serialize}; 9 | use std::{fmt, iter::FromIterator}; 10 | 11 | #[derive( 12 | Constructor, 13 | Clone, 14 | Debug, 15 | Eq, 16 | PartialEq, 17 | Index, 18 | IndexMut, 19 | IntoIterator, 20 | Serialize, 21 | Deserialize, 22 | )] 23 | pub struct Paragraph<'a> { 24 | /// Represents the lines of content contained within the paragraph 25 | #[index] 26 | #[index_mut] 27 | #[into_iterator(owned, ref, ref_mut)] 28 | pub lines: Vec>, 29 | } 30 | 31 | impl ElementLike for Paragraph<'_> {} 32 | 33 | impl<'a> Paragraph<'a> { 34 | /// Returns true if the paragraph only contains blank lines (or has no 35 | /// lines at all) 36 | pub fn is_blank(&self) -> bool { 37 | self.nonblank_lines().count() == 0 38 | } 39 | 40 | /// Returns an iterator over all lines that are not blank, meaning that 41 | /// they contain non-comment inline elements 42 | pub fn nonblank_lines( 43 | &self, 44 | ) -> impl Iterator> { 45 | self.into_iter().filter(|line| { 46 | line.into_iter() 47 | .any(|e| !matches!(e.as_inner(), InlineElement::Comment(_))) 48 | }) 49 | } 50 | } 51 | 52 | impl Paragraph<'_> { 53 | pub fn to_borrowed(&self) -> Paragraph { 54 | Paragraph::new(self.into_iter().map(|x| x.to_borrowed()).collect()) 55 | } 56 | 57 | pub fn into_owned(self) -> Paragraph<'static> { 58 | Paragraph::new(self.into_iter().map(|x| x.into_owned()).collect()) 59 | } 60 | } 61 | 62 | impl<'a> fmt::Display for Paragraph<'a> { 63 | /// Writes out the paragraph by writing out each of its lines using their 64 | /// underlying display impl, separated by line feed 65 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 66 | for (idx, line) in self.into_iter().enumerate() { 67 | write!(f, "{}", line)?; 68 | 69 | // For all lines but last, we add a newline 70 | if idx + 1 < self.lines.len() { 71 | writeln!(f)?; 72 | } 73 | } 74 | 75 | Ok(()) 76 | } 77 | } 78 | 79 | impl<'a> IntoChildren for Paragraph<'a> { 80 | type Child = Located>; 81 | 82 | fn into_children(self) -> Vec { 83 | self.into_iter().flat_map(|x| x.into_children()).collect() 84 | } 85 | } 86 | 87 | impl<'a> FromIterator> for Paragraph<'a> { 88 | fn from_iter>>( 89 | iter: I, 90 | ) -> Self { 91 | Self::new(iter.into_iter().collect()) 92 | } 93 | } 94 | 95 | impl<'a> StrictEq for Paragraph<'a> { 96 | /// Performs strict_eq on content 97 | fn strict_eq(&self, other: &Self) -> bool { 98 | self.lines.strict_eq(&other.lines) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/elements/blocks/placeholders.rs: -------------------------------------------------------------------------------- 1 | use crate::{ElementLike, StrictEq}; 2 | use chrono::NaiveDate; 3 | use serde::{Deserialize, Serialize}; 4 | use std::borrow::Cow; 5 | 6 | #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] 7 | #[serde(rename_all = "snake_case", tag = "type", content = "data")] 8 | pub enum Placeholder<'a> { 9 | Title(Cow<'a, str>), 10 | NoHtml, 11 | Template(Cow<'a, str>), 12 | Date(NaiveDate), 13 | Other { 14 | name: Cow<'a, str>, 15 | value: Cow<'a, str>, 16 | }, 17 | } 18 | 19 | impl ElementLike for Placeholder<'_> {} 20 | 21 | impl Placeholder<'_> { 22 | pub fn to_borrowed(&self) -> Placeholder { 23 | use self::Cow::*; 24 | match self { 25 | Self::Title(ref x) => Placeholder::Title(Cow::Borrowed(match x { 26 | Borrowed(x) => *x, 27 | Owned(x) => x.as_str(), 28 | })), 29 | Self::NoHtml => Placeholder::NoHtml, 30 | Self::Template(ref x) => { 31 | Placeholder::Template(Cow::Borrowed(match x { 32 | Borrowed(x) => *x, 33 | Owned(x) => x.as_str(), 34 | })) 35 | } 36 | Self::Date(x) => Placeholder::Date(*x), 37 | Self::Other { 38 | ref name, 39 | ref value, 40 | } => Placeholder::Other { 41 | name: Cow::Borrowed(match name { 42 | Borrowed(x) => *x, 43 | Owned(x) => x.as_str(), 44 | }), 45 | value: Cow::Borrowed(match value { 46 | Borrowed(x) => *x, 47 | Owned(x) => x.as_str(), 48 | }), 49 | }, 50 | } 51 | } 52 | 53 | pub fn into_owned(self) -> Placeholder<'static> { 54 | match self { 55 | Self::Title(x) => Placeholder::Title(Cow::from(x.into_owned())), 56 | Self::NoHtml => Placeholder::NoHtml, 57 | Self::Template(x) => { 58 | Placeholder::Template(Cow::from(x.into_owned())) 59 | } 60 | Self::Date(x) => Placeholder::Date(x), 61 | Self::Other { name, value } => Placeholder::Other { 62 | name: Cow::from(name.into_owned()), 63 | value: Cow::from(value.into_owned()), 64 | }, 65 | } 66 | } 67 | } 68 | 69 | impl<'a> Placeholder<'a> { 70 | pub fn title_from_str(title: &'a str) -> Self { 71 | Self::Title(Cow::from(title)) 72 | } 73 | 74 | pub fn title_from_string(title: String) -> Self { 75 | Self::Title(Cow::from(title)) 76 | } 77 | 78 | pub fn template_from_str(template: &'a str) -> Self { 79 | Self::Template(Cow::from(template)) 80 | } 81 | 82 | pub fn template_from_string(template: String) -> Self { 83 | Self::Template(Cow::from(template)) 84 | } 85 | 86 | pub fn other_from_str(name: &'a str, value: &'a str) -> Self { 87 | Self::Other { 88 | name: Cow::from(name), 89 | value: Cow::from(value), 90 | } 91 | } 92 | 93 | pub fn other_from_string(name: String, value: String) -> Self { 94 | Self::Other { 95 | name: Cow::from(name), 96 | value: Cow::from(value), 97 | } 98 | } 99 | } 100 | 101 | impl<'a> StrictEq for Placeholder<'a> { 102 | /// Same as PartialEq 103 | fn strict_eq(&self, other: &Self) -> bool { 104 | self == other 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/output/html/error.rs: -------------------------------------------------------------------------------- 1 | use super::LinkResolutionError; 2 | use derive_more::{Display, Error}; 3 | use uriparse::{PathError, RelativeReferenceError, URIReferenceError}; 4 | 5 | pub type HtmlOutputResult = Result<(), HtmlOutputError>; 6 | 7 | #[derive(Debug, Display, Error)] 8 | pub enum HtmlOutputError { 9 | SyntaxOrThemeNotLoaded { 10 | #[error(source)] 11 | source: syntect::LoadingError, 12 | }, 13 | 14 | FailedToConstructUri { 15 | #[error(source)] 16 | source: URIReferenceError, 17 | }, 18 | 19 | FailedToConstructRelativeReference { 20 | #[error(source)] 21 | source: RelativeReferenceError, 22 | }, 23 | 24 | FailedToModifyUriPath { 25 | #[error(source)] 26 | source: PathError, 27 | }, 28 | 29 | FailedToResolveLink { 30 | #[error(source)] 31 | source: LinkResolutionError, 32 | }, 33 | 34 | ThemeMissing(#[error(not(source))] String), 35 | 36 | MissingWikiAtIndex(#[error(not(source))] usize), 37 | 38 | MissingWikiWithName(#[error(not(source))] String), 39 | 40 | TemplateNotLoaded { 41 | #[error(source)] 42 | source: std::io::Error, 43 | }, 44 | 45 | Fmt { 46 | #[error(source)] 47 | source: std::fmt::Error, 48 | }, 49 | } 50 | 51 | impl From for HtmlOutputError { 52 | fn from(source: URIReferenceError) -> Self { 53 | Self::FailedToConstructUri { source } 54 | } 55 | } 56 | 57 | impl From for HtmlOutputError { 58 | fn from(source: RelativeReferenceError) -> Self { 59 | Self::FailedToConstructRelativeReference { source } 60 | } 61 | } 62 | 63 | impl From for HtmlOutputError { 64 | fn from(source: PathError) -> Self { 65 | Self::FailedToModifyUriPath { source } 66 | } 67 | } 68 | 69 | impl From for HtmlOutputError { 70 | fn from(source: std::fmt::Error) -> Self { 71 | Self::Fmt { source } 72 | } 73 | } 74 | 75 | impl From for HtmlOutputError { 76 | fn from(source: LinkResolutionError) -> Self { 77 | Self::FailedToResolveLink { source } 78 | } 79 | } 80 | 81 | impl From for HtmlOutputError { 82 | fn from(source: syntect::LoadingError) -> Self { 83 | Self::SyntaxOrThemeNotLoaded { source } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/output/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "html")] 2 | mod html; 3 | #[cfg(feature = "html")] 4 | pub use html::*; 5 | 6 | mod vimwiki; 7 | pub use self::vimwiki::*; 8 | 9 | use std::{error::Error, fmt}; 10 | 11 | /// Represents the ability to convert some data into some other output form 12 | pub trait Output { 13 | /// Formats the value using the given formatter 14 | fn fmt(&self, f: &mut F) -> Result<(), F::Error>; 15 | } 16 | 17 | /// Represents a formatter used by some output struct 18 | pub trait OutputFormatter: fmt::Write { 19 | /// Represents the types of errors that can be encountered when using 20 | /// this formatter 21 | type Error: Error; 22 | } 23 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/output/vimwiki/convert.rs: -------------------------------------------------------------------------------- 1 | use super::{Output, VimwikiConfig, VimwikiFormatter, VimwikiOutputError}; 2 | 3 | pub trait ToVimwikiString { 4 | fn to_vimwiki_string( 5 | &self, 6 | config: VimwikiConfig, 7 | ) -> Result; 8 | } 9 | 10 | impl> ToVimwikiString for T { 11 | fn to_vimwiki_string( 12 | &self, 13 | config: VimwikiConfig, 14 | ) -> Result { 15 | let mut formatter = VimwikiFormatter::new(config); 16 | self.fmt(&mut formatter)?; 17 | Ok(formatter.into_content()) 18 | } 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use super::*; 24 | use crate::VimwikiOutputResult; 25 | 26 | struct TestOutput VimwikiOutputResult>(F); 27 | impl VimwikiOutputResult> 28 | Output for TestOutput 29 | { 30 | fn fmt(&self, f: &mut VimwikiFormatter) -> VimwikiOutputResult { 31 | self.0(f)?; 32 | Ok(()) 33 | } 34 | } 35 | 36 | fn _text( 37 | text: impl Into, 38 | ) -> impl Fn(&mut VimwikiFormatter) -> VimwikiOutputResult { 39 | let text = text.into(); 40 | move |f: &mut VimwikiFormatter| { 41 | use std::fmt::Write; 42 | write!(f, "{}", text.as_str())?; 43 | Ok(()) 44 | } 45 | } 46 | 47 | #[test] 48 | fn to_vimwiki_string_should_produce_a_string_representing_only_the_vimwiki_of_the_output( 49 | ) { 50 | let output = TestOutput(_text("I am some vimwiki output")); 51 | let result = 52 | output.to_vimwiki_string(VimwikiConfig::default()).unwrap(); 53 | assert_eq!(result, "I am some vimwiki output"); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/output/vimwiki/error.rs: -------------------------------------------------------------------------------- 1 | use derive_more::{Display, Error, From}; 2 | 3 | pub type VimwikiOutputResult = Result<(), VimwikiOutputError>; 4 | 5 | #[derive(Debug, From, Display, Error)] 6 | pub enum VimwikiOutputError { 7 | Fmt { 8 | #[error(source)] 9 | source: std::fmt::Error, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/output/vimwiki/formatter.rs: -------------------------------------------------------------------------------- 1 | use super::{OutputFormatter, VimwikiConfig, VimwikiOutputError}; 2 | use std::fmt::{self, Write}; 3 | 4 | /// Represents the formatter to use to write ivimwiki output that includes various 5 | /// options that can be set as well as a context for use when writing output 6 | #[derive(Clone, Default)] 7 | pub struct VimwikiFormatter { 8 | /// Represents the configuration associated with the formatter 9 | config: VimwikiConfig, 10 | 11 | /// Contains the content to be injected into a template 12 | content: String, 13 | 14 | /// Current level of indentation to use when writing vimwiki elements that 15 | /// care about indentation 16 | pub indent_level: usize, 17 | 18 | /// If true, will skip writing whitespace until the first non-whitespace 19 | /// character is provided, in which case this is reset to false 20 | skip_whitespace: bool, 21 | } 22 | 23 | impl OutputFormatter for VimwikiFormatter { 24 | type Error = VimwikiOutputError; 25 | } 26 | 27 | impl Write for VimwikiFormatter { 28 | fn write_str(&mut self, s: &str) -> fmt::Result { 29 | // If flagged to skip whitespace, we want to skip all whitespace 30 | // until we see the first non-whitespace character 31 | let s = if self.skip_whitespace { 32 | let tmp = s.trim_start(); 33 | if !tmp.is_empty() { 34 | self.skip_whitespace = false; 35 | } 36 | tmp 37 | } else { 38 | s 39 | }; 40 | 41 | self.content.write_str(s) 42 | } 43 | } 44 | 45 | impl VimwikiFormatter { 46 | pub fn new(config: VimwikiConfig) -> Self { 47 | Self { 48 | config, 49 | content: String::new(), 50 | indent_level: 0, 51 | skip_whitespace: false, 52 | } 53 | } 54 | 55 | pub fn clone_without_content(&self) -> Self { 56 | Self { 57 | content: String::new(), 58 | ..self.clone() 59 | } 60 | } 61 | 62 | /// Writes a string representing the indentation for the current level, 63 | /// only if the current content is either empty or the very last character 64 | /// was a linefeed 65 | pub fn write_indent(&mut self) -> Result<(), VimwikiOutputError> { 66 | if self.content.is_empty() || self.content.ends_with('\n') { 67 | let indent_str = self.config.page.indent_str.to_string(); 68 | 69 | for _ in 0..self.indent_level { 70 | write!(self, "{}", indent_str)?; 71 | } 72 | } 73 | 74 | Ok(()) 75 | } 76 | 77 | /// Invokes the given function, passing it a mutable reference to this 78 | /// formatter where the indentation level has been incremented by 1 and 79 | /// will be decremented at the end of the function call 80 | pub fn and_indent(&mut self, f: F) -> Result<(), VimwikiOutputError> 81 | where 82 | F: FnOnce(&mut Self) -> Result<(), VimwikiOutputError>, 83 | { 84 | self.indent_level += 1; 85 | let result = f(self); 86 | self.indent_level -= 1; 87 | result 88 | } 89 | 90 | /// Invokes the given function, passing it a mutable reference to this 91 | /// formatter where all leading and trailing whitespace the mutable ref 92 | /// produces will be removed 93 | pub fn and_trim(&mut self, f: F) -> Result<(), VimwikiOutputError> 94 | where 95 | F: FnOnce(&mut Self) -> Result<(), VimwikiOutputError>, 96 | { 97 | self.skip_whitespace(f)?; 98 | self.trim_end(); 99 | Ok(()) 100 | } 101 | 102 | /// Invokes the given function, passing it a mutable reference to this 103 | /// formatter with a flag set to skip all whitespace until the first 104 | /// non-whitespace character is written to it 105 | pub fn skip_whitespace(&mut self, f: F) -> Result<(), VimwikiOutputError> 106 | where 107 | F: FnOnce(&mut Self) -> Result<(), VimwikiOutputError>, 108 | { 109 | self.skip_whitespace = true; 110 | let result = f(self); 111 | self.skip_whitespace = false; 112 | result 113 | } 114 | 115 | /// Removes whitespace from end of current output content 116 | pub fn trim_end(&mut self) { 117 | let diff = self.content.len() - self.content.trim_end().len(); 118 | self.content.truncate(self.content.len() - diff); 119 | } 120 | 121 | /// Represents the config contained within the formatter 122 | #[inline] 123 | pub fn config(&self) -> &VimwikiConfig { 124 | &self.config 125 | } 126 | 127 | pub fn clear_content(&mut self) { 128 | self.content.clear(); 129 | } 130 | 131 | pub fn get_content(&self) -> &str { 132 | self.content.as_str() 133 | } 134 | 135 | pub fn into_content(self) -> String { 136 | self.content 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/errors.rs: -------------------------------------------------------------------------------- 1 | use super::Span; 2 | use nom::error::{ContextError, ErrorKind, FromExternalError, ParseError}; 3 | use std::{borrow::Cow, fmt}; 4 | 5 | /// Represents an encapsulated error that is encountered 6 | #[derive(Clone, Debug, Default, Eq, PartialEq)] 7 | pub struct LangParserError<'a> { 8 | ctx: Cow<'a, str>, 9 | input: Span<'a>, 10 | next: Option>, 11 | } 12 | 13 | impl<'a> From>> for LangParserError<'a> { 14 | fn from(nom_err: nom::Err>) -> Self { 15 | match nom_err { 16 | nom::Err::Error(x) | nom::Err::Failure(x) => x, 17 | nom::Err::Incomplete(_) => { 18 | Self::from_ctx(&Span::default(), "Incomplete") 19 | } 20 | } 21 | } 22 | } 23 | 24 | impl<'a> fmt::Display for LangParserError<'a> { 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | // Display our context along with the starting line/column 27 | // NOTE: This is an expensive operation to calculate the line/column 28 | writeln!( 29 | f, 30 | "{}: Line {}, Column {}", 31 | self.ctx, 32 | self.input.line(), 33 | self.input.column() 34 | )?; 35 | 36 | // Produce the first line of our input, limiting to no more than 37 | // 100 characters to prevent really long lines 38 | writeln!( 39 | f, 40 | "{}", 41 | &self 42 | .input 43 | .as_unsafe_remaining_str() 44 | .lines() 45 | .next() 46 | .unwrap_or_default() 47 | .chars() 48 | .take(100) 49 | .collect::() 50 | )?; 51 | 52 | if let Some(next) = self.next.as_ref() { 53 | next.fmt(f)?; 54 | } 55 | 56 | Ok(()) 57 | } 58 | } 59 | 60 | impl<'a> std::error::Error for LangParserError<'a> {} 61 | 62 | impl<'a> LangParserError<'a> { 63 | pub fn unsupported() -> Self { 64 | Self { 65 | ctx: Cow::from("Unsupported"), 66 | input: Span::from(""), 67 | next: None, 68 | } 69 | } 70 | 71 | pub fn from_ctx(input: &Span<'a>, ctx: &'static str) -> Self { 72 | Self { 73 | ctx: Cow::from(ctx), 74 | input: *input, 75 | next: None, 76 | } 77 | } 78 | } 79 | 80 | impl<'a, E> FromExternalError, E> for LangParserError<'a> { 81 | fn from_external_error(input: Span<'a>, kind: ErrorKind, _e: E) -> Self { 82 | // TODO: Support unique external error rendering 83 | Self::from_error_kind(input, kind) 84 | } 85 | } 86 | 87 | impl<'a> ParseError> for LangParserError<'a> { 88 | fn from_error_kind(input: Span<'a>, kind: ErrorKind) -> Self { 89 | Self { 90 | ctx: Cow::from(kind.description().to_string()), 91 | input, 92 | next: None, 93 | } 94 | } 95 | 96 | fn append(input: Span<'a>, kind: ErrorKind, other: Self) -> Self { 97 | let mut e = Self::from_error_kind(input, kind); 98 | e.next = Some(Box::new(other)); 99 | e 100 | } 101 | 102 | fn from_char(input: Span<'a>, c: char) -> Self { 103 | Self { 104 | ctx: Cow::from(format!("Char {}", c)), 105 | input, 106 | next: None, 107 | } 108 | } 109 | 110 | fn or(self, other: Self) -> Self { 111 | // Pick error that has progressed further 112 | if self.input.start_offset() > other.input.start_offset() { 113 | self 114 | } else { 115 | other 116 | } 117 | } 118 | } 119 | 120 | impl<'a> ContextError> for LangParserError<'a> { 121 | fn add_context(input: Span<'a>, ctx: &'static str, other: Self) -> Self { 122 | Self { 123 | ctx: Cow::from(ctx), 124 | input, 125 | next: Some(Box::new(other)), 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/mod.rs: -------------------------------------------------------------------------------- 1 | mod errors; 2 | mod span; 3 | mod utils; 4 | 5 | /// Vimwiki-specific parsers 6 | pub mod vimwiki; 7 | 8 | /// Export the span used for input 9 | pub use span::Span; 10 | 11 | /// Alias to the type of error to use with parsing using nom 12 | pub use errors::LangParserError as Error; 13 | 14 | /// Alias to an Result using our custom error and span 15 | pub type IResult<'a, O> = Result<(Span<'a>, O), nom::Err>>; 16 | 17 | /// Represents some data captured with the input used to create it 18 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 19 | pub struct Captured<'a, T> { 20 | inner: T, 21 | input: Span<'a>, 22 | } 23 | 24 | impl<'a, T> Captured<'a, T> { 25 | pub fn new(inner: T, input: Span<'a>) -> Self { 26 | Self { inner, input } 27 | } 28 | 29 | pub fn into_inner(self) -> T { 30 | self.inner 31 | } 32 | 33 | /// Represents the input that was used to construct the data 34 | pub fn input(&self) -> Span<'a> { 35 | self.input 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/utils/character.rs: -------------------------------------------------------------------------------- 1 | use super::context; 2 | use crate::lang::parsers::{Error, IResult, Span}; 3 | use nom::{bytes::complete::take_while1, character::complete::line_ending}; 4 | 5 | /// Parser that consumes next word only if is a whole word, meaning that this 6 | /// parser is not used in the middle of a word; in regex this would be similar 7 | /// to `\bword\b` 8 | pub fn whole_word(input: Span) -> IResult { 9 | let consumed = input.as_consumed(); 10 | let preceeding_is_okay = 11 | consumed.last().map_or(true, |c| c.is_ascii_whitespace()); 12 | 13 | if !preceeding_is_okay { 14 | return Err(nom::Err::Error(Error::from_ctx( 15 | &input, 16 | "Preceeding character is not whitespace", 17 | ))); 18 | } 19 | 20 | take_while1(|c: u8| !c.is_ascii_whitespace())(input) 21 | } 22 | 23 | /// Parser that will consume an end of line (\n or \r\n) or do nothing if 24 | /// the input is empty 25 | pub fn end_of_line_or_input(input: Span) -> IResult<()> { 26 | fn inner(input: Span) -> IResult<()> { 27 | if input.is_empty() { 28 | return Ok((input, ())); 29 | } 30 | 31 | let (input, _) = line_ending(input)?; 32 | Ok((input, ())) 33 | } 34 | 35 | context("End of Line/Input", inner)(input) 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use super::*; 41 | 42 | #[test] 43 | fn whole_word_should_succeed_if_nonwhitespace_starting_at_beginning_of_line( 44 | ) { 45 | let input = Span::from("\nword").advance_start_by(1); 46 | let (_, word) = whole_word(input).unwrap(); 47 | assert_eq!(word, "word"); 48 | } 49 | 50 | #[test] 51 | fn whole_word_should_succeed_if_whitespace_preceeding_and_currently_nonwhitespace( 52 | ) { 53 | let input = Span::from(" word").advance_start_by(1); 54 | let (_, word) = whole_word(input).unwrap(); 55 | assert_eq!(word, "word"); 56 | } 57 | 58 | #[test] 59 | fn whole_word_should_succeed_if_nonwhitespace_at_very_beginning_of_span() { 60 | let input = Span::from("word"); 61 | let (_, word) = whole_word(input).unwrap(); 62 | assert_eq!(word, "word"); 63 | } 64 | 65 | #[test] 66 | fn whole_word_should_fail_if_currently_whitespace() { 67 | let input = Span::from(" word"); 68 | assert!(whole_word(input).is_err()); 69 | 70 | let input = Span::from("\tword"); 71 | assert!(whole_word(input).is_err()); 72 | 73 | let input = Span::from("\rword"); 74 | assert!(whole_word(input).is_err()); 75 | } 76 | 77 | #[test] 78 | fn whole_word_should_fail_if_character_preceeding() { 79 | let input = Span::from("aword").advance_start_by(1); 80 | assert!(whole_word(input).is_err()); 81 | } 82 | 83 | #[test] 84 | fn end_of_line_or_input_should_fail_if_input_not_line_ending_or_empty() { 85 | assert!(end_of_line_or_input(Span::from("a")).is_err()); 86 | } 87 | 88 | #[test] 89 | fn end_of_line_or_input_should_succeed_if_line_ending() { 90 | assert!(end_of_line_or_input(Span::from("\n")).is_ok()); 91 | assert!(end_of_line_or_input(Span::from("\r\n")).is_ok()); 92 | } 93 | 94 | #[test] 95 | fn end_of_line_or_input_should_succeed_if_input_empty() { 96 | assert!(end_of_line_or_input(Span::from("")).is_ok()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/utils/error.rs: -------------------------------------------------------------------------------- 1 | /// Wraps a parser in a contextual label, which makes it easier to identify 2 | /// where parsing failures occur 3 | #[cfg(not(feature = "timekeeper"))] 4 | pub use nom::error::context; 5 | 6 | #[cfg(feature = "timekeeper")] 7 | use crate::lang::parsers::{IResult, Span}; 8 | 9 | /// Wraps a parser in a contextual label, which makes it easier to identify 10 | /// where parsing failures occur. This implementation also logs to a 11 | /// timekeeper table, which can be printed out to evaluate the time spent 12 | /// within each parser wrapped in a context. 13 | #[cfg(feature = "timekeeper")] 14 | pub fn context<'a, T>( 15 | ctx: &'static str, 16 | f: impl FnMut(Span<'a>) -> IResult<'a, T>, 17 | ) -> impl FnMut(Span<'a>) -> IResult<'a, T> { 18 | crate::timekeeper::parsers::context(ctx, f) 19 | } 20 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/utils/mod.rs: -------------------------------------------------------------------------------- 1 | mod bytes; 2 | mod character; 3 | mod convert; 4 | mod error; 5 | mod line; 6 | mod whitespace; 7 | 8 | pub use bytes::*; 9 | pub use character::*; 10 | pub use convert::*; 11 | pub use error::*; 12 | pub use line::*; 13 | pub use whitespace::*; 14 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/vimwiki/blocks/dividers.rs: -------------------------------------------------------------------------------- 1 | use crate::lang::{ 2 | elements::{Divider, Located}, 3 | parsers::{ 4 | utils::{ 5 | beginning_of_line, capture, context, end_of_line_or_input, locate, 6 | take_line_while1, 7 | }, 8 | IResult, Span, 9 | }, 10 | }; 11 | use nom::{character::complete::char, combinator::verify}; 12 | 13 | #[inline] 14 | pub fn divider(input: Span) -> IResult> { 15 | fn inner(input: Span) -> IResult { 16 | let (input, _) = beginning_of_line(input)?; 17 | let (input, _) = verify(take_line_while1(char('-')), |s: &Span| { 18 | s.remaining_len() >= 4 19 | })(input)?; 20 | let (input, _) = end_of_line_or_input(input)?; 21 | 22 | Ok((input, Divider)) 23 | } 24 | 25 | context("Divider", locate(capture(inner)))(input) 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn divider_should_fail_if_not_at_beginning_of_line() { 34 | let input = Span::from(" ----"); 35 | assert!(divider(input).is_err()); 36 | } 37 | 38 | #[test] 39 | fn divider_should_fail_if_not_at_least_four_hyphens() { 40 | let input = Span::from("---"); 41 | assert!(divider(input).is_err()); 42 | } 43 | 44 | #[test] 45 | fn divider_should_fail_if_not_only_hyphens_within_line() { 46 | let input = Span::from("----a"); 47 | assert!(divider(input).is_err()); 48 | } 49 | 50 | #[test] 51 | fn divider_should_succeed_if_four_hyphens_at_start_of_line() { 52 | let input = Span::from("----"); 53 | let (input, _) = divider(input).unwrap(); 54 | assert!(input.is_empty(), "Divider not consumed"); 55 | } 56 | 57 | #[test] 58 | fn divider_should_succeed_if_more_than_four_hyphens_at_start_of_line() { 59 | let input = Span::from("-----"); 60 | let (input, _) = divider(input).unwrap(); 61 | assert!(input.is_empty(), "Divider not consumed"); 62 | } 63 | 64 | #[test] 65 | fn divider_should_consume_end_of_line() { 66 | let input = Span::from("----\nabcd"); 67 | let (input, _) = divider(input).unwrap(); 68 | assert_eq!(input.as_unsafe_remaining_str(), "abcd"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/vimwiki/blocks/inline/code.rs: -------------------------------------------------------------------------------- 1 | use crate::lang::{ 2 | elements::{CodeInline, Located}, 3 | parsers::{ 4 | utils::{ 5 | capture, context, cow_str, locate, not_contains, surround_in_line1, 6 | }, 7 | IResult, Span, 8 | }, 9 | }; 10 | use nom::combinator::{map, map_parser}; 11 | 12 | #[inline] 13 | pub fn code_inline(input: Span) -> IResult> { 14 | fn inner(input: Span) -> IResult { 15 | map( 16 | map_parser( 17 | not_contains("%%", surround_in_line1("`", "`")), 18 | cow_str, 19 | ), 20 | CodeInline::new, 21 | )(input) 22 | } 23 | 24 | context("Code Inline", locate(capture(inner)))(input) 25 | } 26 | 27 | #[cfg(test)] 28 | mod tests { 29 | use super::*; 30 | use indoc::indoc; 31 | 32 | #[test] 33 | fn code_inline_should_fail_if_input_empty() { 34 | let input = Span::from(""); 35 | assert!(code_inline(input).is_err()); 36 | } 37 | 38 | #[test] 39 | fn code_inline_should_fail_if_does_not_start_with_backtick() { 40 | let input = Span::from(r"some code`"); 41 | assert!(code_inline(input).is_err()); 42 | } 43 | 44 | #[test] 45 | fn code_inline_should_fail_if_does_not_end_with_backtick() { 46 | let input = Span::from(r"`some code"); 47 | assert!(code_inline(input).is_err()); 48 | } 49 | 50 | #[test] 51 | fn code_inline_should_fail_if_end_is_on_next_line() { 52 | let input = Span::from(indoc! {r" 53 | `some code 54 | ` 55 | "}); 56 | assert!(code_inline(input).is_err()); 57 | } 58 | 59 | #[test] 60 | fn code_inline_should_consume_all_text_between_backticks_as_code() { 61 | let input = Span::from(r"`some code`"); 62 | let (input, m) = code_inline(input).unwrap(); 63 | assert!(input.is_empty(), "Code inline not consumed"); 64 | assert_eq!(m.as_str(), r"some code"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/vimwiki/blocks/inline/links/diary.rs: -------------------------------------------------------------------------------- 1 | use super::{link_anchor, link_description}; 2 | use crate::lang::{ 3 | elements::{Link, Located}, 4 | parsers::{ 5 | utils::{ 6 | capture, context, locate, not_contains, surround_in_line1, 7 | take_line_until_one_of_two1, 8 | }, 9 | IResult, Span, 10 | }, 11 | }; 12 | use chrono::NaiveDate; 13 | use nom::{ 14 | bytes::complete::tag, 15 | combinator::{map_parser, map_res, opt}, 16 | }; 17 | 18 | pub fn diary_link(input: Span) -> IResult> { 19 | fn inner(input: Span) -> IResult { 20 | // Diary is a specialized link that must start with diary: 21 | let (input, _) = tag("diary:")(input)?; 22 | 23 | // After the specialized start, a valid date must follow before the 24 | // end of a link, start of anchor, or start of a description 25 | let (input, date) = 26 | map_res(take_line_until_one_of_two1("|", "#"), |span| { 27 | NaiveDate::parse_from_str( 28 | span.as_unsafe_remaining_str(), 29 | "%Y-%m-%d", 30 | ) 31 | })(input)?; 32 | 33 | // Check for an optional anchor that we will need to parse 34 | let (input, maybe_anchor) = opt(link_anchor)(input)?; 35 | 36 | // Finally, check if there is a description (preceding with |) 37 | let (input, maybe_description) = opt(link_description)(input)?; 38 | 39 | Ok(( 40 | input, 41 | Link::new_diary_link(date, maybe_description, maybe_anchor), 42 | )) 43 | } 44 | 45 | context( 46 | "Diary Link", 47 | locate(capture(map_parser( 48 | not_contains("%%", surround_in_line1("[[", "]]")), 49 | inner, 50 | ))), 51 | )(input) 52 | } 53 | 54 | #[cfg(test)] 55 | mod tests { 56 | use super::*; 57 | use crate::lang::elements::{Anchor, Description}; 58 | 59 | #[test] 60 | fn diary_link_should_fail_if_not_using_diary_scheme() { 61 | let input = Span::from("[[notdiary:2012-03-05]]"); 62 | assert!(diary_link(input).is_err()); 63 | } 64 | 65 | #[test] 66 | fn diary_link_should_fail_if_not_using_correct_date_format() { 67 | let input = Span::from("[[diary:2012/03/05]]"); 68 | assert!(diary_link(input).is_err()); 69 | } 70 | 71 | #[test] 72 | fn diary_link_should_support_diary_scheme() { 73 | let input = Span::from("[[diary:2012-03-05]]"); 74 | let (input, link) = 75 | diary_link(input).expect("Parser unexpectedly failed"); 76 | 77 | // Link should be consumed 78 | assert!(input.is_empty()); 79 | 80 | assert_eq!(link.date(), Some(NaiveDate::from_ymd(2012, 3, 5))); 81 | assert_eq!(link.description(), None); 82 | assert_eq!(link.to_anchor(), None); 83 | } 84 | 85 | #[test] 86 | fn diary_link_should_support_a_description() { 87 | let input = Span::from("[[diary:2012-03-05|some description]]"); 88 | let (input, link) = 89 | diary_link(input).expect("Parser unexpectedly failed"); 90 | 91 | // Link should be consumed 92 | assert!(input.is_empty()); 93 | 94 | assert_eq!(link.date(), Some(NaiveDate::from_ymd(2012, 3, 5))); 95 | assert_eq!( 96 | link.description(), 97 | Some(&Description::from("some description")) 98 | ); 99 | assert_eq!(link.to_anchor(), None); 100 | } 101 | 102 | #[test] 103 | fn diary_link_should_support_an_anchor() { 104 | let input = Span::from("[[diary:2012-03-05#Tomorrow]]"); 105 | let (input, link) = 106 | diary_link(input).expect("Parser unexpectedly failed"); 107 | 108 | // Link should be consumed 109 | assert!(input.is_empty()); 110 | 111 | assert_eq!(link.date(), Some(NaiveDate::from_ymd(2012, 3, 5))); 112 | assert_eq!(link.description(), None); 113 | assert_eq!(link.to_anchor(), Some(Anchor::from("Tomorrow"))); 114 | } 115 | 116 | #[test] 117 | fn diary_link_should_support_an_anchor_and_description() { 118 | let input = 119 | Span::from("[[diary:2012-03-05#Tomorrow|Tasks for tomorrow]]"); 120 | let (input, link) = 121 | diary_link(input).expect("Parser unexpectedly failed"); 122 | 123 | // Link should be consumed 124 | assert!(input.is_empty()); 125 | 126 | assert_eq!(link.date(), Some(NaiveDate::from_ymd(2012, 3, 5))); 127 | assert_eq!( 128 | link.description(), 129 | Some(&Description::from("Tasks for tomorrow")) 130 | ); 131 | assert_eq!(link.to_anchor(), Some(Anchor::from("Tomorrow"))); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/vimwiki/blocks/inline/links/raw.rs: -------------------------------------------------------------------------------- 1 | use crate::lang::{ 2 | elements::{Link, Located}, 3 | parsers::{ 4 | utils::{capture, context, locate, uri_ref}, 5 | IResult, Span, 6 | }, 7 | }; 8 | use nom::combinator::verify; 9 | 10 | pub fn raw_link(input: Span) -> IResult> { 11 | fn inner(input: Span) -> IResult { 12 | // This will match any URI, but we only want to allow a certain set 13 | // to ensure that we don't mistake some text preceding a tag 14 | // 15 | // NOTE: We don't use link_uri_ref because we don't want to auto-escape 16 | // spaces or other characters. For raw links, that is up to the 17 | // user to do so 18 | let (input, uri_ref) = verify(uri_ref, |uri_ref| { 19 | uri_ref.scheme().map_or(false, |scheme| { 20 | ["http", "https", "ftp", "file", "local", "mailto"] 21 | .contains(&scheme.as_str()) 22 | }) 23 | })(input)?; 24 | 25 | Ok((input, Link::new_raw_link(uri_ref))) 26 | } 27 | 28 | context("Raw Link", locate(capture(inner)))(input) 29 | } 30 | 31 | #[cfg(test)] 32 | mod tests { 33 | use super::*; 34 | 35 | #[test] 36 | fn raw_link_should_support_http_scheme() { 37 | let input = Span::from("http://example.com"); 38 | let (input, link) = raw_link(input).expect("Failed to parse uri"); 39 | 40 | // Link should be consumed 41 | assert!(input.is_empty()); 42 | 43 | assert_eq!(link.scheme().unwrap(), "http"); 44 | assert_eq!( 45 | link.data().uri_ref.host().unwrap().to_string(), 46 | "example.com" 47 | ); 48 | } 49 | 50 | #[test] 51 | fn raw_link_should_support_https_scheme() { 52 | let input = Span::from("https://example.com"); 53 | let (input, link) = raw_link(input).expect("Failed to parse uri"); 54 | 55 | // Link should be consumed 56 | assert!(input.is_empty()); 57 | 58 | assert_eq!(link.scheme().unwrap(), "https"); 59 | assert_eq!( 60 | link.data().uri_ref.host().unwrap().to_string(), 61 | "example.com" 62 | ); 63 | } 64 | 65 | #[test] 66 | #[ignore] 67 | fn raw_link_should_support_no_scheme_with_www() { 68 | let input = Span::from("www.example.com"); 69 | let (input, link) = raw_link(input).expect("Failed to parse uri"); 70 | 71 | // Link should be consumed 72 | assert!(input.is_empty()); 73 | 74 | assert_eq!(link.scheme().unwrap(), "https"); 75 | assert_eq!( 76 | link.data().uri_ref.host().unwrap().to_string(), 77 | "www.example.com" 78 | ); 79 | } 80 | 81 | #[test] 82 | fn raw_link_should_support_ftp_scheme() { 83 | let input = Span::from("ftp://example.com"); 84 | let (input, link) = raw_link(input).expect("Failed to parse uri"); 85 | 86 | // Link should be consumed 87 | assert!(input.is_empty()); 88 | 89 | assert_eq!(link.scheme().unwrap(), "ftp"); 90 | assert_eq!( 91 | link.data().uri_ref.host().unwrap().to_string(), 92 | "example.com" 93 | ); 94 | } 95 | 96 | #[test] 97 | fn raw_link_should_support_file_scheme() { 98 | let input = Span::from("file:///some/path"); 99 | let (input, link) = raw_link(input).expect("Failed to parse uri"); 100 | 101 | // Link should be consumed 102 | assert!(input.is_empty()); 103 | 104 | assert_eq!(link.scheme().unwrap(), "file"); 105 | assert_eq!(link.data().uri_ref.path(), "/some/path"); 106 | } 107 | 108 | #[test] 109 | fn raw_link_should_support_local_scheme() { 110 | let input = Span::from("local:///some/path"); 111 | let (input, link) = raw_link(input).expect("Failed to parse uri"); 112 | 113 | // Link should be consumed 114 | assert!(input.is_empty()); 115 | 116 | assert_eq!(link.scheme().unwrap(), "local"); 117 | assert_eq!(link.data().uri_ref.path(), "/some/path"); 118 | } 119 | 120 | #[test] 121 | fn raw_link_should_support_mailto_scheme() { 122 | let input = Span::from("mailto:person@example.com"); 123 | let (input, link) = raw_link(input).expect("Failed to parse uri"); 124 | 125 | // Link should be consumed 126 | assert!(input.is_empty()); 127 | 128 | assert_eq!(link.scheme().unwrap(), "mailto"); 129 | assert_eq!(link.data().uri_ref.path(), "person@example.com"); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/vimwiki/blocks/inline/math.rs: -------------------------------------------------------------------------------- 1 | use crate::lang::{ 2 | elements::{Located, MathInline}, 3 | parsers::{ 4 | utils::{ 5 | capture, context, cow_str, locate, not_contains, take_line_until1, 6 | }, 7 | IResult, Span, 8 | }, 9 | }; 10 | use nom::{ 11 | character::complete::char, 12 | combinator::{map, map_parser}, 13 | sequence::delimited, 14 | }; 15 | 16 | #[inline] 17 | pub fn math_inline(input: Span) -> IResult> { 18 | fn inner(input: Span) -> IResult { 19 | // TODO: Is there any way to escape a $ inside a formula? If so, we will 20 | // need to support detecting that 21 | map( 22 | map_parser( 23 | delimited( 24 | char('$'), 25 | not_contains("%%", take_line_until1("$")), 26 | char('$'), 27 | ), 28 | cow_str, 29 | ), 30 | MathInline::new, 31 | )(input) 32 | } 33 | 34 | context("Math Inline", locate(capture(inner)))(input) 35 | } 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | use super::*; 40 | use indoc::indoc; 41 | 42 | #[test] 43 | fn math_inline_should_fail_if_input_empty() { 44 | let input = Span::from(""); 45 | assert!(math_inline(input).is_err()); 46 | } 47 | 48 | #[test] 49 | fn math_inline_should_fail_if_does_not_start_with_dollar_sign() { 50 | let input = Span::from(r"\sum_i a_i^2 = 1$"); 51 | assert!(math_inline(input).is_err()); 52 | } 53 | 54 | #[test] 55 | fn math_inline_should_fail_if_does_not_end_with_dollar_sign() { 56 | let input = Span::from(r"$\sum_i a_i^2 = 1"); 57 | assert!(math_inline(input).is_err()); 58 | } 59 | 60 | #[test] 61 | fn math_inline_should_fail_if_end_is_on_next_line() { 62 | let input = Span::from(indoc! {r" 63 | $\sum_i a_i^2 = 1 64 | $ 65 | "}); 66 | assert!(math_inline(input).is_err()); 67 | } 68 | 69 | #[test] 70 | fn math_inline_should_consume_all_text_between_dollar_signs_as_formula() { 71 | let input = Span::from(r"$\sum_i a_i^2 = 1$"); 72 | let (input, m) = math_inline(input).unwrap(); 73 | assert!(input.is_empty(), "Math inline not consumed"); 74 | assert_eq!(m.as_str(), r"\sum_i a_i^2 = 1"); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/vimwiki/blocks/inline/tags.rs: -------------------------------------------------------------------------------- 1 | use crate::lang::{ 2 | elements::{Located, Tag, Tags}, 3 | parsers::{ 4 | utils::{ 5 | capture, context, cow_str, locate, take_line_until_one_of_three1, 6 | whole_word, 7 | }, 8 | IResult, Span, 9 | }, 10 | }; 11 | use nom::{ 12 | character::complete::char, combinator::map_parser, multi::many1, 13 | sequence::terminated, 14 | }; 15 | 16 | #[inline] 17 | pub fn tags(input: Span) -> IResult> { 18 | fn inner(input: Span) -> IResult { 19 | let (input, _) = char(':')(input)?; 20 | let (input, contents) = 21 | many1(terminated(tag_content, char(':')))(input)?; 22 | 23 | Ok((input, Tags::new(contents))) 24 | } 25 | 26 | context("Tags", locate(capture(map_parser(whole_word, inner))))(input) 27 | } 28 | 29 | fn tag_content(input: Span) -> IResult { 30 | let (input, s) = map_parser( 31 | take_line_until_one_of_three1(":", " ", "\t"), 32 | cow_str, 33 | )(input)?; 34 | Ok((input, Tag::new(s))) 35 | } 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | use super::*; 40 | 41 | #[test] 42 | fn tags_should_fail_if_input_empty() { 43 | let input = Span::from(""); 44 | assert!(tags(input).is_err()); 45 | } 46 | 47 | #[test] 48 | fn tags_should_fail_if_not_starting_with_colon() { 49 | let input = Span::from("tag-example:"); 50 | assert!(tags(input).is_err()); 51 | } 52 | 53 | #[test] 54 | fn tags_should_fail_if_not_ending_with_colon() { 55 | let input = Span::from(":tag-example"); 56 | assert!(tags(input).is_err()); 57 | } 58 | 59 | #[test] 60 | fn tags_should_fail_if_only_comprised_of_colons() { 61 | let input = Span::from("::"); 62 | assert!(tags(input).is_err()); 63 | } 64 | 65 | #[test] 66 | fn tags_should_yield_a_single_tag_if_one_pair_of_colons_with_text() { 67 | let input = Span::from(":tag-example:"); 68 | let (input, tags) = tags(input).unwrap(); 69 | assert!(input.is_empty(), "Did not consume tags"); 70 | assert_eq!( 71 | tags.into_inner().into_iter().collect::>(), 72 | vec![Tag::from("tag-example")] 73 | ); 74 | } 75 | 76 | #[test] 77 | fn tags_should_yield_a_single_tag_if_one_pair_of_colons_with_trailing_content( 78 | ) { 79 | let input = Span::from(":tag-example: and other text"); 80 | let (input, tags) = tags(input).unwrap(); 81 | assert_eq!( 82 | input.as_unsafe_remaining_str(), 83 | " and other text", 84 | "Unexpected input consumed" 85 | ); 86 | assert_eq!( 87 | tags.into_inner().into_iter().collect::>(), 88 | vec![Tag::from("tag-example")] 89 | ); 90 | } 91 | 92 | #[test] 93 | fn tags_should_yield_multiple_tags_if_many_colons_with_text() { 94 | let input = Span::from(":tag-one:tag-two:"); 95 | let (input, tags) = tags(input).unwrap(); 96 | assert!(input.is_empty(), "Did not consume tags"); 97 | assert_eq!( 98 | tags.into_inner().into_iter().collect::>(), 99 | vec![Tag::from("tag-one"), Tag::from("tag-two")] 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/vimwiki/blocks/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::lang::{ 2 | elements::{BlockElement, Located}, 3 | parsers::{utils::context, IResult, Span}, 4 | }; 5 | use nom::{branch::alt, combinator::map}; 6 | 7 | pub mod blockquotes; 8 | pub mod code; 9 | pub mod definitions; 10 | pub mod dividers; 11 | pub mod headers; 12 | pub mod inline; 13 | pub mod lists; 14 | pub mod math; 15 | pub mod paragraphs; 16 | pub mod placeholders; 17 | pub mod tables; 18 | 19 | /// Parses any block or top-level block element 20 | /// 21 | /// Top-level block elements are ones that cannot be nested anywhere else, 22 | /// which include: 23 | /// 24 | /// 1. Headers 25 | /// 2. Indented Blockquotes 26 | /// 3. Placeholders 27 | /// 4. Dividers 28 | pub fn top_level_block_element(input: Span) -> IResult> { 29 | context( 30 | "Top Level Block Element", 31 | alt(( 32 | map(headers::header, |c| c.map(BlockElement::from)), 33 | map(definitions::definition_list, |c| c.map(BlockElement::from)), 34 | map(lists::list, |c| c.map(BlockElement::from)), 35 | map(tables::table, |c| c.map(BlockElement::from)), 36 | map(code::code_block, |c| c.map(BlockElement::from)), 37 | map(math::math_block, |c| c.map(BlockElement::from)), 38 | map(blockquotes::blockquote, |c| c.map(BlockElement::from)), 39 | map(dividers::divider, |c| c.map(BlockElement::from)), 40 | map(placeholders::placeholder, |c| c.map(BlockElement::from)), 41 | // NOTE: Final type because will match literally anything in a line 42 | map(paragraphs::paragraph, |c| c.map(BlockElement::from)), 43 | )), 44 | )(input) 45 | } 46 | 47 | /// Parses any block element that can be nested; see [`top_level_block_element`] 48 | /// for an explanation of which elements would or would not show up here 49 | pub fn nested_block_element(input: Span) -> IResult> { 50 | context( 51 | "Block Element", 52 | alt(( 53 | map(definitions::definition_list, |c| c.map(BlockElement::from)), 54 | map(lists::list, |c| c.map(BlockElement::from)), 55 | map(tables::nested_table, |c| c.map(BlockElement::from)), 56 | map(code::code_block, |c| c.map(BlockElement::from)), 57 | map(math::math_block, |c| c.map(BlockElement::from)), 58 | map(blockquotes::arrow_blockquote, |c| c.map(BlockElement::from)), 59 | // NOTE: Final type because will match literally anything in a line 60 | map(paragraphs::paragraph, |c| c.map(BlockElement::from)), 61 | )), 62 | )(input) 63 | } 64 | -------------------------------------------------------------------------------- /vimwiki-core/src/lang/parsers/vimwiki/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::lang::{ 2 | elements::*, 3 | parsers::{ 4 | utils::{blank_line, context}, 5 | IResult, Span, 6 | }, 7 | }; 8 | use nom::{ 9 | branch::alt, 10 | combinator::{all_consuming, map, value}, 11 | multi::many0, 12 | }; 13 | 14 | pub mod blocks; 15 | 16 | pub fn page<'a>(input: Span<'a>) -> IResult> { 17 | fn inner<'a>(input: Span<'a>) -> IResult> { 18 | // Parses one or more lines, either eating blank lines or producing 19 | // a block element 20 | fn maybe_block_element( 21 | input: Span, 22 | ) -> IResult>> { 23 | alt(( 24 | value(None, blank_line), 25 | map(blocks::top_level_block_element, Some), 26 | ))(input) 27 | } 28 | 29 | map(all_consuming(many0(maybe_block_element)), |mut elements| { 30 | Page::new(elements.drain(..).flatten().collect()) 31 | })(input) 32 | } 33 | 34 | context("Page", inner)(input) 35 | } 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | use super::*; 40 | 41 | #[test] 42 | fn page_should_skip_blank_lines_not_within_block_elements() { 43 | let (_, page) = page(Span::from("\n\n")).unwrap(); 44 | assert!(page.elements().is_empty()); 45 | } 46 | 47 | #[test] 48 | fn page_should_parse_blocks() { 49 | let (_, page) = page(Span::from("some text with % signs")).unwrap(); 50 | assert_eq!( 51 | page.elements(), 52 | vec![Located::from(BlockElement::from(Paragraph::new(vec![ 53 | InlineElementContainer::new(vec![Located::from( 54 | InlineElement::Text(Text::from("some text with % signs")) 55 | )]) 56 | ])))] 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /vimwiki-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod lang; 2 | mod utils; 3 | 4 | // Export all elements at top level 5 | pub use lang::elements::*; 6 | 7 | // Export all outputs at top level 8 | pub use lang::output::*; 9 | 10 | // Export our parser error, which is used for language parsing 11 | pub use lang::parsers::Error as ParseError; 12 | 13 | // Export our primary language structure and trait 14 | pub use lang::{FromLanguage, Language}; 15 | 16 | // Export our trait to do stronger comparsisons that include the region of elements 17 | pub use utils::StrictEq; 18 | 19 | // Re-export the vendor libraries so we're able to reconstruct their 20 | // structs from macros 21 | pub mod vendor { 22 | pub use chrono; 23 | pub use uriparse; 24 | } 25 | 26 | #[cfg(feature = "timekeeper")] 27 | pub mod timekeeper; 28 | -------------------------------------------------------------------------------- /vimwiki-core/src/timekeeper.rs: -------------------------------------------------------------------------------- 1 | use crate::lang::parsers::Span; 2 | use lazy_static::lazy_static; 3 | use std::sync::{ 4 | atomic::{AtomicBool, Ordering}, 5 | Mutex, 6 | }; 7 | 8 | lazy_static! { 9 | static ref TIMEKEEPER_ENABLED: AtomicBool = AtomicBool::new(false); 10 | static ref TIMEKEEPER: Mutex> = 11 | std::sync::Mutex::new(std::collections::HashMap::new()); 12 | } 13 | 14 | pub fn is_enabled() -> bool { 15 | TIMEKEEPER_ENABLED.load(Ordering::Relaxed) 16 | } 17 | 18 | pub fn enable() { 19 | TIMEKEEPER_ENABLED.store(true, Ordering::Relaxed); 20 | } 21 | 22 | pub fn disable() { 23 | TIMEKEEPER_ENABLED.store(false, Ordering::Relaxed); 24 | } 25 | 26 | pub fn toggle_enabled() { 27 | if is_enabled() { 28 | disable(); 29 | } else { 30 | enable(); 31 | } 32 | } 33 | 34 | /// Prints a report based on the timekeeper's memory 35 | pub fn print_report(clear_after_print: bool) { 36 | let mut results: Vec<(&'static str, (usize, u128))> = TIMEKEEPER 37 | .lock() 38 | .unwrap() 39 | .iter() 40 | .map(|(k, v)| (*k, *v)) 41 | .collect(); 42 | 43 | // Sort with most expensive average item first 44 | results.sort_unstable_by_key(|k| (k.1 .1 as f64 / k.1 .0 as f64) as u128); 45 | results.reverse(); 46 | 47 | fn time_to_str(x: u128) -> String { 48 | if x >= 10_u128.pow(9) { 49 | format!("{}s", (x as f64) / 10_f64.powi(9)) 50 | } else if x >= 10_u128.pow(6) { 51 | format!("{}ms", (x as f64) / 10_f64.powi(6)) 52 | } else if x >= 10_u128.pow(3) { 53 | format!("{}μs", (x as f64) / 10_f64.powi(3)) 54 | } else { 55 | format!("{}ns", x) 56 | } 57 | } 58 | 59 | println!("====== TIMEKEEPER REPORT ======"); 60 | println!(); 61 | for (ctx, (cnt, nanos)) in results.drain(..) { 62 | println!( 63 | "- {}: ({} calls, total {}, average {})", 64 | ctx, 65 | cnt, 66 | time_to_str(nanos), 67 | time_to_str((nanos as f64 / cnt as f64) as u128), 68 | ); 69 | } 70 | println!(); 71 | println!("==============================="); 72 | 73 | if clear_after_print { 74 | clear(); 75 | } 76 | } 77 | 78 | /// Clears the timekeeper's memory 79 | pub fn clear() { 80 | TIMEKEEPER.lock().unwrap().clear(); 81 | } 82 | 83 | /// Wraps a parser in a contextual label, which makes it easier to identify 84 | /// where parsing failures occur. This implementation also logs to a 85 | /// timekeeper table, which can be printed out to evaluate the time spent 86 | /// within each parser wrapped in a context. 87 | pub mod parsers { 88 | use super::*; 89 | use nom::error::ContextError; 90 | 91 | type Error<'a> = crate::lang::parsers::Error<'a>; 92 | type IResult<'a, O> = Result<(Span<'a>, O), nom::Err>>; 93 | 94 | pub fn context<'a, T>( 95 | ctx: &'static str, 96 | mut f: impl FnMut(Span<'a>) -> IResult, 97 | ) -> impl FnMut(Span<'a>) -> IResult { 98 | move |input: Span| { 99 | let start = std::time::Instant::now(); 100 | 101 | // NOTE: Following is the code found in nom's context parser, but due 102 | // to issues with wrapping a function like above in a parser, 103 | // we have to explicitly call the f parser on its own 104 | let result = match f(input) { 105 | Ok(o) => Ok(o), 106 | Err(nom::Err::Incomplete(i)) => Err(nom::Err::Incomplete(i)), 107 | Err(nom::Err::Error(e)) => { 108 | Err(nom::Err::Error(Error::add_context(input, ctx, e))) 109 | } 110 | Err(nom::Err::Failure(e)) => { 111 | Err(nom::Err::Failure(Error::add_context(input, ctx, e))) 112 | } 113 | }; 114 | 115 | if is_enabled() { 116 | let x = start.elapsed().as_nanos(); 117 | TIMEKEEPER 118 | .lock() 119 | .unwrap() 120 | .entry(ctx) 121 | .and_modify(move |e| { 122 | *e = (e.0 + 1, e.1 + x); 123 | }) 124 | .or_insert((1, x)); 125 | } 126 | 127 | result 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /vimwiki-core/src/utils.rs: -------------------------------------------------------------------------------- 1 | /// Represents an equality check that is considered strict. In the case of 2 | /// a `Located`, will check both the inner type AND the region. 3 | pub trait StrictEq { 4 | fn strict_eq(&self, other: &Rhs) -> bool; 5 | 6 | #[inline] 7 | fn strict_ne(&self, other: &Rhs) -> bool { 8 | !self.strict_eq(other) 9 | } 10 | } 11 | 12 | /// Blanket implementation for two vectors of similarly-typed StrictEq elements 13 | impl StrictEq for Vec { 14 | /// Performs strict_eq check on inner elements 15 | fn strict_eq(&self, other: &Self) -> bool { 16 | self.len() == other.len() 17 | && self.iter().zip(other.iter()).all(|(x, y)| x.strict_eq(y)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vimwiki-core/tests/lib.rs: -------------------------------------------------------------------------------- 1 | mod output; 2 | mod parser; 3 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/html/lists-with-hard-line-wrap.out.html: -------------------------------------------------------------------------------- 1 |
      2 |
    • first item

    • 3 |
    • second item, with embedded hard line wrapping

    • 4 |
    5 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/html/lists-with-hard-line-wrap.wiki: -------------------------------------------------------------------------------- 1 | - first item 2 | - second item, with 3 | embedded hard line wrapping 4 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/html/standalone-anchor-link.out.html: -------------------------------------------------------------------------------- 1 |

    #stuff

    2 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/html/standalone-anchor-link.wiki: -------------------------------------------------------------------------------- 1 | [[#stuff]] 2 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::OsStr, fs, path::PathBuf}; 2 | use vimwiki::*; 3 | use walkdir::WalkDir; 4 | 5 | fn base_path() -> PathBuf { 6 | PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/output") 7 | } 8 | 9 | macro_rules! find_file_pairs { 10 | ($in:expr, $out:expr) => { 11 | WalkDir::new(base_path()) 12 | .into_iter() 13 | .filter_map(|e| e.ok()) 14 | .filter(|e| e.path().extension() == Some(OsStr::new($in))) 15 | .filter(|e| { 16 | !e.path() 17 | .file_stem() 18 | .and_then(OsStr::to_str) 19 | .unwrap() 20 | .ends_with(".out") 21 | }) 22 | .map(|e| (e.path().to_path_buf(), e.path().with_extension($out))) 23 | .filter(|(_, p)| p.exists()) 24 | }; 25 | } 26 | 27 | macro_rules! test_file_pairs { 28 | ($in:expr, $out:expr, $convert:expr) => { 29 | for (path_in, path_out) in find_file_pairs!($in, $out) { 30 | println!("Loading {}...", path_in.to_string_lossy()); 31 | let in_str = fs::read_to_string(path_in).unwrap(); 32 | 33 | // NOTE: On windows, loading the file into a string will yield \r\n for 34 | // the line terminations regardless of what the file actually 35 | // contains; so, we need to remove \r to handle this test 36 | println!("Loading {}...", path_out.to_string_lossy()); 37 | let out_str: String = fs::read_to_string(path_out) 38 | .unwrap() 39 | .chars() 40 | .filter(|c| *c != '\r') 41 | .collect(); 42 | 43 | println!("Parsing input..."); 44 | let language = Language::from_vimwiki_str(&in_str); 45 | let page: Page = language.parse().unwrap(); 46 | 47 | println!("Converting..."); 48 | let actual = $convert(page); 49 | 50 | similar_asserts::assert_str_eq!(actual, out_str); 51 | } 52 | }; 53 | } 54 | 55 | #[test] 56 | fn check_vimwiki_to_vimwiki() { 57 | test_file_pairs!("wiki", "out.wiki", |page: Page| page 58 | .to_vimwiki_string(Default::default()) 59 | .unwrap()); 60 | } 61 | 62 | #[cfg(feature = "html")] 63 | #[test] 64 | fn check_vimwiki_to_html() { 65 | test_file_pairs!("wiki", "out.html", |page: Page| page 66 | .to_html_string(Default::default()) 67 | .unwrap()); 68 | } 69 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/blockquotes.out.wiki: -------------------------------------------------------------------------------- 1 | > this is a blockquote 2 | > that will get translated 3 | > to have > by default 4 | 5 | > this is another blockquote 6 | > using arrows 7 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/blockquotes.wiki: -------------------------------------------------------------------------------- 1 | this is a blockquote 2 | that will get translated 3 | to have > by default 4 | 5 | > this is another blockquote 6 | > using arrows 7 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/code.out.wiki: -------------------------------------------------------------------------------- 1 | {{{ 2 | regular text 3 | }}} 4 | 5 | {{{ 6 | indented code 7 | }}} 8 | 9 | {{{ 10 | more 11 | code 12 | }}} 13 | 14 | {{{class="python" style="color:blue" 15 | for i in range(1, 5): 16 | print(i) 17 | }}} 18 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/code.wiki: -------------------------------------------------------------------------------- 1 | {{{ 2 | regular text 3 | }}} 4 | {{{ 5 | indented code 6 | }}} 7 | {{{ 8 | more 9 | code 10 | }}} 11 | {{{class="python" style="color:blue" 12 | for i in range(1, 5): 13 | print(i) 14 | }}} 15 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/headers.out.wiki: -------------------------------------------------------------------------------- 1 | = header1 = 2 | 3 | paragraph 1 4 | on multiple lines 5 | 6 | == header2 == 7 | 8 | paragraph 2 9 | on multiple lines 10 | 11 | === header3 === 12 | 13 | paragraph 3 14 | on multiple lines 15 | 16 | ==== header4 ==== 17 | 18 | paragraph 4 19 | on multiple lines 20 | 21 | ===== header5 ===== 22 | 23 | paragraph 5 24 | on multiple lines 25 | 26 | ====== header6 ====== 27 | 28 | paragraph 5 29 | on multiple lines 30 | 31 | = centered header = 32 | 33 | paragraph 6 34 | on multiple lines 35 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/headers.wiki: -------------------------------------------------------------------------------- 1 | =header1= 2 | paragraph 1 3 | on multiple lines 4 | ==header2== 5 | paragraph 2 6 | on multiple lines 7 | ===header3=== 8 | paragraph 3 9 | on multiple lines 10 | ====header4==== 11 | paragraph 4 12 | on multiple lines 13 | =====header5===== 14 | paragraph 5 15 | on multiple lines 16 | ======header6====== 17 | paragraph 5 18 | on multiple lines 19 | =centered header= 20 | paragraph 6 21 | on multiple lines 22 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/links.out.wiki: -------------------------------------------------------------------------------- 1 | == external links == 2 | 3 | [[http://google.com/|_Google_ search engine]] 4 | 5 | http://pandoc.org/ 6 | 7 | ftp://vim.org/ 8 | 9 | [[http://google.com/]] 10 | 11 | [[mailto:info@example.org|email me]] 12 | 13 | mailto:hello@bye.com 14 | 15 | == internal links == 16 | 17 | [[This is a link]] 18 | 19 | [[This is a link source|Description of the link]] 20 | 21 | [[projects/Important Project 1]] 22 | 23 | [[../index]] 24 | 25 | [[a subdirectory/|Other files]] 26 | 27 | [[#tag-one|try me to test tag anchors]] 28 | 29 | [[#block quotes|try me to test header anchors]] 30 | 31 | [[#strong|try me to test strong anchors]] 32 | 33 | [[Todo List#Tomorrow|Tasks for tomorrow]] 34 | 35 | [[diary:2017-05-01]] 36 | 37 | [[file:../assets/data.csv|Important Data]] 38 | 39 | === links with thumbnails === 40 | 41 | [[http://www.google.com/|{{./movie.jpg}}]] 42 | 43 | == images == 44 | 45 | {{file:./lalune.jpg}} 46 | 47 | {{http://vimwiki.googlecode.com/hg/images/vimwiki_logo.png|Vimwiki}} 48 | 49 | {{local:./movie.jpg}} 50 | 51 | === image with attributes === 52 | 53 | {{lalune.jpg|_cool stuff_|style="width:150px;height:120px;"}} 54 | 55 | {{nonexist.jpg|*Non-existing* image|class="center flow blabla" style="font-color:red"}} 56 | 57 | {{lalune.jpg|_cool stuff_|style="width:150px;height:120px;"}} 58 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/links.wiki: -------------------------------------------------------------------------------- 1 | == external links == 2 | 3 | [[http://google.com|_Google_ search engine]] 4 | 5 | http://pandoc.org 6 | 7 | ftp://vim.org 8 | 9 | [[http://google.com]] 10 | 11 | [[mailto:info@example.org|email me]] 12 | 13 | mailto:hello@bye.com 14 | 15 | == internal links == 16 | 17 | [[This is a link]] 18 | 19 | [[This is a link source|Description of the link]] 20 | 21 | [[projects/Important Project 1]] 22 | 23 | [[../index]] 24 | 25 | [[a subdirectory/|Other files]] 26 | 27 | [[#tag-one|try me to test tag anchors]] 28 | 29 | [[#block quotes|try me to test header anchors]] 30 | 31 | [[#strong|try me to test strong anchors]] 32 | 33 | [[Todo List#Tomorrow|Tasks for tomorrow]] 34 | 35 | [[diary:2017-05-01]] 36 | 37 | [[file:../assets/data.csv|Important Data]] 38 | 39 | === links with thumbnails === 40 | 41 | [[http://www.google.com|{{./movie.jpg}}]] 42 | 43 | == images == 44 | 45 | {{file:./lalune.jpg}} 46 | 47 | {{http://vimwiki.googlecode.com/hg/images/vimwiki_logo.png|Vimwiki}} 48 | 49 | {{local:./movie.jpg}} 50 | 51 | === image with attributes === 52 | {{lalune.jpg|_cool stuff_|style="width:150px;height:120px;"}} 53 | 54 | {{nonexist.jpg|*Non-existing* image|class="center flow blabla" style="font-color:red"}} 55 | 56 | {{lalune.jpg|_cool stuff_|style="width:150px;height:120px;"|anything in this segment is ignored}} 57 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/lists.out.wiki: -------------------------------------------------------------------------------- 1 | 1. one 2 | 2. two 3 | 3. three 4 | 5 | 1. one 6 | 2. two 7 | 3. three 8 | 9 | * one 10 | * two 11 | * three 12 | 13 | - nested 14 | * list 15 | 1. of 16 | a. content 17 | second line of a 18 | second line of 1 19 | second line of bullet 20 | second line of hyphen 21 | - different list item 22 | 23 | - math 24 | {{$ 25 | math block 26 | }}$ 27 | - code 28 | {{{ 29 | code block 30 | }}} 31 | - table 32 | | table | containing | 33 | | multiple | cells | 34 | - definition list 35 | term:: def 36 | - blockquote 37 | > some blockquote 38 | 39 | I) self adjusting 40 | II) list items 41 | III) by understanding order 42 | 43 | - item 1 44 | with more content 45 | 46 | But this is a new paragraph 47 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/lists.wiki: -------------------------------------------------------------------------------- 1 | 1. one 2 | 2. two 3 | 3. three 4 | 5 | 4. one 6 | 5. two 7 | 6. three 8 | 9 | * one 10 | * two 11 | * three 12 | 13 | - nested 14 | * list 15 | 1. of 16 | a. content 17 | second line of a 18 | second line of 1 19 | second line of bullet 20 | second line of hyphen 21 | * different list item 22 | 23 | - math 24 | {{$ 25 | math block 26 | }}$ 27 | - code 28 | {{{ 29 | code block 30 | }}} 31 | - table 32 | | table | containing | 33 | | multiple | cells | 34 | - definition list 35 | term:: 36 | :: def 37 | - blockquote 38 | > some blockquote 39 | 40 | III) self adjusting 41 | i) list items 42 | X) by understanding order 43 | 44 | - item 1 45 | with more content 46 | But this is a new paragraph 47 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/paragraphs.out.wiki: -------------------------------------------------------------------------------- 1 | Some paragraph with *bold* text, _italic_ text, ~~strikethrough~~, `code`, 2 | $math$, [[links]], %%+ inline 3 | comments +%% 4 | and %% line comments. 5 | -------------------------------------------------------------------------------- /vimwiki-core/tests/output/vimwiki/paragraphs.wiki: -------------------------------------------------------------------------------- 1 | Some paragraph with *bold* text, _italic_ text, ~~strikethrough~~, `code`, 2 | $math$, [[links]], %%+inline 3 | comments+%% 4 | and %% line comments. 5 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/issue/119.wiki: -------------------------------------------------------------------------------- 1 | some words in front: https://example.com/ 2 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/issue/120.wiki: -------------------------------------------------------------------------------- 1 | == not tags == 2 | 3 | 2021-05-30 12:35:03.913609534-07:00 4 | 5 | 2021-05-30 15:41:15-06:00 6 | 7 | 15:41:15 8 | 9 | foo:bar:baz 10 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/issue/122.wiki: -------------------------------------------------------------------------------- 1 | == comments == 2 | 3 | %% this is a comment 4 | 5 | %% this is a comment with embedded:colons:in 6 | 7 | %% mark :: 2017-01-04T15:27:39-0700 8 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/misc/comment-in-definition-list.wiki: -------------------------------------------------------------------------------- 1 | %%term1:: def1 2 | %%term2:: def2 3 | %%+term3:: 4 | :: def3+%% 5 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/misc/depth-check.wiki: -------------------------------------------------------------------------------- 1 | = Header 1 = 2 | 3 | Paragraph with text, *bold*, _italic_, ~~strikeout~~, `code`, $math$, 4 | ^superscript^, ,,subscript,,, and [[links]]. 5 | 6 | - List of items 7 | - Containing a sublist 8 | - With another sublist 9 | - And an additional sublist 10 | with content from the a sublist 11 | and content after that sublist 12 | and *bold* content after that sublist 13 | - With another item after that 14 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/misc/windows-support.wiki: -------------------------------------------------------------------------------- 1 | this is a paragraph 2 | with carriage returns 3 | 4 | %%+this is a comment 5 | with carriage returns+%% 6 | 7 | {{{ 8 | this is a code block 9 | with carriage returns 10 | }}} 11 | 12 | {{$ 13 | this is a math block 14 | with carriage returns 15 | }}$ 16 | 17 | - this is a list 18 | - and a sublist 19 | with carriage returns 20 | - and multiple items 21 | 22 | term1:: 23 | :: with carriage returns 24 | term2:: with carriage returns 25 | 26 | === header with carriage returns === 27 | 28 | ---- 29 | 30 | %title some title 31 | 32 | | table | 33 | |-----------------------| 34 | | with carriage returns | 35 | 36 | `inline code with carriage returns` 37 | $inline math with carriage returns$ 38 | [[wiki link with carriage returns]] 39 | {{transclusion link with carriage returns}} 40 | https://raw-link-with-carriage-returns.example.com/ 41 | :tag:with:carriage:returns: 42 | ending text 43 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, io, path::PathBuf}; 2 | 3 | #[derive(Copy, Clone, PartialEq, Eq)] 4 | pub enum VimwikiFile { 5 | Issue119, 6 | Issue120, 7 | Issue122, 8 | MiscCommentInDefinitionList, 9 | MiscDepthCheck, 10 | MiscWindowsSupport, 11 | PandocVimwikiReader, 12 | VimwikiWikiIndex, 13 | VimwikiWikiTroubleshooting, 14 | VimwikiWikiTipsAndSnips, 15 | VimwikiWikiRelatedTools, 16 | } 17 | 18 | impl VimwikiFile { 19 | /// Loads and returns the file represented by the fixture 20 | pub fn load(&self) -> io::Result { 21 | fs::read_to_string(self.to_path()) 22 | } 23 | 24 | /// Returns the path associated with the fixture 25 | pub fn to_path(self) -> PathBuf { 26 | let head = PathBuf::from(env!("CARGO_MANIFEST_DIR")) 27 | .join("tests/parser/fixtures"); 28 | let tail = match self { 29 | Self::Issue119 => PathBuf::from("issue/119.wiki"), 30 | Self::Issue120 => PathBuf::from("issue/120.wiki"), 31 | Self::Issue122 => PathBuf::from("issue/122.wiki"), 32 | Self::MiscCommentInDefinitionList => { 33 | PathBuf::from("misc/comment-in-definition-list.wiki") 34 | } 35 | Self::MiscDepthCheck => PathBuf::from("misc/depth-check.wiki"), 36 | Self::MiscWindowsSupport => { 37 | PathBuf::from("misc/windows-support.wiki") 38 | } 39 | Self::PandocVimwikiReader => { 40 | PathBuf::from("pandoc/vimwiki-reader.wiki") 41 | } 42 | Self::VimwikiWikiIndex => PathBuf::from("vimwikiwiki/index.wiki"), 43 | Self::VimwikiWikiRelatedTools => { 44 | PathBuf::from("vimwikiwiki/Related Tools.wiki") 45 | } 46 | Self::VimwikiWikiTipsAndSnips => { 47 | PathBuf::from("vimwikiwiki/Tips and Snips.wiki") 48 | } 49 | Self::VimwikiWikiTroubleshooting => { 50 | PathBuf::from("vimwikiwiki/Troubleshooting.wiki") 51 | } 52 | }; 53 | head.join(tail) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/vimwikiwiki/Related Tools.wiki: -------------------------------------------------------------------------------- 1 | = Related Tools = 2 | 3 | This page contains Vim plugins and external tools that can be used with Vimwiki. 4 | These lists are incomplete so please _help update them_ if you know of something 5 | that is missing! 6 | 7 | == Vim Plugins == 8 | 9 | - [[https://github.com/mattn/calendar-vim|calendar-vim]] 10 | - Select a date to open a diary page. 11 | - [[https://github.com/tbabej/taskwiki|taskwiki]] 12 | - Integration with [[https://taskwarrior.org/|taskwarrior]] for task 13 | management. This only supports the default syntax. 14 | - [[https://github.com/majutsushi/tagbar|Tagbar]] 15 | - Creates a sidebar to view generated tags. For Vimwiki this can be 16 | used to display links to headers. 17 | - For this to work an [[https://raw.githubusercontent.com/vimwiki/utils/master/vwtags.py|additional script]] 18 | is needed which is available in the [[https://github.com/vimwiki/utils|utility repository]]. 19 | Additional instructions are in the comments of the script. 20 | - If using Markdown syntax `#` symbols used within code blocks are 21 | recognized as a header i.e. in a shell script snippet. An alternative 22 | version that works for Markdown can be found 23 | [[https://raw.githubusercontent.com/jszakmeister/markdown2ctags/master/markdown2ctags.py|here]]. 24 | - [[https://github.com/teranex/vimwiki-tasks|vimwiki-tasks]] 25 | - Another integration with [[https://taskwarrior.org/|taskwarrior]]. This 26 | plugin does not seem to be maintained any longer. 27 | - [[https://github.com/matt-snider/vim-tagquery|vim-tagquery]] 28 | - A vim plugin that enables improved querying of tags. 29 | - This can be used to search for multiple tags. 30 | - See [[https://github.com/vimwiki/vimwiki/issues/723|Issue #723]] 31 | 32 | == External Tools == 33 | 34 | - [[https://github.com/vimwiki/vimwiki/blob/master/autoload/vimwiki/customwiki2html.sh|customwiki2html.sh]] 35 | - Script available in the official repository to convert Markdown to HTML. 36 | - See the comments for more information and usage instructions. 37 | - [[https://pandoc.org/MANUAL.html|Pandoc]] 38 | - Convert Vimwiki to various other formats such as HTML, PDF, etc. 39 | - [[https://github.com/patrickdavey/vimwiki_markdown|vimwiki_markdown]] 40 | - A Ruby gem to convert vimwiki Markdown files to HTML. Still being actively 41 | developed. 42 | - [[https://github.com/WnP/vimwiki_markdown|vimwiki_markdown Python]] 43 | - A Python script to convert Markdown files to HTML. 44 | - Also see [[https://github.com/vimwiki/vimwiki/issues/578|Issue #578]] 45 | - [[https://github.com/maqiv/vimwiki-godown#todo|vimwiki-godown]] 46 | - HTML converter that adds the ability to prefix relative links to other 47 | Vimwiki pages 48 | - See [[https://github.com/vimwiki/vimwiki/issues/284|Issue #284]] 49 | - [[https://github.com/sstallion/vimwiki-skel|vimwiki-skel]] 50 | - Uses [[https://dynalon.github.io/mdwiki/#!index.md|MDWiki]] to convert 51 | Markdown files to HTML. 52 | - [[https://gist.github.com/maikeldotuk/54a91c21ed9623705fdce7bab2989742|wiki2html.sh Gist]] 53 | - Uses Pandoc to convert Markdown files to HTML. 54 | - [[https://github.com/fasheng/vimwiki2org|vimwiki2org]] 55 | - Convert vimwiki to [[https://orgmode.org/|Emacs orgmode]]. Has not been 56 | updated in 6 years. 57 | - [[https://github.com/ycpei/vwweb|vwweb]] 58 | - Python script to generate a website from Vimwiki files. 59 | - [[https://box.matto.nl/vimwikijabberbot.html|vimwiki-todo-jabberbot]] 60 | - Todo management using Jabberbot. The linked GitHub repository seems to 61 | have been removed. 62 | - [[https://github.com/zweifisch/vimwiki-tools|vimwiki-tools]] 63 | - Python tool to generate an index and convert wiki files to Markdown 64 | format. This has not been updated in 6 years. 65 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/vimwikiwiki/Troubleshooting.wiki: -------------------------------------------------------------------------------- 1 | = Troubleshooting = 2 | 3 | == Minimal Vimrc == 4 | 5 | These steps might help to determine if an issue is related to your 6 | setup/configuration or if the problem is a bug in Vimwiki. 7 | 8 | 1. Clone a fresh copy of the `dev` branch. 9 | {{{sh 10 | cd $HOME 11 | mkdir vw_tmp 12 | cd vw_tmp 13 | git clone -b dev --single-branch https://github.com/vimwiki/vimwiki.git 14 | }}} 15 | 2. Create a minimal `vimrc` (you should still be in `$HOME/vw_tmp`) 16 | - `vim min_vimrc` 17 | - Paste the below text into the opened file. Add any additional Vimwiki 18 | settings that are relevant to the issue being tested but keep in minimal! 19 | {{{vim 20 | set nocompatible 21 | filetype plugin on 22 | syntax on 23 | set runtimepath+=~/vw_tmp/vimwiki 24 | let wiki = {} 25 | let wiki.path = '~/vw_tmp/wiki' 26 | let wiki.path_html = '~/vw_tmp/wiki/html' 27 | let wiki.syntax = 'default' 28 | let wiki.ext = '.wiki' 29 | let g:vimwiki_list = [wiki] 30 | }}} 31 | 3. Start vim using the `min_vimrc` 32 | - `vim -u min_vimrc` 33 | 4. Open up Vimwiki (`\ww`) and verify the issue still occurs. 34 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/fixtures/vimwikiwiki/index.wiki: -------------------------------------------------------------------------------- 1 | = Vimwiki Wiki = 2 | 3 | *Welcome to the Vimwiki wiki!* 4 | 5 | == Official Repositories == 6 | 7 | Here are links to the official Vimwiki repositories: 8 | 9 | - [[https://github.com/vimwiki/vimwiki|Vimwiki]] 10 | - [[https://vimwiki.github.io/vimwikiwiki/|Vimwiki Wiki]] (GitHub pages site built using Vimwiki) 11 | - [[https://github.com/vimwiki/vimwikiwiki|source repository]] 12 | - [[https://github.com/vimwiki/utils|Utilities]] 13 | - [[https://github.com/vimwiki/testwikis|Test wikis]] - sample wikis in each of 14 | the 3 syntax variants. Used for testing and development. 15 | 16 | == Wiki Pages == 17 | 18 | Checkout these pages for additional information and tips! 19 | 20 | - [[Tips and Snips]] 21 | - [[Related Tools]] 22 | - [[Troubleshooting]] 23 | 24 | == Chat/Forums == 25 | 26 | - [[https://groups.google.com/forum/#!forum/vimwiki|Google Vimwiki group]] 27 | - [[https://webchat.freenode.net/?channels=#vimwiki|Freenode Webchat]] 28 | 29 | == Outdated Versions == 30 | 31 | These links point to some old versions of Vimwiki that are no longer maintained. 32 | The original Vimwiki was hosted on Google Code which has since shutdown. 33 | 34 | - [[https://code.google.com/archive/p/vimwiki/|Google Code Archive]] 35 | - [[https://github.com/vimwiki-backup/vimwiki|Google Code Backup on Github]] 36 | - [[https://www.vim.org/scripts/script.php?script_id=2226|Vimwiki on vim.org]] 37 | 38 | == Related Projects == 39 | 40 | - [[https://github.com/lervag/wiki.vim|wiki.vim]] 41 | - [[https://github.com/fcpg/vim-waikiki|vim-waikiki]] 42 | - [[https://github.com/jceb/vim-orgmode|vim-orgmode]] 43 | - [[https://github.com/tbabej/taskwiki|taskwiki]] 44 | - [[https://github.com/xolox/vim-notes|vim-notes]] 45 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/integration/issues.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::fixtures::VimwikiFile; 2 | use std::convert::TryFrom; 3 | use uriparse::URIReference; 4 | use vimwiki_core::*; 5 | 6 | /// https://github.com/chipsenkbeil/vimwiki-rs/issues/119 7 | #[test] 8 | fn issue_119() { 9 | let contents = VimwikiFile::Issue119.load().unwrap(); 10 | let page: Page = Language::from_vimwiki_str(&contents).parse().unwrap(); 11 | 12 | // some words in front: https://example.com/ 13 | assert_eq!( 14 | page[0], 15 | Located::from(BlockElement::from(Paragraph::new(vec![ 16 | InlineElementContainer::new(vec![ 17 | Located::from(InlineElement::from(Text::from( 18 | "some words in front: " 19 | ))), 20 | Located::from(InlineElement::from(Link::new_raw_link( 21 | URIReference::try_from("https://example.com/").unwrap() 22 | ))) 23 | ]), 24 | ]))) 25 | ); 26 | } 27 | 28 | /// https://github.com/chipsenkbeil/vimwiki-rs/issues/120 29 | #[test] 30 | fn issue_120() { 31 | let contents = VimwikiFile::Issue120.load().unwrap(); 32 | let page: Page = Language::from_vimwiki_str(&contents).parse().unwrap(); 33 | 34 | // == not tags == 35 | assert_eq!( 36 | page[0], 37 | Located::from(BlockElement::from(Header::new( 38 | InlineElementContainer::new(vec![Located::from( 39 | InlineElement::from(Text::from("not tags")) 40 | )]), 41 | 2, 42 | false, 43 | ))) 44 | ); 45 | 46 | // 2021-05-30 12:35:03.913609534-07:00 47 | assert_eq!( 48 | page[1], 49 | Located::from(BlockElement::from(Paragraph::new(vec![ 50 | InlineElementContainer::new(vec![Located::from( 51 | InlineElement::from(Text::from( 52 | "2021-05-30 12:35:03.913609534-07:00" 53 | )) 54 | )]), 55 | ]))) 56 | ); 57 | 58 | // 2021-05-30 15:41:15-06:00 59 | assert_eq!( 60 | page[2], 61 | Located::from(BlockElement::from(Paragraph::new(vec![ 62 | InlineElementContainer::new(vec![Located::from( 63 | InlineElement::from(Text::from("2021-05-30 15:41:15-06:00")) 64 | )]), 65 | ]))) 66 | ); 67 | 68 | // 15:41:15 69 | assert_eq!( 70 | page[3], 71 | Located::from(BlockElement::from(Paragraph::new(vec![ 72 | InlineElementContainer::new(vec![Located::from( 73 | InlineElement::from(Text::from("15:41:15")) 74 | )]), 75 | ]))) 76 | ); 77 | 78 | // foo:bar:baz 79 | assert_eq!( 80 | page[4], 81 | Located::from(BlockElement::from(Paragraph::new(vec![ 82 | InlineElementContainer::new(vec![Located::from( 83 | InlineElement::from(Text::from("foo:bar:baz")) 84 | )]), 85 | ]))) 86 | ); 87 | } 88 | 89 | /// https://github.com/chipsenkbeil/vimwiki-rs/issues/122 90 | #[test] 91 | fn issue_122() { 92 | let contents = VimwikiFile::Issue122.load().unwrap(); 93 | let page: Page = Language::from_vimwiki_str(&contents).parse().unwrap(); 94 | 95 | // == comments == 96 | assert_eq!( 97 | page[0], 98 | Located::from(BlockElement::from(Header::new( 99 | InlineElementContainer::new(vec![Located::from( 100 | InlineElement::from(Text::from("comments")) 101 | )]), 102 | 2, 103 | false, 104 | ))) 105 | ); 106 | 107 | // %% this is a comment 108 | assert_eq!( 109 | page[1], 110 | Located::from(BlockElement::from(Paragraph::new(vec![ 111 | InlineElementContainer::new(vec![Located::from( 112 | InlineElement::from(Comment::from(LineComment::from( 113 | " this is a comment" 114 | ))) 115 | )]), 116 | ]))) 117 | ); 118 | 119 | // %% this is a comment with embedded:colons:in 120 | assert_eq!( 121 | page[2], 122 | Located::from(BlockElement::from(Paragraph::new(vec![ 123 | InlineElementContainer::new(vec![Located::from( 124 | InlineElement::from(Comment::from(LineComment::from( 125 | " this is a comment with embedded:colons:in" 126 | ))) 127 | )]), 128 | ]))) 129 | ); 130 | 131 | // %% mark :: 2017-01-04T15:27:39-0700 132 | assert_eq!( 133 | page[3], 134 | Located::from(BlockElement::from(Paragraph::new(vec![ 135 | InlineElementContainer::new(vec![Located::from( 136 | InlineElement::from(Comment::from(LineComment::from( 137 | " mark :: 2017-01-04T15:27:39-0700" 138 | ))) 139 | )]), 140 | ]))) 141 | ); 142 | } 143 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/integration/misc/comment_in_definition_list.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::fixtures::VimwikiFile; 2 | use std::borrow::Cow; 3 | use vimwiki_core::*; 4 | 5 | #[test] 6 | fn test() { 7 | let contents = VimwikiFile::MiscCommentInDefinitionList.load().unwrap(); 8 | let page: Page = Language::from_vimwiki_str(&contents).parse().unwrap(); 9 | 10 | assert_eq!( 11 | page[0], 12 | Located::from(BlockElement::from(Paragraph::new(vec![ 13 | InlineElementContainer::new(vec![Located::from( 14 | InlineElement::from(Comment::from(LineComment::from( 15 | "term1:: def1" 16 | ))) 17 | )]), 18 | InlineElementContainer::new(vec![Located::from( 19 | InlineElement::from(Comment::from(LineComment::from( 20 | "term2:: def2" 21 | ))) 22 | )]), 23 | InlineElementContainer::new(vec![Located::from( 24 | InlineElement::from(Comment::from(MultiLineComment::new( 25 | vec![Cow::Borrowed("term3::"), Cow::Borrowed(":: def3")] 26 | ))) 27 | )]), 28 | ]))), 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/integration/misc/mod.rs: -------------------------------------------------------------------------------- 1 | mod comment_in_definition_list; 2 | mod depth_check; 3 | mod windows_support; 4 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/integration/mod.rs: -------------------------------------------------------------------------------- 1 | mod issues; 2 | mod misc; 3 | mod pandoc; 4 | mod vimwiki_wiki; 5 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/integration/pandoc/mod.rs: -------------------------------------------------------------------------------- 1 | mod vimwiki_reader; 2 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/integration/vimwiki_wiki/index.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::{fixtures::VimwikiFile, utils::compare_page_elements}; 2 | use vimwiki::macros::*; 3 | use vimwiki::*; 4 | 5 | #[test] 6 | fn test() { 7 | let contents = VimwikiFile::VimwikiWikiIndex.load().unwrap(); 8 | let page: Page = Language::from_vimwiki_str(&contents).parse().unwrap(); 9 | let expected = vec![ 10 | vimwiki_header!("= Vimwiki Wiki =") 11 | .into(), 12 | vimwiki_paragraph!("*Welcome to the Vimwiki wiki!*") 13 | .into(), 14 | vimwiki_header!("== Official Repositories ==") 15 | .into(), 16 | vimwiki_paragraph!("Here are links to the official Vimwiki repositories:") 17 | .into(), 18 | vimwiki_list! {r#" 19 | - [[https://github.com/vimwiki/vimwiki|Vimwiki]] 20 | - [[https://vimwiki.github.io/vimwikiwiki/|Vimwiki Wiki]] (GitHub pages site built using Vimwiki) 21 | - [[https://github.com/vimwiki/vimwikiwiki|source repository]] 22 | - [[https://github.com/vimwiki/utils|Utilities]] 23 | - [[https://github.com/vimwiki/testwikis|Test wikis]] - sample wikis in each of 24 | the 3 syntax variants. Used for testing and development. 25 | "#} 26 | .into(), 27 | vimwiki_header!("== Wiki Pages ==") 28 | .into(), 29 | vimwiki_paragraph!("Checkout these pages for additional information and tips!") 30 | .into(), 31 | vimwiki_list! {r#" 32 | - [[Tips and Snips]] 33 | - [[Related Tools]] 34 | - [[Troubleshooting]] 35 | "#} 36 | .into(), 37 | vimwiki_header!("== Chat/Forums ==") 38 | .into(), 39 | vimwiki_list! {r#" 40 | - [[https://groups.google.com/forum/#!forum/vimwiki|Google Vimwiki group]] 41 | - [[https://webchat.freenode.net/?channels=#vimwiki|Freenode Webchat]] 42 | "#} 43 | .into(), 44 | vimwiki_header!("== Outdated Versions ==") 45 | .into(), 46 | vimwiki_paragraph! {r#" 47 | These links point to some old versions of Vimwiki that are no longer maintained. 48 | The original Vimwiki was hosted on Google Code which has since shutdown. 49 | "#} 50 | .into(), 51 | vimwiki_list! {r#" 52 | - [[https://code.google.com/archive/p/vimwiki/|Google Code Archive]] 53 | - [[https://github.com/vimwiki-backup/vimwiki|Google Code Backup on Github]] 54 | - [[https://www.vim.org/scripts/script.php?script_id=2226|Vimwiki on vim.org]] 55 | "#} 56 | .into(), 57 | vimwiki_header!("== Related Projects ==") 58 | .into(), 59 | vimwiki_list! {r#" 60 | - [[https://github.com/lervag/wiki.vim|wiki.vim]] 61 | - [[https://github.com/fcpg/vim-waikiki|vim-waikiki]] 62 | - [[https://github.com/jceb/vim-orgmode|vim-orgmode]] 63 | - [[https://github.com/tbabej/taskwiki|taskwiki]] 64 | - [[https://github.com/xolox/vim-notes|vim-notes]] 65 | "#} 66 | .into(), 67 | ]; 68 | 69 | compare_page_elements(page.elements(), &expected); 70 | } 71 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/integration/vimwiki_wiki/mod.rs: -------------------------------------------------------------------------------- 1 | mod index; 2 | mod related_tools; 3 | mod tips_and_snips; 4 | mod troubleshooting; 5 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/integration/vimwiki_wiki/related_tools.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::{fixtures::VimwikiFile, utils::compare_page_elements}; 2 | use vimwiki::macros::*; 3 | use vimwiki::*; 4 | 5 | #[test] 6 | fn test() { 7 | let contents = VimwikiFile::VimwikiWikiRelatedTools.load().unwrap(); 8 | let page: Page = Language::from_vimwiki_str(&contents).parse().unwrap(); 9 | let expected = vec![ 10 | vimwiki_header!("= Related Tools =") 11 | .into(), 12 | vimwiki_paragraph! {r#" 13 | This page contains Vim plugins and external tools that can be used with Vimwiki. 14 | These lists are incomplete so please _help update them_ if you know of something 15 | that is missing! 16 | "#} 17 | .into(), 18 | vimwiki_header!("== Vim Plugins ==") 19 | .into(), 20 | vimwiki_list! {r#" 21 | - [[https://github.com/mattn/calendar-vim|calendar-vim]] 22 | - Select a date to open a diary page. 23 | - [[https://github.com/tbabej/taskwiki|taskwiki]] 24 | - Integration with [[https://taskwarrior.org/|taskwarrior]] for task 25 | management. This only supports the default syntax. 26 | - [[https://github.com/majutsushi/tagbar|Tagbar]] 27 | - Creates a sidebar to view generated tags. For Vimwiki this can be 28 | used to display links to headers. 29 | - For this to work an [[https://raw.githubusercontent.com/vimwiki/utils/master/vwtags.py|additional script]] 30 | is needed which is available in the [[https://github.com/vimwiki/utils|utility repository]]. 31 | Additional instructions are in the comments of the script. 32 | - If using Markdown syntax `#` symbols used within code blocks are 33 | recognized as a header i.e. in a shell script snippet. An alternative 34 | version that works for Markdown can be found 35 | [[https://raw.githubusercontent.com/jszakmeister/markdown2ctags/master/markdown2ctags.py|here]]. 36 | - [[https://github.com/teranex/vimwiki-tasks|vimwiki-tasks]] 37 | - Another integration with [[https://taskwarrior.org/|taskwarrior]]. This 38 | plugin does not seem to be maintained any longer. 39 | - [[https://github.com/matt-snider/vim-tagquery|vim-tagquery]] 40 | - A vim plugin that enables improved querying of tags. 41 | - This can be used to search for multiple tags. 42 | - See [[https://github.com/vimwiki/vimwiki/issues/723|Issue #723]] 43 | "#} 44 | .into(), 45 | vimwiki_header!("== External Tools ==") 46 | .into(), 47 | vimwiki_list! {r#" 48 | - [[https://github.com/vimwiki/vimwiki/blob/master/autoload/vimwiki/customwiki2html.sh|customwiki2html.sh]] 49 | - Script available in the official repository to convert Markdown to HTML. 50 | - See the comments for more information and usage instructions. 51 | - [[https://pandoc.org/MANUAL.html|Pandoc]] 52 | - Convert Vimwiki to various other formats such as HTML, PDF, etc. 53 | - [[https://github.com/patrickdavey/vimwiki_markdown|vimwiki_markdown]] 54 | - A Ruby gem to convert vimwiki Markdown files to HTML. Still being actively 55 | developed. 56 | - [[https://github.com/WnP/vimwiki_markdown|vimwiki_markdown Python]] 57 | - A Python script to convert Markdown files to HTML. 58 | - Also see [[https://github.com/vimwiki/vimwiki/issues/578|Issue #578]] 59 | - [[https://github.com/maqiv/vimwiki-godown#todo|vimwiki-godown]] 60 | - HTML converter that adds the ability to prefix relative links to other 61 | Vimwiki pages 62 | - See [[https://github.com/vimwiki/vimwiki/issues/284|Issue #284]] 63 | - [[https://github.com/sstallion/vimwiki-skel|vimwiki-skel]] 64 | - Uses [[https://dynalon.github.io/mdwiki/#!index.md|MDWiki]] to convert 65 | Markdown files to HTML. 66 | - [[https://gist.github.com/maikeldotuk/54a91c21ed9623705fdce7bab2989742|wiki2html.sh Gist]] 67 | - Uses Pandoc to convert Markdown files to HTML. 68 | - [[https://github.com/fasheng/vimwiki2org|vimwiki2org]] 69 | - Convert vimwiki to [[https://orgmode.org/|Emacs orgmode]]. Has not been 70 | updated in 6 years. 71 | - [[https://github.com/ycpei/vwweb|vwweb]] 72 | - Python script to generate a website from Vimwiki files. 73 | - [[https://box.matto.nl/vimwikijabberbot.html|vimwiki-todo-jabberbot]] 74 | - Todo management using Jabberbot. The linked GitHub repository seems to 75 | have been removed. 76 | - [[https://github.com/zweifisch/vimwiki-tools|vimwiki-tools]] 77 | - Python tool to generate an index and convert wiki files to Markdown 78 | format. This has not been updated in 6 years. 79 | "#} 80 | .into(), 81 | ]; 82 | 83 | compare_page_elements(page.elements(), &expected); 84 | } 85 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/integration/vimwiki_wiki/troubleshooting.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::{fixtures::VimwikiFile, utils::compare_page_elements}; 2 | use vimwiki::macros::*; 3 | use vimwiki::*; 4 | 5 | #[test] 6 | fn test() { 7 | let contents = VimwikiFile::VimwikiWikiTroubleshooting.load().unwrap(); 8 | let page: Page = Language::from_vimwiki_str(&contents).parse().unwrap(); 9 | let expected = vec![ 10 | vimwiki_header!("= Troubleshooting =") 11 | .into(), 12 | vimwiki_header!("== Minimal Vimrc ==") 13 | .into(), 14 | vimwiki_paragraph! {r#" 15 | These steps might help to determine if an issue is related to your 16 | setup/configuration or if the problem is a bug in Vimwiki. 17 | "#} 18 | .into(), 19 | vimwiki_list! {r#" 20 | 1. Clone a fresh copy of the `dev` branch. 21 | {{{sh 22 | cd $HOME 23 | mkdir vw_tmp 24 | cd vw_tmp 25 | git clone -b dev --single-branch https://github.com/vimwiki/vimwiki.git 26 | }}} 27 | 2. Create a minimal `vimrc` (you should still be in `$HOME/vw_tmp`) 28 | - `vim min_vimrc` 29 | - Paste the below text into the opened file. Add any additional Vimwiki 30 | settings that are relevant to the issue being tested but keep in minimal! 31 | {{{vim 32 | set nocompatible 33 | filetype plugin on 34 | syntax on 35 | set runtimepath+=~/vw_tmp/vimwiki 36 | let wiki = {} 37 | let wiki.path = '~/vw_tmp/wiki' 38 | let wiki.path_html = '~/vw_tmp/wiki/html' 39 | let wiki.syntax = 'default' 40 | let wiki.ext = '.wiki' 41 | let g:vimwiki_list = [wiki] 42 | }}} 43 | 3. Start vim using the `min_vimrc` 44 | - `vim -u min_vimrc` 45 | 4. Open up Vimwiki (`\ww`) and verify the issue still occurs. 46 | "#} 47 | .into(), 48 | ]; 49 | 50 | compare_page_elements(page.elements(), &expected); 51 | } 52 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/mod.rs: -------------------------------------------------------------------------------- 1 | mod fixtures; 2 | mod integration; 3 | mod utils; 4 | -------------------------------------------------------------------------------- /vimwiki-core/tests/parser/utils.rs: -------------------------------------------------------------------------------- 1 | use vimwiki::{BlockElement, Located}; 2 | 3 | /// Compares top-level block elements from a page against an expected set 4 | pub fn compare_page_elements<'a>( 5 | actual: &[Located>], 6 | expected: &[Located>], 7 | ) { 8 | // NOTE: Rather than comparing vecs directly, we iterate through the 9 | // page elements with a zip so we can get finer-grain details on 10 | // what and when there is an issue 11 | for (i, (ac, ec)) in actual.iter().zip(expected.iter()).enumerate() { 12 | assert_eq!(ac, ec, "Elements at index {} are not equal!", i); 13 | } 14 | 15 | // NOTE: Because we are not comparing vecs directly, we need to verify at 16 | // the end that their sizes match because a zip will work with 17 | // uneven vecs, stopping when the first stops 18 | assert_eq!( 19 | actual.len(), 20 | expected.len(), 21 | "Varying number of top-level page elements!" 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /vimwiki-wasm/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | pkg.bundle/ 4 | pkg.node/ 5 | -------------------------------------------------------------------------------- /vimwiki-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vimwiki-wasm" 3 | description = "Wasm binding for vimwiki library" 4 | categories = ["parser-implementations", "template-engine"] 5 | version = "0.1.1" 6 | authors = ["Chip Senkbeil "] 7 | edition = "2018" 8 | homepage = "https://github.com/chipsenkbeil/vimwiki-rs" 9 | repository = "https://github.com/chipsenkbeil/vimwiki-rs" 10 | readme = "README.md" 11 | license = "MIT OR Apache-2.0" 12 | 13 | [package.metadata.wasm-pack.profile.release] 14 | wasm-opt = false 15 | 16 | [lib] 17 | crate-type = ["cdylib"] 18 | 19 | [dependencies] 20 | js-sys = "0.3.51" 21 | wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] } 22 | vimwiki = { version = "=0.1.1", path = "../vimwiki", features = ["html"] } 23 | -------------------------------------------------------------------------------- /vimwiki-wasm/PUBLISH.md: -------------------------------------------------------------------------------- 1 | # Publish Guide 2 | 3 | 1. `wasm-pack build -t bundler --out-dir pkg.bundle` 4 | 2. `wasm-pack build -t nodejs --out-dir pkg.node` 5 | 3. `npm publish` 6 | -------------------------------------------------------------------------------- /vimwiki-wasm/README.md: -------------------------------------------------------------------------------- 1 | # vimwiki wasm 2 | 3 | Provides wasm bindings for vimwiki library. 4 | 5 | ## Usage 6 | 7 | ``` 8 | npm install vimwiki-wasm 9 | ``` 10 | 11 | ## Examples 12 | 13 | ```html 14 | 15 | 60 | ``` 61 | 62 | ## Building from source 63 | 64 | Compiling for webpack bundler: 65 | 66 | `wasm-pack build --target bundler` 67 | 68 | Compiling for nodejs: 69 | 70 | `wasm-pack build --target nodejs` 71 | 72 | ## License 73 | 74 | This project is licensed under either of 75 | 76 | Apache License, Version 2.0, (LICENSE-APACHE or 77 | [apache-license][apache-license]) MIT license (LICENSE-MIT or 78 | [mit-license][mit-license]) at your option. 79 | 80 | [apache-license]: http://www.apache.org/licenses/LICENSE-2.0 81 | [mit-license]: http://opensource.org/licenses/MIT 82 | -------------------------------------------------------------------------------- /vimwiki-wasm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vimwiki-wasm", 3 | "collaborators": [ 4 | "Chip Senkbeil " 5 | ], 6 | "description": "Wasm binding for vimwiki library", 7 | "version": "0.1.1", 8 | "license": "MIT OR Apache-2.0", 9 | "homepage": "https://github.com/chipsenkbeil/vimwiki-rs", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/chipsenkbeil/vimwiki-rs" 13 | }, 14 | "files": [ 15 | "pkg.bundle/vimwiki_wasm.js", 16 | "pkg.bundle/vimwiki_wasm.d.ts", 17 | "pkg.bundle/vimwiki_wasm_bg.js", 18 | "pkg.bundle/vimwiki_wasm_bg.wasm", 19 | "pkg.bundle/vimwiki_wasm_bg.wasm.d.ts", 20 | "pkg.node/vimwiki_wasm.js", 21 | "pkg.node/vimwiki_wasm.d.ts", 22 | "pkg.node/vimwiki_wasm_bg.wasm", 23 | "pkg.node/vimwiki_wasm_bg.wasm.d.ts" 24 | ], 25 | "main": "pkg.node/vimwiki_wasm.js", 26 | "module": "pkg.bundle/vimwiki_wasm.js", 27 | "types": "pkg.bundle/vimwiki_wasm.d.ts", 28 | "sideEffects": false 29 | } 30 | -------------------------------------------------------------------------------- /vimwiki-wasm/publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | wasm-pack build -t bundler --out-dir pkg.bundle 4 | wasm-pack build -t nodejs --out-dir pkg.node 5 | npm publish 6 | -------------------------------------------------------------------------------- /vimwiki-wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | use vimwiki::{self as v, Language, ParseError}; 2 | use wasm_bindgen::prelude::*; 3 | 4 | mod elements; 5 | pub use elements::*; 6 | 7 | mod utils; 8 | 9 | #[wasm_bindgen] 10 | pub fn parse_vimwiki_str(s: &str) -> Result { 11 | let page_res: Result = 12 | Language::from_vimwiki_str(s).parse(); 13 | 14 | match page_res { 15 | Ok(page) => Ok(Page::from(page.into_owned())), 16 | Err(x) => Err(x.to_string().into()), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /vimwiki-wasm/src/utils.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::{convert::FromWasmAbi, prelude::*}; 2 | 3 | // From https://github.com/rustwasm/wasm-bindgen/issues/2231#issuecomment-656293288 4 | pub fn cast_value>( 5 | js: JsValue, 6 | classname: &str, 7 | ) -> Result { 8 | use js_sys::{Object, Reflect}; 9 | let ctor_name = Object::get_prototype_of(&js).constructor().name(); 10 | if ctor_name == classname { 11 | let ptr = Reflect::get(&js, &JsValue::from_str("ptr"))?; 12 | let ptr_u32: u32 = ptr.as_f64().ok_or(JsValue::NULL)? as u32; 13 | let value = unsafe { T::from_abi(ptr_u32) }; 14 | Ok(value) 15 | } else { 16 | Err(JsValue::NULL) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /vimwiki/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /vimwiki/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vimwiki" 3 | description = "Library that provides support to parse, generate, and manipulate vimwiki language." 4 | categories = ["parser-implementations", "template-engine"] 5 | version = "0.1.1" 6 | authors = ["Chip Senkbeil "] 7 | edition = "2018" 8 | homepage = "https://github.com/chipsenkbeil/vimwiki-rs" 9 | repository = "https://github.com/chipsenkbeil/vimwiki-rs" 10 | readme = "README.md" 11 | license = "MIT OR Apache-2.0" 12 | 13 | [features] 14 | default = [] 15 | html = ["vimwiki-core/html"] 16 | macros = ["vimwiki_macros"] 17 | timekeeper = ["vimwiki-core/timekeeper"] 18 | 19 | [dependencies] 20 | vimwiki-core = { version = "=0.1.1", path = "../vimwiki-core" } 21 | vimwiki_macros = { version = "=0.1.1", path = "../vimwiki_macros", optional = true } 22 | -------------------------------------------------------------------------------- /vimwiki/README.md: -------------------------------------------------------------------------------- 1 | # vimwiki 2 | 3 | This crate represents the language definition and parsing support for 4 | the vimwiki language. This has been broken out to be a shareable crate for 5 | others to build on top of the vimwiki language and write their own tooling. 6 | 7 | ## Usage 8 | 9 | Add this to your `Cargo.toml`: 10 | 11 | ```toml 12 | [dependencies] 13 | vimwiki = "0.1" 14 | ``` 15 | 16 | ## Examples 17 | 18 | ```rust 19 | use vimwiki::{Language, elements::*}; 20 | 21 | // Load some language as a string 22 | let language = Language::from_vimwiki_str(r#" 23 | = My Header = 24 | /// 25 | Some paragraph with *decorations* and [[links]] that you would normally 26 | see in a vimwiki file. 27 | "#); 28 | 29 | // Parse the input as a page using vimwiki format 30 | let page: Page = language.parse().unwrap(); 31 | ``` 32 | 33 | ## Features 34 | 35 | By default, no features are enable, but the following are offered: 36 | 37 | - **html**: If specified, builds in support to convert vimwiki elements to 38 | html. 39 | - **macros**: If specified, pulls in `vimwiki_macros` to expose top-level macro 40 | functions to parse and produce vimwiki elements at compile-time. 41 | - **timekeeper**: If specified, all parser logic runs through a 42 | statically-allocated `HashMap` that logs the time taken to parse various 43 | elements and can print out results in a human-readable format. This is 44 | predominately useful for performance optimizations internally. 45 | 46 | ## License 47 | 48 | This project is licensed under either of 49 | 50 | Apache License, Version 2.0, (LICENSE-APACHE or 51 | [apache-license][apache-license]) MIT license (LICENSE-MIT or 52 | [mit-license][mit-license]) at your option. 53 | 54 | [apache-license]: http://www.apache.org/licenses/LICENSE-2.0 55 | [mit-license]: http://opensource.org/licenses/MIT 56 | -------------------------------------------------------------------------------- /vimwiki/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Export core at all times 2 | pub use vimwiki_core::*; 3 | 4 | // Export macros if specified as a separate module to avoid 5 | // potential name collisions in the future 6 | #[cfg(feature = "macros")] 7 | pub mod macros { 8 | pub use vimwiki_macros::*; 9 | } 10 | -------------------------------------------------------------------------------- /vimwiki_macros/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /vimwiki_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vimwiki_macros" 3 | description = "Macro library that provides macros to generate vimwiki language at compile time." 4 | categories = ["parser-implementations"] 5 | version = "0.1.1" 6 | authors = ["Chip Senkbeil "] 7 | edition = "2018" 8 | homepage = "https://github.com/chipsenkbeil/vimwiki-rs" 9 | repository = "https://github.com/chipsenkbeil/vimwiki-rs" 10 | readme = "README.md" 11 | license = "MIT OR Apache-2.0" 12 | 13 | [lib] 14 | proc-macro = true 15 | name = "vimwiki_macros" 16 | 17 | [dependencies] 18 | lazy_static = "1.4.0" 19 | paste = "1.0" 20 | proc-macro2 = { version = "1.0", features = [ "span-locations" ] } 21 | proc-macro-crate = "1.0.0" 22 | quote = "1.0" 23 | regex = "1.4.6" 24 | syn = "1.0.70" 25 | vimwiki-core = { version = "=0.1.1", path = "../vimwiki-core" } 26 | -------------------------------------------------------------------------------- /vimwiki_macros/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help build clean test 2 | 3 | help: ## Display help information 4 | @printf 'usage: make [target] ...\n\ntargets:\n' 5 | @egrep '^(.+)\:\ .*##\ (.+)' ${MAKEFILE_LIST} | sed 's/:.*##/#/' | column -t -c 2 -s '#' 6 | 7 | build: ## Build debug version 8 | @cargo build 9 | 10 | clean: ## Cleans build resources 11 | @cargo clean 12 | 13 | test: ## Run all tests 14 | @cargo test 15 | -------------------------------------------------------------------------------- /vimwiki_macros/README.md: -------------------------------------------------------------------------------- 1 | # vimwiki macros 2 | 3 | Contains macros to generate vimwiki components in Rust at compile time. 4 | 5 | It should NOT be imported directly. Instead, when importing `vimwiki`, this 6 | crate is brought in when supplying the `macros` feature. 7 | 8 | ## Usage 9 | 10 | Add this to your `Cargo.toml`: 11 | 12 | ```toml 13 | [dependencies] 14 | vimwiki = { version = "0.1", features = ["macros"] } 15 | ``` 16 | 17 | ## Examples 18 | 19 | ```rust 20 | use vimwiki::macros::vimwiki_page; 21 | 22 | // Parse vimwiki language at compile-time and generate Rust-based elements 23 | let page = vimwiki_page! {r#" 24 | = Header = 25 | 26 | Some paragraph with *bold* content 27 | and some [[links]]. 28 | 29 | - List item 1 30 | - List item 2 31 | "#}; 32 | ``` 33 | 34 | ## License 35 | 36 | This project is licensed under either of 37 | 38 | Apache License, Version 2.0, (LICENSE-APACHE or 39 | [apache-license][apache-license]) MIT license (LICENSE-MIT or 40 | [mit-license][mit-license]) at your option. 41 | 42 | [apache-license]: http://www.apache.org/licenses/LICENSE-2.0 43 | [mit-license]: http://opensource.org/licenses/MIT 44 | -------------------------------------------------------------------------------- /vimwiki_macros/src/args.rs: -------------------------------------------------------------------------------- 1 | // FormatArgs Parse implementation from https://github.com/dtolnay/syn/blob/master/src/mac.rs 2 | 3 | use syn::ext::IdentExt; 4 | use syn::parse::{Parse, ParseStream, Result}; 5 | use syn::{Expr, Ident, Token}; 6 | 7 | // The arguments expected by libcore's format_args macro, and as a 8 | // result most other formatting and printing macros like println. 9 | // 10 | // println!("{} is {number:.prec$}", "x", prec=5, number=0.01) 11 | #[derive(Debug)] 12 | pub struct FormatArgs { 13 | pub format_string: Expr, 14 | pub positional_args: Vec, 15 | pub named_args: Vec<(Ident, Expr)>, 16 | } 17 | 18 | impl Parse for FormatArgs { 19 | fn parse(input: ParseStream) -> Result { 20 | let mut positional_args = Vec::new(); 21 | let mut named_args = Vec::new(); 22 | 23 | let format_string: Expr = input.parse()?; 24 | while !input.is_empty() { 25 | input.parse::()?; 26 | if input.is_empty() { 27 | break; 28 | } 29 | if input.peek(Ident::peek_any) && input.peek2(Token![=]) { 30 | while !input.is_empty() { 31 | let name: Ident = input.call(Ident::parse_any)?; 32 | input.parse::()?; 33 | let value: Expr = input.parse()?; 34 | named_args.push((name, value)); 35 | if input.is_empty() { 36 | break; 37 | } 38 | input.parse::()?; 39 | } 40 | break; 41 | } 42 | positional_args.push(input.parse()?); 43 | } 44 | 45 | Ok(FormatArgs { 46 | format_string, 47 | positional_args, 48 | named_args, 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vimwiki_macros/src/error.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{ 2 | Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, 3 | TokenTree, 4 | }; 5 | use std::iter::FromIterator; 6 | 7 | pub type Result = std::result::Result; 8 | 9 | pub struct Error { 10 | begin: Span, 11 | end: Span, 12 | msg: String, 13 | } 14 | 15 | impl Error { 16 | pub fn new(span: Span, msg: &str) -> Self { 17 | Self::new2(span, span, msg) 18 | } 19 | 20 | pub fn new2(begin: Span, end: Span, msg: &str) -> Self { 21 | Error { 22 | begin, 23 | end, 24 | msg: msg.to_owned(), 25 | } 26 | } 27 | 28 | pub fn to_compile_error(&self) -> TokenStream { 29 | // compile_error! { $msg } 30 | TokenStream::from_iter(vec![ 31 | TokenTree::Ident(Ident::new("compile_error", self.begin)), 32 | TokenTree::Punct({ 33 | let mut punct = Punct::new('!', Spacing::Alone); 34 | punct.set_span(self.begin); 35 | punct 36 | }), 37 | TokenTree::Group({ 38 | let mut group = Group::new(Delimiter::Brace, { 39 | TokenStream::from_iter(vec![TokenTree::Literal({ 40 | let mut string = Literal::string(&self.msg); 41 | string.set_span(self.end); 42 | string 43 | })]) 44 | }); 45 | group.set_span(self.end); 46 | group 47 | }), 48 | ]) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/blockquotes.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::Blockquote; 5 | 6 | impl_tokenize!(tokenize_blockquote, Blockquote<'a>, 'a); 7 | fn tokenize_blockquote( 8 | ctx: &TokenizeContext, 9 | blockquote: &Blockquote, 10 | ) -> TokenStream { 11 | let root = root_crate(); 12 | let lines = blockquote.lines.iter().map(|x| do_tokenize!(ctx, x)); 13 | quote! { 14 | #root::Blockquote::new(::std::vec![#(#lines),*]) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/code.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{ 2 | utils::{ 3 | root_crate, tokenize_cow_str_type, tokenize_hashmap, tokenize_option, 4 | }, 5 | Tokenize, TokenizeContext, 6 | }; 7 | use proc_macro2::TokenStream; 8 | use quote::quote; 9 | use vimwiki_core::CodeBlock; 10 | 11 | impl_tokenize!(tokenize_code_block, CodeBlock<'a>, 'a); 12 | fn tokenize_code_block( 13 | ctx: &TokenizeContext, 14 | code_block: &CodeBlock, 15 | ) -> TokenStream { 16 | let root = root_crate(); 17 | let lang = tokenize_option(ctx, &code_block.language, |ctx, x| { 18 | do_tokenize!(ctx, x) 19 | }); 20 | let metadata = tokenize_hashmap( 21 | &code_block.metadata, 22 | tokenize_cow_str_type(), 23 | tokenize_cow_str_type(), 24 | |x| do_tokenize!(ctx, x), 25 | |x| do_tokenize!(ctx, x), 26 | ); 27 | let lines = code_block.lines.iter().map(|x| do_tokenize!(ctx, x)); 28 | quote! { 29 | #root::CodeBlock::new( 30 | #lang, 31 | #metadata, 32 | ::std::vec![#(#lines),*], 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/definitions.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::{ 5 | DefinitionBundle, DefinitionList, DefinitionListValue, TermAndDefinitions, 6 | }; 7 | 8 | impl_tokenize!(tokenize_definition_list, DefinitionList<'a>, 'a); 9 | fn tokenize_definition_list( 10 | ctx: &TokenizeContext, 11 | definition_list: &DefinitionList, 12 | ) -> TokenStream { 13 | let root = root_crate(); 14 | let td = definition_list.iter().map(|x| do_tokenize!(ctx, x)); 15 | quote! { 16 | #root::DefinitionList::new(::std::vec![#(#td),*]) 17 | } 18 | } 19 | 20 | impl_tokenize!(tokenize_term_and_definitions, TermAndDefinitions<'a>, 'a); 21 | fn tokenize_term_and_definitions( 22 | ctx: &TokenizeContext, 23 | term_and_definitions: &TermAndDefinitions, 24 | ) -> TokenStream { 25 | let root = root_crate(); 26 | let term = do_tokenize!(ctx, term_and_definitions.term); 27 | let definitions = do_tokenize!(ctx, term_and_definitions.definitions); 28 | quote! { 29 | #root::TermAndDefinitions::new(#term, #definitions) 30 | } 31 | } 32 | 33 | impl_tokenize!(tokenize_definition_bundle, DefinitionBundle<'a>, 'a); 34 | fn tokenize_definition_bundle( 35 | ctx: &TokenizeContext, 36 | definition_bundle: &DefinitionBundle, 37 | ) -> TokenStream { 38 | let root = root_crate(); 39 | let definitions = definition_bundle.iter().map(|x| do_tokenize!(ctx, x)); 40 | quote! { 41 | #root::DefinitionBundle::new(::std::vec![#(#definitions),*]) 42 | } 43 | } 44 | 45 | impl_tokenize!(tokenize_definition_list_value, DefinitionListValue<'a>, 'a); 46 | fn tokenize_definition_list_value( 47 | ctx: &TokenizeContext, 48 | definition_list_value: &DefinitionListValue, 49 | ) -> TokenStream { 50 | let root = root_crate(); 51 | let inner = do_tokenize!(ctx, definition_list_value.as_inner()); 52 | quote! { 53 | #root::DefinitionListValue::new(#inner) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/dividers.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::Divider; 5 | 6 | impl_tokenize!(tokenize_divider, Divider); 7 | fn tokenize_divider(_ctx: &TokenizeContext, _divider: &Divider) -> TokenStream { 8 | let root = root_crate(); 9 | quote! { 10 | #root::Divider 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/headers.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::Header; 5 | 6 | impl_tokenize!(tokenize_header, Header<'a>, 'a); 7 | fn tokenize_header(ctx: &TokenizeContext, header: &Header) -> TokenStream { 8 | let root = root_crate(); 9 | let level = header.level; 10 | let centered = header.centered; 11 | let content_t = do_tokenize!(ctx, &header.content); 12 | quote! { 13 | #root::Header::new( 14 | #content_t, 15 | #level, 16 | #centered, 17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/inline/code.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use std::borrow::Cow; 5 | use vimwiki_core::CodeInline; 6 | 7 | impl_tokenize!(tokenize_code_inline, CodeInline<'a>, 'a); 8 | fn tokenize_code_inline( 9 | ctx: &TokenizeContext, 10 | code_inline: &CodeInline, 11 | ) -> TokenStream { 12 | let root = root_crate(); 13 | let code = do_tokenize!(ctx, Cow::Borrowed(code_inline.as_str())); 14 | quote! { 15 | #root::CodeInline::new(#code) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/inline/comments.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use std::borrow::Cow; 5 | use vimwiki_core::{Comment, LineComment, MultiLineComment}; 6 | 7 | impl_tokenize!(tokenize_comment, Comment<'a>, 'a); 8 | fn tokenize_comment(ctx: &TokenizeContext, comment: &Comment) -> TokenStream { 9 | let root = root_crate(); 10 | match comment { 11 | Comment::Line(x) => { 12 | let t = do_tokenize!(ctx, x); 13 | quote! { #root::Comment::Line(#t) } 14 | } 15 | Comment::MultiLine(x) => { 16 | let t = do_tokenize!(ctx, x); 17 | quote! { #root::Comment::MultiLine(#t) } 18 | } 19 | } 20 | } 21 | 22 | impl_tokenize!(tokenize_line_comment, LineComment<'a>, 'a); 23 | fn tokenize_line_comment( 24 | ctx: &TokenizeContext, 25 | line_comment: &LineComment, 26 | ) -> TokenStream { 27 | let root = root_crate(); 28 | let t = do_tokenize!(ctx, Cow::Borrowed(line_comment.as_str())); 29 | quote! { 30 | #root::LineComment::new(#t) 31 | } 32 | } 33 | 34 | impl_tokenize!(tokenize_multi_line_comment, MultiLineComment<'a>, 'a); 35 | fn tokenize_multi_line_comment( 36 | ctx: &TokenizeContext, 37 | multi_line_comment: &MultiLineComment, 38 | ) -> TokenStream { 39 | let root = root_crate(); 40 | let t = multi_line_comment 41 | .iter() 42 | .map(|x| do_tokenize!(ctx, Cow::Borrowed(x))); 43 | quote! { 44 | #root::MultiLineComment::new(::std::vec![#(#t),*]) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/inline/links.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{ 2 | utils::{root_crate, tokenize_cow_str_type, tokenize_hashmap}, 3 | Tokenize, TokenizeContext, 4 | }; 5 | use proc_macro2::TokenStream; 6 | use quote::quote; 7 | use vimwiki_core::{Anchor, Description, Link, LinkData}; 8 | 9 | impl_tokenize!(tokenize_link, Link<'a>, 'a); 10 | fn tokenize_link(ctx: &TokenizeContext, link: &Link) -> TokenStream { 11 | let root = root_crate(); 12 | match &link { 13 | Link::Diary { date, data } => { 14 | let date_t = do_tokenize!(ctx, date); 15 | let data_t = do_tokenize!(ctx, data); 16 | quote!(#root::Link::Diary { date: #date_t, data: #data_t }) 17 | } 18 | Link::IndexedInterWiki { index, data } => { 19 | let data_t = do_tokenize!(ctx, data); 20 | quote!(#root::Link::IndexedInterWiki { index: #index, data: #data_t }) 21 | } 22 | Link::NamedInterWiki { name, data } => { 23 | let name_t = do_tokenize!(ctx, name); 24 | let data_t = do_tokenize!(ctx, data); 25 | quote!(#root::Link::NamedInterWiki { name: #name_t, data: #data_t }) 26 | } 27 | Link::Raw { data } => { 28 | let data_t = do_tokenize!(ctx, data); 29 | quote!(#root::Link::Raw { data: #data_t }) 30 | } 31 | Link::Transclusion { data } => { 32 | let data_t = do_tokenize!(ctx, data); 33 | quote!(#root::Link::Transclusion { data: #data_t }) 34 | } 35 | Link::Wiki { data } => { 36 | let data_t = do_tokenize!(ctx, data); 37 | quote!(#root::Link::Wiki { data: #data_t }) 38 | } 39 | } 40 | } 41 | 42 | impl_tokenize!(tokenize_link_data, LinkData<'a>, 'a); 43 | fn tokenize_link_data(ctx: &TokenizeContext, data: &LinkData) -> TokenStream { 44 | let root = root_crate(); 45 | let uri_ref_t = do_tokenize!(ctx, &data.uri_ref); 46 | let description_t = if let Some(d) = data.description.as_ref() { 47 | let t = tokenize_description(ctx, d); 48 | quote!(::std::option::Option::Some(#t)) 49 | } else { 50 | quote!(::std::option::Option::None) 51 | }; 52 | let properties_t = if let Some(properties) = data.properties.as_ref() { 53 | let t = tokenize_hashmap( 54 | properties, 55 | tokenize_cow_str_type(), 56 | tokenize_cow_str_type(), 57 | |x| do_tokenize!(ctx, x), 58 | |x| do_tokenize!(ctx, x), 59 | ); 60 | quote!(::std::option::Option::Some(#t)) 61 | } else { 62 | quote!(::std::option::Option::None) 63 | }; 64 | 65 | quote!(#root::LinkData::new(#uri_ref_t, #description_t, #properties_t)) 66 | } 67 | 68 | impl_tokenize!(tokenize_description, Description<'a>, 'a); 69 | fn tokenize_description( 70 | ctx: &TokenizeContext, 71 | description: &Description, 72 | ) -> TokenStream { 73 | let root = root_crate(); 74 | match &description { 75 | Description::Text(x) => { 76 | let t = do_tokenize!(ctx, x); 77 | quote! { #root::Description::Text(#t) } 78 | } 79 | Description::TransclusionLink(x) => { 80 | let t = tokenize_link_data(ctx, x); 81 | quote! { 82 | #root::Description::TransclusionLink(::std::boxed::Box::new(#t)) 83 | } 84 | } 85 | } 86 | } 87 | 88 | impl_tokenize!(tokenize_anchor, Anchor<'a>, 'a); 89 | fn tokenize_anchor(ctx: &TokenizeContext, anchor: &Anchor) -> TokenStream { 90 | let root = root_crate(); 91 | let elements = anchor.iter().map(|x| do_tokenize!(ctx, x)); 92 | quote!(#root::Anchor::new(::std::vec![#(#elements),*])) 93 | } 94 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/inline/math.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use std::borrow::Cow; 5 | use vimwiki_core::MathInline; 6 | 7 | impl_tokenize!(tokenize_math_inline, MathInline<'a>, 'a); 8 | fn tokenize_math_inline( 9 | ctx: &TokenizeContext, 10 | math_inline: &MathInline, 11 | ) -> TokenStream { 12 | let root = root_crate(); 13 | let formula = do_tokenize!(ctx, Cow::Borrowed(math_inline.as_str())); 14 | quote! { 15 | #root::MathInline::new(#formula) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/inline/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::{InlineElement, InlineElementContainer}; 5 | 6 | pub mod code; 7 | pub mod comments; 8 | pub mod links; 9 | pub mod math; 10 | pub mod tags; 11 | pub mod typefaces; 12 | 13 | impl_tokenize!(tokenize_inline_element_container, InlineElementContainer<'a>, 'a); 14 | fn tokenize_inline_element_container( 15 | ctx: &TokenizeContext, 16 | inline_element_container: &InlineElementContainer, 17 | ) -> TokenStream { 18 | let root = root_crate(); 19 | let elements = inline_element_container 20 | .iter() 21 | .map(|c| do_tokenize!(ctx, c)); 22 | quote! { 23 | #root::InlineElementContainer::new(::std::vec![#(#elements),*]) 24 | } 25 | } 26 | 27 | impl_tokenize!(tokenize_inline_element, InlineElement<'a>, 'a); 28 | fn tokenize_inline_element( 29 | ctx: &TokenizeContext, 30 | inline_element: &InlineElement, 31 | ) -> TokenStream { 32 | let root = root_crate(); 33 | match inline_element { 34 | InlineElement::Text(x) => { 35 | let t = do_tokenize!(ctx, x); 36 | quote! { #root::InlineElement::Text(#t) } 37 | } 38 | InlineElement::DecoratedText(x) => { 39 | let t = do_tokenize!(ctx, x); 40 | quote! { #root::InlineElement::DecoratedText(#t) } 41 | } 42 | InlineElement::Keyword(x) => { 43 | let t = do_tokenize!(ctx, x); 44 | quote! { #root::InlineElement::Keyword(#t) } 45 | } 46 | InlineElement::Link(x) => { 47 | let t = do_tokenize!(ctx, x); 48 | quote! { #root::InlineElement::Link(#t) } 49 | } 50 | InlineElement::Tags(x) => { 51 | let t = do_tokenize!(ctx, x); 52 | quote! { #root::InlineElement::Tags(#t) } 53 | } 54 | InlineElement::Code(x) => { 55 | let t = do_tokenize!(ctx, x); 56 | quote! { #root::InlineElement::Code(#t) } 57 | } 58 | InlineElement::Math(x) => { 59 | let t = do_tokenize!(ctx, x); 60 | quote! { #root::InlineElement::Math(#t) } 61 | } 62 | InlineElement::Comment(x) => { 63 | let t = do_tokenize!(ctx, x); 64 | quote! { #root::InlineElement::Comment(#t) } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/inline/tags.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use std::borrow::Cow; 5 | use vimwiki_core::{Tag, Tags}; 6 | 7 | impl_tokenize!(tokenize_tags, Tags<'a>, 'a); 8 | fn tokenize_tags(ctx: &TokenizeContext, tags: &Tags) -> TokenStream { 9 | let root = root_crate(); 10 | let inner = tags.into_iter().map(|x| tokenize_tag(ctx, x)); 11 | quote! { 12 | #root::Tags::new(::std::vec![#(#inner),*]) 13 | } 14 | } 15 | 16 | impl_tokenize!(tokenize_tag, Tag<'a>, 'a); 17 | fn tokenize_tag(ctx: &TokenizeContext, tag: &Tag) -> TokenStream { 18 | let root = root_crate(); 19 | let inner = do_tokenize!(ctx, Cow::Borrowed(tag.as_str())); 20 | quote! { 21 | #root::Tag::new(#inner) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/inline/typefaces.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::{DecoratedText, DecoratedTextContent, Keyword, Text}; 5 | 6 | impl_tokenize!(tokenize_text, Text<'a>, 'a); 7 | fn tokenize_text(ctx: &TokenizeContext, text: &Text) -> TokenStream { 8 | let root = root_crate(); 9 | let inner = do_tokenize!(ctx, text.as_ref()); 10 | quote! { 11 | #root::Text::new(#inner) 12 | } 13 | } 14 | 15 | impl_tokenize!(tokenize_decorated_text_content, DecoratedTextContent<'a>, 'a); 16 | fn tokenize_decorated_text_content( 17 | ctx: &TokenizeContext, 18 | decorated_text_content: &DecoratedTextContent, 19 | ) -> TokenStream { 20 | let root = root_crate(); 21 | match &decorated_text_content { 22 | DecoratedTextContent::Keyword(x) => { 23 | let t = do_tokenize!(ctx, x); 24 | quote! { #root::DecoratedTextContent::Keyword(#t) } 25 | } 26 | DecoratedTextContent::Link(x) => { 27 | let t = do_tokenize!(ctx, x); 28 | quote! { #root::DecoratedTextContent::Link(#t) } 29 | } 30 | DecoratedTextContent::DecoratedText(x) => { 31 | let t = do_tokenize!(ctx, x); 32 | quote! { #root::DecoratedTextContent::DecoratedText(#t) } 33 | } 34 | DecoratedTextContent::Text(x) => { 35 | let t = do_tokenize!(ctx, x); 36 | quote! { #root::DecoratedTextContent::Text(#t) } 37 | } 38 | } 39 | } 40 | 41 | impl_tokenize!(tokenize_decorated_text, DecoratedText<'a>, 'a); 42 | fn tokenize_decorated_text( 43 | ctx: &TokenizeContext, 44 | decorated_text: &DecoratedText, 45 | ) -> TokenStream { 46 | let root = root_crate(); 47 | 48 | match decorated_text { 49 | DecoratedText::Bold(x) => { 50 | let contents = x.iter().map(|x| do_tokenize!(ctx, x)); 51 | quote! { 52 | #root::DecoratedText::Bold( 53 | ::std::vec![#(#contents),*], 54 | ) 55 | } 56 | } 57 | DecoratedText::Italic(x) => { 58 | let contents = x.iter().map(|x| do_tokenize!(ctx, x)); 59 | { 60 | quote! { 61 | #root::DecoratedText::Italic( 62 | ::std::vec![#(#contents),*], 63 | ) 64 | } 65 | } 66 | } 67 | DecoratedText::Strikeout(x) => { 68 | let contents = x.iter().map(|x| do_tokenize!(ctx, x)); 69 | { 70 | quote! { 71 | #root::DecoratedText::Strikeout( 72 | ::std::vec![#(#contents),*], 73 | ) 74 | } 75 | } 76 | } 77 | DecoratedText::Subscript(x) => { 78 | let contents = x.iter().map(|x| do_tokenize!(ctx, x)); 79 | { 80 | quote! { 81 | #root::DecoratedText::Subscript( 82 | ::std::vec![#(#contents),*], 83 | ) 84 | } 85 | } 86 | } 87 | DecoratedText::Superscript(x) => { 88 | let contents = x.iter().map(|x| do_tokenize!(ctx, x)); 89 | { 90 | quote! { 91 | #root::DecoratedText::Superscript( 92 | ::std::vec![#(#contents),*], 93 | ) 94 | } 95 | } 96 | } 97 | } 98 | } 99 | 100 | impl_tokenize!(tokenize_keyword, Keyword); 101 | fn tokenize_keyword(_ctx: &TokenizeContext, keyword: &Keyword) -> TokenStream { 102 | let root = root_crate(); 103 | match keyword { 104 | Keyword::Done => { 105 | quote! { #root::Keyword::Done } 106 | } 107 | Keyword::Fixed => { 108 | quote! { #root::Keyword::Fixed } 109 | } 110 | Keyword::Fixme => { 111 | quote! { #root::Keyword::Fixme } 112 | } 113 | Keyword::Started => { 114 | quote! { #root::Keyword::Started } 115 | } 116 | Keyword::Todo => { 117 | quote! { #root::Keyword::Todo } 118 | } 119 | Keyword::Xxx => { 120 | quote! { #root::Keyword::Xxx } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/math.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{ 2 | utils::root_crate, utils::tokenize_option, Tokenize, TokenizeContext, 3 | }; 4 | use proc_macro2::TokenStream; 5 | use quote::quote; 6 | use vimwiki_core::MathBlock; 7 | 8 | impl_tokenize!(tokenize_math_block, MathBlock<'a>, 'a); 9 | fn tokenize_math_block( 10 | ctx: &TokenizeContext, 11 | math_block: &MathBlock, 12 | ) -> TokenStream { 13 | let root = root_crate(); 14 | let lines = math_block.lines.iter().map(|x| do_tokenize!(ctx, x)); 15 | let environment = 16 | tokenize_option(ctx, &math_block.environment, |ctx, x| { 17 | do_tokenize!(ctx, x) 18 | }); 19 | quote! { 20 | #root::MathBlock::new( 21 | ::std::vec![#(#lines),*], 22 | #environment, 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::{BlockElement, InlineBlockElement}; 5 | 6 | pub mod blockquotes; 7 | pub mod code; 8 | pub mod definitions; 9 | pub mod dividers; 10 | pub mod headers; 11 | pub mod inline; 12 | pub mod lists; 13 | pub mod math; 14 | pub mod paragraphs; 15 | pub mod placeholders; 16 | pub mod tables; 17 | 18 | impl_tokenize!(tokenize_block_element, BlockElement<'a>, 'a); 19 | fn tokenize_block_element( 20 | ctx: &TokenizeContext, 21 | block_element: &BlockElement, 22 | ) -> TokenStream { 23 | let root = root_crate(); 24 | match block_element { 25 | BlockElement::Blockquote(x) => { 26 | let t = do_tokenize!(ctx, x); 27 | quote! { #root::BlockElement::Blockquote(#t) } 28 | } 29 | BlockElement::DefinitionList(x) => { 30 | let t = do_tokenize!(ctx, x); 31 | quote! { #root::BlockElement::DefinitionList(#t) } 32 | } 33 | BlockElement::Divider(x) => { 34 | let t = do_tokenize!(ctx, x); 35 | quote! { #root::BlockElement::Divider(#t) } 36 | } 37 | BlockElement::Header(x) => { 38 | let t = do_tokenize!(ctx, x); 39 | quote! { #root::BlockElement::Header(#t) } 40 | } 41 | BlockElement::List(x) => { 42 | let t = do_tokenize!(ctx, x); 43 | quote! { #root::BlockElement::List(#t) } 44 | } 45 | BlockElement::MathBlock(x) => { 46 | let t = do_tokenize!(ctx, x); 47 | quote! { #root::BlockElement::MathBlock(#t) } 48 | } 49 | BlockElement::Paragraph(x) => { 50 | let t = do_tokenize!(ctx, x); 51 | quote! { #root::BlockElement::Paragraph(#t) } 52 | } 53 | BlockElement::Placeholder(x) => { 54 | let t = do_tokenize!(ctx, x); 55 | quote! { #root::BlockElement::Placeholder(#t) } 56 | } 57 | BlockElement::CodeBlock(x) => { 58 | let t = do_tokenize!(ctx, x); 59 | quote! { #root::BlockElement::CodeBlock(#t) } 60 | } 61 | BlockElement::Table(x) => { 62 | let t = do_tokenize!(ctx, x); 63 | quote! { #root::BlockElement::Table(#t) } 64 | } 65 | } 66 | } 67 | 68 | impl_tokenize!(tokenize_inline_block_element, InlineBlockElement<'a>, 'a); 69 | fn tokenize_inline_block_element( 70 | ctx: &TokenizeContext, 71 | inline_block_element: &InlineBlockElement, 72 | ) -> TokenStream { 73 | let root = root_crate(); 74 | match inline_block_element { 75 | InlineBlockElement::ListItem(x) => { 76 | let t = do_tokenize!(ctx, x); 77 | quote! { #root::InlineBlockElement::ListItem(#t) } 78 | } 79 | InlineBlockElement::Term(x) => { 80 | let t = do_tokenize!(ctx, x); 81 | quote! { #root::InlineBlockElement::Term(#t) } 82 | } 83 | InlineBlockElement::Definition(x) => { 84 | let t = do_tokenize!(ctx, x); 85 | quote! { #root::InlineBlockElement::Definition(#t) } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/paragraphs.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::Paragraph; 5 | 6 | impl_tokenize!(tokenize_paragraph, Paragraph<'a>, 'a); 7 | fn tokenize_paragraph( 8 | ctx: &TokenizeContext, 9 | paragraph: &Paragraph, 10 | ) -> TokenStream { 11 | let root = root_crate(); 12 | let lines = paragraph.lines.iter().map(|line| do_tokenize!(ctx, line)); 13 | quote! { 14 | #root::Paragraph::new(::std::vec![#(#lines),*]) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/placeholders.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::Placeholder; 5 | 6 | impl_tokenize!(tokenize_placeholder, Placeholder<'a>, 'a); 7 | fn tokenize_placeholder( 8 | ctx: &TokenizeContext, 9 | placeholder: &Placeholder, 10 | ) -> TokenStream { 11 | let root = root_crate(); 12 | match &placeholder { 13 | Placeholder::Date(x) => { 14 | let t = do_tokenize!(ctx, x); 15 | quote! { #root::Placeholder::Date(#t) } 16 | } 17 | Placeholder::NoHtml => { 18 | quote! { #root::Placeholder::NoHtml } 19 | } 20 | Placeholder::Other { name, value } => { 21 | let name_t = do_tokenize!(ctx, name); 22 | let value_t = do_tokenize!(ctx, value); 23 | quote! { 24 | #root::Placeholder::Other { 25 | name: #name_t, 26 | value: #value_t, 27 | } 28 | } 29 | } 30 | Placeholder::Template(x) => { 31 | let t = do_tokenize!(ctx, x); 32 | quote! { #root::Placeholder::Template(#t) } 33 | } 34 | Placeholder::Title(x) => { 35 | let t = do_tokenize!(ctx, x); 36 | quote! { #root::Placeholder::Title(#t) } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/blocks/tables.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{ 2 | utils::{root_crate, tokenize_hashmap}, 3 | Tokenize, TokenizeContext, 4 | }; 5 | use proc_macro2::TokenStream; 6 | use quote::quote; 7 | use vimwiki_core::{Cell, CellPos, CellSpan, ColumnAlign, Table}; 8 | 9 | impl_tokenize!(tokenize_table, Table<'a>, 'a); 10 | fn tokenize_table(ctx: &TokenizeContext, table: &Table) -> TokenStream { 11 | let root = root_crate(); 12 | let centered = table.centered; 13 | let cells = tokenize_hashmap( 14 | table.as_data(), 15 | quote!(#root::CellPos), 16 | quote!(#root::Located<#root::Cell>), 17 | |x| do_tokenize!(ctx, x), 18 | |x| do_tokenize!(ctx, x), 19 | ); 20 | 21 | quote! { 22 | #root::Table::new( 23 | #cells, 24 | #centered, 25 | ) 26 | } 27 | } 28 | 29 | impl_tokenize!(tokenize_cell, Cell<'a>, 'a); 30 | fn tokenize_cell(ctx: &TokenizeContext, cell: &Cell) -> TokenStream { 31 | let root = root_crate(); 32 | match &cell { 33 | Cell::Content(x) => { 34 | let t = do_tokenize!(ctx, x); 35 | quote! { #root::Cell::Content(#t) } 36 | } 37 | Cell::Span(x) => { 38 | let t = do_tokenize!(ctx, x); 39 | quote! { #root::Cell::Span(#t) } 40 | } 41 | Cell::Align(x) => { 42 | let t = do_tokenize!(ctx, x); 43 | quote! { #root::Cell::Align(#t) } 44 | } 45 | } 46 | } 47 | 48 | impl_tokenize!(tokenize_column_align, ColumnAlign); 49 | fn tokenize_column_align( 50 | _ctx: &TokenizeContext, 51 | column_align: &ColumnAlign, 52 | ) -> TokenStream { 53 | let root = root_crate(); 54 | match column_align { 55 | ColumnAlign::None => { 56 | quote! { #root::ColumnAlign::None } 57 | } 58 | ColumnAlign::Left => { 59 | quote! { #root::ColumnAlign::Left } 60 | } 61 | ColumnAlign::Center => { 62 | quote! { #root::ColumnAlign::Center } 63 | } 64 | ColumnAlign::Right => { 65 | quote! { #root::ColumnAlign::Right } 66 | } 67 | } 68 | } 69 | 70 | impl_tokenize!(tokenize_cell_span, CellSpan); 71 | fn tokenize_cell_span( 72 | _ctx: &TokenizeContext, 73 | cell_span: &CellSpan, 74 | ) -> TokenStream { 75 | let root = root_crate(); 76 | match cell_span { 77 | CellSpan::FromAbove => { 78 | quote! { #root::CellSpan::FromAbove } 79 | } 80 | CellSpan::FromLeft => { 81 | quote! { #root::CellSpan::FromLeft } 82 | } 83 | } 84 | } 85 | 86 | impl_tokenize!(tokenize_cell_pos, CellPos); 87 | fn tokenize_cell_pos( 88 | _ctx: &TokenizeContext, 89 | cell_pos: &CellPos, 90 | ) -> TokenStream { 91 | let root = root_crate(); 92 | let row = cell_pos.row; 93 | let col = cell_pos.col; 94 | quote! { #root::CellPos::new(#row, #col) } 95 | } 96 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/location.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::{ElementLike, Located, Region}; 5 | 6 | impl Tokenize for Located 7 | where 8 | T: ElementLike, 9 | { 10 | fn tokenize(&self, ctx: &TokenizeContext, stream: &mut TokenStream) { 11 | let root = root_crate(); 12 | let mut element = TokenStream::new(); 13 | self.as_inner().tokenize(ctx, &mut element); 14 | 15 | let region = do_tokenize!(ctx, self.region()); 16 | 17 | let self_stream = quote! { 18 | #root::Located::new( 19 | #element, 20 | #region, 21 | ) 22 | }; 23 | 24 | stream.extend(std::iter::once(self_stream)) 25 | } 26 | } 27 | 28 | impl_tokenize!(tokenize_region, Region); 29 | fn tokenize_region(_ctx: &TokenizeContext, region: &Region) -> TokenStream { 30 | let root = root_crate(); 31 | let offset = region.offset(); 32 | let len = region.len(); 33 | quote! { 34 | #root::Region::new( 35 | #offset, 36 | #len, 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/elements/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::root_crate, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use vimwiki_core::Page; 5 | 6 | pub mod blocks; 7 | pub mod location; 8 | 9 | // Top-level types 10 | impl_tokenize!(tokenize_page, Page<'a>, 'a); 11 | fn tokenize_page(ctx: &TokenizeContext, page: &Page) -> TokenStream { 12 | let root = root_crate(); 13 | let elements = page.elements().iter().map(|x| do_tokenize!(ctx, x)); 14 | quote! { 15 | #root::Page::new(::std::vec![#(#elements),*]) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/mod.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use syn::Error; 4 | 5 | /// Represents some context to apply when tokenizing a stream 6 | pub struct TokenizeContext { 7 | pub verbatim: bool, 8 | pub formatter: crate::Formatter, 9 | } 10 | 11 | impl TokenizeContext { 12 | /// Performs a &str tokenization, potentially using format args 13 | pub fn quote_str(&self, s: &str) -> TokenStream { 14 | if self.verbatim { 15 | quote!(#s) 16 | } else { 17 | self.formatter 18 | .quote_format_str(s) 19 | .unwrap_or_else(Error::into_compile_error) 20 | } 21 | } 22 | } 23 | 24 | /// Tokenize a value into a stream of tokens. 25 | pub trait Tokenize { 26 | /// Inject self into a [`TokenStream`]. 27 | fn tokenize(&self, ctx: &TokenizeContext, stream: &mut TokenStream); 28 | } 29 | 30 | /// Implements `Tokenize` using either a type that implements `ToTokens` or 31 | /// a custom tokenize function and optional generic type information 32 | macro_rules! impl_tokenize { 33 | ($type:ty) => { 34 | impl Tokenize for $type { 35 | fn tokenize(&self, _ctx: &crate::TokenizeContext, stream: &mut TokenStream) { 36 | self.to_tokens(stream) 37 | } 38 | } 39 | }; 40 | ($tokenizer:ident, $type:ty) => { 41 | impl Tokenize for $type { 42 | fn tokenize(&self, ctx: &crate::TokenizeContext, stream: &mut TokenStream) { 43 | stream.extend(std::iter::once($tokenizer(ctx, self))) 44 | } 45 | } 46 | }; 47 | ($tokenizer:ident, $type:ty, $($type_args:tt)+) => { 48 | impl<$($type_args)+> Tokenize for $type { 49 | fn tokenize(&self, ctx: &crate::TokenizeContext, stream: &mut TokenStream) { 50 | stream.extend(std::iter::once($tokenizer(ctx, self))) 51 | } 52 | } 53 | }; 54 | } 55 | 56 | /// Transforms expression implementing `Tokenize` into a `TokenStream` 57 | macro_rules! do_tokenize { 58 | ($ctx:expr, $value:expr) => {{ 59 | let mut stream = ::proc_macro2::TokenStream::new(); 60 | $value.tokenize($ctx, &mut stream); 61 | stream 62 | }}; 63 | } 64 | 65 | pub mod elements; 66 | pub mod primitives; 67 | pub mod utils; 68 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/primitives.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{utils::vendor_path, Tokenize, TokenizeContext}; 2 | use proc_macro2::TokenStream; 3 | use quote::{quote, ToTokens}; 4 | use std::{borrow::Cow, path::Path}; 5 | use vimwiki_core::vendor::{ 6 | chrono::{Datelike, NaiveDate}, 7 | uriparse::URIReference, 8 | }; 9 | 10 | // Implement primitives that already implement ToTokens via quote crate 11 | impl_tokenize!(bool); 12 | impl_tokenize!(i32); 13 | impl_tokenize!(u32); 14 | impl_tokenize!(f32); 15 | impl_tokenize!(f64); 16 | 17 | impl_tokenize!(tokenize_str, str); 18 | impl_tokenize!(tokenize_str, String); 19 | pub fn tokenize_str(ctx: &TokenizeContext, s: &str) -> TokenStream { 20 | ctx.quote_str(s) 21 | } 22 | 23 | impl_tokenize!(tokenize_cow_str, Cow<'a, str>, 'a); 24 | pub fn tokenize_cow_str(ctx: &TokenizeContext, inner: &str) -> TokenStream { 25 | let inner_t = ctx.quote_str(inner); 26 | if ctx.verbatim { 27 | quote! { ::std::borrow::Cow::Borrowed(#inner_t) } 28 | } else { 29 | quote! { ::std::borrow::Cow::Owned(#inner_t) } 30 | } 31 | } 32 | 33 | impl_tokenize!(tokenize_cow_path, Cow<'a, Path>, 'a); 34 | pub fn tokenize_cow_path(ctx: &TokenizeContext, path: &Path) -> TokenStream { 35 | let inner = path.to_str().expect("Unable to translate path to str"); 36 | let inner_t = ctx.quote_str(inner); 37 | 38 | if ctx.verbatim { 39 | quote! { ::std::borrow::Cow::Borrowed(::std::path::Path::new(#inner_t)) } 40 | } else { 41 | quote! { 42 | ::std::borrow::Cow::Owned( 43 | < 44 | ::std::path::PathBuf as 45 | ::std::convert::From<::std::string::String> 46 | >::from( 47 | #inner_t 48 | ) 49 | ) 50 | } 51 | } 52 | } 53 | 54 | impl_tokenize!(tokenize_naive_date, NaiveDate); 55 | fn tokenize_naive_date( 56 | _ctx: &TokenizeContext, 57 | naive_date: &NaiveDate, 58 | ) -> TokenStream { 59 | let root = vendor_path(); 60 | let year = naive_date.year(); 61 | let month = naive_date.month(); 62 | let day = naive_date.day(); 63 | quote! { #root::chrono::NaiveDate::from_ymd(#year, #month, #day) } 64 | } 65 | 66 | impl_tokenize!(tokenize_uri_reference, URIReference<'a>, 'a); 67 | fn tokenize_uri_reference( 68 | _ctx: &TokenizeContext, 69 | uri: &URIReference, 70 | ) -> TokenStream { 71 | let root = vendor_path(); 72 | let uri_string = uri.to_string(); 73 | quote! { 74 | { 75 | use ::std::convert::TryFrom; 76 | #root::uriparse::URIReference::try_from(#uri_string) 77 | .expect("Failed to parse URIReference").into_owned() 78 | } 79 | } 80 | } 81 | 82 | impl_tokenize!(tokenize_path, Path); 83 | fn tokenize_path(_ctx: &TokenizeContext, path: &Path) -> TokenStream { 84 | // TODO: Support cases where pathbuf cannot be converted back to Rust str 85 | let t = path.to_str().expect("Path cannot be converted to &str"); 86 | quote! { 87 | ::std::path::Path::new(#t) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /vimwiki_macros/src/tokens/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::tokens::{Tokenize, TokenizeContext}; 2 | use proc_macro2::{Span, TokenStream}; 3 | use proc_macro_crate::{crate_name, FoundCrate}; 4 | use quote::quote; 5 | use std::collections::HashMap; 6 | use syn::{parse_quote, Ident, Path}; 7 | 8 | /// Generates a `TokenStream` that provides the root of an import path for 9 | /// the `vimwiki` crate 10 | #[inline] 11 | pub fn root_crate() -> Path { 12 | get_crate("vimwiki") 13 | .or_else(|_| get_crate("vimwiki-core")) 14 | .expect("vimwiki crate exists") 15 | } 16 | 17 | fn get_crate(cname: &str) -> syn::Result { 18 | crate_name(cname) 19 | .map(|found_crate| match found_crate { 20 | FoundCrate::Itself => { 21 | // Special case for vimwiki integration tests that can be removed when 22 | // https://github.com/bkchr/proc-macro-crate/issues/10 is resolved 23 | // 24 | // NOTE: Must check at compile-time as CARGO_BIN_EXE_ is not 25 | // available during runtime 26 | //parse_quote!(crate) 27 | parse_quote!(::vimwiki) 28 | } 29 | FoundCrate::Name(name) => { 30 | let crate_ident = Ident::new(&name, Span::mixed_site()); 31 | parse_quote!(::#crate_ident) 32 | } 33 | }) 34 | .map_err(|msg| syn::Error::new(Span::mixed_site(), msg)) 35 | } 36 | 37 | /// Generates a `TokenStream` that provides the path to vendor types in 38 | /// the `vimwiki` crate 39 | #[inline] 40 | pub fn vendor_path() -> TokenStream { 41 | let root = root_crate(); 42 | quote! { #root::vendor } 43 | } 44 | 45 | /// Produces a `TokenStream` for the `String` type 46 | #[inline] 47 | pub fn tokenize_cow_str_type() -> TokenStream { 48 | quote! { ::std::borrow::Cow<'_, str >} 49 | } 50 | 51 | /// Tokenizes a `HashMap` where both the key and value implement the 52 | /// `Tokenize` trait. Additionally, uses *kty* and *vty* as the types for 53 | /// the hashmap and *fk* and *fv* as the functions to transform each key 54 | /// and value to `TokenStream`. 55 | pub fn tokenize_hashmap( 56 | m: &HashMap, 57 | kty: TokenStream, 58 | vty: TokenStream, 59 | fk: impl Fn(&K) -> TokenStream, 60 | fv: impl Fn(&V) -> TokenStream, 61 | ) -> TokenStream { 62 | let pairs = m.iter().map(|(k, v)| { 63 | let tk = fk(k); 64 | let tv = fv(v); 65 | quote! { (#tk, #tv) } 66 | }); 67 | quote! { 68 | ::std::iter::Iterator::collect::<::std::collections::HashMap<#kty,#vty>>( 69 | ::std::vec![#(#pairs),*].drain(..), 70 | ) 71 | } 72 | } 73 | 74 | /// Tokenizes an `Option` where the inner type implements the `Tokenize` 75 | /// trait. Additionally, uses the *f* function to transform each inner value 76 | /// into a `TokenStream`. 77 | pub fn tokenize_option( 78 | ctx: &TokenizeContext, 79 | o: &Option, 80 | f: impl Fn(&TokenizeContext, &T) -> TokenStream, 81 | ) -> TokenStream { 82 | if let Some(x) = o { 83 | let t = f(ctx, x); 84 | quote! { ::std::option::Option::Some(#t) } 85 | } else { 86 | quote! { ::std::option::Option::None } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /vimwiki_macros/src/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Error, Result}; 2 | use proc_macro2::{Span, TokenTree}; 3 | 4 | /// Converts a token tree that is a string or byte string into a Rust string 5 | /// instance. Removes any blank lines (whitespace only) before and after 6 | /// lines with content. 7 | /// 8 | /// If `raw_mode` is specified, will leave lines unaltered, otherwise will 9 | /// find the minimum indentation level and remove that from all lines. 10 | pub fn input_to_string(token: TokenTree, raw_mode: bool) -> Result { 11 | let repr = token.to_string(); 12 | let repr = repr.trim(); 13 | let is_string = repr.starts_with('"') || repr.starts_with('r'); 14 | let is_byte_string = repr.starts_with("b\"") || repr.starts_with("br"); 15 | 16 | if !is_string && !is_byte_string { 17 | return Err(Error::new( 18 | token.span(), 19 | "argument must be a single string literal", 20 | )); 21 | } 22 | 23 | // Get the raw string as it appears 24 | let begin = repr.find('"').unwrap() + 1; 25 | let end = repr.rfind('"').unwrap(); 26 | let s = repr[begin..end].to_string(); 27 | 28 | // Determine which lines in the macro are blank and what each line's 29 | // indentation level is, which will be used if not in raw mode 30 | let mut line_data = s 31 | .lines() 32 | .map(move |l| { 33 | let is_blank = l.trim().is_empty(); 34 | let indentation = l.len() - l.trim_start().len(); 35 | (is_blank, indentation, l) 36 | }) 37 | .collect::>(); 38 | 39 | // Special handling to remove any blank lines at beginning and end of 40 | // the string; this includes lines that have whitespace but nothing else 41 | if !line_data.is_empty() { 42 | let mut start = 0; 43 | let mut end = line_data.len() - 1; 44 | 45 | // Keep moving backward while lines are blank and we haven't advanced 46 | // before the start 47 | while end > 0 && end >= start && line_data[end].0 { 48 | end -= 1; 49 | } 50 | 51 | // Keep moving forward while lines are blank and we haven't advanced 52 | // past the end 53 | while start <= end && line_data[start].0 { 54 | start += 1; 55 | } 56 | 57 | // Update line data to a subset 58 | if start <= end { 59 | line_data = line_data[start..=end].to_vec(); 60 | } else { 61 | return Err(Error::new( 62 | Span::call_site(), 63 | &format!( 64 | "Blank input provided! Need non-empty lines! {}/{}", 65 | start, end 66 | ), 67 | )); 68 | } 69 | } 70 | 71 | // Process the lines back into a single string, either by doing nothing 72 | // to them or removing a set minimum indentation from all 73 | let lines = 74 | if raw_mode { 75 | line_data 76 | .iter() 77 | .map(|x| x.2) 78 | .collect::>() 79 | .join("\n") 80 | } else { 81 | let min_indentation = line_data 82 | .iter() 83 | .fold(usize::MAX, |acc, x| if x.1 < acc { x.1 } else { acc }); 84 | line_data 85 | .iter() 86 | .map(|x| &x.2[min_indentation..]) 87 | .collect::>() 88 | .join("\n") 89 | }; 90 | 91 | Ok(lines) 92 | } 93 | -------------------------------------------------------------------------------- /vimwiki_macros/tests/functions/mod.rs: -------------------------------------------------------------------------------- 1 | mod vimwiki; 2 | mod vimwiki_format; 3 | -------------------------------------------------------------------------------- /vimwiki_macros/tests/hygiene.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![no_implicit_prelude] 3 | 4 | use ::vimwiki_macros::*; 5 | 6 | #[test] 7 | fn hygiene_passes_for_vimwiki() { 8 | let _ = vimwiki_page!("some text"); 9 | let _ = vimwiki_block_element!("some text"); 10 | let _ = vimwiki_inline_element_container!("some text"); 11 | let _ = vimwiki_inline_element!("some text"); 12 | let _ = vimwiki_blockquote!("> some text"); 13 | let _ = vimwiki_comment!("%% some comment"); 14 | let _ = vimwiki_line_comment!("%% some comment"); 15 | let _ = vimwiki_multi_line_comment!("%%+ some comment +%%"); 16 | let _ = vimwiki_definition_list! {r#" 17 | term:: definition 18 | term2:: 19 | :: def 2 20 | :: def 3 21 | "#}; 22 | let _ = vimwiki_divider!("----"); 23 | let _ = vimwiki_header!("= header ="); 24 | let _ = vimwiki_link!("[[link]]"); 25 | let _ = vimwiki_link!("[[diary:2012-03-05]]"); 26 | let _ = vimwiki_link!("[[file:path/to/file]]"); 27 | let _ = vimwiki_link!("[[wiki1:Some link]]"); 28 | let _ = vimwiki_link!("[[wn.MyWiki:Some link]]"); 29 | let _ = vimwiki_link!("https://example.com"); 30 | let _ = vimwiki_link!("{{https://example.com/img.jpg}}"); 31 | let _ = vimwiki_placeholder!("%date 2012-03-05"); 32 | let _ = vimwiki_placeholder!("%nohtml"); 33 | let _ = vimwiki_placeholder!("%other some text"); 34 | let _ = vimwiki_placeholder!("%template my_template"); 35 | let _ = vimwiki_placeholder!("%title some text"); 36 | let _ = vimwiki_code_block! {r#" 37 | {{{ 38 | some code 39 | }}} 40 | "#}; 41 | let _ = vimwiki_table!("|cell|"); 42 | let _ = vimwiki_tags!(":tag:"); 43 | let _ = vimwiki_text!("some text"); 44 | let _ = vimwiki_decorated_text!("*some text*"); 45 | let _ = vimwiki_decorated_text!("_some text_"); 46 | let _ = vimwiki_decorated_text!("~~some text~~"); 47 | let _ = vimwiki_decorated_text!("^some text^"); 48 | let _ = vimwiki_decorated_text!(",,some text,,"); 49 | let _ = vimwiki_keyword!("TODO"); 50 | } 51 | -------------------------------------------------------------------------------- /vimwiki_macros/tests/lib.rs: -------------------------------------------------------------------------------- 1 | // Provide crate rename at top level as macros should never be used directly 2 | // and therefore we are expecting ::vimwiki or similar 3 | extern crate vimwiki_core as vimwiki; 4 | 5 | mod functions; 6 | mod hygiene; 7 | --------------------------------------------------------------------------------