├── .eslintrc.js ├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ ├── fuzz.yml │ ├── lint.yml │ └── release.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── Package.swift ├── README.md ├── binding.gyp ├── bindings ├── node │ ├── binding.cc │ └── index.js ├── rust │ ├── README.md │ ├── build.rs │ └── lib.rs └── swift │ └── TreeSitterDoxygen │ └── doxygen.h ├── grammar.js ├── package.json ├── queries ├── highlights.scm ├── indents.scm └── injections.scm ├── src ├── grammar.json ├── node-types.json ├── parser.c ├── scanner.c └── tree_sitter │ ├── alloc.h │ ├── array.h │ └── parser.h └── test └── corpus ├── single.txt └── tests.txt /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'env': { 3 | 'commonjs': true, 4 | 'es2021': true, 5 | }, 6 | 'extends': 'google', 7 | 'overrides': [ 8 | ], 9 | 'parserOptions': { 10 | 'ecmaVersion': 'latest', 11 | 'sourceType': 'module', 12 | }, 13 | 'rules': { 14 | 'indent': ['error', 2, {'SwitchCase': 1}], 15 | 'max-len': [ 16 | 'error', 17 | {'code': 120, 'ignoreComments': true, 'ignoreUrls': true, 'ignoreStrings': true}, 18 | ], 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /src/** linguist-vendored 2 | /examples/* linguist-vendored 3 | 4 | src/grammar.json linguist-generated 5 | src/node-types.json linguist-generated 6 | src/parser.c linguist-generated 7 | 8 | src/grammar.json -diff 9 | src/node-types.json -diff 10 | src/parser.c -diff 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - "**" 10 | 11 | jobs: 12 | test: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | fail-fast: true 16 | matrix: 17 | os: [macos-latest, ubuntu-latest, windows-2019] 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: actions/setup-node@v3 21 | with: 22 | node-version: 18 23 | - run: npm install 24 | - run: npm test 25 | -------------------------------------------------------------------------------- /.github/workflows/fuzz.yml: -------------------------------------------------------------------------------- 1 | name: Fuzz Parser 2 | 3 | on: 4 | push: 5 | paths: 6 | - src/scanner.c 7 | pull_request: 8 | paths: 9 | - src/scanner.c 10 | workflow_dispatch: 11 | 12 | jobs: 13 | test: 14 | name: Parser fuzzing 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: vigoux/tree-sitter-fuzz-action@v1 19 | with: 20 | language: doxygen 21 | external-scanner: src/scanner.c 22 | time: 60 23 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - "**" 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Install modules 17 | run: npm install 18 | - name: Run ESLint 19 | run: npm run lint 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["CI"] 6 | types: 7 | - completed 8 | 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | 13 | jobs: 14 | release: 15 | runs-on: ubuntu-latest 16 | if: ${{ github.event.workflow_run.conclusion == 'success' && github.ref == 'refs/heads/master' }} 17 | steps: 18 | - uses: google-github-actions/release-please-action@v3 19 | id: release 20 | with: 21 | release-type: node 22 | package-name: tree-sitter-doxygen 23 | 24 | - uses: actions/checkout@v3 25 | with: 26 | token: ${{ secrets.GITHUB_TOKEN }} 27 | 28 | - name: Update Rust version 29 | run: | 30 | git fetch origin release-please--branches--master--components--tree-sitter-doxygen 31 | git checkout release-please--branches--master--components--tree-sitter-doxygen 32 | 33 | git config user.name github-actions[bot] 34 | git config user.email github-actions[bot]@users.noreply.github.com 35 | 36 | repo_name="${{ github.repository }}" 37 | repo_name="${repo_name##*/}" 38 | version=$(grep -o '"version": *"[^"]*"' package.json | sed 's/"version": "\(.*\)"/\1/') 39 | 40 | sed -i "s/version = \"[^\"]*\"/version = \"$version\"/g" Cargo.toml 41 | sed -i "s/$repo_name = \"[^\"]*\"/$repo_name = \"$version\"/g" bindings/rust/README.md 42 | 43 | git add Cargo.toml bindings/rust/README.md 44 | git commit --amend --no-edit 45 | git push -f 46 | 47 | - name: Setup Node 48 | if: ${{ steps.release.outputs.release_created }} 49 | uses: actions/setup-node@v3 50 | with: 51 | node-version: 18 52 | registry-url: "https://registry.npmjs.org" 53 | - name: Publish to NPM 54 | if: ${{ steps.release.outputs.release_created }} 55 | env: 56 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 57 | run: npm publish 58 | 59 | - name: Setup Rust 60 | if: ${{ steps.release.outputs.release_created }} 61 | uses: actions-rs/toolchain@v1 62 | with: 63 | profile: minimal 64 | toolchain: stable 65 | override: true 66 | - name: Publish to Crates.io 67 | if: ${{ steps.release.outputs.release_created }} 68 | uses: katyo/publish-crates@v2 69 | with: 70 | registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} 71 | 72 | - name: Tag stable versions 73 | if: ${{ steps.release.outputs.release_created }} 74 | run: | 75 | git checkout master 76 | git config user.name github-actions[bot] 77 | git config user.email github-actions[bot]@users.noreply.github.com 78 | git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git" 79 | git tag -d stable || true 80 | git push origin :stable || true 81 | git tag -a stable -m "Last Stable Release" 82 | git push origin stable 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | node_modules 3 | build 4 | package-lock.json 5 | /target/ 6 | .build/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /test 2 | /examples 3 | /build 4 | /script 5 | /target 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.1.0](https://github.com/amaanq/tree-sitter-doxygen/compare/v1.0.0...v1.1.0) (2023-08-26) 4 | 5 | 6 | ### Features 7 | 8 | * initial links support ([70399c7](https://github.com/amaanq/tree-sitter-doxygen/commit/70399c76faf72826de00e47350c8cf54ad8ebf70)) 9 | * queries for links ([ac29aed](https://github.com/amaanq/tree-sitter-doxygen/commit/ac29aedcaf036faa85fa0dbd1685a11c95a0d432)) 10 | 11 | 12 | ### Bug Fixes 13 | 14 | * do not attempt to get column on eof (upstream bug) ([1928411](https://github.com/amaanq/tree-sitter-doxygen/commit/19284113dbd42263c13b39d81b2a3b2492022c9b)) 15 | 16 | ## 1.0.0 (2023-08-25) 17 | 18 | 19 | ### Features 20 | 21 | * Initial commit ([18158c0](https://github.com/amaanq/tree-sitter-doxygen/commit/18158c094093455a7f54b75cb0948217bf3bd07c)) 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-doxygen" 3 | description = "Doxygen grammar for tree-sitter" 4 | version = "1.1.0" 5 | authors = ["Amaan Qureshi "] 6 | license = "MIT" 7 | keywords = ["incremental", "parsing", "doxygen"] 8 | categories = ["parsing", "text-editors"] 9 | repository = "https://github.com/amaanq/tree-sitter-doxygen" 10 | edition = "2021" 11 | autoexamples = false 12 | 13 | build = "bindings/rust/build.rs" 14 | include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"] 15 | 16 | [lib] 17 | path = "bindings/rust/lib.rs" 18 | 19 | [dependencies] 20 | tree-sitter = "~0.20.10" 21 | 22 | [build-dependencies] 23 | cc = "~1.0.83" 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Amaan Qureshi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "TreeSitterObjc", 6 | platforms: [.macOS(.v10_13), .iOS(.v11)], 7 | products: [ 8 | .library(name: "TreeSitterObjc", targets: ["TreeSitterObjc"]), 9 | ], 10 | dependencies: [], 11 | targets: [ 12 | .target(name: "TreeSitterObjc", 13 | path: ".", 14 | exclude: [ 15 | "binding.gyp", 16 | "bindings", 17 | "Cargo.toml", 18 | "test", 19 | "examples", 20 | "grammar.js", 21 | "LICENSE", 22 | "package.json", 23 | "README.md", 24 | "script", 25 | "src/grammar.json", 26 | "src/node-types.json", 27 | ], 28 | sources: [ 29 | "src/parser.c", 30 | "src/scanner.c", 31 | ], 32 | resources: [ 33 | .copy("queries") 34 | ], 35 | publicHeadersPath: "bindings/swift", 36 | cSettings: [.headerSearchPath("src")]) 37 | ] 38 | ) 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tree-sitter-doxygen 2 | 3 | [![Build Status](https://github.com/amaanq/tree-sitter-doxygen/actions/workflows/ci.yml/badge.svg)](https://github.com/amaanq/tree-sitter-doxygen/actions/workflows/ci.yml) 4 | [![Discord](https://img.shields.io/discord/1063097320771698699?logo=discord)](https://discord.gg/w7nTvsVJhm) 5 | 6 | [Doxygen](https://www.doxygen.nl/manual/commands.html) grammar for [tree-sitter](https://tree-sitter.github.io) 7 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_doxygen_binding", 5 | "include_dirs": [ 6 | " 4 | 5 | using namespace v8; 6 | 7 | extern "C" TSLanguage *tree_sitter_doxygen(); 8 | 9 | namespace { 10 | 11 | NAN_METHOD(New) {} 12 | 13 | void Init(Local exports, Local module) { 14 | Local tpl = Nan::New(New); 15 | tpl->SetClassName(Nan::New("Language").ToLocalChecked()); 16 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 17 | 18 | Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); 19 | Local instance = 20 | constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked(); 21 | Nan::SetInternalFieldPointer(instance, 0, tree_sitter_doxygen()); 22 | 23 | Nan::Set(instance, Nan::New("name").ToLocalChecked(), 24 | Nan::New("doxygen").ToLocalChecked()); 25 | Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance); 26 | } 27 | 28 | NODE_MODULE(tree_sitter_doxygen_binding, Init) 29 | 30 | } // namespace 31 | -------------------------------------------------------------------------------- /bindings/node/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | module.exports = require('../../build/Release/tree_sitter_doxygen_binding'); 3 | } catch (error1) { 4 | if (error1.code !== 'MODULE_NOT_FOUND') { 5 | throw error1; 6 | } 7 | try { 8 | module.exports = require('../../build/Debug/tree_sitter_doxygen_binding'); 9 | } catch (error2) { 10 | if (error2.code !== 'MODULE_NOT_FOUND') { 11 | throw error2; 12 | } 13 | throw error1; 14 | } 15 | } 16 | 17 | try { 18 | module.exports.nodeTypeInfo = require('../../src/node-types.json'); 19 | } catch (_) {} 20 | -------------------------------------------------------------------------------- /bindings/rust/README.md: -------------------------------------------------------------------------------- 1 | # tree-sitter-doxygen 2 | 3 | This crate provides a Doxygen grammar for the [tree-sitter][] parsing library. 4 | To use this crate, add it to the `[dependencies]` section of your `Cargo.toml` 5 | file. (Note that you will probably also need to depend on the 6 | [`tree-sitter`][tree-sitter crate] crate to use the parsed result in any useful 7 | way.) 8 | 9 | ```toml 10 | [dependencies] 11 | tree-sitter = "0.20.10" 12 | tree-sitter-doxygen = "1.1.0" 13 | ``` 14 | 15 | Typically, you will use the [language][language func] function to add this 16 | grammar to a tree-sitter [Parser][], and then use the parser to parse some code: 17 | 18 | ```rust 19 | let code = r#" 20 | /** 21 | * \brief This is a brief description. 22 | * 23 | * \param[in] param1 This is the first parameter. 24 | */ 25 | "#; 26 | let mut parser = Parser::new(); 27 | parser.set_language(tree_sitter_doxygen::language()).expect("Error loading Doxygen grammar"); 28 | let parsed = parser.parse(code, None); 29 | ``` 30 | 31 | If you have any questions, please reach out to us in the [tree-sitter 32 | discussions] page. 33 | 34 | [language func]: https://docs.rs/tree-sitter-doxygen/*/tree_sitter_doxygen/fn.language.html 35 | [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 36 | [tree-sitter]: https://tree-sitter.github.io/ 37 | [tree-sitter crate]: https://crates.io/crates/tree-sitter 38 | [tree-sitter discussions]: https://github.com/tree-sitter/tree-sitter/discussions 39 | -------------------------------------------------------------------------------- /bindings/rust/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let src_dir = std::path::Path::new("src"); 3 | 4 | let mut c_config = cc::Build::new(); 5 | c_config.include(src_dir); 6 | c_config 7 | .flag_if_supported("-Wno-unused-parameter") 8 | .flag_if_supported("-Wno-unused-but-set-variable") 9 | .flag_if_supported("-Wno-trigraphs"); 10 | let parser_path = src_dir.join("parser.c"); 11 | c_config.file(&parser_path); 12 | 13 | let scanner_path = src_dir.join("scanner.c"); 14 | c_config.file(&scanner_path); 15 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 16 | 17 | c_config.compile("parser"); 18 | println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 19 | } 20 | -------------------------------------------------------------------------------- /bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides Doxygen language support for the [tree-sitter][] parsing library. 2 | //! 3 | //! Typically, you will use the [language][language func] function to add this language to a 4 | //! tree-sitter [Parser][], and then use the parser to parse some code: 5 | //! 6 | //! ``` 7 | //! let code = ""; 8 | //! let mut parser = tree_sitter::Parser::new(); 9 | //! parser.set_language(tree_sitter_doxygen::language()).expect("Error loading Doxygen grammar"); 10 | //! let tree = parser.parse(code, None).unwrap(); 11 | //! ``` 12 | //! 13 | //! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 14 | //! [language func]: fn.language.html 15 | //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 16 | //! [tree-sitter]: https://tree-sitter.github.io/ 17 | 18 | use tree_sitter::Language; 19 | 20 | extern "C" { 21 | fn tree_sitter_doxygen() -> Language; 22 | } 23 | 24 | /// Get the tree-sitter [Language][] for this grammar. 25 | /// 26 | /// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 27 | pub fn language() -> Language { 28 | unsafe { tree_sitter_doxygen() } 29 | } 30 | 31 | /// The content of the [`node-types.json`][] file for this grammar. 32 | /// 33 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 34 | pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); 35 | 36 | // Uncomment these to include any queries that this grammar contains 37 | 38 | // pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm"); 39 | // pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm"); 40 | // pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm"); 41 | // pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm"); 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | #[test] 46 | fn test_can_load_grammar() { 47 | let mut parser = tree_sitter::Parser::new(); 48 | parser 49 | .set_language(super::language()) 50 | .expect("Error loading Doxygen grammar"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterDoxygen/doxygen.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_DOXYGEN_H_ 2 | #define TREE_SITTER_DOXYGEN_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | extern TSLanguage *tree_sitter_doxygen(); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_DOXYGEN_H_ 17 | -------------------------------------------------------------------------------- /grammar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Doxygen grammar for tree-sitter 3 | * @author Amaan Qureshi 4 | * @license MIT 5 | */ 6 | 7 | /* eslint-disable arrow-parens */ 8 | /* eslint-disable camelcase */ 9 | /* eslint-disable-next-line spaced-comment */ 10 | /// 11 | // @ts-check 12 | 13 | module.exports = grammar({ 14 | name: 'doxygen', 15 | 16 | externals: $ => [ 17 | $.brief_text, 18 | $.code_block_start, 19 | $.code_block_language, 20 | $.code_block_content, 21 | $.code_block_end, 22 | ], 23 | 24 | extras: _ => [ 25 | token(choice( 26 | // Skip over stars at the beginnings of lines 27 | seq(/\n/, /[ \t]*/, repeat(seq('*', /[ \t]*/))), 28 | /\s/, 29 | )), 30 | ], 31 | 32 | rules: { 33 | document: $ => choice( 34 | seq( 35 | $._multiline_begin, 36 | optional($.brief_header), 37 | optional($.description), 38 | repeat(choice($.tag, $.code_block, $._text_line)), 39 | $._multiline_end, 40 | ), 41 | seq( 42 | $._singleline_begin, 43 | optional($.brief_header), 44 | optional($.description), 45 | repeat($.tag), 46 | ), 47 | ), 48 | 49 | brief_header: $ => prec(1, choice( 50 | seq(alias(tagName('brief'), $.tag_name), $.brief_description), 51 | // brief desc that ends at dot 52 | alias(/[^\s\\*@<\[][^.<]+[.]/, $.brief_description), 53 | )), 54 | 55 | brief_description: $ => prec.right(repeat1(choice( 56 | $.brief_text, 57 | $.tag_name, 58 | ))), 59 | 60 | description: $ => repeat1(choice( 61 | $._text, 62 | $.emphasis, 63 | $.code_word, 64 | $.link, 65 | $.function_link, 66 | )), 67 | 68 | tag: $ => prec.right(choice( 69 | // type, name, and description 70 | seq( 71 | alias($.tag_name_with_argument, $.tag_name), 72 | optional($._expression), 73 | optional($.description), 74 | ), 75 | 76 | // type, several names, and description 77 | seq( 78 | alias($.tag_name_with_multiple_arguments, $.tag_name), 79 | optional($.storageclass), 80 | commaSep1($._expression), 81 | optional($.description), 82 | ), 83 | 84 | // Marking a parameter inside 85 | seq( 86 | $.storageclass, 87 | optional($.description), 88 | ), 89 | 90 | // multiple types 91 | seq( 92 | alias($.tag_name_with_types, $.tag_name), 93 | commaSep1(seq( 94 | field('function', choice($.function_link, $.identifier)), 95 | optional(/[a-zA-Z_][a-zA-Z_0-9]*\s+/), 96 | )), 97 | optional(field('function', choice($.function_link, $.identifier))), 98 | ), 99 | 100 | // c types 101 | seq( 102 | alias($.tag_name_with_self_types, $.tag_name), 103 | alias(/.*/, $.type), 104 | ), 105 | 106 | // type and description 107 | seq( 108 | alias($.tag_name_with_type, $.tag_name), 109 | optional(alias($.identifier, $.type)), 110 | optional($.description), 111 | ), 112 | 113 | // description only 114 | seq( 115 | $.tag_name, 116 | optional($.description), 117 | ), 118 | )), 119 | 120 | tag_name_with_argument: _ => token(choice( 121 | tagName('namespace'), 122 | tagName('exception'), 123 | )), 124 | 125 | tag_name_with_multiple_arguments: _ => token(choice( 126 | tagName('param'), 127 | )), 128 | 129 | tag_name_with_types: _ => token(choice( 130 | tagName('sa'), 131 | )), 132 | 133 | tag_name_with_self_types: _ => token(choice( 134 | tagName('fn'), 135 | tagName('property'), 136 | tagName('var'), 137 | tagName('overload'), 138 | tagName('typedef'), 139 | )), 140 | 141 | tag_name_with_type: _ => token(choice( 142 | tagName('class'), 143 | tagName('var'), 144 | tagName('defgroup'), 145 | tagName('addtogroup'), 146 | )), 147 | 148 | tag_name: _ => /(@|\\)~?([a-zA-Z_]+|\{|\})/, 149 | 150 | _expression: $ => choice( 151 | $.identifier, 152 | $.qualified_identifier, 153 | // prec(-1, $.storageclassi), 154 | $.function, 155 | // $.optional_identifier, 156 | // $.member_expression, 157 | // $.path_expression, 158 | // $.qualified_expression, 159 | ), 160 | 161 | identifier: _ => /[a-zA-Z_][a-zA-Z_0-9]*/, 162 | 163 | qualified_identifier: $ => seq( 164 | $.identifier, 165 | repeat1(seq(token.immediate('::'), $.identifier)), 166 | ), 167 | 168 | function: $ => seq( 169 | optional('~'), 170 | $.identifier, 171 | '(', 172 | ')', 173 | ), 174 | 175 | storageclass: _ => seq('[', choice('in', 'out', 'inout'), ']'), 176 | 177 | emphasis: $ => seq('\\a', alias(/[a-zA-Z_][a-zA-Z_0-9]*/, $.text)), 178 | 179 | code_word: $ => seq('\\c', alias(/[a-zA-Z_][a-zA-Z_0-9]*/, $.code)), 180 | 181 | link: $ => seq( 182 | ']*/, 184 | '>', 185 | alias(/[^<]*/, $.text), 186 | '', 187 | ), 188 | 189 | function_link: _ => token(choice( 190 | seq(/~?[a-zA-Z_][a-zA-Z_0-9]*/, '(', /[^)\n]*/, ')'), 191 | seq('::', /~?[a-zA-Z_][a-zA-Z_0-9]*/), 192 | seq( 193 | /~?[a-zA-Z_][a-zA-Z_0-9]*/, 194 | repeat1(seq('::', /~?[a-zA-Z_][a-zA-Z_0-9]*/)), 195 | '(', 196 | /[^)\n]*/, 197 | ')', 198 | ), 199 | )), 200 | 201 | code_block: $ => choice( 202 | seq( 203 | $.code_block_start, 204 | $.code_block_language, 205 | $.code_block_content, 206 | $.code_block_end, 207 | ), 208 | seq( 209 | '@code', 210 | optional(seq( 211 | token.immediate('{'), 212 | token.immediate('.'), 213 | $.code_block_language, 214 | token.immediate('}'), 215 | )), 216 | $.code_block_content, 217 | '@endcode', 218 | ), 219 | ), 220 | 221 | _text: _ => token(prec(-1, /[^*{}@\\\s][^*!{}\\\n]*([^*/{}\\\n][^*{}\\\n]*\*+)*/)), 222 | 223 | _singleline_begin: _ => token(seq('//', optional('!'), optional('<'))), 224 | 225 | _multiline_begin: _ => token(seq('/', repeat(choice('*', '/')), optional('!'), optional('<'))), 226 | 227 | _multiline_end: $ => choice('/', '*/'), 228 | 229 | _text_line: _ => token(prec(-2, /[^\s<@\\*].*/)), 230 | }, 231 | }); 232 | 233 | /** 234 | * 235 | * Create a tag name that starts with @ or \ 236 | * 237 | * @param {string} name 238 | * 239 | * @return {ChoiceRule} 240 | */ 241 | function tagName(name) { 242 | return choice(`@${name}`, `\\${name}`); 243 | } 244 | 245 | /** 246 | * Creates a rule to match one or more of the rules separated by a comma 247 | * 248 | * @param {Rule} rule 249 | * 250 | * @return {SeqRule} 251 | * 252 | */ 253 | function commaSep1(rule) { 254 | return seq(rule, repeat(seq(',', rule))); 255 | } 256 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-doxygen", 3 | "version": "1.1.0", 4 | "description": "Doxygen grammar for tree-sitter", 5 | "main": "bindings/node", 6 | "keywords": [ 7 | "parser", 8 | "lexer", 9 | "doxygen" 10 | ], 11 | "author": "Amaan Qureshi ", 12 | "license": "MIT", 13 | "bugs": { 14 | "url": "https://github.com/amaanq/tree-sitter-doxygen/issues" 15 | }, 16 | "homepage": "https://github.com/amaanq/tree-sitter-doxygen#readme", 17 | "dependencies": { 18 | "nan": "^2.17.0" 19 | }, 20 | "devDependencies": { 21 | "eslint": "^8.47.0", 22 | "eslint-config-google": "^0.14.0", 23 | "tree-sitter-cli": "0.20.8" 24 | }, 25 | "repository": "https://github.com/amaanq/tree-sitter-doxygen", 26 | "scripts": { 27 | "build": "tree-sitter generate && node-gyp build", 28 | "lint": "eslint grammar.js", 29 | "parse": "tree-sitter parse", 30 | "test": "tree-sitter test" 31 | }, 32 | "tree-sitter": [ 33 | { 34 | "scope": "text.doxygen", 35 | "injection-regex": "doxygen", 36 | "highlights": [ 37 | "queries/highlights.scm" 38 | ], 39 | "injections": [ 40 | "queries/injections.scm" 41 | ] 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /queries/highlights.scm: -------------------------------------------------------------------------------- 1 | ((tag_name) @keyword 2 | (#set! "priority" 105)) 3 | 4 | [ 5 | "@code" 6 | "@endcode" 7 | ] @keyword 8 | 9 | (identifier) @variable 10 | 11 | ((tag 12 | (tag_name) @_param 13 | (identifier) @parameter) 14 | (#any-of? @_param "@param" "\\param")) 15 | 16 | (function (identifier) @function) 17 | 18 | (function_link) @function 19 | 20 | (emphasis) @text.emphasis 21 | 22 | [ 23 | "\\a" 24 | "\\c" 25 | ] @tag 26 | 27 | (code_block_language) @label 28 | 29 | [ 30 | "in" 31 | "out" 32 | "inout" 33 | ] @storageclass 34 | 35 | "~" @operator 36 | 37 | [ 38 | "" 40 | "" 41 | ] @tag 42 | 43 | [ 44 | "." 45 | "," 46 | "::" 47 | (code_block_start) 48 | (code_block_end) 49 | ] @punctuation.delimiter 50 | 51 | [ "(" ")" "{" "}" "[" "]" ] @punctuation.bracket 52 | 53 | (code_block_content) @none 54 | -------------------------------------------------------------------------------- /queries/indents.scm: -------------------------------------------------------------------------------- 1 | (document) @indent.auto 2 | -------------------------------------------------------------------------------- /queries/injections.scm: -------------------------------------------------------------------------------- 1 | ((type) @injection.content 2 | (#set! injection.parent)) 3 | 4 | ([ (function_link) (code) ] @injection.content 5 | (#set! injection.parent)) 6 | 7 | ((link) @injection.content 8 | (#set! injection.language "html")) 9 | 10 | ((code_block 11 | (code_block_language) @injection.language 12 | (code_block_content) @injection.content)) 13 | -------------------------------------------------------------------------------- /src/grammar.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "doxygen", 3 | "rules": { 4 | "document": { 5 | "type": "CHOICE", 6 | "members": [ 7 | { 8 | "type": "SEQ", 9 | "members": [ 10 | { 11 | "type": "SYMBOL", 12 | "name": "_multiline_begin" 13 | }, 14 | { 15 | "type": "CHOICE", 16 | "members": [ 17 | { 18 | "type": "SYMBOL", 19 | "name": "brief_header" 20 | }, 21 | { 22 | "type": "BLANK" 23 | } 24 | ] 25 | }, 26 | { 27 | "type": "CHOICE", 28 | "members": [ 29 | { 30 | "type": "SYMBOL", 31 | "name": "description" 32 | }, 33 | { 34 | "type": "BLANK" 35 | } 36 | ] 37 | }, 38 | { 39 | "type": "REPEAT", 40 | "content": { 41 | "type": "CHOICE", 42 | "members": [ 43 | { 44 | "type": "SYMBOL", 45 | "name": "tag" 46 | }, 47 | { 48 | "type": "SYMBOL", 49 | "name": "code_block" 50 | }, 51 | { 52 | "type": "SYMBOL", 53 | "name": "_text_line" 54 | } 55 | ] 56 | } 57 | }, 58 | { 59 | "type": "SYMBOL", 60 | "name": "_multiline_end" 61 | } 62 | ] 63 | }, 64 | { 65 | "type": "SEQ", 66 | "members": [ 67 | { 68 | "type": "SYMBOL", 69 | "name": "_singleline_begin" 70 | }, 71 | { 72 | "type": "CHOICE", 73 | "members": [ 74 | { 75 | "type": "SYMBOL", 76 | "name": "brief_header" 77 | }, 78 | { 79 | "type": "BLANK" 80 | } 81 | ] 82 | }, 83 | { 84 | "type": "CHOICE", 85 | "members": [ 86 | { 87 | "type": "SYMBOL", 88 | "name": "description" 89 | }, 90 | { 91 | "type": "BLANK" 92 | } 93 | ] 94 | }, 95 | { 96 | "type": "REPEAT", 97 | "content": { 98 | "type": "SYMBOL", 99 | "name": "tag" 100 | } 101 | } 102 | ] 103 | } 104 | ] 105 | }, 106 | "brief_header": { 107 | "type": "PREC", 108 | "value": 1, 109 | "content": { 110 | "type": "CHOICE", 111 | "members": [ 112 | { 113 | "type": "SEQ", 114 | "members": [ 115 | { 116 | "type": "ALIAS", 117 | "content": { 118 | "type": "CHOICE", 119 | "members": [ 120 | { 121 | "type": "STRING", 122 | "value": "@brief" 123 | }, 124 | { 125 | "type": "STRING", 126 | "value": "\\brief" 127 | } 128 | ] 129 | }, 130 | "named": true, 131 | "value": "tag_name" 132 | }, 133 | { 134 | "type": "SYMBOL", 135 | "name": "brief_description" 136 | } 137 | ] 138 | }, 139 | { 140 | "type": "ALIAS", 141 | "content": { 142 | "type": "PATTERN", 143 | "value": "[^\\s\\\\*@<\\[][^.<]+[.]" 144 | }, 145 | "named": true, 146 | "value": "brief_description" 147 | } 148 | ] 149 | } 150 | }, 151 | "brief_description": { 152 | "type": "PREC_RIGHT", 153 | "value": 0, 154 | "content": { 155 | "type": "REPEAT1", 156 | "content": { 157 | "type": "CHOICE", 158 | "members": [ 159 | { 160 | "type": "SYMBOL", 161 | "name": "brief_text" 162 | }, 163 | { 164 | "type": "SYMBOL", 165 | "name": "tag_name" 166 | } 167 | ] 168 | } 169 | } 170 | }, 171 | "description": { 172 | "type": "REPEAT1", 173 | "content": { 174 | "type": "CHOICE", 175 | "members": [ 176 | { 177 | "type": "SYMBOL", 178 | "name": "_text" 179 | }, 180 | { 181 | "type": "SYMBOL", 182 | "name": "emphasis" 183 | }, 184 | { 185 | "type": "SYMBOL", 186 | "name": "code_word" 187 | }, 188 | { 189 | "type": "SYMBOL", 190 | "name": "link" 191 | }, 192 | { 193 | "type": "SYMBOL", 194 | "name": "function_link" 195 | } 196 | ] 197 | } 198 | }, 199 | "tag": { 200 | "type": "PREC_RIGHT", 201 | "value": 0, 202 | "content": { 203 | "type": "CHOICE", 204 | "members": [ 205 | { 206 | "type": "SEQ", 207 | "members": [ 208 | { 209 | "type": "ALIAS", 210 | "content": { 211 | "type": "SYMBOL", 212 | "name": "tag_name_with_argument" 213 | }, 214 | "named": true, 215 | "value": "tag_name" 216 | }, 217 | { 218 | "type": "CHOICE", 219 | "members": [ 220 | { 221 | "type": "SYMBOL", 222 | "name": "_expression" 223 | }, 224 | { 225 | "type": "BLANK" 226 | } 227 | ] 228 | }, 229 | { 230 | "type": "CHOICE", 231 | "members": [ 232 | { 233 | "type": "SYMBOL", 234 | "name": "description" 235 | }, 236 | { 237 | "type": "BLANK" 238 | } 239 | ] 240 | } 241 | ] 242 | }, 243 | { 244 | "type": "SEQ", 245 | "members": [ 246 | { 247 | "type": "ALIAS", 248 | "content": { 249 | "type": "SYMBOL", 250 | "name": "tag_name_with_multiple_arguments" 251 | }, 252 | "named": true, 253 | "value": "tag_name" 254 | }, 255 | { 256 | "type": "CHOICE", 257 | "members": [ 258 | { 259 | "type": "SYMBOL", 260 | "name": "storageclass" 261 | }, 262 | { 263 | "type": "BLANK" 264 | } 265 | ] 266 | }, 267 | { 268 | "type": "SEQ", 269 | "members": [ 270 | { 271 | "type": "SYMBOL", 272 | "name": "_expression" 273 | }, 274 | { 275 | "type": "REPEAT", 276 | "content": { 277 | "type": "SEQ", 278 | "members": [ 279 | { 280 | "type": "STRING", 281 | "value": "," 282 | }, 283 | { 284 | "type": "SYMBOL", 285 | "name": "_expression" 286 | } 287 | ] 288 | } 289 | } 290 | ] 291 | }, 292 | { 293 | "type": "CHOICE", 294 | "members": [ 295 | { 296 | "type": "SYMBOL", 297 | "name": "description" 298 | }, 299 | { 300 | "type": "BLANK" 301 | } 302 | ] 303 | } 304 | ] 305 | }, 306 | { 307 | "type": "SEQ", 308 | "members": [ 309 | { 310 | "type": "SYMBOL", 311 | "name": "storageclass" 312 | }, 313 | { 314 | "type": "CHOICE", 315 | "members": [ 316 | { 317 | "type": "SYMBOL", 318 | "name": "description" 319 | }, 320 | { 321 | "type": "BLANK" 322 | } 323 | ] 324 | } 325 | ] 326 | }, 327 | { 328 | "type": "SEQ", 329 | "members": [ 330 | { 331 | "type": "ALIAS", 332 | "content": { 333 | "type": "SYMBOL", 334 | "name": "tag_name_with_types" 335 | }, 336 | "named": true, 337 | "value": "tag_name" 338 | }, 339 | { 340 | "type": "SEQ", 341 | "members": [ 342 | { 343 | "type": "SEQ", 344 | "members": [ 345 | { 346 | "type": "FIELD", 347 | "name": "function", 348 | "content": { 349 | "type": "CHOICE", 350 | "members": [ 351 | { 352 | "type": "SYMBOL", 353 | "name": "function_link" 354 | }, 355 | { 356 | "type": "SYMBOL", 357 | "name": "identifier" 358 | } 359 | ] 360 | } 361 | }, 362 | { 363 | "type": "CHOICE", 364 | "members": [ 365 | { 366 | "type": "PATTERN", 367 | "value": "[a-zA-Z_][a-zA-Z_0-9]*\\s+" 368 | }, 369 | { 370 | "type": "BLANK" 371 | } 372 | ] 373 | } 374 | ] 375 | }, 376 | { 377 | "type": "REPEAT", 378 | "content": { 379 | "type": "SEQ", 380 | "members": [ 381 | { 382 | "type": "STRING", 383 | "value": "," 384 | }, 385 | { 386 | "type": "SEQ", 387 | "members": [ 388 | { 389 | "type": "FIELD", 390 | "name": "function", 391 | "content": { 392 | "type": "CHOICE", 393 | "members": [ 394 | { 395 | "type": "SYMBOL", 396 | "name": "function_link" 397 | }, 398 | { 399 | "type": "SYMBOL", 400 | "name": "identifier" 401 | } 402 | ] 403 | } 404 | }, 405 | { 406 | "type": "CHOICE", 407 | "members": [ 408 | { 409 | "type": "PATTERN", 410 | "value": "[a-zA-Z_][a-zA-Z_0-9]*\\s+" 411 | }, 412 | { 413 | "type": "BLANK" 414 | } 415 | ] 416 | } 417 | ] 418 | } 419 | ] 420 | } 421 | } 422 | ] 423 | }, 424 | { 425 | "type": "CHOICE", 426 | "members": [ 427 | { 428 | "type": "FIELD", 429 | "name": "function", 430 | "content": { 431 | "type": "CHOICE", 432 | "members": [ 433 | { 434 | "type": "SYMBOL", 435 | "name": "function_link" 436 | }, 437 | { 438 | "type": "SYMBOL", 439 | "name": "identifier" 440 | } 441 | ] 442 | } 443 | }, 444 | { 445 | "type": "BLANK" 446 | } 447 | ] 448 | } 449 | ] 450 | }, 451 | { 452 | "type": "SEQ", 453 | "members": [ 454 | { 455 | "type": "ALIAS", 456 | "content": { 457 | "type": "SYMBOL", 458 | "name": "tag_name_with_self_types" 459 | }, 460 | "named": true, 461 | "value": "tag_name" 462 | }, 463 | { 464 | "type": "ALIAS", 465 | "content": { 466 | "type": "PATTERN", 467 | "value": ".*" 468 | }, 469 | "named": true, 470 | "value": "type" 471 | } 472 | ] 473 | }, 474 | { 475 | "type": "SEQ", 476 | "members": [ 477 | { 478 | "type": "ALIAS", 479 | "content": { 480 | "type": "SYMBOL", 481 | "name": "tag_name_with_type" 482 | }, 483 | "named": true, 484 | "value": "tag_name" 485 | }, 486 | { 487 | "type": "CHOICE", 488 | "members": [ 489 | { 490 | "type": "ALIAS", 491 | "content": { 492 | "type": "SYMBOL", 493 | "name": "identifier" 494 | }, 495 | "named": true, 496 | "value": "type" 497 | }, 498 | { 499 | "type": "BLANK" 500 | } 501 | ] 502 | }, 503 | { 504 | "type": "CHOICE", 505 | "members": [ 506 | { 507 | "type": "SYMBOL", 508 | "name": "description" 509 | }, 510 | { 511 | "type": "BLANK" 512 | } 513 | ] 514 | } 515 | ] 516 | }, 517 | { 518 | "type": "SEQ", 519 | "members": [ 520 | { 521 | "type": "SYMBOL", 522 | "name": "tag_name" 523 | }, 524 | { 525 | "type": "CHOICE", 526 | "members": [ 527 | { 528 | "type": "SYMBOL", 529 | "name": "description" 530 | }, 531 | { 532 | "type": "BLANK" 533 | } 534 | ] 535 | } 536 | ] 537 | } 538 | ] 539 | } 540 | }, 541 | "tag_name_with_argument": { 542 | "type": "TOKEN", 543 | "content": { 544 | "type": "CHOICE", 545 | "members": [ 546 | { 547 | "type": "CHOICE", 548 | "members": [ 549 | { 550 | "type": "STRING", 551 | "value": "@namespace" 552 | }, 553 | { 554 | "type": "STRING", 555 | "value": "\\namespace" 556 | } 557 | ] 558 | }, 559 | { 560 | "type": "CHOICE", 561 | "members": [ 562 | { 563 | "type": "STRING", 564 | "value": "@exception" 565 | }, 566 | { 567 | "type": "STRING", 568 | "value": "\\exception" 569 | } 570 | ] 571 | } 572 | ] 573 | } 574 | }, 575 | "tag_name_with_multiple_arguments": { 576 | "type": "TOKEN", 577 | "content": { 578 | "type": "CHOICE", 579 | "members": [ 580 | { 581 | "type": "CHOICE", 582 | "members": [ 583 | { 584 | "type": "STRING", 585 | "value": "@param" 586 | }, 587 | { 588 | "type": "STRING", 589 | "value": "\\param" 590 | } 591 | ] 592 | } 593 | ] 594 | } 595 | }, 596 | "tag_name_with_types": { 597 | "type": "TOKEN", 598 | "content": { 599 | "type": "CHOICE", 600 | "members": [ 601 | { 602 | "type": "CHOICE", 603 | "members": [ 604 | { 605 | "type": "STRING", 606 | "value": "@sa" 607 | }, 608 | { 609 | "type": "STRING", 610 | "value": "\\sa" 611 | } 612 | ] 613 | } 614 | ] 615 | } 616 | }, 617 | "tag_name_with_self_types": { 618 | "type": "TOKEN", 619 | "content": { 620 | "type": "CHOICE", 621 | "members": [ 622 | { 623 | "type": "CHOICE", 624 | "members": [ 625 | { 626 | "type": "STRING", 627 | "value": "@fn" 628 | }, 629 | { 630 | "type": "STRING", 631 | "value": "\\fn" 632 | } 633 | ] 634 | }, 635 | { 636 | "type": "CHOICE", 637 | "members": [ 638 | { 639 | "type": "STRING", 640 | "value": "@property" 641 | }, 642 | { 643 | "type": "STRING", 644 | "value": "\\property" 645 | } 646 | ] 647 | }, 648 | { 649 | "type": "CHOICE", 650 | "members": [ 651 | { 652 | "type": "STRING", 653 | "value": "@var" 654 | }, 655 | { 656 | "type": "STRING", 657 | "value": "\\var" 658 | } 659 | ] 660 | }, 661 | { 662 | "type": "CHOICE", 663 | "members": [ 664 | { 665 | "type": "STRING", 666 | "value": "@overload" 667 | }, 668 | { 669 | "type": "STRING", 670 | "value": "\\overload" 671 | } 672 | ] 673 | }, 674 | { 675 | "type": "CHOICE", 676 | "members": [ 677 | { 678 | "type": "STRING", 679 | "value": "@typedef" 680 | }, 681 | { 682 | "type": "STRING", 683 | "value": "\\typedef" 684 | } 685 | ] 686 | } 687 | ] 688 | } 689 | }, 690 | "tag_name_with_type": { 691 | "type": "TOKEN", 692 | "content": { 693 | "type": "CHOICE", 694 | "members": [ 695 | { 696 | "type": "CHOICE", 697 | "members": [ 698 | { 699 | "type": "STRING", 700 | "value": "@class" 701 | }, 702 | { 703 | "type": "STRING", 704 | "value": "\\class" 705 | } 706 | ] 707 | }, 708 | { 709 | "type": "CHOICE", 710 | "members": [ 711 | { 712 | "type": "STRING", 713 | "value": "@var" 714 | }, 715 | { 716 | "type": "STRING", 717 | "value": "\\var" 718 | } 719 | ] 720 | }, 721 | { 722 | "type": "CHOICE", 723 | "members": [ 724 | { 725 | "type": "STRING", 726 | "value": "@defgroup" 727 | }, 728 | { 729 | "type": "STRING", 730 | "value": "\\defgroup" 731 | } 732 | ] 733 | }, 734 | { 735 | "type": "CHOICE", 736 | "members": [ 737 | { 738 | "type": "STRING", 739 | "value": "@addtogroup" 740 | }, 741 | { 742 | "type": "STRING", 743 | "value": "\\addtogroup" 744 | } 745 | ] 746 | } 747 | ] 748 | } 749 | }, 750 | "tag_name": { 751 | "type": "PATTERN", 752 | "value": "(@|\\\\)~?([a-zA-Z_]+|\\{|\\})" 753 | }, 754 | "_expression": { 755 | "type": "CHOICE", 756 | "members": [ 757 | { 758 | "type": "SYMBOL", 759 | "name": "identifier" 760 | }, 761 | { 762 | "type": "SYMBOL", 763 | "name": "qualified_identifier" 764 | }, 765 | { 766 | "type": "SYMBOL", 767 | "name": "function" 768 | } 769 | ] 770 | }, 771 | "identifier": { 772 | "type": "PATTERN", 773 | "value": "[a-zA-Z_][a-zA-Z_0-9]*" 774 | }, 775 | "qualified_identifier": { 776 | "type": "SEQ", 777 | "members": [ 778 | { 779 | "type": "SYMBOL", 780 | "name": "identifier" 781 | }, 782 | { 783 | "type": "REPEAT1", 784 | "content": { 785 | "type": "SEQ", 786 | "members": [ 787 | { 788 | "type": "IMMEDIATE_TOKEN", 789 | "content": { 790 | "type": "STRING", 791 | "value": "::" 792 | } 793 | }, 794 | { 795 | "type": "SYMBOL", 796 | "name": "identifier" 797 | } 798 | ] 799 | } 800 | } 801 | ] 802 | }, 803 | "function": { 804 | "type": "SEQ", 805 | "members": [ 806 | { 807 | "type": "CHOICE", 808 | "members": [ 809 | { 810 | "type": "STRING", 811 | "value": "~" 812 | }, 813 | { 814 | "type": "BLANK" 815 | } 816 | ] 817 | }, 818 | { 819 | "type": "SYMBOL", 820 | "name": "identifier" 821 | }, 822 | { 823 | "type": "STRING", 824 | "value": "(" 825 | }, 826 | { 827 | "type": "STRING", 828 | "value": ")" 829 | } 830 | ] 831 | }, 832 | "storageclass": { 833 | "type": "SEQ", 834 | "members": [ 835 | { 836 | "type": "STRING", 837 | "value": "[" 838 | }, 839 | { 840 | "type": "CHOICE", 841 | "members": [ 842 | { 843 | "type": "STRING", 844 | "value": "in" 845 | }, 846 | { 847 | "type": "STRING", 848 | "value": "out" 849 | }, 850 | { 851 | "type": "STRING", 852 | "value": "inout" 853 | } 854 | ] 855 | }, 856 | { 857 | "type": "STRING", 858 | "value": "]" 859 | } 860 | ] 861 | }, 862 | "emphasis": { 863 | "type": "SEQ", 864 | "members": [ 865 | { 866 | "type": "STRING", 867 | "value": "\\a" 868 | }, 869 | { 870 | "type": "ALIAS", 871 | "content": { 872 | "type": "PATTERN", 873 | "value": "[a-zA-Z_][a-zA-Z_0-9]*" 874 | }, 875 | "named": true, 876 | "value": "text" 877 | } 878 | ] 879 | }, 880 | "code_word": { 881 | "type": "SEQ", 882 | "members": [ 883 | { 884 | "type": "STRING", 885 | "value": "\\c" 886 | }, 887 | { 888 | "type": "ALIAS", 889 | "content": { 890 | "type": "PATTERN", 891 | "value": "[a-zA-Z_][a-zA-Z_0-9]*" 892 | }, 893 | "named": true, 894 | "value": "code" 895 | } 896 | ] 897 | }, 898 | "link": { 899 | "type": "SEQ", 900 | "members": [ 901 | { 902 | "type": "STRING", 903 | "value": "]*" 908 | }, 909 | { 910 | "type": "STRING", 911 | "value": ">" 912 | }, 913 | { 914 | "type": "ALIAS", 915 | "content": { 916 | "type": "PATTERN", 917 | "value": "[^<]*" 918 | }, 919 | "named": true, 920 | "value": "text" 921 | }, 922 | { 923 | "type": "STRING", 924 | "value": "" 925 | } 926 | ] 927 | }, 928 | "function_link": { 929 | "type": "TOKEN", 930 | "content": { 931 | "type": "CHOICE", 932 | "members": [ 933 | { 934 | "type": "SEQ", 935 | "members": [ 936 | { 937 | "type": "PATTERN", 938 | "value": "~?[a-zA-Z_][a-zA-Z_0-9]*" 939 | }, 940 | { 941 | "type": "STRING", 942 | "value": "(" 943 | }, 944 | { 945 | "type": "PATTERN", 946 | "value": "[^)\\n]*" 947 | }, 948 | { 949 | "type": "STRING", 950 | "value": ")" 951 | } 952 | ] 953 | }, 954 | { 955 | "type": "SEQ", 956 | "members": [ 957 | { 958 | "type": "STRING", 959 | "value": "::" 960 | }, 961 | { 962 | "type": "PATTERN", 963 | "value": "~?[a-zA-Z_][a-zA-Z_0-9]*" 964 | } 965 | ] 966 | }, 967 | { 968 | "type": "SEQ", 969 | "members": [ 970 | { 971 | "type": "PATTERN", 972 | "value": "~?[a-zA-Z_][a-zA-Z_0-9]*" 973 | }, 974 | { 975 | "type": "REPEAT1", 976 | "content": { 977 | "type": "SEQ", 978 | "members": [ 979 | { 980 | "type": "STRING", 981 | "value": "::" 982 | }, 983 | { 984 | "type": "PATTERN", 985 | "value": "~?[a-zA-Z_][a-zA-Z_0-9]*" 986 | } 987 | ] 988 | } 989 | }, 990 | { 991 | "type": "STRING", 992 | "value": "(" 993 | }, 994 | { 995 | "type": "PATTERN", 996 | "value": "[^)\\n]*" 997 | }, 998 | { 999 | "type": "STRING", 1000 | "value": ")" 1001 | } 1002 | ] 1003 | } 1004 | ] 1005 | } 1006 | }, 1007 | "code_block": { 1008 | "type": "CHOICE", 1009 | "members": [ 1010 | { 1011 | "type": "SEQ", 1012 | "members": [ 1013 | { 1014 | "type": "SYMBOL", 1015 | "name": "code_block_start" 1016 | }, 1017 | { 1018 | "type": "SYMBOL", 1019 | "name": "code_block_language" 1020 | }, 1021 | { 1022 | "type": "SYMBOL", 1023 | "name": "code_block_content" 1024 | }, 1025 | { 1026 | "type": "SYMBOL", 1027 | "name": "code_block_end" 1028 | } 1029 | ] 1030 | }, 1031 | { 1032 | "type": "SEQ", 1033 | "members": [ 1034 | { 1035 | "type": "STRING", 1036 | "value": "@code" 1037 | }, 1038 | { 1039 | "type": "CHOICE", 1040 | "members": [ 1041 | { 1042 | "type": "SEQ", 1043 | "members": [ 1044 | { 1045 | "type": "IMMEDIATE_TOKEN", 1046 | "content": { 1047 | "type": "STRING", 1048 | "value": "{" 1049 | } 1050 | }, 1051 | { 1052 | "type": "IMMEDIATE_TOKEN", 1053 | "content": { 1054 | "type": "STRING", 1055 | "value": "." 1056 | } 1057 | }, 1058 | { 1059 | "type": "SYMBOL", 1060 | "name": "code_block_language" 1061 | }, 1062 | { 1063 | "type": "IMMEDIATE_TOKEN", 1064 | "content": { 1065 | "type": "STRING", 1066 | "value": "}" 1067 | } 1068 | } 1069 | ] 1070 | }, 1071 | { 1072 | "type": "BLANK" 1073 | } 1074 | ] 1075 | }, 1076 | { 1077 | "type": "SYMBOL", 1078 | "name": "code_block_content" 1079 | }, 1080 | { 1081 | "type": "STRING", 1082 | "value": "@endcode" 1083 | } 1084 | ] 1085 | } 1086 | ] 1087 | }, 1088 | "_text": { 1089 | "type": "TOKEN", 1090 | "content": { 1091 | "type": "PREC", 1092 | "value": -1, 1093 | "content": { 1094 | "type": "PATTERN", 1095 | "value": "[^*{}@\\\\\\s][^*!{}\\\\\\n]*([^*/{}\\\\\\n][^*{}\\\\\\n]*\\*+)*" 1096 | } 1097 | } 1098 | }, 1099 | "_singleline_begin": { 1100 | "type": "TOKEN", 1101 | "content": { 1102 | "type": "SEQ", 1103 | "members": [ 1104 | { 1105 | "type": "STRING", 1106 | "value": "//" 1107 | }, 1108 | { 1109 | "type": "CHOICE", 1110 | "members": [ 1111 | { 1112 | "type": "STRING", 1113 | "value": "!" 1114 | }, 1115 | { 1116 | "type": "BLANK" 1117 | } 1118 | ] 1119 | }, 1120 | { 1121 | "type": "CHOICE", 1122 | "members": [ 1123 | { 1124 | "type": "STRING", 1125 | "value": "<" 1126 | }, 1127 | { 1128 | "type": "BLANK" 1129 | } 1130 | ] 1131 | } 1132 | ] 1133 | } 1134 | }, 1135 | "_multiline_begin": { 1136 | "type": "TOKEN", 1137 | "content": { 1138 | "type": "SEQ", 1139 | "members": [ 1140 | { 1141 | "type": "STRING", 1142 | "value": "/" 1143 | }, 1144 | { 1145 | "type": "REPEAT", 1146 | "content": { 1147 | "type": "CHOICE", 1148 | "members": [ 1149 | { 1150 | "type": "STRING", 1151 | "value": "*" 1152 | }, 1153 | { 1154 | "type": "STRING", 1155 | "value": "/" 1156 | } 1157 | ] 1158 | } 1159 | }, 1160 | { 1161 | "type": "CHOICE", 1162 | "members": [ 1163 | { 1164 | "type": "STRING", 1165 | "value": "!" 1166 | }, 1167 | { 1168 | "type": "BLANK" 1169 | } 1170 | ] 1171 | }, 1172 | { 1173 | "type": "CHOICE", 1174 | "members": [ 1175 | { 1176 | "type": "STRING", 1177 | "value": "<" 1178 | }, 1179 | { 1180 | "type": "BLANK" 1181 | } 1182 | ] 1183 | } 1184 | ] 1185 | } 1186 | }, 1187 | "_multiline_end": { 1188 | "type": "CHOICE", 1189 | "members": [ 1190 | { 1191 | "type": "STRING", 1192 | "value": "/" 1193 | }, 1194 | { 1195 | "type": "STRING", 1196 | "value": "*/" 1197 | } 1198 | ] 1199 | }, 1200 | "_text_line": { 1201 | "type": "TOKEN", 1202 | "content": { 1203 | "type": "PREC", 1204 | "value": -2, 1205 | "content": { 1206 | "type": "PATTERN", 1207 | "value": "[^\\s<@\\\\*].*" 1208 | } 1209 | } 1210 | } 1211 | }, 1212 | "extras": [ 1213 | { 1214 | "type": "TOKEN", 1215 | "content": { 1216 | "type": "CHOICE", 1217 | "members": [ 1218 | { 1219 | "type": "SEQ", 1220 | "members": [ 1221 | { 1222 | "type": "PATTERN", 1223 | "value": "\\n" 1224 | }, 1225 | { 1226 | "type": "PATTERN", 1227 | "value": "[ \\t]*" 1228 | }, 1229 | { 1230 | "type": "REPEAT", 1231 | "content": { 1232 | "type": "SEQ", 1233 | "members": [ 1234 | { 1235 | "type": "STRING", 1236 | "value": "*" 1237 | }, 1238 | { 1239 | "type": "PATTERN", 1240 | "value": "[ \\t]*" 1241 | } 1242 | ] 1243 | } 1244 | } 1245 | ] 1246 | }, 1247 | { 1248 | "type": "PATTERN", 1249 | "value": "\\s" 1250 | } 1251 | ] 1252 | } 1253 | } 1254 | ], 1255 | "conflicts": [], 1256 | "precedences": [], 1257 | "externals": [ 1258 | { 1259 | "type": "SYMBOL", 1260 | "name": "brief_text" 1261 | }, 1262 | { 1263 | "type": "SYMBOL", 1264 | "name": "code_block_start" 1265 | }, 1266 | { 1267 | "type": "SYMBOL", 1268 | "name": "code_block_language" 1269 | }, 1270 | { 1271 | "type": "SYMBOL", 1272 | "name": "code_block_content" 1273 | }, 1274 | { 1275 | "type": "SYMBOL", 1276 | "name": "code_block_end" 1277 | } 1278 | ], 1279 | "inline": [], 1280 | "supertypes": [] 1281 | } 1282 | -------------------------------------------------------------------------------- /src/node-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "brief_description", 4 | "named": true, 5 | "fields": {}, 6 | "children": { 7 | "multiple": true, 8 | "required": false, 9 | "types": [ 10 | { 11 | "type": "brief_text", 12 | "named": true 13 | }, 14 | { 15 | "type": "tag_name", 16 | "named": true 17 | } 18 | ] 19 | } 20 | }, 21 | { 22 | "type": "brief_header", 23 | "named": true, 24 | "fields": {}, 25 | "children": { 26 | "multiple": true, 27 | "required": true, 28 | "types": [ 29 | { 30 | "type": "brief_description", 31 | "named": true 32 | }, 33 | { 34 | "type": "tag_name", 35 | "named": true 36 | } 37 | ] 38 | } 39 | }, 40 | { 41 | "type": "code_block", 42 | "named": true, 43 | "fields": {}, 44 | "children": { 45 | "multiple": true, 46 | "required": true, 47 | "types": [ 48 | { 49 | "type": "code_block_content", 50 | "named": true 51 | }, 52 | { 53 | "type": "code_block_end", 54 | "named": true 55 | }, 56 | { 57 | "type": "code_block_language", 58 | "named": true 59 | }, 60 | { 61 | "type": "code_block_start", 62 | "named": true 63 | } 64 | ] 65 | } 66 | }, 67 | { 68 | "type": "code_word", 69 | "named": true, 70 | "fields": {}, 71 | "children": { 72 | "multiple": false, 73 | "required": true, 74 | "types": [ 75 | { 76 | "type": "code", 77 | "named": true 78 | } 79 | ] 80 | } 81 | }, 82 | { 83 | "type": "description", 84 | "named": true, 85 | "fields": {}, 86 | "children": { 87 | "multiple": true, 88 | "required": false, 89 | "types": [ 90 | { 91 | "type": "code_word", 92 | "named": true 93 | }, 94 | { 95 | "type": "emphasis", 96 | "named": true 97 | }, 98 | { 99 | "type": "function_link", 100 | "named": true 101 | }, 102 | { 103 | "type": "link", 104 | "named": true 105 | } 106 | ] 107 | } 108 | }, 109 | { 110 | "type": "document", 111 | "named": true, 112 | "fields": {}, 113 | "children": { 114 | "multiple": true, 115 | "required": false, 116 | "types": [ 117 | { 118 | "type": "brief_header", 119 | "named": true 120 | }, 121 | { 122 | "type": "code_block", 123 | "named": true 124 | }, 125 | { 126 | "type": "description", 127 | "named": true 128 | }, 129 | { 130 | "type": "tag", 131 | "named": true 132 | } 133 | ] 134 | } 135 | }, 136 | { 137 | "type": "emphasis", 138 | "named": true, 139 | "fields": {}, 140 | "children": { 141 | "multiple": false, 142 | "required": true, 143 | "types": [ 144 | { 145 | "type": "text", 146 | "named": true 147 | } 148 | ] 149 | } 150 | }, 151 | { 152 | "type": "function", 153 | "named": true, 154 | "fields": {}, 155 | "children": { 156 | "multiple": false, 157 | "required": true, 158 | "types": [ 159 | { 160 | "type": "identifier", 161 | "named": true 162 | } 163 | ] 164 | } 165 | }, 166 | { 167 | "type": "identifier", 168 | "named": true, 169 | "fields": {} 170 | }, 171 | { 172 | "type": "link", 173 | "named": true, 174 | "fields": {}, 175 | "children": { 176 | "multiple": false, 177 | "required": true, 178 | "types": [ 179 | { 180 | "type": "text", 181 | "named": true 182 | } 183 | ] 184 | } 185 | }, 186 | { 187 | "type": "qualified_identifier", 188 | "named": true, 189 | "fields": {}, 190 | "children": { 191 | "multiple": true, 192 | "required": true, 193 | "types": [ 194 | { 195 | "type": "identifier", 196 | "named": true 197 | } 198 | ] 199 | } 200 | }, 201 | { 202 | "type": "storageclass", 203 | "named": true, 204 | "fields": {} 205 | }, 206 | { 207 | "type": "tag", 208 | "named": true, 209 | "fields": { 210 | "function": { 211 | "multiple": true, 212 | "required": false, 213 | "types": [ 214 | { 215 | "type": "function_link", 216 | "named": true 217 | }, 218 | { 219 | "type": "identifier", 220 | "named": true 221 | } 222 | ] 223 | } 224 | }, 225 | "children": { 226 | "multiple": true, 227 | "required": true, 228 | "types": [ 229 | { 230 | "type": "description", 231 | "named": true 232 | }, 233 | { 234 | "type": "function", 235 | "named": true 236 | }, 237 | { 238 | "type": "identifier", 239 | "named": true 240 | }, 241 | { 242 | "type": "qualified_identifier", 243 | "named": true 244 | }, 245 | { 246 | "type": "storageclass", 247 | "named": true 248 | }, 249 | { 250 | "type": "tag_name", 251 | "named": true 252 | }, 253 | { 254 | "type": "type", 255 | "named": true 256 | } 257 | ] 258 | } 259 | }, 260 | { 261 | "type": "type", 262 | "named": true, 263 | "fields": {} 264 | }, 265 | { 266 | "type": "(", 267 | "named": false 268 | }, 269 | { 270 | "type": ")", 271 | "named": false 272 | }, 273 | { 274 | "type": "*/", 275 | "named": false 276 | }, 277 | { 278 | "type": ",", 279 | "named": false 280 | }, 281 | { 282 | "type": ".", 283 | "named": false 284 | }, 285 | { 286 | "type": "/", 287 | "named": false 288 | }, 289 | { 290 | "type": "::", 291 | "named": false 292 | }, 293 | { 294 | "type": "", 295 | "named": false 296 | }, 297 | { 298 | "type": "", 303 | "named": false 304 | }, 305 | { 306 | "type": "@code", 307 | "named": false 308 | }, 309 | { 310 | "type": "@endcode", 311 | "named": false 312 | }, 313 | { 314 | "type": "[", 315 | "named": false 316 | }, 317 | { 318 | "type": "\\a", 319 | "named": false 320 | }, 321 | { 322 | "type": "\\c", 323 | "named": false 324 | }, 325 | { 326 | "type": "]", 327 | "named": false 328 | }, 329 | { 330 | "type": "brief_text", 331 | "named": true 332 | }, 333 | { 334 | "type": "code", 335 | "named": true 336 | }, 337 | { 338 | "type": "code_block_content", 339 | "named": true 340 | }, 341 | { 342 | "type": "code_block_end", 343 | "named": true 344 | }, 345 | { 346 | "type": "code_block_language", 347 | "named": true 348 | }, 349 | { 350 | "type": "code_block_start", 351 | "named": true 352 | }, 353 | { 354 | "type": "function_link", 355 | "named": true 356 | }, 357 | { 358 | "type": "in", 359 | "named": false 360 | }, 361 | { 362 | "type": "inout", 363 | "named": false 364 | }, 365 | { 366 | "type": "out", 367 | "named": false 368 | }, 369 | { 370 | "type": "tag_name", 371 | "named": true 372 | }, 373 | { 374 | "type": "text", 375 | "named": true 376 | }, 377 | { 378 | "type": "{", 379 | "named": false 380 | }, 381 | { 382 | "type": "}", 383 | "named": false 384 | }, 385 | { 386 | "type": "~", 387 | "named": false 388 | } 389 | ] -------------------------------------------------------------------------------- /src/scanner.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "tree_sitter/parser.h" 6 | 7 | enum TokenType { 8 | BRIEF_TEXT, 9 | CODE_BLOCK_START, 10 | CODE_BLOCK_LANGUAGE, 11 | CODE_BLOCK_CONTENT, 12 | CODE_BLOCK_END, 13 | }; 14 | 15 | typedef struct { 16 | uint32_t codeblock_delimiter_length; 17 | uint32_t codeblock_start_column; 18 | } Scanner; 19 | 20 | static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); } 21 | 22 | static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); } 23 | 24 | unsigned tree_sitter_doxygen_external_scanner_serialize(void *payload, char *buffer) { 25 | Scanner *scanner = (Scanner *)payload; 26 | 27 | if (scanner->codeblock_start_column > 255 || scanner->codeblock_delimiter_length > 255) { 28 | return 0; 29 | } 30 | 31 | buffer[0] = (char)scanner->codeblock_delimiter_length; 32 | buffer[1] = (char)scanner->codeblock_start_column; 33 | return 2; 34 | } 35 | 36 | void tree_sitter_doxygen_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) { 37 | Scanner *scanner = (Scanner *)payload; 38 | 39 | if (length == 2) { 40 | scanner->codeblock_delimiter_length = (uint32_t)buffer[0]; 41 | scanner->codeblock_start_column = (uint32_t)buffer[1]; 42 | } else if (length != 0 && length != 2) { 43 | fprintf(stderr, 44 | "tree-sitter-doxygen: Invalid buffer length %d! This should " 45 | "never happen\n", 46 | length); 47 | abort(); 48 | } 49 | } 50 | 51 | bool tree_sitter_doxygen_external_scanner_scan(void *payload, TSLexer *lexer, const bool *valid_symbols) { 52 | Scanner *scanner = (Scanner *)payload; 53 | 54 | if (valid_symbols[BRIEF_TEXT] && !valid_symbols[CODE_BLOCK_LANGUAGE]) { 55 | uint32_t column_start = 0; 56 | bool advanced_once = false; 57 | 58 | while ((iswspace(lexer->lookahead) || lexer->lookahead == '*') && lexer->lookahead != '\n' && 59 | !lexer->eof(lexer)) { 60 | skip(lexer); 61 | } 62 | 63 | if (lexer->lookahead == '\n' || lexer->eof(lexer)) { 64 | return false; 65 | } 66 | 67 | column_start = lexer->get_column(lexer); 68 | 69 | content: 70 | while (lexer->lookahead != '\n' && !lexer->eof(lexer) && lexer->lookahead != '\\') { 71 | advanced_once = true; 72 | if (lexer->lookahead == '*') { 73 | lexer->mark_end(lexer); 74 | advance(lexer); 75 | if (lexer->lookahead == '/') { 76 | lexer->result_symbol = BRIEF_TEXT; 77 | return advanced_once; 78 | } 79 | } else { 80 | advance(lexer); 81 | } 82 | } 83 | 84 | if (lexer->eof(lexer)) { 85 | return false; 86 | } 87 | 88 | lexer->mark_end(lexer); 89 | advance(lexer); 90 | 91 | // go past space, / and * to check next text's column 92 | while (lexer->lookahead != '\n' && !lexer->eof(lexer) && 93 | (iswspace(lexer->lookahead) || lexer->lookahead == '/' || lexer->lookahead == '*')) { 94 | advance(lexer); 95 | } 96 | 97 | if (!lexer->eof(lexer) && lexer->get_column(lexer) == column_start) { 98 | goto content; 99 | } else if (advanced_once) { 100 | lexer->result_symbol = BRIEF_TEXT; 101 | return true; 102 | } 103 | 104 | return false; 105 | } 106 | 107 | if (valid_symbols[CODE_BLOCK_START]) { 108 | while (iswspace(lexer->lookahead) && !lexer->eof(lexer)) { 109 | skip(lexer); 110 | } 111 | 112 | if (lexer->eof(lexer)) { 113 | return false; 114 | } 115 | 116 | if (lexer->lookahead == '`') { 117 | scanner->codeblock_start_column = lexer->get_column(lexer); 118 | advance(lexer); 119 | scanner->codeblock_delimiter_length = 1; 120 | 121 | while (lexer->lookahead == '`') { 122 | advance(lexer); 123 | scanner->codeblock_delimiter_length++; 124 | } 125 | if (isalpha(lexer->lookahead)) { 126 | lexer->mark_end(lexer); 127 | lexer->result_symbol = CODE_BLOCK_START; 128 | return true; 129 | } 130 | } 131 | 132 | return false; 133 | } 134 | 135 | if (valid_symbols[CODE_BLOCK_LANGUAGE] && isalnum(lexer->lookahead)) { 136 | while (isalnum(lexer->lookahead)) { 137 | advance(lexer); 138 | } 139 | 140 | lexer->mark_end(lexer); 141 | 142 | while (iswspace(lexer->lookahead) && lexer->lookahead != '\n') { 143 | advance(lexer); 144 | } 145 | 146 | lexer->result_symbol = CODE_BLOCK_LANGUAGE; 147 | return lexer->lookahead == '\n' || lexer->lookahead == '}'; 148 | } 149 | 150 | if (valid_symbols[CODE_BLOCK_CONTENT]) { 151 | // optional language 152 | if (lexer->lookahead == '{') { 153 | return false; 154 | } 155 | 156 | // skip ws and newline before block 157 | while (iswspace(lexer->lookahead)) { 158 | skip(lexer); 159 | if (lexer->lookahead == '\n') { 160 | break; 161 | } 162 | } 163 | 164 | while (lexer->lookahead != '`' && lexer->lookahead != '@' && !lexer->eof(lexer)) { 165 | advance(lexer); 166 | } 167 | 168 | if (lexer->eof(lexer)) { 169 | return false; 170 | } 171 | 172 | if (lexer->lookahead == '`' && lexer->get_column(lexer) == scanner->codeblock_start_column) { 173 | lexer->mark_end(lexer); 174 | advance(lexer); 175 | uint32_t col_count = 1; 176 | 177 | while (lexer->lookahead == '`') { 178 | advance(lexer); 179 | col_count++; 180 | } 181 | 182 | if (col_count == scanner->codeblock_delimiter_length) { 183 | lexer->result_symbol = CODE_BLOCK_CONTENT; 184 | return true; 185 | } 186 | } 187 | 188 | if (lexer->lookahead == '@') { 189 | lexer->mark_end(lexer); 190 | advance(lexer); 191 | const char *const remainder = "endcode"; 192 | 193 | for (uint32_t i = 0; i < 7; i++) { 194 | if (lexer->lookahead != remainder[i]) { 195 | return false; 196 | } 197 | 198 | advance(lexer); 199 | } 200 | 201 | lexer->result_symbol = CODE_BLOCK_CONTENT; 202 | return true; 203 | } 204 | 205 | return false; 206 | } 207 | 208 | if (valid_symbols[CODE_BLOCK_END]) { 209 | if (lexer->lookahead == '`') { 210 | advance(lexer); 211 | uint32_t col_count = 1; 212 | 213 | while (lexer->lookahead == '`') { 214 | advance(lexer); 215 | col_count++; 216 | } 217 | 218 | if (col_count == scanner->codeblock_delimiter_length) { 219 | lexer->result_symbol = CODE_BLOCK_END; 220 | return true; 221 | } 222 | } 223 | 224 | return false; 225 | } 226 | 227 | return false; 228 | } 229 | 230 | void *tree_sitter_doxygen_external_scanner_create() { 231 | Scanner *scanner = (Scanner *)calloc(1, sizeof(Scanner)); 232 | return scanner; 233 | } 234 | 235 | void tree_sitter_doxygen_external_scanner_destroy(void *payload) { 236 | Scanner *scanner = (Scanner *)payload; 237 | free(scanner); 238 | } 239 | -------------------------------------------------------------------------------- /src/tree_sitter/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ALLOC_H_ 2 | #define TREE_SITTER_ALLOC_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // Allow clients to override allocation functions 13 | #ifdef TREE_SITTER_REUSE_ALLOCATOR 14 | 15 | extern void *(*ts_current_malloc)(size_t); 16 | extern void *(*ts_current_calloc)(size_t, size_t); 17 | extern void *(*ts_current_realloc)(void *, size_t); 18 | extern void (*ts_current_free)(void *); 19 | 20 | #ifndef ts_malloc 21 | #define ts_malloc ts_current_malloc 22 | #endif 23 | #ifndef ts_calloc 24 | #define ts_calloc ts_current_calloc 25 | #endif 26 | #ifndef ts_realloc 27 | #define ts_realloc ts_current_realloc 28 | #endif 29 | #ifndef ts_free 30 | #define ts_free ts_current_free 31 | #endif 32 | 33 | #else 34 | 35 | #ifndef ts_malloc 36 | #define ts_malloc malloc 37 | #endif 38 | #ifndef ts_calloc 39 | #define ts_calloc calloc 40 | #endif 41 | #ifndef ts_realloc 42 | #define ts_realloc realloc 43 | #endif 44 | #ifndef ts_free 45 | #define ts_free free 46 | #endif 47 | 48 | #endif 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif // TREE_SITTER_ALLOC_H_ 55 | -------------------------------------------------------------------------------- /src/tree_sitter/array.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ARRAY_H_ 2 | #define TREE_SITTER_ARRAY_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./alloc.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef _MSC_VER 17 | #pragma warning(disable : 4101) 18 | #elif defined(__GNUC__) || defined(__clang__) 19 | #pragma GCC diagnostic push 20 | #pragma GCC diagnostic ignored "-Wunused-variable" 21 | #endif 22 | 23 | #define Array(T) \ 24 | struct { \ 25 | T *contents; \ 26 | uint32_t size; \ 27 | uint32_t capacity; \ 28 | } 29 | 30 | /// Initialize an array. 31 | #define array_init(self) \ 32 | ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL) 33 | 34 | /// Create an empty array. 35 | #define array_new() \ 36 | { NULL, 0, 0 } 37 | 38 | /// Get a pointer to the element at a given `index` in the array. 39 | #define array_get(self, _index) \ 40 | (assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index]) 41 | 42 | /// Get a pointer to the first element in the array. 43 | #define array_front(self) array_get(self, 0) 44 | 45 | /// Get a pointer to the last element in the array. 46 | #define array_back(self) array_get(self, (self)->size - 1) 47 | 48 | /// Clear the array, setting its size to zero. Note that this does not free any 49 | /// memory allocated for the array's contents. 50 | #define array_clear(self) ((self)->size = 0) 51 | 52 | /// Reserve `new_capacity` elements of space in the array. If `new_capacity` is 53 | /// less than the array's current capacity, this function has no effect. 54 | #define array_reserve(self, new_capacity) \ 55 | _array__reserve((Array *)(self), array_elem_size(self), new_capacity) 56 | 57 | /// Free any memory allocated for this array. Note that this does not free any 58 | /// memory allocated for the array's contents. 59 | #define array_delete(self) _array__delete((Array *)(self)) 60 | 61 | /// Push a new `element` onto the end of the array. 62 | #define array_push(self, element) \ 63 | (_array__grow((Array *)(self), 1, array_elem_size(self)), \ 64 | (self)->contents[(self)->size++] = (element)) 65 | 66 | /// Increase the array's size by `count` elements. 67 | /// New elements are zero-initialized. 68 | #define array_grow_by(self, count) \ 69 | do { \ 70 | if ((count) == 0) break; \ 71 | _array__grow((Array *)(self), count, array_elem_size(self)); \ 72 | memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \ 73 | (self)->size += (count); \ 74 | } while (0) 75 | 76 | /// Append all elements from one array to the end of another. 77 | #define array_push_all(self, other) \ 78 | array_extend((self), (other)->size, (other)->contents) 79 | 80 | /// Append `count` elements to the end of the array, reading their values from the 81 | /// `contents` pointer. 82 | #define array_extend(self, count, contents) \ 83 | _array__splice( \ 84 | (Array *)(self), array_elem_size(self), (self)->size, \ 85 | 0, count, contents \ 86 | ) 87 | 88 | /// Remove `old_count` elements from the array starting at the given `index`. At 89 | /// the same index, insert `new_count` new elements, reading their values from the 90 | /// `new_contents` pointer. 91 | #define array_splice(self, _index, old_count, new_count, new_contents) \ 92 | _array__splice( \ 93 | (Array *)(self), array_elem_size(self), _index, \ 94 | old_count, new_count, new_contents \ 95 | ) 96 | 97 | /// Insert one `element` into the array at the given `index`. 98 | #define array_insert(self, _index, element) \ 99 | _array__splice((Array *)(self), array_elem_size(self), _index, 0, 1, &(element)) 100 | 101 | /// Remove one element from the array at the given `index`. 102 | #define array_erase(self, _index) \ 103 | _array__erase((Array *)(self), array_elem_size(self), _index) 104 | 105 | /// Pop the last element off the array, returning the element by value. 106 | #define array_pop(self) ((self)->contents[--(self)->size]) 107 | 108 | /// Assign the contents of one array to another, reallocating if necessary. 109 | #define array_assign(self, other) \ 110 | _array__assign((Array *)(self), (const Array *)(other), array_elem_size(self)) 111 | 112 | /// Swap one array with another 113 | #define array_swap(self, other) \ 114 | _array__swap((Array *)(self), (Array *)(other)) 115 | 116 | /// Get the size of the array contents 117 | #define array_elem_size(self) (sizeof *(self)->contents) 118 | 119 | /// Search a sorted array for a given `needle` value, using the given `compare` 120 | /// callback to determine the order. 121 | /// 122 | /// If an existing element is found to be equal to `needle`, then the `index` 123 | /// out-parameter is set to the existing value's index, and the `exists` 124 | /// out-parameter is set to true. Otherwise, `index` is set to an index where 125 | /// `needle` should be inserted in order to preserve the sorting, and `exists` 126 | /// is set to false. 127 | #define array_search_sorted_with(self, compare, needle, _index, _exists) \ 128 | _array__search_sorted(self, 0, compare, , needle, _index, _exists) 129 | 130 | /// Search a sorted array for a given `needle` value, using integer comparisons 131 | /// of a given struct field (specified with a leading dot) to determine the order. 132 | /// 133 | /// See also `array_search_sorted_with`. 134 | #define array_search_sorted_by(self, field, needle, _index, _exists) \ 135 | _array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists) 136 | 137 | /// Insert a given `value` into a sorted array, using the given `compare` 138 | /// callback to determine the order. 139 | #define array_insert_sorted_with(self, compare, value) \ 140 | do { \ 141 | unsigned _index, _exists; \ 142 | array_search_sorted_with(self, compare, &(value), &_index, &_exists); \ 143 | if (!_exists) array_insert(self, _index, value); \ 144 | } while (0) 145 | 146 | /// Insert a given `value` into a sorted array, using integer comparisons of 147 | /// a given struct field (specified with a leading dot) to determine the order. 148 | /// 149 | /// See also `array_search_sorted_by`. 150 | #define array_insert_sorted_by(self, field, value) \ 151 | do { \ 152 | unsigned _index, _exists; \ 153 | array_search_sorted_by(self, field, (value) field, &_index, &_exists); \ 154 | if (!_exists) array_insert(self, _index, value); \ 155 | } while (0) 156 | 157 | // Private 158 | 159 | typedef Array(void) Array; 160 | 161 | /// This is not what you're looking for, see `array_delete`. 162 | static inline void _array__delete(Array *self) { 163 | if (self->contents) { 164 | ts_free(self->contents); 165 | self->contents = NULL; 166 | self->size = 0; 167 | self->capacity = 0; 168 | } 169 | } 170 | 171 | /// This is not what you're looking for, see `array_erase`. 172 | static inline void _array__erase(Array *self, size_t element_size, 173 | uint32_t index) { 174 | assert(index < self->size); 175 | char *contents = (char *)self->contents; 176 | memmove(contents + index * element_size, contents + (index + 1) * element_size, 177 | (self->size - index - 1) * element_size); 178 | self->size--; 179 | } 180 | 181 | /// This is not what you're looking for, see `array_reserve`. 182 | static inline void _array__reserve(Array *self, size_t element_size, uint32_t new_capacity) { 183 | if (new_capacity > self->capacity) { 184 | if (self->contents) { 185 | self->contents = ts_realloc(self->contents, new_capacity * element_size); 186 | } else { 187 | self->contents = ts_malloc(new_capacity * element_size); 188 | } 189 | self->capacity = new_capacity; 190 | } 191 | } 192 | 193 | /// This is not what you're looking for, see `array_assign`. 194 | static inline void _array__assign(Array *self, const Array *other, size_t element_size) { 195 | _array__reserve(self, element_size, other->size); 196 | self->size = other->size; 197 | memcpy(self->contents, other->contents, self->size * element_size); 198 | } 199 | 200 | /// This is not what you're looking for, see `array_swap`. 201 | static inline void _array__swap(Array *self, Array *other) { 202 | Array swap = *other; 203 | *other = *self; 204 | *self = swap; 205 | } 206 | 207 | /// This is not what you're looking for, see `array_push` or `array_grow_by`. 208 | static inline void _array__grow(Array *self, uint32_t count, size_t element_size) { 209 | uint32_t new_size = self->size + count; 210 | if (new_size > self->capacity) { 211 | uint32_t new_capacity = self->capacity * 2; 212 | if (new_capacity < 8) new_capacity = 8; 213 | if (new_capacity < new_size) new_capacity = new_size; 214 | _array__reserve(self, element_size, new_capacity); 215 | } 216 | } 217 | 218 | /// This is not what you're looking for, see `array_splice`. 219 | static inline void _array__splice(Array *self, size_t element_size, 220 | uint32_t index, uint32_t old_count, 221 | uint32_t new_count, const void *elements) { 222 | uint32_t new_size = self->size + new_count - old_count; 223 | uint32_t old_end = index + old_count; 224 | uint32_t new_end = index + new_count; 225 | assert(old_end <= self->size); 226 | 227 | _array__reserve(self, element_size, new_size); 228 | 229 | char *contents = (char *)self->contents; 230 | if (self->size > old_end) { 231 | memmove( 232 | contents + new_end * element_size, 233 | contents + old_end * element_size, 234 | (self->size - old_end) * element_size 235 | ); 236 | } 237 | if (new_count > 0) { 238 | if (elements) { 239 | memcpy( 240 | (contents + index * element_size), 241 | elements, 242 | new_count * element_size 243 | ); 244 | } else { 245 | memset( 246 | (contents + index * element_size), 247 | 0, 248 | new_count * element_size 249 | ); 250 | } 251 | } 252 | self->size += new_count - old_count; 253 | } 254 | 255 | /// A binary search routine, based on Rust's `std::slice::binary_search_by`. 256 | /// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`. 257 | #define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \ 258 | do { \ 259 | *(_index) = start; \ 260 | *(_exists) = false; \ 261 | uint32_t size = (self)->size - *(_index); \ 262 | if (size == 0) break; \ 263 | int comparison; \ 264 | while (size > 1) { \ 265 | uint32_t half_size = size / 2; \ 266 | uint32_t mid_index = *(_index) + half_size; \ 267 | comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \ 268 | if (comparison <= 0) *(_index) = mid_index; \ 269 | size -= half_size; \ 270 | } \ 271 | comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \ 272 | if (comparison == 0) *(_exists) = true; \ 273 | else if (comparison < 0) *(_index) += 1; \ 274 | } while (0) 275 | 276 | /// Helper macro for the `_sorted_by` routines below. This takes the left (existing) 277 | /// parameter by reference in order to work with the generic sorting function above. 278 | #define _compare_int(a, b) ((int)*(a) - (int)(b)) 279 | 280 | #ifdef _MSC_VER 281 | #pragma warning(default : 4101) 282 | #elif defined(__GNUC__) || defined(__clang__) 283 | #pragma GCC diagnostic pop 284 | #endif 285 | 286 | #ifdef __cplusplus 287 | } 288 | #endif 289 | 290 | #endif // TREE_SITTER_ARRAY_H_ 291 | -------------------------------------------------------------------------------- /src/tree_sitter/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_PARSER_H_ 2 | #define TREE_SITTER_PARSER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define ts_builtin_sym_error ((TSSymbol)-1) 13 | #define ts_builtin_sym_end 0 14 | #define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 15 | 16 | #ifndef TREE_SITTER_API_H_ 17 | typedef uint16_t TSStateId; 18 | typedef uint16_t TSSymbol; 19 | typedef uint16_t TSFieldId; 20 | typedef struct TSLanguage TSLanguage; 21 | #endif 22 | 23 | typedef struct { 24 | TSFieldId field_id; 25 | uint8_t child_index; 26 | bool inherited; 27 | } TSFieldMapEntry; 28 | 29 | typedef struct { 30 | uint16_t index; 31 | uint16_t length; 32 | } TSFieldMapSlice; 33 | 34 | typedef struct { 35 | bool visible; 36 | bool named; 37 | bool supertype; 38 | } TSSymbolMetadata; 39 | 40 | typedef struct TSLexer TSLexer; 41 | 42 | struct TSLexer { 43 | int32_t lookahead; 44 | TSSymbol result_symbol; 45 | void (*advance)(TSLexer *, bool); 46 | void (*mark_end)(TSLexer *); 47 | uint32_t (*get_column)(TSLexer *); 48 | bool (*is_at_included_range_start)(const TSLexer *); 49 | bool (*eof)(const TSLexer *); 50 | }; 51 | 52 | typedef enum { 53 | TSParseActionTypeShift, 54 | TSParseActionTypeReduce, 55 | TSParseActionTypeAccept, 56 | TSParseActionTypeRecover, 57 | } TSParseActionType; 58 | 59 | typedef union { 60 | struct { 61 | uint8_t type; 62 | TSStateId state; 63 | bool extra; 64 | bool repetition; 65 | } shift; 66 | struct { 67 | uint8_t type; 68 | uint8_t child_count; 69 | TSSymbol symbol; 70 | int16_t dynamic_precedence; 71 | uint16_t production_id; 72 | } reduce; 73 | uint8_t type; 74 | } TSParseAction; 75 | 76 | typedef struct { 77 | uint16_t lex_state; 78 | uint16_t external_lex_state; 79 | } TSLexMode; 80 | 81 | typedef union { 82 | TSParseAction action; 83 | struct { 84 | uint8_t count; 85 | bool reusable; 86 | } entry; 87 | } TSParseActionEntry; 88 | 89 | typedef struct { 90 | int32_t start; 91 | int32_t end; 92 | } TSCharacterRange; 93 | 94 | struct TSLanguage { 95 | uint32_t version; 96 | uint32_t symbol_count; 97 | uint32_t alias_count; 98 | uint32_t token_count; 99 | uint32_t external_token_count; 100 | uint32_t state_count; 101 | uint32_t large_state_count; 102 | uint32_t production_id_count; 103 | uint32_t field_count; 104 | uint16_t max_alias_sequence_length; 105 | const uint16_t *parse_table; 106 | const uint16_t *small_parse_table; 107 | const uint32_t *small_parse_table_map; 108 | const TSParseActionEntry *parse_actions; 109 | const char * const *symbol_names; 110 | const char * const *field_names; 111 | const TSFieldMapSlice *field_map_slices; 112 | const TSFieldMapEntry *field_map_entries; 113 | const TSSymbolMetadata *symbol_metadata; 114 | const TSSymbol *public_symbol_map; 115 | const uint16_t *alias_map; 116 | const TSSymbol *alias_sequences; 117 | const TSLexMode *lex_modes; 118 | bool (*lex_fn)(TSLexer *, TSStateId); 119 | bool (*keyword_lex_fn)(TSLexer *, TSStateId); 120 | TSSymbol keyword_capture_token; 121 | struct { 122 | const bool *states; 123 | const TSSymbol *symbol_map; 124 | void *(*create)(void); 125 | void (*destroy)(void *); 126 | bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); 127 | unsigned (*serialize)(void *, char *); 128 | void (*deserialize)(void *, const char *, unsigned); 129 | } external_scanner; 130 | const TSStateId *primary_state_ids; 131 | }; 132 | 133 | static inline bool set_contains(TSCharacterRange *ranges, uint32_t len, int32_t lookahead) { 134 | uint32_t index = 0; 135 | uint32_t size = len - index; 136 | while (size > 1) { 137 | uint32_t half_size = size / 2; 138 | uint32_t mid_index = index + half_size; 139 | TSCharacterRange *range = &ranges[mid_index]; 140 | if (lookahead >= range->start && lookahead <= range->end) { 141 | return true; 142 | } else if (lookahead > range->end) { 143 | index = mid_index; 144 | } 145 | size -= half_size; 146 | } 147 | TSCharacterRange *range = &ranges[index]; 148 | return (lookahead >= range->start && lookahead <= range->end); 149 | } 150 | 151 | /* 152 | * Lexer Macros 153 | */ 154 | 155 | #ifdef _MSC_VER 156 | #define UNUSED __pragma(warning(suppress : 4101)) 157 | #else 158 | #define UNUSED __attribute__((unused)) 159 | #endif 160 | 161 | #define START_LEXER() \ 162 | bool result = false; \ 163 | bool skip = false; \ 164 | UNUSED \ 165 | bool eof = false; \ 166 | int32_t lookahead; \ 167 | goto start; \ 168 | next_state: \ 169 | lexer->advance(lexer, skip); \ 170 | start: \ 171 | skip = false; \ 172 | lookahead = lexer->lookahead; 173 | 174 | #define ADVANCE(state_value) \ 175 | { \ 176 | state = state_value; \ 177 | goto next_state; \ 178 | } 179 | 180 | #define ADVANCE_MAP(...) \ 181 | { \ 182 | static const uint16_t map[] = { __VA_ARGS__ }; \ 183 | for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \ 184 | if (map[i] == lookahead) { \ 185 | state = map[i + 1]; \ 186 | goto next_state; \ 187 | } \ 188 | } \ 189 | } 190 | 191 | #define SKIP(state_value) \ 192 | { \ 193 | skip = true; \ 194 | state = state_value; \ 195 | goto next_state; \ 196 | } 197 | 198 | #define ACCEPT_TOKEN(symbol_value) \ 199 | result = true; \ 200 | lexer->result_symbol = symbol_value; \ 201 | lexer->mark_end(lexer); 202 | 203 | #define END_STATE() return result; 204 | 205 | /* 206 | * Parse Table Macros 207 | */ 208 | 209 | #define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT) 210 | 211 | #define STATE(id) id 212 | 213 | #define ACTIONS(id) id 214 | 215 | #define SHIFT(state_value) \ 216 | {{ \ 217 | .shift = { \ 218 | .type = TSParseActionTypeShift, \ 219 | .state = (state_value) \ 220 | } \ 221 | }} 222 | 223 | #define SHIFT_REPEAT(state_value) \ 224 | {{ \ 225 | .shift = { \ 226 | .type = TSParseActionTypeShift, \ 227 | .state = (state_value), \ 228 | .repetition = true \ 229 | } \ 230 | }} 231 | 232 | #define SHIFT_EXTRA() \ 233 | {{ \ 234 | .shift = { \ 235 | .type = TSParseActionTypeShift, \ 236 | .extra = true \ 237 | } \ 238 | }} 239 | 240 | #define REDUCE(symbol_name, children, precedence, prod_id) \ 241 | {{ \ 242 | .reduce = { \ 243 | .type = TSParseActionTypeReduce, \ 244 | .symbol = symbol_name, \ 245 | .child_count = children, \ 246 | .dynamic_precedence = precedence, \ 247 | .production_id = prod_id \ 248 | }, \ 249 | }} 250 | 251 | #define RECOVER() \ 252 | {{ \ 253 | .type = TSParseActionTypeRecover \ 254 | }} 255 | 256 | #define ACCEPT_INPUT() \ 257 | {{ \ 258 | .type = TSParseActionTypeAccept \ 259 | }} 260 | 261 | #ifdef __cplusplus 262 | } 263 | #endif 264 | 265 | #endif // TREE_SITTER_PARSER_H_ 266 | -------------------------------------------------------------------------------- /test/corpus/single.txt: -------------------------------------------------------------------------------- 1 | ============================== 2 | Brief description 3 | ============================== 4 | 5 | //! \brief Brief description. 6 | //! Multiline brief not supported for single line comments. 7 | //! 8 | //! Detailed description not supported for single line comments. 9 | 10 | --- 11 | 12 | (document 13 | (brief_header 14 | (tag_name) 15 | (brief_description 16 | (brief_text))) 17 | (description)) 18 | 19 | ======================================== 20 | Brief description ending with dot 21 | ======================================== 22 | 23 | //! Brief description which ends at this dot. Details follow 24 | //! here. 25 | 26 | --- 27 | 28 | (document 29 | (brief_header 30 | (brief_description)) 31 | (description)) 32 | 33 | ======================================== 34 | Function docs with \ (param 1) 35 | ======================================== 36 | 37 | //! \param a an integer argument. 38 | 39 | --- 40 | 41 | (document 42 | (tag 43 | (tag_name) 44 | (identifier) 45 | (description))) 46 | 47 | ======================================== 48 | Function docs with \ (param 2) 49 | ======================================== 50 | 51 | //! \param s a constant character pointer. 52 | 53 | --- 54 | 55 | (document 56 | (tag 57 | (tag_name) 58 | (identifier) 59 | (description))) 60 | 61 | ======================================== 62 | Function docs with \ (return) 63 | ======================================== 64 | 65 | //! \return The test results 66 | 67 | --- 68 | 69 | (document 70 | (tag 71 | (tag_name) 72 | (description))) 73 | 74 | ======================================== 75 | Function docs with \ (function_link) 76 | ======================================== 77 | 78 | //! \sa QTstyle_Test(), ~QTstyle_Test(), testMeToo() and publicVar() 79 | 80 | --- 81 | 82 | (document 83 | (tag 84 | (tag_name) 85 | (function_link) 86 | (function_link) 87 | (function_link) 88 | (function_link))) 89 | 90 | ======================================== 91 | Function docs with @ (brief) 92 | ======================================== 93 | 94 | //! A pure virtual member. 95 | 96 | --- 97 | 98 | (document 99 | (brief_header 100 | (brief_description))) 101 | 102 | ======================================== 103 | Function docs with @ (see) 104 | ======================================== 105 | 106 | //! @see testMe() 107 | 108 | --- 109 | 110 | (document 111 | (tag 112 | (tag_name) 113 | (description 114 | (function_link)))) 115 | 116 | ======================================== 117 | Function docs with @ (param 1) 118 | ======================================== 119 | 120 | //! @param c1 the first argument. 121 | 122 | --- 123 | 124 | (document 125 | (tag 126 | (tag_name) 127 | (identifier) 128 | (description))) 129 | 130 | ======================================== 131 | Function docs with @ (param 2) 132 | ======================================== 133 | 134 | //! @param c2 the second argument. 135 | 136 | --- 137 | 138 | (document 139 | (tag 140 | (tag_name) 141 | (identifier) 142 | (description))) 143 | 144 | ======================================== 145 | Storageclass 146 | ======================================== 147 | 148 | //!< [in] docs for input parameter v. 149 | 150 | --- 151 | 152 | (document 153 | (tag 154 | (storageclass) 155 | (description))) 156 | 157 | ======================================= 158 | Param with Storageclass (out) 159 | ======================================= 160 | 161 | //! @param[out] dest The memory area to copy to. 162 | 163 | --- 164 | 165 | (document 166 | (tag 167 | (tag_name) 168 | (storageclass) 169 | (identifier) 170 | (description))) 171 | 172 | ======================================= 173 | Param with Storageclass (in) 174 | ======================================= 175 | 176 | //! @param[in] src The memory area to copy from. 177 | 178 | --- 179 | 180 | (document 181 | (tag 182 | (tag_name) 183 | (storageclass) 184 | (identifier) 185 | (description))) 186 | 187 | ======================================= 188 | Multiple Params 189 | ======================================= 190 | 191 | //! @param x,y,z Coordinates of the position in 3D space. 192 | 193 | --- 194 | 195 | (document 196 | (tag 197 | (tag_name) 198 | (identifier) 199 | (identifier) 200 | (identifier) 201 | (description))) 202 | 203 | ======================================= 204 | Function Links (foo) 205 | ======================================= 206 | 207 | //! @see foo() 208 | 209 | --- 210 | 211 | (document 212 | (tag 213 | (tag_name) 214 | (description 215 | (function_link)))) 216 | 217 | ======================================= 218 | Function Links(::bar) 219 | ======================================= 220 | 221 | //! @see ::bar 222 | 223 | --- 224 | 225 | (document 226 | (tag 227 | (tag_name) 228 | (description 229 | (function_link)))) 230 | 231 | ======================================= 232 | Function Links (bax::qux) 233 | ======================================= 234 | 235 | //! @see baz::qux(int quux) 236 | 237 | --- 238 | 239 | (document 240 | (tag 241 | (tag_name) 242 | (description 243 | (function_link)))) 244 | 245 | ======================================= 246 | URL Links 247 | ======================================= 248 | 249 | //! Example 250 | 251 | --- 252 | 253 | (document 254 | (description 255 | (link 256 | (text)))) 257 | 258 | ======================================= 259 | Param 260 | ======================================= 261 | 262 | //! \param foo foo 263 | 264 | --- 265 | 266 | (document (tag (tag_name) (identifier) (description))) 267 | 268 | ======================================= 269 | Code Word 270 | ======================================= 271 | 272 | //! It returns \c void 273 | 274 | --- 275 | 276 | (document 277 | (description 278 | (code_word 279 | (code)))) 280 | 281 | ======================================= 282 | Storageclass in brief 283 | ======================================= 284 | 285 | //! \brief blah blah blah in blah blah blah 286 | 287 | --- 288 | 289 | (document 290 | (brief_header 291 | (tag_name) 292 | (brief_description 293 | (brief_text)))) 294 | -------------------------------------------------------------------------------- /test/corpus/tests.txt: -------------------------------------------------------------------------------- 1 | ============================== 2 | Brief description 3 | ============================== 4 | 5 | /*! \brief Brief description. 6 | * Brief description continued. 7 | * 8 | * Detailed description starts here. 9 | */ 10 | 11 | --- 12 | 13 | (document 14 | (brief_header 15 | (tag_name) 16 | (brief_description 17 | (brief_text))) 18 | (description)) 19 | 20 | ======================================== 21 | Brief description ending with dot 22 | ======================================== 23 | 24 | /** Brief description which ends at this dot. Details follow 25 | * here. 26 | */ 27 | 28 | --- 29 | 30 | (document 31 | (brief_header 32 | (brief_description)) 33 | (description)) 34 | 35 | ======================================== 36 | Function docs with \ 37 | ======================================== 38 | 39 | /*! 40 | \param a an integer argument. 41 | \param s a constant character pointer. 42 | \return The test results 43 | \sa QTstyle_Test(), ~QTstyle_Test(), testMeToo() and publicVar() 44 | */ 45 | 46 | --- 47 | 48 | (document 49 | (tag 50 | (tag_name) 51 | (identifier) 52 | (description)) 53 | (tag 54 | (tag_name) 55 | (identifier) 56 | (description)) 57 | (tag 58 | (tag_name) 59 | (description)) 60 | (tag 61 | (tag_name) 62 | (function_link) 63 | (function_link) 64 | (function_link) 65 | (function_link))) 66 | 67 | ======================================== 68 | Function docs with @ 69 | ======================================== 70 | 71 | /** 72 | * A pure virtual member. 73 | * @see testMe() 74 | * @param c1 the first argument. 75 | * @param c2 the second argument. 76 | */ 77 | 78 | --- 79 | 80 | (document 81 | (brief_header 82 | (brief_description)) 83 | (tag 84 | (tag_name) 85 | (description 86 | (function_link))) 87 | (tag 88 | (tag_name) 89 | (identifier) 90 | (description)) 91 | (tag 92 | (tag_name) 93 | (identifier) 94 | (description))) 95 | 96 | ======================================== 97 | Storageclass 98 | ======================================== 99 | 100 | /**< [in] docs for input parameter v. */ 101 | 102 | --- 103 | 104 | (document 105 | (tag 106 | (storageclass) 107 | (description))) 108 | 109 | ======================================= 110 | Param with Storageclass 111 | ======================================= 112 | 113 | /*! 114 | Copies bytes from a source memory area to a destination memory area, 115 | where both areas may not overlap. 116 | @param[out] dest The memory area to copy to. 117 | @param[in] src The memory area to copy from. 118 | @param[in] n The number of bytes to copy 119 | */ 120 | 121 | --- 122 | 123 | (document 124 | (brief_header 125 | (brief_description)) 126 | (tag 127 | (tag_name) 128 | (storageclass) 129 | (identifier) 130 | (description)) 131 | (tag 132 | (tag_name) 133 | (storageclass) 134 | (identifier) 135 | (description)) 136 | (tag 137 | (tag_name) 138 | (storageclass) 139 | (identifier) 140 | (description))) 141 | 142 | ======================================= 143 | Multiple Params 144 | ======================================= 145 | 146 | /** Sets the position. 147 | @param x,y,z Coordinates of the position in 3D space. 148 | */ 149 | 150 | --- 151 | 152 | (document 153 | (brief_header 154 | (brief_description)) 155 | (tag 156 | (tag_name) 157 | (identifier) 158 | (identifier) 159 | (identifier) 160 | (description))) 161 | 162 | ======================================= 163 | Function Links 164 | ======================================= 165 | 166 | /*! Test 167 | * @see foo() 168 | * @see ::bar 169 | * @see baz::qux(int quux) 170 | */ 171 | 172 | --- 173 | 174 | (document 175 | (description) 176 | (tag 177 | (tag_name) 178 | (description 179 | (function_link))) 180 | (tag 181 | (tag_name) 182 | (description 183 | (function_link))) 184 | (tag 185 | (tag_name) 186 | (description 187 | (function_link)))) 188 | 189 | ======================================= 190 | URL Links 191 | ======================================= 192 | 193 | /*! Test 194 | * Example 195 | */ 196 | 197 | --- 198 | 199 | (document 200 | (description 201 | (link 202 | (text)))) 203 | 204 | ======================================= 205 | Param 206 | ======================================= 207 | 208 | /*! 209 | * \param foo foo 210 | */ 211 | 212 | --- 213 | 214 | (document (tag (tag_name) (identifier) (description))) 215 | 216 | ======================================= 217 | Code Blocks 218 | ======================================= 219 | 220 | /*! 221 | ```c 222 | int main() { 223 | return 0; 224 | } 225 | ``` 226 | 227 | @code{.cpp} 228 | int main() { 229 | return 0; 230 | } 231 | @endcode 232 | */ 233 | 234 | --- 235 | 236 | (document 237 | (code_block 238 | (code_block_start) 239 | (code_block_language) 240 | (code_block_content) 241 | (code_block_end)) 242 | (code_block 243 | (code_block_language) 244 | (code_block_content))) 245 | 246 | ======================================= 247 | Code Word 248 | ======================================= 249 | 250 | /*! 251 | * My function foo 252 | It returns \c void 253 | */ 254 | 255 | --- 256 | 257 | (document 258 | (description 259 | (code_word 260 | (code)))) 261 | --------------------------------------------------------------------------------