This is page 1 of 10. Use http://codebase.md/moisnx/arc?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .clang-format ├── .config │ └── arceditor │ ├── config.yaml │ ├── keybinds.conf │ └── themes │ ├── catppuccin-mocha.theme │ ├── cyberpunk-neon.theme │ ├── default.theme │ ├── dracula.theme │ ├── github_dark.theme │ ├── gruvbox_dark.theme │ ├── gruvbox_light.theme │ ├── high_constrast_dark.theme │ ├── monokai.theme │ ├── onedark.theme │ ├── solarized_dark.theme │ ├── solarized_light.theme │ ├── tokyo_night.theme │ └── vscode_light.theme ├── .github │ └── assets │ └── screenshot.gif ├── .gitignore ├── .gitmessage ├── .gitmodules ├── build.md ├── CMakeLists.txt ├── deps │ └── tree-sitter-markdown │ ├── .editorconfig │ ├── .gitattributes │ ├── .github │ │ ├── screenshot.png │ │ └── workflows │ │ ├── ci.yml │ │ ├── publish.yml │ │ └── release.yml │ ├── .gitignore │ ├── binding.gyp │ ├── bindings │ │ ├── go │ │ │ ├── binding_test.go │ │ │ ├── markdown_inline.go │ │ │ └── markdown.go │ │ ├── node │ │ │ ├── binding_test.js │ │ │ ├── binding.cc │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ └── inline.js │ │ ├── python │ │ │ ├── tests │ │ │ │ └── test_binding.py │ │ │ └── tree_sitter_markdown │ │ │ ├── __init__.py │ │ │ ├── __init__.pyi │ │ │ ├── binding.c │ │ │ └── py.typed │ │ ├── rust │ │ │ ├── benchmark.rs │ │ │ ├── build.rs │ │ │ ├── lib.rs │ │ │ └── parser.rs │ │ └── swift │ │ ├── .gitignore │ │ └── TreeSitterMarkdownTests │ │ └── TreeSitterMarkdownTests.swift │ ├── Cargo.toml │ ├── CMakeLists.txt │ ├── common │ │ ├── common.js │ │ ├── common.mak │ │ └── html_entities.json │ ├── CONTRIBUTING.md │ ├── go.mod │ ├── LICENSE │ ├── Makefile │ ├── package-lock.json │ ├── package.json │ ├── Package.resolved │ ├── Package.swift │ ├── pyproject.toml │ ├── README.md │ ├── scripts │ │ ├── build.js │ │ └── test.js │ ├── setup.py │ ├── tree-sitter-markdown │ │ ├── bindings │ │ │ ├── c │ │ │ │ ├── tree-sitter-markdown.h │ │ │ │ └── tree-sitter-markdown.pc.in │ │ │ └── swift │ │ │ └── TreeSitterMarkdown │ │ │ └── markdown.h │ │ ├── CMakeLists.txt │ │ ├── grammar.js │ │ ├── Makefile │ │ ├── package.json │ │ ├── queries │ │ │ ├── highlights.scm │ │ │ └── injections.scm │ │ ├── src │ │ │ ├── grammar.json │ │ │ ├── node-types.json │ │ │ ├── parser.c │ │ │ ├── scanner.c │ │ │ └── tree_sitter │ │ │ ├── alloc.h │ │ │ ├── array.h │ │ │ └── parser.h │ │ └── test │ │ └── corpus │ │ ├── extension_minus_metadata.txt │ │ ├── extension_pipe_table.txt │ │ ├── extension_plus_metadata.txt │ │ ├── extension_task_list.txt │ │ ├── failing.txt │ │ ├── issues.txt │ │ └── spec.txt │ ├── tree-sitter-markdown-inline │ │ ├── bindings │ │ │ ├── c │ │ │ │ ├── tree-sitter-markdown-inline.h │ │ │ │ └── tree-sitter-markdown-inline.pc.in │ │ │ └── swift │ │ │ └── TreeSitterMarkdownInline │ │ │ └── markdown_inline.h │ │ ├── CMakeLists.txt │ │ ├── grammar.js │ │ ├── Makefile │ │ ├── package.json │ │ ├── queries │ │ │ ├── highlights.scm │ │ │ └── injections.scm │ │ ├── src │ │ │ ├── grammar.json │ │ │ ├── node-types.json │ │ │ ├── parser.c │ │ │ ├── scanner.c │ │ │ └── tree_sitter │ │ │ ├── alloc.h │ │ │ ├── array.h │ │ │ └── parser.h │ │ └── test │ │ └── corpus │ │ ├── extension_latex.txt │ │ ├── extension_strikethrough.txt │ │ ├── extension_wikilink.txt │ │ ├── failing.txt │ │ ├── issues.txt │ │ ├── spec.txt │ │ └── tags.txt │ └── tree-sitter.json ├── LICENSE ├── Makefile ├── quickstart.md ├── README.md ├── src │ ├── core │ │ ├── buffer.cpp │ │ ├── buffer.h │ │ ├── config_manager.cpp │ │ ├── config_manager.h │ │ ├── editor_delta.h │ │ ├── editor_validation.h │ │ ├── editor.cpp │ │ └── editor.h │ ├── features │ │ ├── markdown_state.h │ │ ├── syntax_config_loader.cpp │ │ ├── syntax_config_loader.h │ │ ├── syntax_highlighter.cpp │ │ └── syntax_highlighter.h │ ├── main.cpp │ └── ui │ ├── input_handler.cpp │ ├── input_handler.h │ ├── renderer.cpp │ ├── renderer.h │ ├── style_manager.cpp │ └── style_manager.h └── treesitter ├── languages.yaml └── queries ├── _javascript │ ├── highlights.scm │ ├── locals.scm │ └── tags.scm ├── _jsx │ ├── highlights.scm │ ├── indents.scm │ └── textobjects.scm ├── _typescript │ ├── highlights.scm │ ├── indents.scm │ ├── locals.scm │ ├── tags.scm │ └── textobjects.scm ├── bash │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── rainbows.scm │ ├── tags.scm │ └── textobjects.scm ├── c │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── locals.scm │ ├── rainbows.scm │ ├── tags.scm │ └── textobjects.scm ├── cpp │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── rainbows.scm │ ├── tags.scm │ └── textobjects.scm ├── css │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ └── rainbows.scm ├── ecma │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── locals.scm │ ├── rainbows.scm │ ├── README.md │ └── textobjects.scm ├── go │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── locals.scm │ ├── rainbows.scm │ ├── tags.scm │ └── textobjects.scm ├── javascript │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── locals.scm │ ├── rainbows.scm │ ├── tags.scm │ └── textobjects.scm ├── markdown │ ├── highlights.scm │ ├── injections.scm │ └── tags.scm ├── markdown.inline │ ├── highlights.scm │ └── injections.scm ├── python │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── locals.scm │ ├── rainbows.scm │ ├── tags.scm │ └── textobjects.scm ├── rust │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── locals.scm │ ├── rainbows.scm │ ├── tags.scm │ └── textobjects.scm ├── toml │ ├── highlights.scm │ ├── injections.scm │ ├── rainbows.scm │ └── textobjects.scm ├── tsx │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── locals.scm │ ├── rainbows.scm │ ├── tags.scm │ └── textobjects.scm ├── typescript │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── locals.scm │ ├── rainbows.scm │ ├── tags.scm │ └── textobjects.scm ├── yaml │ ├── highlights.scm │ ├── indents.scm │ ├── injections.scm │ ├── rainbows.scm │ └── textobjects.scm └── zig ├── highlights.scm ├── indents.scm ├── injections.scm └── textobjects.scm ``` # Files -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/swift/.gitignore: -------------------------------------------------------------------------------- ``` 1 | TreeSitterMarkdown 2 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Rust artifacts 2 | Cargo.lock 3 | target/ 4 | 5 | # Node artifacts 6 | build/ 7 | prebuilds/ 8 | node_modules/ 9 | *.tgz 10 | 11 | # Swift artifacts 12 | .build/ 13 | 14 | # Go artifacts 15 | go.sum 16 | _obj/ 17 | 18 | # Python artifacts 19 | .venv/ 20 | dist/ 21 | *.egg-info 22 | *.whl 23 | 24 | # C artifacts 25 | *.a 26 | *.so 27 | *.so.* 28 | *.dylib 29 | *.dll 30 | *.pc 31 | 32 | # Example dirs 33 | /examples/*/ 34 | 35 | # Grammar volatiles 36 | *.wasm 37 | *.obj 38 | *.o 39 | 40 | # Moved bindings 41 | bindings/c/ 42 | bindings/swift/TreeSitterMarkdown/ 43 | bindings/swift/TreeSitterMarkdownInline/ 44 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/.editorconfig: -------------------------------------------------------------------------------- ``` 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [*.{json,toml,yml,gyp}] 7 | indent_style = space 8 | indent_size = 2 9 | 10 | [*.js] 11 | indent_style = space 12 | indent_size = 2 13 | quote_type = single 14 | 15 | [*.{c,cc,h}] 16 | indent_style = space 17 | indent_size = 4 18 | 19 | [*.rs] 20 | indent_style = space 21 | indent_size = 4 22 | 23 | [*.{py,pyi}] 24 | indent_style = space 25 | indent_size = 4 26 | 27 | [*.swift] 28 | indent_style = space 29 | indent_size = 4 30 | 31 | [*.go] 32 | indent_style = tab 33 | indent_size = 8 34 | 35 | [Makefile] 36 | indent_style = tab 37 | indent_size = 8 38 | 39 | [parser.c] 40 | indent_size = 2 41 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Build directories 2 | build/ 3 | Build/ 4 | BUILD/ 5 | out/ 6 | 7 | # CMake generated files 8 | CMakeFiles/ 9 | CMakeCache.txt 10 | cmake_install.cmake 11 | *.cmake 12 | !CMakeLists.txt 13 | 14 | # Compiled binaries and libraries 15 | *.exe 16 | *.out 17 | *.app 18 | *.dll 19 | *.so 20 | *.dylib 21 | *.a 22 | *.lib 23 | *.o 24 | *.obj 25 | 26 | # Debug files 27 | *.pdb 28 | *.ilk 29 | *.map 30 | *.exp 31 | 32 | # IDE and editor files 33 | .vscode/ 34 | .vs/ 35 | *.vcxproj* 36 | *.sln 37 | *.suo 38 | *.user 39 | *.userosscache 40 | *.sln.docstates 41 | 42 | # Language server cache 43 | .cache/ 44 | .clangd/ 45 | 46 | # OS generated files 47 | .DS_Store 48 | .DS_Store? 49 | ._* 50 | .Spotlight-V100 51 | .Trashes 52 | ehthumbs.db 53 | Thumbs.db 54 | *~ 55 | 56 | # Temporary files 57 | *.tmp 58 | *.temp 59 | *.log 60 | *.swp 61 | *.swo 62 | *~ 63 | 64 | # Package manager files (if using) 65 | vcpkg_installed/ 66 | conan.lock 67 | conanbuildinfo.* 68 | 69 | # Testing 70 | Testing/ 71 | tests/build/ 72 | 73 | # Documentation build 74 | docs/build/ 75 | doc/build/ 76 | 77 | # Dev files 78 | todo.md 79 | samples/ 80 | gitworklow.md 81 | debug.txt ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/.gitattributes: -------------------------------------------------------------------------------- ``` 1 | * text=auto eol=lf 2 | 3 | *.png -text 4 | 5 | # Generated source files 6 | */src/*.json linguist-generated 7 | */src/parser.c linguist-generated 8 | */src/tree_sitter/* linguist-generated 9 | 10 | # C bindings 11 | */bindings/c/* linguist-generated 12 | 13 | # Rust bindings 14 | bindings/rust/build.rs linguist-generated 15 | Cargo.lock linguist-generated 16 | 17 | # Node.js bindings 18 | bindings/node/* linguist-generated 19 | binding.gyp linguist-generated 20 | package.json linguist-generated 21 | package-lock.json linguist-generated 22 | 23 | # Python bindings 24 | bindings/python/** linguist-generated 25 | setup.py linguist-generated 26 | pyproject.toml linguist-generated 27 | 28 | # Go bindings 29 | bindings/go/* linguist-generated 30 | go.mod linguist-generated 31 | go.sum linguist-generated 32 | 33 | # Swift bindings 34 | /bindings/swift/** linguist-generated 35 | */bindings/swift/** linguist-generated 36 | Package.swift linguist-generated 37 | Package.resolved linguist-generated 38 | ``` -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- ``` 1 | [submodule "third_party/third_party/ftxui"] 2 | path = third_party/third_party/ftxui 3 | url = https://github.com/ArthurSonzogni/FTXUI.git 4 | [submodule "deps/tree-sitter-core"] 5 | path = deps/tree-sitter-core 6 | url = https://github.com/tree-sitter/tree-sitter.git 7 | [submodule "deps/tree-sitter-python"] 8 | path = deps/tree-sitter-python 9 | url = https://github.com/tree-sitter/tree-sitter-python.git 10 | [submodule "deps/tree-sitter-c"] 11 | path = deps/tree-sitter-c 12 | url = https://github.com/tree-sitter/tree-sitter-c.git 13 | [submodule "deps/tree-sitter-markdown"] 14 | path = deps/tree-sitter-markdown 15 | url = https://github.com/tree-sitter-grammars/tree-sitter-markdown.git 16 | [submodule "deps/tree-sitter-cpp"] 17 | path = deps/tree-sitter-cpp 18 | url = https://github.com/tree-sitter/tree-sitter-cpp 19 | [submodule "deps/tree-sitter-rust"] 20 | path = deps/tree-sitter-rust 21 | url = https://github.com/tree-sitter/tree-sitter-rust 22 | [submodule "deps/efsw"] 23 | path = deps/efsw 24 | url = https://github.com/SpartanJ/efsw 25 | [submodule "deps/tree-sitter-javascript"] 26 | path = deps/tree-sitter-javascript 27 | url = https://github.com/tree-sitter/tree-sitter-javascript 28 | [submodule "deps/tree-sitter-typescript"] 29 | path = deps/tree-sitter-typescript 30 | url = https://github.com/tree-sitter/tree-sitter-typescript 31 | [submodule "deps/tree-sitter-zig"] 32 | path = deps/tree-sitter-zig 33 | url = https://github.com/GrayJack/tree-sitter-zig 34 | [submodule "deps/tree-sitter-go"] 35 | path = deps/tree-sitter-go 36 | url = https://github.com/tree-sitter/tree-sitter-go 37 | [submodule "deps/PDCursesMod"] 38 | path = deps/PDCursesMod 39 | url = https://github.com/Bill-Gray/PDCursesMod 40 | ``` -------------------------------------------------------------------------------- /.gitmessage: -------------------------------------------------------------------------------- ``` 1 | ``` 2 | # <type>: <subject> (max 50 chars) 3 | 4 | # Body (wrap at 72 chars) 5 | 6 | # Footer (optional) 7 | 8 | # --- COMMIT END --- 9 | # Type can be: 10 | # feat : New feature 11 | # fix : Bug fix 12 | # refactor : Code change that neither fixes a bug nor adds a feature 13 | # style : Changes that don't affect code meaning (formatting, etc) 14 | # perf : Performance improvement 15 | # test : Adding or updating tests 16 | # docs : Documentation only changes 17 | # build : Changes to build system or dependencies 18 | # ci : Changes to CI configuration 19 | # chore : Other changes (updating grunt tasks, etc) 20 | # revert : Revert a previous commit 21 | # wip : Work in progress (squash these before merging!) 22 | # 23 | # Subject line: 24 | # - Use imperative mood ("add feature" not "added feature") 25 | # - Don't capitalize first letter 26 | # - No period at the end 27 | # 28 | # Body: 29 | # - Explain WHAT and WHY (not HOW) 30 | # - Separate from subject with blank line 31 | # - Wrap at 72 characters 32 | # 33 | # Footer: 34 | # - Reference issues: "Fixes #123" 35 | # - Breaking changes: "BREAKING CHANGE: description" 36 | ``` 37 | 38 | ## Example Commits 39 | 40 | ### Feature Addition 41 | 42 | ``` 43 | feat: add Windows MSVC build support 44 | 45 | - Fix CMake parser discovery for Windows drive letter paths 46 | - Add platform-specific curses header includes 47 | - Implement Windows-compatible setenv wrapper 48 | - Force static runtime linking across all libraries 49 | 50 | Tested on Windows 10 with MSVC 2019 and vcpkg static libraries. 51 | 52 | Fixes #42 53 | ``` 54 | 55 | ### Bug Fix 56 | 57 | ``` 58 | fix: resolve cursor positioning in PDCurses 59 | 60 | Update renderer to use PDCurses-specific refresh sequences. 61 | Prevents cursor from jumping during scroll operations on Windows. 62 | 63 | Fixes #123 64 | ``` 65 | 66 | ### Refactoring 67 | 68 | ``` 69 | refactor: simplify parser auto-discovery logic 70 | 71 | Replace colon-separated string parsing with CMake lists to avoid 72 | issues with Windows drive letters (C:, D:, etc). 73 | 74 | No functional changes. 75 | ``` 76 | 77 | ### Documentation 78 | 79 | ``` 80 | docs: add comprehensive build instructions 81 | 82 | Create BUILD.md with platform-specific setup guides for Linux, 83 | macOS, and Windows. Include troubleshooting section and vcpkg 84 | integration details. 85 | ``` 86 | 87 | ### Work in Progress (Squash Later!) 88 | 89 | ``` 90 | wip: experimenting with PDCurses rendering 91 | ``` 92 | 93 | ## Quick Conventional Commit Examples 94 | 95 | ```bash 96 | # New feature 97 | git commit -m "feat: add syntax highlighting for Rust" 98 | 99 | # Bug fix 100 | git commit -m "fix: prevent crash on empty file" 101 | 102 | # Documentation 103 | git commit -m "docs: update README with installation steps" 104 | 105 | # Refactoring 106 | git commit -m "refactor: extract rendering logic to separate class" 107 | 108 | # Performance 109 | git commit -m "perf: optimize buffer gap operations" 110 | 111 | # Build/tooling 112 | git commit -m "build: update CMake minimum version to 3.16" 113 | 114 | # Style (formatting, no code change) 115 | git commit -m "style: format code with clang-format" 116 | 117 | # Revert 118 | git commit -m "revert: undo experimental PDCurses changes" 119 | ``` 120 | 121 | ## Squashing WIP Commits 122 | 123 | ```bash 124 | # After many "wip:" commits, squash them: 125 | git reset --soft HEAD~5 # Last 5 commits 126 | git commit -m "feat: complete Windows build support 127 | 128 | - Fix all platform-specific compilation issues 129 | - Add static library linking 130 | - Implement compatibility wrappers 131 | 132 | Closes #42" 133 | ``` 134 | 135 | ## Multi-line Commits from CLI 136 | 137 | ```bash 138 | git commit -m "feat: add Tree-sitter integration" -m " 139 | - Implement language registry auto-discovery 140 | - Add syntax highlighting with Tree-sitter API 141 | - Support Python, C, C++, Rust, JavaScript, TypeScript 142 | 143 | This enables superior syntax highlighting compared to 144 | the regex-based fallback system. 145 | " 146 | ``` 147 | ``` -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- ``` 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignArrayOfStructures: None 7 | AlignConsecutiveAssignments: 8 | Enabled: false 9 | AcrossEmptyLines: false 10 | AcrossComments: false 11 | AlignCompound: false 12 | AlignFunctionPointers: false 13 | PadOperators: true 14 | AlignConsecutiveBitFields: 15 | Enabled: false 16 | AcrossEmptyLines: false 17 | AcrossComments: false 18 | AlignCompound: false 19 | AlignFunctionPointers: false 20 | PadOperators: false 21 | AlignConsecutiveDeclarations: 22 | Enabled: false 23 | AcrossEmptyLines: false 24 | AcrossComments: false 25 | AlignCompound: false 26 | AlignFunctionPointers: false 27 | PadOperators: false 28 | AlignConsecutiveMacros: 29 | Enabled: false 30 | AcrossEmptyLines: false 31 | AcrossComments: false 32 | AlignCompound: false 33 | AlignFunctionPointers: false 34 | PadOperators: false 35 | AlignConsecutiveShortCaseStatements: 36 | Enabled: false 37 | AcrossEmptyLines: false 38 | AcrossComments: false 39 | AlignCaseColons: false 40 | AlignEscapedNewlines: Right 41 | AlignOperands: Align 42 | AlignTrailingComments: 43 | Kind: Always 44 | OverEmptyLines: 0 45 | AllowAllArgumentsOnNextLine: true 46 | AllowAllParametersOfDeclarationOnNextLine: true 47 | AllowBreakBeforeNoexceptSpecifier: Never 48 | AllowShortBlocksOnASingleLine: Never 49 | AllowShortCaseLabelsOnASingleLine: false 50 | AllowShortCompoundRequirementOnASingleLine: true 51 | AllowShortEnumsOnASingleLine: true 52 | AllowShortFunctionsOnASingleLine: All 53 | AllowShortIfStatementsOnASingleLine: Never 54 | AllowShortLambdasOnASingleLine: All 55 | AllowShortLoopsOnASingleLine: false 56 | AlwaysBreakAfterDefinitionReturnType: None 57 | AlwaysBreakAfterReturnType: None 58 | AlwaysBreakBeforeMultilineStrings: false 59 | AlwaysBreakTemplateDeclarations: MultiLine 60 | AttributeMacros: 61 | - __capability 62 | BinPackArguments: true 63 | BinPackParameters: true 64 | BitFieldColonSpacing: Both 65 | BraceWrapping: 66 | AfterCaseLabel: false 67 | AfterClass: false 68 | AfterControlStatement: Never 69 | AfterEnum: false 70 | AfterExternBlock: false 71 | AfterFunction: false 72 | AfterNamespace: false 73 | AfterObjCDeclaration: false 74 | AfterStruct: false 75 | AfterUnion: false 76 | BeforeCatch: false 77 | BeforeElse: false 78 | BeforeLambdaBody: false 79 | BeforeWhile: false 80 | IndentBraces: false 81 | SplitEmptyFunction: true 82 | SplitEmptyRecord: true 83 | SplitEmptyNamespace: true 84 | BreakAdjacentStringLiterals: true 85 | BreakAfterAttributes: Leave 86 | BreakAfterJavaFieldAnnotations: false 87 | BreakArrays: true 88 | BreakBeforeBinaryOperators: None 89 | BreakBeforeConceptDeclarations: Always 90 | BreakBeforeBraces: Allman 91 | BreakBeforeInlineASMColon: OnlyMultiline 92 | BreakBeforeTernaryOperators: true 93 | BreakConstructorInitializers: BeforeColon 94 | BreakInheritanceList: BeforeColon 95 | BreakStringLiterals: true 96 | ColumnLimit: 80 97 | CommentPragmas: '^ IWYU pragma:' 98 | CompactNamespaces: false 99 | ConstructorInitializerIndentWidth: 4 100 | ContinuationIndentWidth: 4 101 | Cpp11BracedListStyle: true 102 | DerivePointerAlignment: false 103 | DisableFormat: false 104 | EmptyLineAfterAccessModifier: Never 105 | EmptyLineBeforeAccessModifier: LogicalBlock 106 | ExperimentalAutoDetectBinPacking: false 107 | FixNamespaceComments: true 108 | ForEachMacros: 109 | - foreach 110 | - Q_FOREACH 111 | - BOOST_FOREACH 112 | IfMacros: 113 | - KJ_IF_MAYBE 114 | IncludeBlocks: Preserve 115 | IncludeCategories: 116 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 117 | Priority: 2 118 | SortPriority: 0 119 | CaseSensitive: false 120 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 121 | Priority: 3 122 | SortPriority: 0 123 | CaseSensitive: false 124 | - Regex: '.*' 125 | Priority: 1 126 | SortPriority: 0 127 | CaseSensitive: false 128 | IncludeIsMainRegex: '(Test)?$' 129 | IncludeIsMainSourceRegex: '' 130 | IndentAccessModifiers: false 131 | IndentCaseBlocks: false 132 | IndentCaseLabels: false 133 | IndentExternBlock: AfterExternBlock 134 | IndentGotoLabels: true 135 | IndentPPDirectives: None 136 | IndentRequiresClause: true 137 | IndentWidth: 2 138 | IndentWrappedFunctionNames: false 139 | InsertBraces: false 140 | InsertNewlineAtEOF: false 141 | InsertTrailingCommas: None 142 | IntegerLiteralSeparator: 143 | Binary: 0 144 | BinaryMinDigits: 0 145 | Decimal: 0 146 | DecimalMinDigits: 0 147 | Hex: 0 148 | HexMinDigits: 0 149 | JavaScriptQuotes: Leave 150 | JavaScriptWrapImports: true 151 | KeepEmptyLinesAtTheStartOfBlocks: true 152 | KeepEmptyLinesAtEOF: false 153 | LambdaBodyIndentation: Signature 154 | LineEnding: DeriveLF 155 | MacroBlockBegin: '' 156 | MacroBlockEnd: '' 157 | MaxEmptyLinesToKeep: 1 158 | NamespaceIndentation: None 159 | ObjCBinPackProtocolList: Auto 160 | ObjCBlockIndentWidth: 2 161 | ObjCBreakBeforeNestedBlockParam: true 162 | ObjCSpaceAfterProperty: false 163 | ObjCSpaceBeforeProtocolList: true 164 | PackConstructorInitializers: BinPack 165 | PenaltyBreakAssignment: 2 166 | PenaltyBreakBeforeFirstCallParameter: 19 167 | PenaltyBreakComment: 300 168 | PenaltyBreakFirstLessLess: 120 169 | PenaltyBreakOpenParenthesis: 0 170 | PenaltyBreakScopeResolution: 500 171 | PenaltyBreakString: 1000 172 | PenaltyBreakTemplateDeclaration: 10 173 | PenaltyExcessCharacter: 1000000 174 | PenaltyIndentedWhitespace: 0 175 | PenaltyReturnTypeOnItsOwnLine: 60 176 | PointerAlignment: Right 177 | PPIndentWidth: -1 178 | QualifierAlignment: Leave 179 | ReferenceAlignment: Pointer 180 | ReflowComments: true 181 | RemoveBracesLLVM: false 182 | RemoveParentheses: Leave 183 | RemoveSemicolon: false 184 | RequiresClausePosition: OwnLine 185 | RequiresExpressionIndentation: OuterScope 186 | SeparateDefinitionBlocks: Leave 187 | ShortNamespaceLines: 1 188 | SkipMacroDefinitionBody: false 189 | SortIncludes: CaseSensitive 190 | SortJavaStaticImport: Before 191 | SortUsingDeclarations: LexicographicNumeric 192 | SpaceAfterCStyleCast: false 193 | SpaceAfterLogicalNot: false 194 | SpaceAfterTemplateKeyword: true 195 | SpaceAroundPointerQualifiers: Default 196 | SpaceBeforeAssignmentOperators: true 197 | SpaceBeforeCaseColon: false 198 | SpaceBeforeCpp11BracedList: false 199 | SpaceBeforeCtorInitializerColon: true 200 | SpaceBeforeInheritanceColon: true 201 | SpaceBeforeJsonColon: false 202 | SpaceBeforeParens: ControlStatements 203 | SpaceBeforeParensOptions: 204 | AfterControlStatements: true 205 | AfterForeachMacros: true 206 | AfterFunctionDefinitionName: false 207 | AfterFunctionDeclarationName: false 208 | AfterIfMacros: true 209 | AfterOverloadedOperator: false 210 | AfterPlacementOperator: true 211 | AfterRequiresInClause: false 212 | AfterRequiresInExpression: false 213 | BeforeNonEmptyParentheses: false 214 | SpaceBeforeRangeBasedForLoopColon: true 215 | SpaceBeforeSquareBrackets: false 216 | SpaceInEmptyBlock: false 217 | SpacesBeforeTrailingComments: 1 218 | SpacesInAngles: Never 219 | SpacesInContainerLiterals: true 220 | SpacesInLineCommentPrefix: 221 | Minimum: 1 222 | Maximum: -1 223 | SpacesInParens: Never 224 | SpacesInParensOptions: 225 | InCStyleCasts: false 226 | InConditionalStatements: false 227 | InEmptyParentheses: false 228 | Other: false 229 | SpacesInSquareBrackets: false 230 | Standard: Latest 231 | StatementAttributeLikeMacros: 232 | - Q_EMIT 233 | StatementMacros: 234 | - Q_UNUSED 235 | - QT_REQUIRE_VERSION 236 | TabWidth: 8 237 | UseTab: Never 238 | VerilogBreakBetweenInstancePorts: true 239 | WhitespaceSensitiveMacros: 240 | - BOOST_PP_STRINGIZE 241 | - CF_SWIFT_NAME 242 | - NS_SWIFT_NAME 243 | - PP_STRINGIZE 244 | - STRINGIZE 245 | ... 246 | 247 | ``` -------------------------------------------------------------------------------- /treesitter/queries/ecma/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Inheritance model for ecma-based languages 2 | 3 | Ecma-based languages share many traits. Because of this we want to share as many queries as possible while avoiding nested inheritance that can make query behaviour unpredictable due to unexpected precedence. 4 | 5 | To achieve that, there are "public" and "private" versions for javascript, jsx, and typescript query files, that share the same name, but the "private" version name starts with an underscore (with the exception of ecma, that already exists as a sort of base "private" language). This allows the "private" versions to host the specific queries of the language excluding any `; inherits` statement, in order to make them safe to be inherited by the "public" version of the same language and other languages as well. The tsx language doesn't have a "private" version given that currently it doesn't need to be inherited by other languages. 6 | 7 | | Language | Inherits from | 8 | | ---------- | ----------------------- | 9 | | javascript | _javascript, ecma | 10 | | jsx | _jsx, _javascript, ecma | 11 | | typescript | _typescript, ecma | 12 | | tsx | _jsx, _typescript, ecma | 13 | 14 | If you intend to add queries to any of the ecma-based languages above, make sure you add them to the correct private language they belong to, so that other languages down the line can benefit from them. 15 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # tree-sitter-markdown 2 | 3 | [![CI][ci]](https://github.com/tree-sitter-grammars/tree-sitter-markdown/actions) 4 | [![discord][discord]](https://discord.gg/w7nTvsVJhm) 5 | [![matrix][matrix]](https://matrix.to/#/#tree-sitter-chat:matrix.org) 6 | [![npm][npm]](https://www.npmjs.com/package/@tree-sitter-grammars/tree-sitter-markdown) 7 | [![crates][crates]](https://crates.io/crates/tree-sitter-md) 8 | [![pypi][pypi]](https://pypi.org/project/tree-sitter-markdown/) 9 | 10 | A Markdown parser for [tree-sitter]. 11 | 12 |  13 | 14 | The parser is designed to read markdown according to the [CommonMark Spec], 15 | but some extensions to the spec from different sources such as [Github flavored 16 | markdown] are also included. These can be toggled on or off at compile time. 17 | For specifics see [Extensions](#extensions) 18 | 19 | ## Goals 20 | 21 | Even though this parser has existed for some while and obvious issues are 22 | mostly solved, there are still lots of inaccuracies in the output. These stem 23 | from restricting a complex format such as markdown to the quite restricting 24 | tree-sitter parsing rules. 25 | 26 | As such it is not recommended to use this parser where correctness is 27 | important. The main goal for this parser is to provide syntactical information 28 | for syntax highlighting in parsers such as [neovim] and [helix]. 29 | 30 | ## Contributing 31 | 32 | All contributions are welcome. For details refer to [CONTRIBUTING.md]. 33 | 34 | ## Extensions 35 | 36 | Extensions can be enabled at compile time through environment variables. Some 37 | of them are on by default, these can be disabled with the environment variable 38 | `NO_DEFAULT_EXTENSIONS`. 39 | 40 | | Name | Environment variable | Specification | Default | Also enables | 41 | |:----:|:--------------------:|:-------------:|:-------:|:------------:| 42 | | Github flavored markdown | `EXTENSION_GFM` | [link](https://github.github.com/gfm/) | ✓ | Task lists, strikethrough, pipe tables | 43 | | Task lists | `EXTENSION_TASK_LIST` | [link](https://github.github.com/gfm/#task-list-items-extension-) | ✓ | | 44 | | Strikethrough | `EXTENSION_STRIKETHROUGH` | [link](https://github.github.com/gfm/#strikethrough-extension-) | ✓ | | 45 | | Pipe tables | `EXTENSION_PIPE_TABLE` | [link](https://github.github.com/gfm/#tables-extension-) | ✓ | | 46 | | YAML metadata | `EXTENSION_MINUS_METADATA` | [link](https://gohugo.io/content-management/front-matter/) | ✓ | | 47 | | TOML metadata | `EXTENSION_PLUS_METADATA` | [link](https://gohugo.io/content-management/front-matter/) | ✓ | | 48 | | Tags | `EXTENSION_TAGS` | [link](https://help.obsidian.md/Editing+and+formatting/Tags#Tag+format) | | | 49 | | Wiki Link | `EXTENSION_WIKI_LINK` | [link](https://help.obsidian.md/Linking+notes+and+files/Internal+links) | | | 50 | 51 | ## Usage in Editors 52 | 53 | For guides on how to use this parser in a specific editor, refer to that 54 | editor's specific documentation, e.g. 55 | * [neovim](https://github.com/nvim-treesitter/nvim-treesitter) 56 | * [helix](https://docs.helix-editor.com/guides/adding_languages.html) 57 | 58 | ## Standalone usage 59 | 60 | To use the two grammars, first parse the document with the block 61 | grammar. Then perform a second parse with the inline grammar using 62 | `ts_parser_set_included_ranges` to specify which parts are inline content. 63 | These parts are marked as `inline` nodes. Children of those inline nodes should 64 | be excluded from these ranges. For an example implementation see `lib.rs` in 65 | the `bindings` folder. 66 | 67 | ### Usage with WASM 68 | 69 | Unfortunately using this parser with WASM/web-tree-sitter does not work out of the box at the moment. This is because the parser uses some C functions that are not exported by tree-sitter by default. To fix this you can statically link the parser to tree-sitter. See also https://github.com/tree-sitter/tree-sitter/issues/949, https://github.com/MDeiml/tree-sitter-markdown/issues/126, and https://github.com/MDeiml/tree-sitter-markdown/issues/93 70 | 71 | [CommonMark Spec]: https://spec.commonmark.org/ 72 | [Github flavored markdown]: https://github.github.com/gfm/ 73 | [tree-sitter]: https://tree-sitter.github.io/tree-sitter/ 74 | [neovim]: https://neovim.io/ 75 | [helix]: https://helix-editor.com/ 76 | [CONTRIBUTING.md]: https://github.com/MDeiml/tree-sitter-markdown/blob/split_parser/CONTRIBUTING.md 77 | [ci]: https://img.shields.io/github/actions/workflow/status/tree-sitter-grammars/tree-sitter-markdown/ci.yml?logo=github&label=CI 78 | [discord]: https://img.shields.io/discord/1063097320771698699?logo=discord&label=discord 79 | [matrix]: https://img.shields.io/matrix/tree-sitter-chat%3Amatrix.org?logo=matrix&label=matrix 80 | [npm]: https://img.shields.io/npm/v/%40tree-sitter-grammars%2Ftree-sitter-markdown?logo=npm 81 | [crates]: https://img.shields.io/crates/v/tree-sitter-md?logo=rust 82 | [pypi]: https://img.shields.io/pypi/v/tree-sitter-markdown?logo=pypi&logoColor=ffd242 83 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | <!-- Banner / Logo --> 2 | 3 | <h1 align="center">Arc Editor</h1> 4 | <p align="center"><b>Simple. Modern. Efficient.</b></p> 5 | <p align="center"> 6 | <em>A modern terminal-based text editor with Tree-sitter powered syntax highlighting.</em> 7 | </p> 8 | 9 | <p align="center"> 10 | <a href="https://github.com/moisnx/arc/actions"><img src="https://img.shields.io/github/actions/workflow/status/moisnx/arc/ci.yml?branch=main&style=flat-square" alt="Build Status"></a> 11 | <a href="https://github.com/moisnx/arc/blob/main/LICENSE"><img src="https://img.shields.io/github/license/moisnx/arc?style=flat-square" alt="License"></a> 12 | <img src="https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-blue?style=flat-square" alt="Platforms"> 13 | <img src="https://img.shields.io/badge/editor%20type-terminal-informational?style=flat-square"> 14 | </p> 15 | 16 | --- 17 | 18 | > **Arc** combines the accessibility of nano with powerful features and a clean interface. 19 | 20 | --- 21 | 22 | ## ✨ Features 23 | 24 | - **Intuitive Interface**: Minimal, focused on your content with smart viewport management 25 | - **Advanced Syntax Highlighting**: Tree-sitter powered, accurate AST-based coloring 26 | - **Modern Editing**: Gap buffer for efficient text ops, unlimited undo/redo 27 | - **Smart Selection**: Visual selection with copy, cut, paste 28 | - **Live Configuration**: Hot-reload themes and settings 29 | - **Cross-Platform**: Windows (PDCurses), Linux, macOS (NCurses) 30 | - **Lightweight**: Fast startup, minimal resource usage 31 | - **Customizable**: Multiple color themes, configurable behavior 32 | 33 | --- 34 | 35 | ## 🖥️ Demo 36 | 37 | <p align="center"> 38 | <img src="https://raw.githubusercontent.com/moisnx/arc/master/.github/assets/screenshot.gif" alt="Arc Editor Demo" width="650"/> 39 | </p> 40 | 41 | --- 42 | 43 | ## 🚀 Getting Started 44 | 45 | ### Prerequisites 46 | 47 | - **All Platforms:** CMake 3.16+, C++20 compiler, Git (for submodules) 48 | - **Linux:** `build-essential cmake libncurses5-dev libyaml-cpp-dev` 49 | - **macOS:** `cmake ncurses yaml-cpp` (via Homebrew) 50 | - **Windows:** Visual Studio 2019+ with vcpkg 51 | 52 | ### Installation 53 | 54 | <details> 55 | <summary><b>Linux/Ubuntu</b></summary> 56 | 57 | ```bash 58 | sudo apt install build-essential cmake libncurses5-dev libyaml-cpp-dev 59 | git clone --recursive https://github.com/moisnx/arc.git 60 | cd arc 61 | mkdir build && cd build 62 | cmake .. -DCMAKE_BUILD_TYPE=Release 63 | make -j$(nproc) 64 | ./arc 65 | ``` 66 | 67 | </details> 68 | 69 | <details> 70 | <summary><b>macOS</b></summary> 71 | 72 | ```bash 73 | brew install cmake ncurses yaml-cpp 74 | git clone --recursive https://github.com/moisnx/arc.git 75 | cd arc 76 | mkdir build && cd build 77 | cmake .. -DCMAKE_BUILD_TYPE=Release 78 | make -j$(sysctl -n hw.ncpu) 79 | ./arc 80 | ``` 81 | 82 | </details> 83 | 84 | <details> 85 | <summary><b>Windows</b></summary> 86 | 87 | ```powershell 88 | git clone https://github.com/microsoft/vcpkg.git C:\tools\vcpkg 89 | cd C:\tools\vcpkg 90 | .\bootstrap-vcpkg.bat 91 | .\vcpkg integrate install 92 | .\vcpkg install pdcurses:x64-windows-static yaml-cpp:x64-windows-static 93 | 94 | git clone --recursive https://github.com/moisnx/arc.git 95 | cd arc 96 | mkdir build 97 | cd build 98 | cmake .. -DCMAKE_TOOLCHAIN_FILE=C:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ` 99 | -DVCPKG_TARGET_TRIPLET=x64-windows-static 100 | cmake --build . --config Release 101 | .\Release\arc.exe 102 | ``` 103 | 104 | </details> 105 | 106 | > **Note:** Forgot `--recursive`? Run `git submodule update --init --recursive` 107 | 108 | --- 109 | 110 | ## 📂 Supported Languages 111 | 112 | - **C/C++** 113 | - **Python** 114 | - **Rust** 115 | - **JavaScript/TypeScript** (incl. JSX/TSX) 116 | - **Markdown** (with inline code) 117 | - **Go** 118 | - **Zig** 119 | 120 | --- 121 | 122 | ## 🎮 Usage 123 | 124 | ```bash 125 | # Open a file 126 | arc filename.txt 127 | 128 | # Start with empty buffer 129 | arc 130 | 131 | # View help 132 | arc --help 133 | ``` 134 | 135 | ### Key Bindings 136 | 137 | <details> 138 | <summary><b>Show Key Bindings</b></summary> 139 | 140 | #### File Operations 141 | 142 | | Key | Action | 143 | | ------ | ---------------------------------- | 144 | | Ctrl+S | Save file | 145 | | Ctrl+Q | Quit (with unsaved changes prompt) | 146 | 147 | #### Editing 148 | 149 | | Key | Action | 150 | | --------- | ------------------------- | 151 | | Ctrl+Z | Undo | 152 | | Ctrl+Y | Redo | 153 | | Backspace | Delete char before cursor | 154 | | Delete | Delete char at cursor | 155 | | Enter | Insert new line | 156 | | Tab | Insert 4 spaces | 157 | 158 | #### Navigation 159 | 160 | | Key | Action | 161 | | ---------- | --------------- | 162 | | Arrow Keys | Move cursor | 163 | | Home/End | Line start/end | 164 | | PgUp/PgDn | Scroll viewport | 165 | 166 | #### Selection 167 | 168 | | Key | Action | 169 | | ------------ | -------------------- | 170 | | Shift+Arrows | Extend selection | 171 | | Ctrl+A | Select all | 172 | | Ctrl+C | Copy selection | 173 | | Ctrl+X | Cut selection | 174 | | Ctrl+V | Paste from clipboard | 175 | | Esc | Clear selection | 176 | 177 | </details> 178 | 179 | --- 180 | 181 | ## ⚙️ Configuration 182 | 183 | Arc auto-creates config files on first run: 184 | 185 | - Linux/macOS: `~/.config/arceditor/` 186 | - Windows: `%APPDATA%\arceditor\` 187 | 188 | #### Example: `config.yaml` 189 | 190 | ```yaml 191 | appearance: 192 | theme: default 193 | 194 | editor: 195 | tab_size: 4 196 | line_numbers: true 197 | cursor_style: auto 198 | 199 | syntax: 200 | highlighting: viewport 201 | ``` 202 | 203 | #### Themes 204 | 205 | - 14 built-in themes. 206 | - Add custom themes in `.config/arceditor/themes/`. 207 | - Supports hex, 256-color, and named colors. 208 | - **Hot-reload:** Changes are auto-reloaded. 209 | 210 | ```yaml 211 | # mytheme.theme 212 | name: "My Theme" 213 | background: "#1e1e2e" 214 | foreground: "#cdd6f4" 215 | keyword: "#cba6f7" 216 | string_literal: "#a6e3a1" 217 | comment: "#6c7086" 218 | # ... see existing themes for all options 219 | ``` 220 | 221 | --- 222 | 223 | ## 🛠️ Development 224 | 225 | <details> 226 | <summary><b>Project Structure</b></summary> 227 | 228 | ``` 229 | arc/ 230 | ├── src/ 231 | │ ├── core/ 232 | │ ├── features/ 233 | │ ├── ui/ 234 | │ └── main.cpp 235 | ├── deps/ # Git submodules: tree-sitter, efsw, etc. 236 | ├── treesitter/ 237 | │ ├── languages.yaml 238 | │ └── queries/ 239 | ├── .config/arceditor/ 240 | └── CMakeLists.txt 241 | ``` 242 | 243 | </details> 244 | 245 | See [build.md](build.md) for advanced setup and troubleshooting. 246 | 247 | --- 248 | 249 | ## 📈 Roadmap 250 | 251 | - [ ] Multiple file tabs/buffers 252 | - [ ] Advanced search/replace (regex) 253 | - [ ] Git integration (diff, blame) 254 | - [ ] LSP support 255 | - [ ] Plugin system 256 | - [ ] Mouse support enhancements 257 | - [ ] Split panes 258 | - [ ] Configurable keybindings 259 | 260 | --- 261 | 262 | ## 👥 Community & Contributing 263 | 264 | 💡 **Contributions welcome!** 265 | 266 | 1. Fork & create a feature branch 267 | 2. Follow [.gitmessage](.gitmessage) for commits 268 | 3. Run tests before submitting 269 | 4. Open a PR 270 | 271 | - **Issues:** [GitHub Issues](https://github.com/moisnx/arc/issues) 272 | - **Discussions:** [GitHub Discussions](https://github.com/moisnx/arc/discussions) 273 | - **Docs:** [build.md](build.md) | [quickstart.md](quickstart.md) 274 | 275 | --- 276 | 277 | ## 📝 License 278 | 279 | MIT License – see [LICENSE](LICENSE) for details. 280 | 281 | --- 282 | 283 | ## 🙏 Acknowledgments 284 | 285 | - [Tree-sitter](https://tree-sitter.github.io/) 286 | - [NCurses](https://invisible-island.net/ncurses/) 287 | - [PDCurses](https://github.com/Bill-Gray/PDCursesMod) 288 | - [EFSW](https://github.com/SpartanJ/efsw) 289 | - [yaml-cpp](https://github.com/jbeder/yaml-cpp) 290 | - Inspired by nano, micro, and helix editors 291 | 292 | --- 293 | 294 | <p align="center"><b>Arc Editor</b> – Simple. Modern. Efficient.</p> 295 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/CONTRIBUTING.md: -------------------------------------------------------------------------------- ```markdown 1 | # Contributing 2 | 3 | All contributions are welcome. Specifically, if you found a bug or have a 4 | suggestion for a new feature or markdown extension, you can always open an 5 | [issue] or [pull request]. 6 | 7 | ## Issues 8 | 9 | If you open an issue please give a short description of your bug or feature 10 | along with a code example. If there is a relevant spec like the [CommonMark 11 | Spec][commonmark] or the [GitHub Flavored Markdown Spec][gfm] please link it in 12 | the issue. 13 | 14 | Any feature suggestions are welcome. The grammar should by default only support 15 | very common syntax, but any extension can be implemented behind a compile time 16 | flag. (See below) 17 | 18 | Some bug reports belong in other repositories if they only concern the 19 | implementation of the grammar in a specific context like `nvim-treesitter`, but 20 | you can always open an issue here and I will point you in the right direction. 21 | 22 | ## Code Overview 23 | 24 | Please refer to the [tree-sitter spec] for more details on how to write a tree- 25 | sitter grammar. 26 | 27 | This parse is split into two grammars. One for block structure, which can be 28 | found in the `tree-sitter-markdown` folder, and one for inline structure, which 29 | can be found in the `tree-sitter-markdown-inline` folder. Components that are 30 | parts of either grammar can be found in the `common` folder. 31 | 32 | For either of the grammar the most important files are the `grammar.js` which 33 | defines most nodes and the `src/scanner.c` which defines nodes that cannot 34 | be parsed with normal tree-sitter rules. All other files in the `src` subfolder 35 | are auto-generated by running `tree-sitter generate`. (You need to install the 36 | [tree-sitter cli tool][tree-sitter-cli] and [Node.js][nodejs] first.) 37 | 38 | Some syntactical components can be enabled or disabled by environment variables 39 | at compile time. The logic for this can be found in the `common/grammar.js` 40 | file. 41 | 42 | Tests are located in the `test/corpus` subfolder: 43 | * `spec.txt` is taken from the examples in the [GFM spec][gfm]. 44 | * `failing.txt` are those examples from the spec that do not pass yet. 45 | * `issues.txt` are test cases covering solved issues. 46 | * `extension_<>.txt` are covering specific extensions. Some of these are also 47 | taken from the GFM spec. 48 | 49 | ## Pull Requests 50 | 51 | I will happily accept any pull requests. 52 | 53 | Before submitting any code please check the following: 54 | 55 | * You ran `tree-sitter generate` in the `tree-sitter-markdown` or 56 | `tree-sitter-markdown-inline` directories respectively after modifying any 57 | `grammar.js` file. 58 | * When running `tree-sitter test` only the cases defined in `failing.txt` or 59 | `extension_<>.txt` for not activated extensions fail for **both** grammars. 60 | * If you implemented new behavior please add tests. (In most cases these belong 61 | in a `extension_<>.txt`.) 62 | * You deleted any auto-generated bindings and files for debugging purposes 63 | like `log.html` 64 | 65 | ## Tests 66 | 67 | To run the tests, first install the development dependencies (see above), then 68 | execute: 69 | 70 | ```sh 71 | ALL_EXTENSIONS=1 node scripts/build.js 72 | node scripts/test.js 73 | ``` 74 | 75 | [issue]: https://github.com/tree-sitter-grammars/tree-sitter-markdown/issues/new 76 | [pull request]: https://github.com/tree-sitter-grammars/tree-sitter-markdown/compare 77 | [gfm]: https://github.github.com/gfm/ 78 | [commonmark]: https://spec.commonmark.org/ 79 | [tree-sitter spec]: https://tree-sitter.github.io/tree-sitter/ 80 | [tree-sitter-cli]: https://github.com/tree-sitter/tree-sitter/blob/master/cli/README.md 81 | [nodejs]: https://nodejs.org/ 82 | ``` -------------------------------------------------------------------------------- /.config/arceditor/keybinds.conf: -------------------------------------------------------------------------------- ``` 1 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/node/inline.js: -------------------------------------------------------------------------------- ```javascript 1 | module.exports = require(".").inline; 2 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/python/tree_sitter_markdown/__init__.py: -------------------------------------------------------------------------------- ```python 1 | "Markdown grammar for tree-sitter" 2 | 3 | from ._binding import language, inline_language 4 | 5 | __all__ = ["language", "inline_language"] 6 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "main": "../bindings/node/index.js", 3 | "private": true, 4 | "scripts": { 5 | "build": "tree-sitter generate", 6 | "test": "tree-sitter test" 7 | } 8 | } 9 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "main": "../bindings/node/inline.js", 3 | "private": true, 4 | "scripts": { 5 | "build": "tree-sitter generate", 6 | "test": "tree-sitter test" 7 | } 8 | } 9 | ``` -------------------------------------------------------------------------------- /.config/arceditor/config.yaml: -------------------------------------------------------------------------------- ```yaml 1 | # arceditor Configuration File 2 | # This file is automatically generated 3 | appearance: 4 | theme: default 5 | editor: 6 | tab_size: 4 7 | line_numbers: true 8 | cursor_style: auto 9 | syntax: 10 | highlighting: full 11 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/bindings/c/tree-sitter-markdown.h: -------------------------------------------------------------------------------- ``` 1 | #ifndef TREE_SITTER_MARKDOWN_H_ 2 | #define TREE_SITTER_MARKDOWN_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | const TSLanguage *tree_sitter_markdown(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_MARKDOWN_H_ 17 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/bindings/swift/TreeSitterMarkdown/markdown.h: -------------------------------------------------------------------------------- ``` 1 | #ifndef TREE_SITTER_MARKDOWN_H_ 2 | #define TREE_SITTER_MARKDOWN_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | const TSLanguage *tree_sitter_markdown(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_MARKDOWN_H_ 17 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/bindings/c/tree-sitter-markdown-inline.h: -------------------------------------------------------------------------------- ``` 1 | #ifndef TREE_SITTER_MARKDOWN_INLINE_H_ 2 | #define TREE_SITTER_MARKDOWN_INLINE_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | const TSLanguage *tree_sitter_markdown_inline(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_MARKDOWN_INLINE_H_ 17 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/bindings/swift/TreeSitterMarkdownInline/markdown_inline.h: -------------------------------------------------------------------------------- ``` 1 | #ifndef TREE_SITTER_MARKDOWN_INLINE_H_ 2 | #define TREE_SITTER_MARKDOWN_INLINE_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | const TSLanguage *tree_sitter_markdown_inline(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_MARKDOWN_INLINE_H_ 17 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/scripts/build.js: -------------------------------------------------------------------------------- ```javascript 1 | #!/usr/bin/env node 2 | 3 | const { execSync } = require("child_process"); 4 | const { join } = require("path"); 5 | 6 | for (const dir of ["tree-sitter-markdown", "tree-sitter-markdown-inline"]) { 7 | console.log(`building ${dir}`); 8 | execSync("tree-sitter generate", { 9 | stdio: "inherit", 10 | cwd: join(__dirname, "..", dir) 11 | }); 12 | } 13 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/node/index.js: -------------------------------------------------------------------------------- ```javascript 1 | const root = require("path").join(__dirname, "..", ".."); 2 | 3 | module.exports = require("node-gyp-build")(root); 4 | 5 | try { 6 | module.exports.nodeTypeInfo = require("../../tree-sitter-markdown/src/node-types.json"); 7 | module.exports.inline.nodeTypeInfo = require("../../tree-sitter-markdown-inline/src/node-types.json"); 8 | } catch (_) { } 9 | ``` -------------------------------------------------------------------------------- /src/features/markdown_state.h: -------------------------------------------------------------------------------- ``` 1 | // src/features/markdown_state.h 2 | 3 | #pragma once 4 | 5 | enum class MarkdownState 6 | { 7 | DEFAULT = 0, // Not inside any block 8 | IN_FENCED_CODE_BLOCK, // Inside ```...``` 9 | IN_HTML_COMMENT, // Inside IN_BLOCKQUOTE, // Inside a > block 10 | // Add other multi-line states here (e.g., IN_MULTI_LINE_LIST) 11 | IN_BLOCKQUOTE 12 | }; ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/CMakeLists.txt: -------------------------------------------------------------------------------- ``` 1 | set(PROJECT_DESCRIPTION "Markdown inline grammar for tree-sitter") 2 | 3 | add_parser(markdown-inline) 4 | 5 | add_custom_target(test-inline "${TREE_SITTER_CLI}" test 6 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 7 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" 8 | COMMENT "tree-sitter test") 9 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/.github/workflows/release.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: Create release 2 | 3 | on: 4 | push: 5 | tags: ["*"] 6 | workflow_dispatch: 7 | 8 | concurrency: 9 | group: ${{github.workflow}}-${{github.ref}} 10 | cancel-in-progress: true 11 | 12 | permissions: 13 | contents: write 14 | id-token: write 15 | attestations: write 16 | 17 | jobs: 18 | release: 19 | uses: tree-sitter/workflows/.github/workflows/release.yml@main 20 | with: 21 | attestations: true 22 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/CMakeLists.txt: -------------------------------------------------------------------------------- ``` 1 | set(PROJECT_DESCRIPTION "Markdown block grammar for tree-sitter") 2 | set(TS_REQUIRES tree-sitter-markdown-inline) 3 | 4 | add_parser(markdown) 5 | 6 | add_custom_target(test "${TREE_SITTER_CLI}" test 7 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 8 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" 9 | COMMENT "tree-sitter test") 10 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/go/markdown.go: -------------------------------------------------------------------------------- ```go 1 | package tree_sitter_markdown 2 | 3 | // #cgo CPPFLAGS: -I../../tree-sitter-markdown 4 | // #cgo CFLAGS: -std=c11 -fPIC 5 | // #include "../../tree-sitter-markdown/src/parser.c" 6 | // #include "../../tree-sitter-markdown/src/scanner.c" 7 | import "C" 8 | 9 | import "unsafe" 10 | 11 | // Get the tree-sitter Language for the block grammar. 12 | func Language() unsafe.Pointer { 13 | return unsafe.Pointer(C.tree_sitter_markdown()) 14 | } 15 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/go/markdown_inline.go: -------------------------------------------------------------------------------- ```go 1 | package tree_sitter_markdown 2 | 3 | // #cgo CPPFLAGS: -I../../tree-sitter-markdown-inline 4 | // #cgo CFLAGS: -std=c11 -fPIC 5 | // #include "../../tree-sitter-markdown-inline/src/parser.c" 6 | // #include "../../tree-sitter-markdown-inline/src/scanner.c" 7 | import "C" 8 | 9 | import "unsafe" 10 | 11 | // Get the tree-sitter Language for the inline grammar. 12 | func InlineLanguage() unsafe.Pointer { 13 | return unsafe.Pointer(C.tree_sitter_markdown_inline()) 14 | } 15 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/scripts/test.js: -------------------------------------------------------------------------------- ```javascript 1 | #!/usr/bin/env node 2 | 3 | const { execSync } = require("child_process"); 4 | const { join } = require("path"); 5 | 6 | const parsers = ["tree-sitter-markdown", "tree-sitter-markdown-inline"]; 7 | 8 | for (const dir of parsers) { 9 | console.log(`testing ${dir}`); 10 | try { 11 | execSync("tree-sitter test", { 12 | stdio: "inherit", 13 | cwd: join(__dirname, "..", dir) 14 | }); 15 | } catch(error) { 16 | process.exitCode |= parsers.indexOf(dir) + 1; 17 | } 18 | } 19 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/node/binding_test.js: -------------------------------------------------------------------------------- ```javascript 1 | /// <reference types="node" /> 2 | 3 | const assert = require("node:assert"); 4 | const { test } = require("node:test"); 5 | 6 | const Parser = require("tree-sitter"); 7 | 8 | test("can load block grammar", () => { 9 | const parser = new Parser(); 10 | assert.doesNotThrow(() => parser.setLanguage(require("."))); 11 | }); 12 | 13 | test("can load inline grammar", () => { 14 | const parser = new Parser(); 15 | assert.doesNotThrow(() => parser.setLanguage(require(".").inline)); 16 | }); 17 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/node/index.d.ts: -------------------------------------------------------------------------------- ```typescript 1 | type BaseNode = { 2 | type: string; 3 | named: boolean; 4 | }; 5 | 6 | type ChildNode = { 7 | multiple: boolean; 8 | required: boolean; 9 | types: BaseNode[]; 10 | }; 11 | 12 | type NodeInfo = 13 | | (BaseNode & { 14 | subtypes: BaseNode[]; 15 | }) 16 | | (BaseNode & { 17 | fields: { [name: string]: ChildNode }; 18 | children: ChildNode[]; 19 | }); 20 | 21 | type Language = { 22 | name: string; 23 | language: unknown; 24 | nodeTypeInfo: NodeInfo[]; 25 | }; 26 | 27 | declare const _exports: Language & { 28 | inline: Language 29 | }; 30 | export = _exports; 31 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/test/corpus/failing.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | Example 107 - https://github.github.com/gfm/#example-107 3 | :skip 4 | ================================================================================ 5 | ``` 6 | aaa 7 | ``` 8 | 9 | -------------------------------------------------------------------------------- 10 | 11 | (document 12 | (section 13 | (fenced_code_block 14 | (fenced_code_block_delimiter) 15 | (block_continuation) 16 | (code_fence_content 17 | (block_continuation))))) 18 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/test/corpus/extension_plus_metadata.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | EXTENSION_PLUS_METADATA 3 | ================================================================================ 4 | +++ 5 | title: 'This is the title: it contains a colon' 6 | author: 7 | - Author One 8 | - Author Two 9 | keywords: [nothing, nothingness] 10 | abstract: | 11 | This is the abstract. 12 | 13 | It consists of two paragraphs. 14 | +++ 15 | 16 | -------------------------------------------------------------------------------- 17 | 18 | (document 19 | (plus_metadata)) 20 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/python/tests/test_binding.py: -------------------------------------------------------------------------------- ```python 1 | from unittest import TestCase 2 | 3 | import tree_sitter, tree_sitter_markdown 4 | 5 | 6 | class TestLanguage(TestCase): 7 | def test_can_load_block_grammar(self): 8 | try: 9 | tree_sitter.Language(tree_sitter_markdown.language()) 10 | except Exception: 11 | self.fail("Error loading Markdown block grammar") 12 | 13 | def test_can_load_block_grammar(self): 14 | try: 15 | tree_sitter.Language(tree_sitter_markdown.inline_language()) 16 | except Exception: 17 | self.fail("Error loading Markdown inline grammar") 18 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/test/corpus/extension_minus_metadata.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | EXTENSION_MINUS_METADATA - https://pandoc.org/MANUAL.html#extension-yaml_metadata_block 3 | ================================================================================ 4 | --- 5 | title: 'This is the title: it contains a colon' 6 | author: 7 | - Author One 8 | - Author Two 9 | keywords: [nothing, nothingness] 10 | abstract: | 11 | This is the abstract. 12 | 13 | It consists of two paragraphs. 14 | --- 15 | 16 | -------------------------------------------------------------------------------- 17 | 18 | (document 19 | (minus_metadata)) 20 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/go/binding_test.go: -------------------------------------------------------------------------------- ```go 1 | package tree_sitter_markdown_test 2 | 3 | import ( 4 | "testing" 5 | 6 | tree_sitter "github.com/tree-sitter/go-tree-sitter" 7 | tree_sitter_markdown "github.com/tree-sitter-grammars/tree-sitter-markdown/bindings/go" 8 | ) 9 | 10 | func TestCanLoadBlockGrammar(t *testing.T) { 11 | language := tree_sitter.NewLanguage(tree_sitter_markdown.Language()) 12 | if language == nil { 13 | t.Errorf("Error loading Markdown block grammar") 14 | } 15 | } 16 | 17 | func TestCanLoadInlineGrammar(t *testing.T) { 18 | language := tree_sitter.NewLanguage(tree_sitter_markdown.InlineLanguage()) 19 | if language == nil { 20 | t.Errorf("Error loading Markdown inline grammar") 21 | } 22 | } 23 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/rust/build.rs: -------------------------------------------------------------------------------- ```rust 1 | fn main() { 2 | let block_dir = std::path::Path::new("tree-sitter-markdown").join("src"); 3 | let inline_dir = std::path::Path::new("tree-sitter-markdown-inline").join("src"); 4 | 5 | let mut c_config = cc::Build::new(); 6 | c_config.std("c11").include(&block_dir); 7 | 8 | #[cfg(target_env = "msvc")] 9 | c_config.flag("-utf-8"); 10 | 11 | for path in &[ 12 | block_dir.join("parser.c"), 13 | block_dir.join("scanner.c"), 14 | inline_dir.join("parser.c"), 15 | inline_dir.join("scanner.c"), 16 | ] { 17 | c_config.file(path); 18 | println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); 19 | } 20 | 21 | c_config.compile("tree-sitter-markdown"); 22 | } 23 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/test/corpus/tags.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | Tags are working 3 | ================================================================================ 4 | #tag #_tag_with-symbols0123and/numbers #0123tag_starting_with_numbers 5 | 6 | -------------------------------------------------------------------------------- 7 | 8 | (inline 9 | (tag) 10 | (tag) 11 | (tag)) 12 | 13 | ================================================================================ 14 | Purely numeric tags are forbidden 15 | ================================================================================ 16 | #0123 17 | 18 | -------------------------------------------------------------------------------- 19 | 20 | (inline) 21 | 22 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/.github/workflows/publish.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: Publish package 2 | 3 | on: 4 | push: 5 | tags: ["*"] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | npm: 10 | uses: tree-sitter/workflows/.github/workflows/package-npm.yml@main 11 | with: 12 | package-name: "@tree-sitter-grammars/tree-sitter-markdown" 13 | secrets: 14 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 15 | crates: 16 | uses: tree-sitter/workflows/.github/workflows/package-crates.yml@main 17 | with: 18 | package-name: tree-sitter-md 19 | secrets: 20 | CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_TOKEN}} 21 | pypi: 22 | uses: tree-sitter/workflows/.github/workflows/package-pypi.yml@main 23 | with: 24 | package-name: tree-sitter-markdown 25 | secrets: 26 | PYPI_API_TOKEN: ${{secrets.PYPI_TOKEN}} 27 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/swift/TreeSitterMarkdownTests/TreeSitterMarkdownTests.swift: -------------------------------------------------------------------------------- ```swift 1 | import XCTest 2 | import SwiftTreeSitter 3 | import TreeSitterMarkdown 4 | import TreeSitterMarkdownInline 5 | 6 | final class TreeSitterXMLTests: XCTestCase { 7 | func testCanLoadBlockGrammar() throws { 8 | let parser = Parser() 9 | let language = Language(language: tree_sitter_markdown()) 10 | XCTAssertNoThrow(try parser.setLanguage(language), 11 | "Error loading Markdown block grammar") 12 | } 13 | 14 | func testCanLoadInlineGrammar() throws { 15 | let parser = Parser() 16 | let language = Language(language: tree_sitter_markdown_inline()) 17 | XCTAssertNoThrow(try parser.setLanguage(language), 18 | "Error loading Markdown inline grammar") 19 | } 20 | } 21 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/pyproject.toml: -------------------------------------------------------------------------------- ```toml 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "tree-sitter-markdown" 7 | description = "Markdown grammar for tree-sitter" 8 | version = "0.5.1" 9 | keywords = ["incremental", "parsing", "tree-sitter", "markdown"] 10 | classifiers = [ 11 | "Intended Audience :: Developers", 12 | "License :: OSI Approved :: MIT License", 13 | "Topic :: Software Development :: Compilers", 14 | "Topic :: Text Processing :: Linguistic", 15 | "Typing :: Typed" 16 | ] 17 | authors = [ 18 | {name = "MDeiml"} 19 | ] 20 | requires-python = ">=3.9" 21 | license.text = "MIT" 22 | readme = "README.md" 23 | 24 | [project.urls] 25 | Homepage = "https://github.com/tree-sitter-grammars/tree-sitter-markdown" 26 | 27 | [project.optional-dependencies] 28 | core = ["tree-sitter~=0.23"] 29 | 30 | [tool.cibuildwheel] 31 | build = "cp39-*" 32 | build-frontend = "build" 33 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/rust/benchmark.rs: -------------------------------------------------------------------------------- ```rust 1 | use std::env::args; 2 | 3 | use tree_sitter::{InputEdit, Point}; 4 | use tree_sitter_md::{MarkdownParser, MarkdownTree}; 5 | 6 | fn main() { 7 | let filename = args() 8 | .nth_back(1) 9 | .take_if(|f| f != "benchmark") 10 | .unwrap_or("README.md".to_string()); 11 | let source = std::fs::read(filename).unwrap(); 12 | 13 | let mut parser = MarkdownParser::default(); 14 | let mut tree = parser.parse(&source, None).unwrap(); 15 | tree.edit(&InputEdit { 16 | start_byte: 0, 17 | old_end_byte: 1, 18 | new_end_byte: 0, 19 | start_position: Point::new(0, 0), 20 | old_end_position: Point::new(0, 1), 21 | new_end_position: Point::new(0, 0), 22 | }); 23 | reparse(&mut parser, &source[1..], tree); 24 | } 25 | 26 | fn reparse(parser: &mut MarkdownParser, source: &[u8], old_tree: MarkdownTree) { 27 | parser.parse(source, Some(&old_tree)).unwrap(); 28 | } 29 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/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 <stdbool.h> 9 | #include <stdio.h> 10 | #include <stdlib.h> 11 | 12 | // Allow clients to override allocation functions 13 | #ifdef TREE_SITTER_REUSE_ALLOCATOR 14 | 15 | extern void *(*ts_current_malloc)(size_t size); 16 | extern void *(*ts_current_calloc)(size_t count, size_t size); 17 | extern void *(*ts_current_realloc)(void *ptr, size_t size); 18 | extern void (*ts_current_free)(void *ptr); 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 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/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 <stdbool.h> 9 | #include <stdio.h> 10 | #include <stdlib.h> 11 | 12 | // Allow clients to override allocation functions 13 | #ifdef TREE_SITTER_REUSE_ALLOCATOR 14 | 15 | extern void *(*ts_current_malloc)(size_t size); 16 | extern void *(*ts_current_calloc)(size_t count, size_t size); 17 | extern void *(*ts_current_realloc)(void *ptr, size_t size); 18 | extern void (*ts_current_free)(void *ptr); 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 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/node/binding.cc: -------------------------------------------------------------------------------- ```cpp 1 | #include <napi.h> 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | extern "C" TSLanguage * tree_sitter_markdown(); 6 | extern "C" TSLanguage * tree_sitter_markdown_inline(); 7 | 8 | // "tree-sitter", "language" hashed with BLAKE2 9 | const napi_type_tag LANGUAGE_TYPE_TAG = { 10 | 0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16 11 | }; 12 | 13 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 14 | exports["name"] = Napi::String::New(env, "markdown"); 15 | auto markdown_language = Napi::External<TSLanguage>::New(env, tree_sitter_markdown()); 16 | markdown_language.TypeTag(&LANGUAGE_TYPE_TAG); 17 | exports["language"] = markdown_language; 18 | 19 | auto md_inline = Napi::Object::New(env); 20 | md_inline["name"] = Napi::String::New(env, "markdown_inline"); 21 | auto md_inline_language = Napi::External<TSLanguage>::New(env, tree_sitter_markdown_inline()); 22 | md_inline_language.TypeTag(&LANGUAGE_TYPE_TAG); 23 | md_inline["language"] = md_inline_language; 24 | exports["inline"] = md_inline; 25 | 26 | return exports; 27 | } 28 | 29 | NODE_API_MODULE(tree_sitter_markdown_binding, Init); 30 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/test/corpus/extension_strikethrough.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | Example 491 - https://github.github.com/gfm/#example-491 3 | ================================================================================ 4 | ~~Hi~~ Hello, world! 5 | 6 | -------------------------------------------------------------------------------- 7 | 8 | (inline 9 | (strikethrough 10 | (emphasis_delimiter) 11 | (strikethrough 12 | (emphasis_delimiter) 13 | (emphasis_delimiter)) 14 | (emphasis_delimiter))) 15 | 16 | ================================================================================ 17 | Example 492 - https://github.github.com/gfm/#example-492 18 | ================================================================================ 19 | This ~~has a 20 | 21 | new paragraph~~. 22 | (This test does not work here because it relies on block structure) 23 | 24 | -------------------------------------------------------------------------------- 25 | 26 | (inline 27 | (strikethrough 28 | (emphasis_delimiter) 29 | (strikethrough 30 | (emphasis_delimiter) 31 | (emphasis_delimiter)) 32 | (emphasis_delimiter))) 33 | ``` -------------------------------------------------------------------------------- /src/core/editor_validation.h: -------------------------------------------------------------------------------- ``` 1 | // editor_validation.h - Add this new header for validation utilities 2 | #ifndef EDITOR_VALIDATION_H 3 | #define EDITOR_VALIDATION_H 4 | 5 | #include <sstream> 6 | #include <string> 7 | 8 | // Validation result that tells us exactly what's wrong 9 | struct ValidationResult 10 | { 11 | bool valid; 12 | std::string error; 13 | 14 | ValidationResult() : valid(true) {} 15 | ValidationResult(const std::string &err) : valid(false), error(err) {} 16 | 17 | operator bool() const { return valid; } 18 | }; 19 | 20 | // Snapshot of editor state for comparison 21 | struct EditorSnapshot 22 | { 23 | int lineCount; 24 | int cursorLine; 25 | int cursorCol; 26 | int viewportTop; 27 | int viewportLeft; 28 | size_t bufferSize; 29 | std::string firstLine; 30 | std::string lastLine; 31 | std::string cursorLineContent; 32 | 33 | std::string toString() const 34 | { 35 | std::ostringstream oss; 36 | oss << "Lines: " << lineCount << " | Cursor: (" << cursorLine << "," 37 | << cursorCol << ")" << " | Viewport: (" << viewportTop << "," 38 | << viewportLeft << ")" << " | BufferSize: " << bufferSize; 39 | return oss.str(); 40 | } 41 | }; 42 | 43 | #endif // EDITOR_VALIDATION_H ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/python/tree_sitter_markdown/binding.c: -------------------------------------------------------------------------------- ```cpp 1 | #include <Python.h> 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | TSLanguage *tree_sitter_markdown(void); 6 | 7 | TSLanguage *tree_sitter_markdown_inline(void); 8 | 9 | static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { 10 | return PyCapsule_New(tree_sitter_markdown(), "tree_sitter.Language", NULL); 11 | } 12 | 13 | static PyObject* _binding_inline_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { 14 | return PyCapsule_New(tree_sitter_markdown_inline(), "tree_sitter.Language", NULL); 15 | } 16 | 17 | static PyMethodDef methods[] = { 18 | {"language", _binding_language, METH_NOARGS, 19 | "Get the tree-sitter language for the block grammar."}, 20 | {"inline_language", _binding_inline_language, METH_NOARGS, 21 | "Get the tree-sitter language for the inline grammar."}, 22 | {NULL, NULL, 0, NULL} 23 | }; 24 | 25 | static struct PyModuleDef module = { 26 | .m_base = PyModuleDef_HEAD_INIT, 27 | .m_name = "_binding", 28 | .m_doc = NULL, 29 | .m_size = -1, 30 | .m_methods = methods 31 | }; 32 | 33 | PyMODINIT_FUNC PyInit__binding(void) { 34 | return PyModule_Create(&module); 35 | } 36 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "metadata": { 3 | "version": "0.5.1", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "MDeiml" 8 | } 9 | ], 10 | "description": "Markdown grammar for tree-sitter", 11 | "links": { 12 | "repository": "https://github.com/tree-sitter-grammars/tree-sitter-markdown" 13 | } 14 | }, 15 | "grammars": [ 16 | { 17 | "name": "markdown", 18 | "scope": "text.markdown", 19 | "path": "tree-sitter-markdown", 20 | "injection-regex": "^(markdown|md)$", 21 | "file-types": [ 22 | "md" 23 | ], 24 | "highlights": "tree-sitter-markdown/queries/highlights.scm", 25 | "injections": "tree-sitter-markdown/queries/injections.scm", 26 | "external-files": [ 27 | "common/common.js" 28 | ] 29 | }, 30 | { 31 | "name": "markdown_inline", 32 | "camelcase": "MarkdownInline", 33 | "scope": "text.markdown_inline", 34 | "path": "tree-sitter-markdown-inline", 35 | "highlights": "tree-sitter-markdown-inline/queries/highlights.scm", 36 | "injections": "tree-sitter-markdown-inline/queries/injections.scm", 37 | "external-files": [ 38 | "common/common.js" 39 | ] 40 | } 41 | ] 42 | } 43 | ``` -------------------------------------------------------------------------------- /src/ui/input_handler.h: -------------------------------------------------------------------------------- ``` 1 | #pragma once 2 | 3 | #include "src/core/editor.h" 4 | #include <optional> 5 | 6 | #ifdef _WIN32 7 | #include <curses.h> 8 | #else 9 | #include <ncurses.h> 10 | #endif 11 | 12 | class Editor; 13 | 14 | /** 15 | * Simplified input handler for modeless editing 16 | * Handles standard editor keybindings without vim-style modes 17 | */ 18 | class InputHandler 19 | { 20 | public: 21 | enum class KeyResult 22 | { 23 | HANDLED, // Key was processed successfully 24 | NOT_HANDLED, // Key was not recognized 25 | QUIT, // User requested quit 26 | REDRAW // Screen needs redraw 27 | }; 28 | 29 | explicit InputHandler(Editor &editor); 30 | 31 | // Main input processing 32 | KeyResult handleKey(int key); 33 | 34 | // Enable/disable specific key categories 35 | void setMouseEnabled(bool enabled) { mouse_enabled_ = enabled; } 36 | 37 | private: 38 | Editor &editor_; 39 | bool mouse_enabled_; 40 | 41 | // Special input types 42 | KeyResult handleMouseEvent(); 43 | KeyResult handleResizeEvent(); 44 | 45 | // Movement and editing 46 | bool handleMovementKey(int key, bool shift_held); 47 | bool handleEditingKey(int key); 48 | std::optional<KeyResult> handleGlobalShortcut(int key); 49 | 50 | // Utility functions 51 | bool isPrintableChar(int key) const; 52 | }; ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/Cargo.toml: -------------------------------------------------------------------------------- ```toml 1 | [package] 2 | name = "tree-sitter-md" 3 | description = "Markdown grammar for tree-sitter" 4 | version = "0.5.1" 5 | authors = ["MDeiml"] 6 | license = "MIT" 7 | readme = "README.md" 8 | keywords = ["incremental", "parsing", "tree-sitter", "markdown"] 9 | categories = ["parsing", "text-editors"] 10 | repository = "https://github.com/tree-sitter-grammars/tree-sitter-markdown" 11 | edition = "2021" 12 | autoexamples = false 13 | 14 | build = "bindings/rust/build.rs" 15 | include = [ 16 | "bindings/rust/*", 17 | "tree-sitter-markdown/src/*", 18 | "tree-sitter-markdown-inline/src/*", 19 | "tree-sitter-markdown/grammar.js", 20 | "tree-sitter-markdown-inline/grammar.js", 21 | "tree-sitter-markdown/queries/*", 22 | "tree-sitter-markdown-inline/queries/*", 23 | "common/grammar.js", 24 | "common/html_entities.json", 25 | ] 26 | 27 | [features] 28 | parser = ["tree-sitter"] 29 | 30 | [lib] 31 | path = "bindings/rust/lib.rs" 32 | 33 | [dependencies] 34 | tree-sitter-language = "0.1" 35 | tree-sitter = { version = "0.24", optional = true } 36 | 37 | [dev-dependencies] 38 | tree-sitter = "0.24.3" 39 | 40 | [build-dependencies] 41 | cc = "1.1.22" 42 | 43 | [[bin]] 44 | name = "benchmark" 45 | path = "bindings/rust/benchmark.rs" 46 | required-features = ["parser"] 47 | 48 | [profile.release] 49 | debug = true 50 | 51 | [package.metadata.docs.rs] 52 | features = ["parser"] 53 | rustdoc-args = ["--cfg", "docsrs"] 54 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "@tree-sitter-grammars/tree-sitter-markdown", 3 | "version": "0.5.1", 4 | "description": "Markdown grammar for tree-sitter", 5 | "repository": "github:tree-sitter-grammars/tree-sitter-markdown", 6 | "license": "MIT", 7 | "main": "bindings/node", 8 | "types": "bindings/node", 9 | "author": { 10 | "name": "MDeiml" 11 | }, 12 | "keywords": [ 13 | "incremental", 14 | "parsing", 15 | "tree-sitter", 16 | "markdown" 17 | ], 18 | "files": [ 19 | "binding.gyp", 20 | "prebuilds/**", 21 | "bindings/node/*", 22 | "tree-sitter.json", 23 | "tree-sitter-*/grammar.js", 24 | "tree-sitter-*/queries/*", 25 | "tree-sitter-*/src/**", 26 | "common/common.js", 27 | "common/html_entities.json" 28 | ], 29 | "dependencies": { 30 | "node-addon-api": "^8.2.1", 31 | "node-gyp-build": "^4.8.2" 32 | }, 33 | "devDependencies": { 34 | "prebuildify": "^6.0.1", 35 | "tree-sitter-cli": "^0.24.3" 36 | }, 37 | "peerDependencies": { 38 | "tree-sitter": "^0.21.1" 39 | }, 40 | "peerDependenciesMeta": { 41 | "tree_sitter": { 42 | "optional": true 43 | } 44 | }, 45 | "scripts": { 46 | "build": "node scripts/build.js", 47 | "install": "node-gyp-build", 48 | "prestart": "tree-sitter build --wasm", 49 | "start": "tree-sitter playground", 50 | "test": "node --test bindings/node/*_test.js" 51 | }, 52 | "publishConfig": { 53 | "access": "public" 54 | } 55 | } 56 | ``` -------------------------------------------------------------------------------- /quickstart.md: -------------------------------------------------------------------------------- ```markdown 1 | # Quick Build Guide 2 | 3 | **TL;DR:** Get up and running fast. 4 | 5 | ## Linux 6 | 7 | ```bash 8 | # Install deps 9 | sudo apt install build-essential cmake libncurses5-dev libyaml-cpp-dev 10 | 11 | # Build 12 | git clone --recursive https://github.com/moisnx/arc.git 13 | cd arc 14 | mkdir build && cd build 15 | cmake .. -DCMAKE_BUILD_TYPE=Release 16 | make -j$(nproc) 17 | ./arc 18 | ``` 19 | 20 | ## macOS 21 | 22 | ```bash 23 | # Install deps 24 | brew install cmake ncurses yaml-cpp 25 | 26 | # Build 27 | git clone --recursive https://github.com/moisnx/arc.git 28 | cd arc 29 | mkdir build && cd build 30 | cmake .. -DCMAKE_BUILD_TYPE=Release 31 | make -j$(sysctl -n hw.ncpu) 32 | ./arc 33 | ``` 34 | 35 | ## Windows 36 | 37 | ```powershell 38 | # Install vcpkg (one-time setup) 39 | git clone https://github.com/microsoft/vcpkg.git C:\tools\vcpkg 40 | cd C:\tools\vcpkg 41 | .\bootstrap-vcpkg.bat 42 | .\vcpkg integrate install 43 | 44 | # Install deps 45 | .\vcpkg install pdcurses:x64-windows-static yaml-cpp:x64-windows-static 46 | 47 | # Build 48 | git clone --recursive https://github.com/moisnx/arc.git 49 | cd arc 50 | mkdir build 51 | cd build 52 | cmake .. -DCMAKE_TOOLCHAIN_FILE=C:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static 53 | cmake --build . --config Release 54 | .\Release\arc.exe 55 | ``` 56 | 57 | ## Troubleshooting 58 | 59 | **Missing dependencies?** See [BUILD.md](BUILD.md) for detailed instructions. 60 | 61 | **Build errors?** Try cleaning: `rm -rf build && mkdir build && cd build && cmake ..` 62 | 63 | **Runtime issues?** Make sure you're running the Release build, not Debug. 64 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/Package.swift: -------------------------------------------------------------------------------- ```swift 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "TreeSitterMarkdown", 6 | platforms: [.macOS(.v10_13), .iOS(.v11)], 7 | products: [ 8 | .library(name: "TreeSitterMarkdown", targets: ["TreeSitterMarkdown", "TreeSitterMarkdownInline"]), 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/ChimeHQ/SwiftTreeSitter", from: "0.8.0"), 12 | ], 13 | targets: [ 14 | .target( 15 | name: "TreeSitterMarkdown", 16 | path: "tree-sitter-markdown", 17 | sources: [ 18 | "src/parser.c", 19 | "src/scanner.c", 20 | ], 21 | resources: [ 22 | .copy("queries") 23 | ], 24 | publicHeadersPath: "bindings/swift", 25 | cSettings: [.headerSearchPath("src")] 26 | ), 27 | .target( 28 | name: "TreeSitterMarkdownInline", 29 | path: "tree-sitter-markdown-inline", 30 | exclude: [ 31 | "test", 32 | "grammar.js", 33 | ], 34 | sources: [ 35 | "src/parser.c", 36 | "src/scanner.c", 37 | ], 38 | resources: [ 39 | .copy("queries") 40 | ], 41 | publicHeadersPath: "bindings/swift", 42 | cSettings: [.headerSearchPath("src")] 43 | ), 44 | .testTarget( 45 | name: "TreeSitterMarkdownTests", 46 | dependencies: [ 47 | "SwiftTreeSitter", 48 | "TreeSitterMarkdown", 49 | "TreeSitterMarkdownInline", 50 | ], 51 | path: "bindings/swift/TreeSitterMarkdownTests" 52 | ) 53 | ], 54 | cLanguageStandard: .c11 55 | ) 56 | ``` -------------------------------------------------------------------------------- /src/features/syntax_config_loader.h: -------------------------------------------------------------------------------- ``` 1 | #pragma once 2 | 3 | #include <memory> 4 | #include <string> 5 | #include <unordered_map> 6 | #include <vector> 7 | 8 | // Language configuration structure 9 | struct LanguageConfig 10 | { 11 | std::string name; 12 | std::vector<std::string> extensions; 13 | bool builtin; // NEW: Is this a built-in language? 14 | 15 | // Tree-sitter specific configuration 16 | std::string parser_name; // e.g., "python" -> maps to tree_sitter_python() 17 | std::string query_file_path; // e.g., "syntax_rules/python.scm" 18 | std::vector<std::string> 19 | queries; // e.g., "javascript" -> queries/ecma/highlights.scm, 20 | // queries/javascript/highlights.scm 21 | }; 22 | 23 | class SyntaxConfigLoader 24 | { 25 | public: 26 | SyntaxConfigLoader(); 27 | 28 | // NEW: Load from unified registry 29 | bool loadFromRegistry(const std::string ®istry_path); 30 | 31 | // Legacy: Load individual language config (for backward compatibility) 32 | bool loadLanguageConfig(const std::string &language_name, 33 | const std::string &config_path); 34 | 35 | // Get loaded language configuration 36 | const LanguageConfig * 37 | getLanguageConfig(const std::string &language_name) const; 38 | 39 | // Get language name from file extension 40 | std::string getLanguageFromExtension(const std::string &extension) const; 41 | 42 | // Load all language configurations from a directory (DEPRECATED - use 43 | // loadFromRegistry) 44 | bool loadAllLanguageConfigs(const std::string &config_directory); 45 | 46 | // Debug functionality 47 | void debugCurrentState() const; 48 | 49 | // Public for direct access (needed by syntax_highlighter) 50 | std::unordered_map<std::string, std::unique_ptr<LanguageConfig>> 51 | language_configs_; 52 | std::unordered_map<std::string, std::string> extension_to_language_; 53 | 54 | private: 55 | // Parse YAML registry file 56 | bool parseRegistryFile(const std::string &filepath); 57 | 58 | // Simplified YAML parsing for individual configs (legacy) 59 | bool parseYamlFile(const std::string &filepath, LanguageConfig &config); 60 | }; ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/.github/workflows/ci.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [split_parser] 6 | pull_request: 7 | branches: [split_parser] 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{github.workflow}}-${{github.ref}} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | name: Test parsers 17 | runs-on: ${{matrix.os}} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ubuntu-latest, windows-latest, macos-latest] 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v5 25 | 26 | - name: Set up tree-sitter 27 | uses: tree-sitter/setup-action@v2 28 | with: 29 | install-lib: false 30 | 31 | - name: Set up Node.js 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: ${{vars.NODE_VERSION}} 35 | 36 | - name: Build with all extensions 37 | run: node scripts/build.js 38 | env: 39 | ALL_EXTENSIONS: 1 40 | 41 | - name: Run tests 42 | uses: tree-sitter/parser-test-action@v3 43 | with: 44 | generate: false 45 | test-parser-cmd: node scripts/test.js 46 | 47 | - name: Rebuild with default extensions 48 | run: node scripts/build.js 49 | - name: Verify grammar consistency 50 | run: git diff --exit-code -- */src/grammar.json 51 | 52 | fuzz: 53 | name: Fuzz parsers 54 | runs-on: ubuntu-latest 55 | strategy: 56 | fail-fast: false 57 | matrix: 58 | parser: [tree-sitter-markdown, tree-sitter-markdown-inline] 59 | steps: 60 | - name: Checkout repository 61 | uses: actions/checkout@v4 62 | with: 63 | fetch-depth: 2 64 | - name: Check for scanner changes 65 | id: scanner-changes 66 | run: |- 67 | if git diff --quiet HEAD^ -- '${{matrix.parser}}/src/scanner.c'; then 68 | printf 'changed=false\n' >> "$GITHUB_OUTPUT" 69 | else 70 | printf 'changed=true\n' >> "$GITHUB_OUTPUT" 71 | fi 72 | - name: Fuzz ${{matrix.parser}} parser 73 | uses: tree-sitter/fuzz-action@v4 74 | if: steps.scanner-changes.outputs.changed == 'true' 75 | with: 76 | directory: ${{matrix.parser}} 77 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/test/corpus/extension_wikilink.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | Basic Wiki-link parsing. 3 | ================================================================================ 4 | [[Tree laws of motion]] 5 | 6 | ------------------------------------------------------------------------------- 7 | (inline 8 | (wiki_link 9 | (link_destination) 10 | )) 11 | 12 | ================================================================================ 13 | Wiki-link to a file 14 | ================================================================================ 15 | [[Figure 1.png]] 16 | 17 | ------------------------------------------------------------------------------- 18 | (inline 19 | (wiki_link 20 | (link_destination) 21 | )) 22 | 23 | ================================================================================ 24 | Wiki-link to a heading in a note 25 | ================================================================================ 26 | [[Tree laws of motion#Second law]] 27 | 28 | ------------------------------------------------------------------------------- 29 | (inline 30 | (wiki_link 31 | (link_destination) 32 | )) 33 | 34 | ================================================================================ 35 | Wiki-link with title 36 | ================================================================================ 37 | [[Internal links|custom display text]] 38 | 39 | ------------------------------------------------------------------------------- 40 | (inline 41 | (wiki_link 42 | (link_destination) 43 | (link_text) 44 | )) 45 | 46 | ================================================================================ 47 | Wiki-link opener with no closer 48 | ================================================================================ 49 | [[ 50 | 51 | ------------------------------------------------------------------------------- 52 | (inline) 53 | 54 | ================================================================================ 55 | Wiki-link version of Example 556 56 | ================================================================================ 57 | [[[foo]]] 58 | 59 | -------------------------------------------------------------------------------- 60 | 61 | (inline 62 | (wiki_link 63 | (link_destination))) 64 | 65 | ``` -------------------------------------------------------------------------------- /treesitter/languages.yaml: -------------------------------------------------------------------------------- ```yaml 1 | # Language Registry for Arc Editor 2 | # This file defines all supported languages and their configurations 3 | 4 | languages: 5 | c: 6 | name: "C" 7 | extensions: ["c", "h"] 8 | builtin: true 9 | parser_name: "c" 10 | queries: ["treesitter/queries/c/highlights.scm"] 11 | 12 | cpp: 13 | name: "C++" 14 | extensions: ["cpp", "cc", "cxx", "hpp", "hxx"] 15 | builtin: true 16 | parser_name: "cpp" 17 | queries: ["treesitter/queries/c/highlights.scm", "treesitter/queries/cpp/highlights.scm"] 18 | 19 | python: 20 | name: "Python" 21 | extensions: ["py", "pyw", "pyi"] 22 | builtin: true 23 | parser_name: "python" 24 | queries: ["treesitter/queries/python/highlights.scm",] 25 | 26 | rust: 27 | name: "Rust" 28 | extensions: ["rs"] 29 | builtin: true 30 | parser_name: "rust" 31 | queries: ["treesitter/queries/rust/highlights.scm"] 32 | 33 | markdown: 34 | name: "Markdown" 35 | extensions: ["md", "markdown", "mdown", "mkd"] 36 | builtin: true 37 | parser_name: "markdown" 38 | queries: ["treesitter/queries/markdown/highlights.scm", "treesitter/queries/markdown/injections.scm"] 39 | 40 | javascript: 41 | name: "JavaScript" 42 | extensions: ["js", "mjs", "jsx"] 43 | builtin: true 44 | parser_name: "javascript" 45 | queries: ["treesitter/queries/ecma/highlights.scm", "treesitter/queries/_javascript/highlights.scm", "treesitter/queries/_jsx/highlights.scm"] 46 | 47 | typescript: 48 | name: "TypeScript" 49 | extensions: ["ts"] 50 | builtin: true 51 | parser_name: "typescript" 52 | queries: ["treesitter/queries/ecma/highlights.scm", "treesitter/queries/_typescript/highlights.scm"] 53 | 54 | tsx: 55 | name: "Tsx" 56 | extensions: ["tsx"] 57 | builtin: true 58 | parser_name: "tsx" 59 | queries: ["treesitter/queries/ecma/highlights.scm", "treesitter/queries/_typescript/highlights.scm", "treesitter/queries/_jsx/highlights.scm"] 60 | 61 | zig: 62 | name: "Zig" 63 | extensions: ["zig", "zir", "zigr", "zon"] 64 | builtin: true 65 | parser_name: "zig" 66 | queries: ["treesitter/queries/zig/highlights.scm"] 67 | 68 | go: 69 | name: "Go" 70 | extensions: ["go"] 71 | parser_name: "go" 72 | queries: ["treesitter/queries/go/highlights.scm"] ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/setup.py: -------------------------------------------------------------------------------- ```python 1 | from pathlib import Path 2 | from platform import system 3 | 4 | from setuptools import Extension, find_packages, setup 5 | from setuptools.command.build import build 6 | from wheel.bdist_wheel import bdist_wheel 7 | 8 | 9 | class Build(build): 10 | def run(self): 11 | if (block_queries := Path("tree-sitter-markdown", "queries")).is_dir(): 12 | dest = Path(self.build_lib, "tree_sitter_markdown", "queries", "markdown") 13 | self.copy_tree(str(block_queries), str(dest)) 14 | if (inline_queries := Path("tree-sitter-markdown-inline", "queries")).is_dir(): 15 | dest = Path(self.build_lib, "tree_sitter_markdown", "queries", "markdown_inline") 16 | self.copy_tree(str(inline_queries), str(dest)) 17 | super().run() 18 | 19 | 20 | class BdistWheel(bdist_wheel): 21 | def get_tag(self): 22 | python, abi, platform = super().get_tag() 23 | if python.startswith("cp"): 24 | python, abi = "cp39", "abi3" 25 | return python, abi, platform 26 | 27 | 28 | setup( 29 | packages=find_packages("bindings/python"), 30 | package_dir={"": "bindings/python"}, 31 | package_data={ 32 | "tree_sitter_markdown": ["*.pyi", "py.typed"], 33 | "tree_sitter_markdown.queries": ["*.scm"], 34 | }, 35 | ext_package="tree_sitter_markdown", 36 | ext_modules=[ 37 | Extension( 38 | name="_binding", 39 | sources=[ 40 | "bindings/python/tree_sitter_markdown/binding.c", 41 | "tree-sitter-markdown/src/parser.c", 42 | "tree-sitter-markdown/src/scanner.c", 43 | "tree-sitter-markdown-inline/src/parser.c", 44 | "tree-sitter-markdown-inline/src/scanner.c", 45 | ], 46 | extra_compile_args=( 47 | ["-std=c11"] if system() != "Windows" else [] 48 | ), 49 | define_macros=[ 50 | ("Py_LIMITED_API", "0x03090000"), 51 | ("PY_SSIZE_T_CLEAN", None) 52 | ], 53 | include_dirs=["tree-sitter-markdown/src"], 54 | py_limited_api=True, 55 | ) 56 | ], 57 | cmdclass={ 58 | "build": Build, 59 | "bdist_wheel": BdistWheel 60 | }, 61 | zip_safe=False 62 | ) 63 | ``` -------------------------------------------------------------------------------- /src/core/buffer.h: -------------------------------------------------------------------------------- ``` 1 | #ifndef BUFFER_H 2 | #define BUFFER_H 3 | 4 | #include <string> 5 | #include <utility> 6 | #include <vector> 7 | 8 | class GapBuffer 9 | { 10 | public: 11 | // Constructor 12 | GapBuffer(); 13 | explicit GapBuffer(const std::string &initialText); 14 | void loadFromString(const std::string &content); 15 | 16 | // File operations 17 | bool loadFromFile(const std::string &filename); 18 | bool saveToFile(const std::string &filename) const; 19 | void clear(); 20 | 21 | // Line-based interface (for compatibility with existing Editor code) 22 | int getLineCount() const; 23 | std::string getLine(int lineNum) const; 24 | size_t getLineLength(int lineNum) const; 25 | bool isEmpty() const; 26 | 27 | // Position conversion utilities 28 | size_t lineColToPos(int line, int col) const; 29 | std::pair<int, int> posToLineCol(size_t pos) const; 30 | 31 | // Editing operations 32 | void insertChar(size_t pos, char c); 33 | void insertText(size_t pos, const std::string &text); 34 | void deleteChar(size_t pos); 35 | void deleteRange(size_t start, size_t length); 36 | 37 | // Line-based editing (for easier migration) 38 | void insertLine(int lineNum, const std::string &line); 39 | void deleteLine(int lineNum); 40 | void replaceLine(int lineNum, const std::string &newLine); 41 | 42 | // Get text content 43 | std::string getText() const; 44 | std::string getTextRange(size_t start, size_t length) const; 45 | 46 | // Statistics 47 | size_t size() const; 48 | size_t getBufferSize() const; 49 | void invalidateLineIndex(); 50 | 51 | private: 52 | std::vector<char> buffer; 53 | size_t gapStart; 54 | size_t gapSize; 55 | 56 | // Line index cache for performance 57 | mutable std::vector<size_t> lineIndex; 58 | mutable bool lineIndexDirty; 59 | 60 | // Internal operations 61 | void moveGapTo(size_t pos); 62 | void expandGap(size_t minSize = 1024); 63 | void rebuildLineIndex() const; 64 | 65 | // Utilities 66 | size_t gapEnd() const { return gapStart + gapSize; } 67 | size_t textSize() const { return buffer.size() - gapSize; } 68 | char charAt(size_t pos) const; 69 | 70 | // Constants 71 | static const size_t DEFAULT_GAP_SIZE = 1024; 72 | static const size_t MIN_GAP_SIZE = 512; 73 | mutable std::vector<size_t> line_start_cache_; 74 | mutable bool line_cache_valid_ = false; 75 | }; 76 | 77 | #endif // GAP_BUFFER_H ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/test/corpus/extension_task_list.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | Example 279 - https://github.github.com/gfm/#example-279 3 | ================================================================================ 4 | - [ ] foo 5 | - [x] bar 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (document 10 | (section 11 | (list 12 | (list_item 13 | (list_marker_minus) 14 | (task_list_marker_unchecked) 15 | (paragraph 16 | (inline))) 17 | (list_item 18 | (list_marker_minus) 19 | (task_list_marker_checked) 20 | (paragraph 21 | (inline)))))) 22 | 23 | ================================================================================ 24 | Example 280 - https://github.github.com/gfm/#example-280 25 | ================================================================================ 26 | - [x] foo 27 | - [ ] bar 28 | - [x] baz 29 | - [ ] bim 30 | 31 | -------------------------------------------------------------------------------- 32 | 33 | (document 34 | (section 35 | (list 36 | (list_item 37 | (list_marker_minus) 38 | (task_list_marker_checked) 39 | (paragraph 40 | (inline) 41 | (block_continuation)) 42 | (list 43 | (list_item 44 | (list_marker_minus) 45 | (task_list_marker_unchecked) 46 | (paragraph 47 | (inline) 48 | (block_continuation))) 49 | (list_item 50 | (list_marker_minus) 51 | (task_list_marker_checked) 52 | (paragraph 53 | (inline))))) 54 | (list_item 55 | (list_marker_minus) 56 | (task_list_marker_unchecked) 57 | (paragraph 58 | (inline)))))) 59 | 60 | ================================================================================ 61 | task list item marker … the letter x in either lowercase or UPPERCASE 62 | ================================================================================ 63 | - [ ] foo 64 | - [X] bar 65 | 66 | -------------------------------------------------------------------------------- 67 | 68 | (document 69 | (section 70 | (list 71 | (list_item 72 | (list_marker_minus) 73 | (task_list_marker_unchecked) 74 | (paragraph 75 | (inline))) 76 | (list_item 77 | (list_marker_minus) 78 | (task_list_marker_checked) 79 | (paragraph 80 | (inline)))))) 81 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/test/corpus/extension_latex.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | Basic LaTeX parsing. 3 | ================================================================================ 4 | $$This$$ has $$an odd$$ number of instances of $$. 5 | 6 | ------------------------------------------------------------------------------- 7 | (inline 8 | (latex_block 9 | (latex_span_delimiter) 10 | (latex_span_delimiter)) 11 | (latex_block 12 | (latex_span_delimiter) 13 | (latex_span_delimiter))) 14 | 15 | ================================================================================ 16 | LaTeX and markup clashes. 17 | ================================================================================ 18 | $$This should prevent *this from parsing$$ the bold.* 19 | 20 | ------------------------------------------------------------------------------- 21 | (inline 22 | (latex_block 23 | (latex_span_delimiter) 24 | (latex_span_delimiter))) 25 | 26 | ================================================================================ 27 | LaTeX and link clashes 28 | ================================================================================ 29 | $$This should prevent [this from parsing$$ the link](https://google.com) 30 | 31 | ------------------------------------------------------------------------------- 32 | (inline 33 | (latex_block 34 | (latex_span_delimiter) 35 | (latex_span_delimiter))) 36 | ================================================================================ 37 | LaTeX inside markup 38 | ================================================================================ 39 | *This bold $$should still parse $$*. 40 | 41 | ------------------------------------------------------------------------------- 42 | (inline 43 | (emphasis 44 | (emphasis_delimiter) 45 | (latex_block 46 | (latex_span_delimiter) 47 | (latex_span_delimiter)) 48 | (emphasis_delimiter))) 49 | ================================================================================ 50 | LaTeX within one paragraph 51 | ================================================================================ 52 | $$This should all be captured 53 | as one instance of LaTeX.$$ 54 | 55 | $$This presumably 56 | 57 | should not, but will because we need the blocks.$$ 58 | -------------------------------------------------------------------------------- 59 | (inline 60 | (latex_block 61 | (latex_span_delimiter) 62 | (latex_span_delimiter)) 63 | (latex_block 64 | (latex_span_delimiter) 65 | (latex_span_delimiter))) 66 | ================================================================================ 67 | LaTeX with escaped dollar signs 68 | ================================================================================ 69 | $Hello\$th*er*e$ 70 | -------------------------------------------------------------------------------- 71 | (inline 72 | (latex_block 73 | (latex_span_delimiter) 74 | (backslash_escape) 75 | (latex_span_delimiter))) 76 | ``` -------------------------------------------------------------------------------- /src/ui/renderer.h: -------------------------------------------------------------------------------- ``` 1 | #pragma once 2 | 3 | #include "src/core/buffer.h" 4 | #include "src/core/editor.h" 5 | #include "src/features/syntax_highlighter.h" 6 | #include <string> 7 | #include <vector> 8 | 9 | #ifdef _WIN32 10 | #include <curses.h> 11 | #else 12 | #include <ncurses.h> 13 | #endif 14 | 15 | // Forward declarations 16 | class Editor; 17 | struct ColorSpan; 18 | 19 | /** 20 | * Handles all rendering/display logic for the editor 21 | * Separated from Editor to keep business logic clean 22 | */ 23 | class Renderer 24 | { 25 | public: 26 | struct ViewportInfo 27 | { 28 | int top; 29 | int left; 30 | int height; 31 | int width; 32 | int content_start_col; // Where content starts after line numbers 33 | }; 34 | 35 | struct CursorInfo 36 | { 37 | int line; 38 | int col; 39 | int screen_row; 40 | int screen_col; 41 | }; 42 | 43 | struct EditorState 44 | { 45 | ViewportInfo viewport; 46 | CursorInfo cursor; 47 | EditorMode mode; 48 | bool has_selection; 49 | int selection_start_line; 50 | int selection_start_col; 51 | int selection_end_line; 52 | int selection_end_col; 53 | std::string filename; 54 | bool is_modified; 55 | }; 56 | 57 | Renderer(); 58 | ~Renderer(); 59 | 60 | // Main rendering functions 61 | void renderEditor(const EditorState &state, const GapBuffer &buffer, 62 | const SyntaxHighlighter *highlighter); 63 | 64 | // Individual component rendering 65 | void renderContent(const EditorState &state, const GapBuffer &buffer, 66 | const SyntaxHighlighter *highlighter); 67 | void renderStatusBar(const EditorState &state, const GapBuffer &buffer); 68 | void renderLineNumbers(int start_line, int end_line, int current_line, 69 | int line_num_width, int viewport_top); 70 | 71 | // Cursor management 72 | void positionCursor(const CursorInfo &cursor, const ViewportInfo &viewport); 73 | void updateCursorStyle(EditorMode mode); 74 | void restoreDefaultCursor(); 75 | 76 | // Screen management 77 | void clear(); 78 | void refresh(); 79 | void handleResize(); 80 | ViewportInfo calculateViewport() const; 81 | 82 | // Utility functions 83 | int calculateLineNumberWidth(int max_line) const; 84 | bool isPositionSelected(int line, int col, const EditorState &state) const; 85 | std::string expandTabs(const std::string &line, int tab_size = 4) const; 86 | 87 | private: 88 | int tab_size_; 89 | 90 | // Internal rendering helpers 91 | void renderLine(const std::string &line, int line_number, 92 | const std::vector<ColorSpan> &spans, 93 | const ViewportInfo &viewport, const EditorState &state); 94 | 95 | void renderStatusMode(EditorMode mode); 96 | void renderStatusFile(const std::string &filename, bool is_modified); 97 | void renderStatusPosition(const CursorInfo &cursor, int total_lines, 98 | bool has_selection, int selection_size); 99 | 100 | // Color/attribute helpers 101 | void applyColorSpan(const ColorSpan &span, char ch); 102 | void setDefaultColors(); 103 | }; ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/test/corpus/issues.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | #17 - Titles not detected after an empty inner list item (bullet point) 3 | ================================================================================ 4 | * a 5 | * b 6 | * 7 | 8 | 9 | # C 10 | 11 | -------------------------------------------------------------------------------- 12 | 13 | (document 14 | (section 15 | (list 16 | (list_item 17 | (list_marker_star) 18 | (paragraph 19 | (inline) 20 | (block_continuation)) 21 | (list 22 | (list_item 23 | (list_marker_star) 24 | (paragraph 25 | (inline) 26 | (block_continuation))) 27 | (list_item 28 | (list_marker_star) 29 | (block_continuation) 30 | (block_continuation)))))) 31 | (section 32 | (atx_heading 33 | (atx_h1_marker) 34 | (inline)))) 35 | 36 | ================================================================================ 37 | #33 - Fenced code block attributes 38 | ================================================================================ 39 | ```{R} 40 | 1 + 1 41 | ``` 42 | 43 | ```{} 44 | 1 + 1 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- 48 | 49 | (document 50 | (section 51 | (fenced_code_block 52 | (fenced_code_block_delimiter) 53 | (info_string 54 | (language)) 55 | (block_continuation) 56 | (code_fence_content 57 | (block_continuation)) 58 | (fenced_code_block_delimiter)) 59 | (fenced_code_block 60 | (fenced_code_block_delimiter) 61 | (info_string) 62 | (block_continuation) 63 | (code_fence_content 64 | (block_continuation)) 65 | (fenced_code_block_delimiter)))) 66 | 67 | ================================================================================ 68 | #72 - Can't create list item after a list item with a newline and indent 69 | ================================================================================ 70 | 1. a 71 | 1. b 72 | c 73 | 2. d 74 | 75 | -------------------------------------------------------------------------------- 76 | 77 | (document 78 | (section 79 | (list 80 | (list_item 81 | (list_marker_dot) 82 | (paragraph 83 | (inline))) 84 | (list_item 85 | (list_marker_dot) 86 | (paragraph 87 | (inline 88 | (block_continuation)))) 89 | (list_item 90 | (list_marker_dot) 91 | (paragraph 92 | (inline)))))) 93 | 94 | ================================================================================ 95 | #135 - Closing code block fence not recognized when it has trailing space 96 | ================================================================================ 97 | ``` 98 | // returns 2 99 | globalNS.method1(5, 10); 100 | ``` 101 | 102 | @example 103 | 104 | -------------------------------------------------------------------------------- 105 | 106 | (document 107 | (section 108 | (fenced_code_block 109 | (fenced_code_block_delimiter) 110 | (block_continuation) 111 | (code_fence_content 112 | (block_continuation) 113 | (block_continuation)) 114 | (fenced_code_block_delimiter)) 115 | (paragraph 116 | (inline)))) 117 | ``` -------------------------------------------------------------------------------- /src/core/config_manager.h: -------------------------------------------------------------------------------- ``` 1 | // ============================================================================ 2 | // config_manager.h - Enhanced with Editor Settings 3 | // ============================================================================ 4 | #pragma once 5 | 6 | #include <atomic> 7 | #include <functional> 8 | #include <memory> 9 | #include <string> 10 | #include <vector> 11 | 12 | namespace efsw 13 | { 14 | class FileWatcher; 15 | class FileWatchListener; 16 | } // namespace efsw 17 | using ConfigReloadCallback = std::function<void()>; 18 | 19 | // Syntax highlighting modes 20 | enum class SyntaxMode 21 | { 22 | NONE, // Completely disabled 23 | VIEWPORT, // Only highlight visible lines (default, fastest) 24 | FULL // Highlight entire file (slower for large files) 25 | }; 26 | 27 | // Editor configuration structure 28 | struct EditorConfig 29 | { 30 | int tab_size = 4; 31 | bool line_numbers = true; 32 | std::string cursor_style = "auto"; // auto, block, bar, underline 33 | }; 34 | 35 | // Syntax configuration structure 36 | struct SyntaxConfig 37 | { 38 | SyntaxMode highlighting = SyntaxMode::VIEWPORT; 39 | }; 40 | 41 | class ConfigManager 42 | { 43 | public: 44 | // Existing methods... 45 | static bool ensureConfigStructure(); 46 | static std::string getConfigDir(); 47 | static std::string getThemesDir(); 48 | static std::string getSyntaxRulesDir(); 49 | static std::string getConfigFile(); 50 | static std::string getThemeFile(const std::string &theme_name); 51 | static std::string getSyntaxFile(const std::string &language); 52 | static bool loadConfig(); 53 | static bool saveConfig(); 54 | static std::string getActiveTheme(); 55 | static bool setActiveTheme(const std::string &theme_name); 56 | static bool copyProjectFilesToConfig(); 57 | static bool startWatchingConfig(); 58 | static void registerReloadCallback(ConfigReloadCallback callback); 59 | static void handleFileChange(); 60 | static bool isReloadPending(); 61 | 62 | // NEW: Configuration getters 63 | static EditorConfig getEditorConfig() { return editor_config_; } 64 | static SyntaxConfig getSyntaxConfig() { return syntax_config_; } 65 | static int getTabSize() { return editor_config_.tab_size; } 66 | static bool getLineNumbers() { return editor_config_.line_numbers; } 67 | static std::string getCursorStyle() { return editor_config_.cursor_style; } 68 | static SyntaxMode getSyntaxMode() { return syntax_config_.highlighting; } 69 | 70 | // NEW: Configuration setters (also saves to file) 71 | static void setTabSize(int size); 72 | static void setLineNumbers(bool enabled); 73 | static void setCursorStyle(const std::string &style); 74 | static void setSyntaxMode(SyntaxMode mode); 75 | 76 | // NEW: Helper to convert string to SyntaxMode 77 | static SyntaxMode parseSyntaxMode(const std::string &mode_str); 78 | static std::string syntaxModeToString(SyntaxMode mode); 79 | 80 | private: 81 | static std::string config_dir_cache_; 82 | static std::string active_theme_; 83 | static std::unique_ptr<efsw::FileWatchListener> watcher_listener_; 84 | static std::unique_ptr<efsw::FileWatcher> watcher_instance_; 85 | static std::vector<ConfigReloadCallback> reload_callbacks_; 86 | static std::atomic<bool> reload_pending_; 87 | static bool createDefaultConfig(const std::string &config_file); 88 | 89 | // NEW: Cached configuration 90 | static EditorConfig editor_config_; 91 | static SyntaxConfig syntax_config_; 92 | }; 93 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/CMakeLists.txt: -------------------------------------------------------------------------------- ``` 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(tree-sitter-markdown 4 | VERSION "0.5.1" 5 | DESCRIPTION "Markdown grammar for tree-sitter" 6 | HOMEPAGE_URL "https://github.com/tree-sitter-grammars/tree-sitter-markdown" 7 | LANGUAGES C) 8 | 9 | option(BUILD_SHARED_LIBS "Build using shared libraries" ON) 10 | option(TREE_SITTER_REUSE_ALLOCATOR "Reuse the library allocator" OFF) 11 | option(ALL_EXTENSIONS "Enable all Markdown extensions" OFF) 12 | 13 | set(TREE_SITTER_ABI_VERSION 14 CACHE STRING "Tree-sitter ABI version") 14 | if(NOT ${TREE_SITTER_ABI_VERSION} MATCHES "^[0-9]+$") 15 | unset(TREE_SITTER_ABI_VERSION CACHE) 16 | message(FATAL_ERROR "TREE_SITTER_ABI_VERSION must be an integer") 17 | endif() 18 | 19 | find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI") 20 | 21 | include(GNUInstallDirs) 22 | 23 | macro(add_parser name) 24 | add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" 25 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/grammar.js" 26 | COMMAND "${CMAKE_COMMAND}" -E env ALL_EXTENSIONS=$<BOOL:${ALL_EXTENSIONS}> 27 | -- "${TREE_SITTER_CLI}" generate --abi=${TREE_SITTER_ABI_VERSION} 28 | BYPRODUCTS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" 29 | "${CMAKE_CURRENT_SOURCE_DIR}/src/node-types.json" 30 | "${CMAKE_CURRENT_SOURCE_DIR}/src/tree_sitter/alloc.h" 31 | "${CMAKE_CURRENT_SOURCE_DIR}/src/tree_sitter/array.h" 32 | "${CMAKE_CURRENT_SOURCE_DIR}/src/tree_sitter/parser.h" 33 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 34 | COMMENT "Generating parser.c") 35 | 36 | add_library(tree-sitter-${name} 37 | "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" 38 | "${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c") 39 | target_include_directories(tree-sitter-${name} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") 40 | 41 | target_compile_definitions(tree-sitter-${name} PRIVATE 42 | $<$<BOOL:${TREE_SITTER_REUSE_ALLOCATOR}>:TREE_SITTER_REUSE_ALLOCATOR> 43 | $<$<CONFIG:Debug>:TREE_SITTER_DEBUG>) 44 | 45 | set_target_properties(tree-sitter-${name} 46 | PROPERTIES 47 | C_STANDARD 11 48 | POSITION_INDEPENDENT_CODE ON 49 | SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}" 50 | DEFINE_SYMBOL "") 51 | 52 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bindings/c/tree-sitter-${name}.pc.in" 53 | "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-${name}.pc" @ONLY) 54 | 55 | install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/bindings/c/tree-sitter-${name}.h" 56 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/tree_sitter") 57 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-${name}.pc" 58 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 59 | install(TARGETS tree-sitter-${name} 60 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") 61 | endmacro() 62 | 63 | add_subdirectory(tree-sitter-markdown tree-sitter-markdown) 64 | 65 | add_subdirectory(tree-sitter-markdown-inline tree-sitter-markdown-inline) 66 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/bindings/rust/lib.rs: -------------------------------------------------------------------------------- ```rust 1 | //! This crate provides Markdown language support for the [tree-sitter][] parsing library. 2 | //! 3 | //! It contains two grammars: [`LANGUAGE`] to parse the block structure of markdown documents and 4 | //! [`INLINE_LANGUAGE`] to parse inline content. 5 | //! 6 | //! It also supplies [`MarkdownParser`] as a convenience wrapper around the two grammars. 7 | //! [`MarkdownParser::parse`] returns a [`MarkdownTree`] instread of a [`Tree`][Tree]. This struct 8 | //! contains a block tree and an inline tree for each node in the block tree that has inline 9 | //! content. 10 | //! 11 | //! [LanguageFn]: https://docs.rs/tree-sitter-language/*/tree_sitter_language/struct.LanguageFn.html 12 | //! [Tree]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Tree.html 13 | //! [tree-sitter]: https://tree-sitter.github.io/ 14 | 15 | #![cfg_attr(docsrs, feature(doc_cfg))] 16 | 17 | use tree_sitter_language::LanguageFn; 18 | 19 | extern "C" { 20 | fn tree_sitter_markdown() -> *const (); 21 | fn tree_sitter_markdown_inline() -> *const (); 22 | } 23 | 24 | /// The tree-sitter [`LanguageFn`][LanguageFn] for the block grammar. 25 | pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_markdown) }; 26 | 27 | /// The tree-sitter [`LanguageFn`][LanguageFn] for the inline grammar. 28 | pub const INLINE_LANGUAGE: LanguageFn = 29 | unsafe { LanguageFn::from_raw(tree_sitter_markdown_inline) }; 30 | 31 | /// The syntax highlighting queries for the block grammar. 32 | pub const HIGHLIGHT_QUERY_BLOCK: &str = 33 | include_str!("../../tree-sitter-markdown/queries/highlights.scm"); 34 | 35 | /// The language injection queries for the block grammar. 36 | pub const INJECTION_QUERY_BLOCK: &str = 37 | include_str!("../../tree-sitter-markdown/queries/injections.scm"); 38 | 39 | /// The syntax highlighting queries for the inline grammar. 40 | pub const HIGHLIGHT_QUERY_INLINE: &str = 41 | include_str!("../../tree-sitter-markdown-inline/queries/highlights.scm"); 42 | 43 | /// The language injection queries for the inline grammar. 44 | pub const INJECTION_QUERY_INLINE: &str = 45 | include_str!("../../tree-sitter-markdown-inline/queries/injections.scm"); 46 | 47 | /// The content of the [`node-types.json`][] file for the block grammar. 48 | /// 49 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 50 | pub const NODE_TYPES_BLOCK: &str = include_str!("../../tree-sitter-markdown/src/node-types.json"); 51 | 52 | /// The content of the [`node-types.json`][] file for the inline grammar. 53 | /// 54 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 55 | pub const NODE_TYPES_INLINE: &str = 56 | include_str!("../../tree-sitter-markdown-inline/src/node-types.json"); 57 | 58 | #[cfg(feature = "parser")] 59 | #[cfg_attr(docsrs, doc(cfg(feature = "parser")))] 60 | mod parser; 61 | 62 | #[cfg(feature = "parser")] 63 | #[cfg_attr(docsrs, doc(cfg(feature = "parser")))] 64 | pub use parser::*; 65 | 66 | #[cfg(test)] 67 | mod tests { 68 | use super::*; 69 | 70 | #[test] 71 | fn can_load_block_grammar() { 72 | let mut parser = tree_sitter::Parser::new(); 73 | parser 74 | .set_language(&LANGUAGE.into()) 75 | .expect("Error loading Markdown block grammar"); 76 | } 77 | 78 | #[test] 79 | fn can_load_inline_grammar() { 80 | let mut parser = tree_sitter::Parser::new(); 81 | parser 82 | .set_language(&INLINE_LANGUAGE.into()) 83 | .expect("Error loading Markdown inline grammar"); 84 | } 85 | } 86 | ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown-inline/test/corpus/issues.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | #18 - Error on markdown images 3 | ================================================================================ 4 |  5 |  6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | (inline 10 | (image 11 | (image_description) 12 | (link_destination)) 13 | (image 14 | (image_description) 15 | (link_destination))) 16 | 17 | ================================================================================ 18 | #6 - HTML tag can sometimes be parsed as code span (1) 19 | ================================================================================ 20 | test `Option<T>` test `Option` and `</>` 21 | 22 | -------------------------------------------------------------------------------- 23 | 24 | (inline 25 | (code_span 26 | (code_span_delimiter) 27 | (code_span_delimiter)) 28 | (code_span 29 | (code_span_delimiter) 30 | (code_span_delimiter)) 31 | (code_span 32 | (code_span_delimiter) 33 | (code_span_delimiter))) 34 | 35 | ================================================================================ 36 | #6 - HTML tag can sometimes be parsed as code span (2) 37 | ================================================================================ 38 | test `Option<T>` test `Option` and `test` 39 | 40 | -------------------------------------------------------------------------------- 41 | 42 | (inline 43 | (code_span 44 | (code_span_delimiter) 45 | (code_span_delimiter)) 46 | (code_span 47 | (code_span_delimiter) 48 | (code_span_delimiter)) 49 | (code_span 50 | (code_span_delimiter) 51 | (code_span_delimiter))) 52 | 53 | ================================================================================ 54 | #36 - Multiple code spans with HTML comments does not working properly (1) 55 | ================================================================================ 56 | foo `<!--comment-->` 57 | 58 | -------------------------------------------------------------------------------- 59 | 60 | (inline 61 | (code_span 62 | (code_span_delimiter) 63 | (code_span_delimiter))) 64 | 65 | ================================================================================ 66 | #36 - Multiple code spans with HTML comments does not working properly (2) 67 | ================================================================================ 68 | `<!--comment-->` foo 69 | 70 | -------------------------------------------------------------------------------- 71 | 72 | (inline 73 | (code_span 74 | (code_span_delimiter) 75 | (code_span_delimiter))) 76 | 77 | ================================================================================ 78 | #36 - Multiple code spans with HTML comments does not working properly (3) 79 | ================================================================================ 80 | `<!--comment-->` foo `<!--comment-->` 81 | 82 | -------------------------------------------------------------------------------- 83 | 84 | (inline 85 | (code_span 86 | (code_span_delimiter) 87 | (code_span_delimiter)) 88 | (code_span 89 | (code_span_delimiter) 90 | (code_span_delimiter))) 91 | 92 | ================================================================================ 93 | #75 - code spans with `[x][]` 94 | ================================================================================ 95 | `some code` 96 | normal text (or even nothing) `[index][]` 97 | 98 | -------------------------------------------------------------------------------- 99 | 100 | (inline 101 | (code_span 102 | (code_span_delimiter) 103 | (code_span_delimiter)) 104 | (code_span 105 | (code_span_delimiter) 106 | (code_span_delimiter))) 107 | ``` -------------------------------------------------------------------------------- /src/core/editor_delta.h: -------------------------------------------------------------------------------- ``` 1 | // editor_delta.h - New file for delta-based undo/redo 2 | #ifndef EDITOR_DELTA_H 3 | #define EDITOR_DELTA_H 4 | 5 | #include <chrono> 6 | #include <string> 7 | #include <vector> 8 | 9 | // Represents a single atomic edit operation 10 | struct EditDelta 11 | { 12 | // Operation types 13 | enum OpType 14 | { 15 | INSERT_CHAR, // Single character insertion 16 | DELETE_CHAR, // Single character deletion 17 | INSERT_TEXT, // Multi-character insertion (paste, etc) 18 | DELETE_TEXT, // Multi-character deletion 19 | SPLIT_LINE, // Enter/newline - splits one line into two 20 | JOIN_LINES, // Backspace at line start - joins two lines 21 | REPLACE_LINE // Entire line replacement (e.g., replaceLine call) 22 | }; 23 | 24 | OpType operation; 25 | 26 | // === Position Information === 27 | // Where the edit occurred (line/col BEFORE the edit) 28 | int startLine; 29 | int startCol; 30 | 31 | // For multi-line operations, where it ended 32 | int endLine; 33 | int endCol; 34 | 35 | // === Content === 36 | // Text that was deleted (needed to undo insertion) 37 | std::string deletedContent; 38 | 39 | // Text that was inserted (needed to redo/undo deletion) 40 | std::string insertedContent; 41 | 42 | // For SPLIT_LINE: content of line before split 43 | std::string lineBeforeSplit; 44 | 45 | // For JOIN_LINES: content of both lines before join 46 | std::string firstLineBeforeJoin; 47 | std::string secondLineBeforeJoin; 48 | 49 | // === Cursor State === 50 | // Where cursor was BEFORE edit 51 | int preCursorLine; 52 | int preCursorCol; 53 | 54 | // Where cursor is AFTER edit 55 | int postCursorLine; 56 | int postCursorCol; 57 | 58 | // === Structural Changes === 59 | // How many lines were added (positive) or removed (negative) 60 | int lineCountDelta; 61 | 62 | // === Viewport State === 63 | int preViewportTop; 64 | int preViewportLeft; 65 | int postViewportTop; 66 | int postViewportLeft; 67 | 68 | // === Metadata === 69 | std::chrono::steady_clock::time_point timestamp; 70 | 71 | // Constructor 72 | EditDelta() 73 | : operation(INSERT_CHAR), startLine(0), startCol(0), endLine(0), 74 | endCol(0), preCursorLine(0), preCursorCol(0), postCursorLine(0), 75 | postCursorCol(0), lineCountDelta(0), preViewportTop(0), 76 | preViewportLeft(0), postViewportTop(0), postViewportLeft(0), 77 | timestamp(std::chrono::steady_clock::now()) 78 | { 79 | } 80 | 81 | // Helper: Get memory size for debugging 82 | size_t getMemorySize() const 83 | { 84 | return sizeof(*this) + deletedContent.capacity() + 85 | insertedContent.capacity() + lineBeforeSplit.capacity() + 86 | firstLineBeforeJoin.capacity() + secondLineBeforeJoin.capacity(); 87 | } 88 | 89 | // Debug string 90 | std::string toString() const 91 | { 92 | std::ostringstream oss; 93 | oss << "Op: "; 94 | switch (operation) 95 | { 96 | case INSERT_CHAR: 97 | oss << "INSERT_CHAR"; 98 | break; 99 | case DELETE_CHAR: 100 | oss << "DELETE_CHAR"; 101 | break; 102 | case INSERT_TEXT: 103 | oss << "INSERT_TEXT"; 104 | break; 105 | case DELETE_TEXT: 106 | oss << "DELETE_TEXT"; 107 | break; 108 | case SPLIT_LINE: 109 | oss << "SPLIT_LINE"; 110 | break; 111 | case JOIN_LINES: 112 | oss << "JOIN_LINES"; 113 | break; 114 | case REPLACE_LINE: 115 | oss << "REPLACE_LINE"; 116 | break; 117 | } 118 | oss << " | Pos: (" << startLine << "," << startCol << ")"; 119 | oss << " | Cursor: (" << preCursorLine << "," << preCursorCol << ") -> (" 120 | << postCursorLine << "," << postCursorCol << ")"; 121 | oss << " | Lines: " << (lineCountDelta >= 0 ? "+" : "") << lineCountDelta; 122 | return oss.str(); 123 | } 124 | }; 125 | 126 | // A compound state that groups related deltas together 127 | // This is what gets pushed to undo/redo stacks 128 | struct DeltaGroup 129 | { 130 | std::vector<EditDelta> deltas; 131 | std::chrono::steady_clock::time_point timestamp; 132 | 133 | // Initial state (for safety/validation) 134 | int initialLineCount; 135 | size_t initialBufferSize; 136 | 137 | DeltaGroup() 138 | : timestamp(std::chrono::steady_clock::now()), initialLineCount(0), 139 | initialBufferSize(0) 140 | { 141 | } 142 | 143 | void addDelta(const EditDelta &delta) { deltas.push_back(delta); } 144 | 145 | bool isEmpty() const { return deltas.empty(); } 146 | 147 | size_t getMemorySize() const 148 | { 149 | size_t total = sizeof(*this); 150 | for (const auto &delta : deltas) 151 | { 152 | total += delta.getMemorySize(); 153 | } 154 | return total; 155 | } 156 | 157 | std::string toString() const 158 | { 159 | std::ostringstream oss; 160 | oss << "DeltaGroup: " << deltas.size() << " delta(s), " << getMemorySize() 161 | << " bytes\n"; 162 | for (size_t i = 0; i < deltas.size(); ++i) 163 | { 164 | oss << " [" << i << "] " << deltas[i].toString() << "\n"; 165 | } 166 | return oss.str(); 167 | } 168 | }; 169 | 170 | #endif // EDITOR_DELTA_H ``` -------------------------------------------------------------------------------- /src/core/editor.h: -------------------------------------------------------------------------------- ``` 1 | #ifndef EDITOR_H 2 | #define EDITOR_H 3 | 4 | #include <stack> 5 | #include <string> 6 | 7 | #ifdef _WIN32 8 | #include <curses.h> 9 | #else 10 | #include <ncurses.h> 11 | #endif 12 | 13 | #include "buffer.h" 14 | #include "editor_delta.h" 15 | #include "editor_validation.h" 16 | #include "src/features/syntax_highlighter.h" 17 | 18 | // Undo/Redo system 19 | struct EditorState 20 | { 21 | std::string content; 22 | int cursorLine; 23 | int cursorCol; 24 | int viewportTop; 25 | int viewportLeft; 26 | }; 27 | 28 | enum CursorMode 29 | { 30 | NORMAL, 31 | INSERT, 32 | VISUAL 33 | }; 34 | 35 | class Editor 36 | { 37 | public: 38 | // Core API 39 | Editor(SyntaxHighlighter *highlighter); 40 | void setSyntaxHighlighter(SyntaxHighlighter *highlighter); 41 | bool loadFile(const std::string &fname); 42 | bool saveFile(); 43 | void display(); 44 | void drawStatusBar(); 45 | void handleResize(); 46 | void handleMouse(MEVENT &event); 47 | 48 | std::string getFilename() const { return filename; } 49 | std::string getFirstLine() const { return buffer.getLine(0); } 50 | GapBuffer getBuffer() { return buffer; } 51 | 52 | // Movement 53 | void moveCursorUp(); 54 | void moveCursorDown(); 55 | void moveCursorLeft(); 56 | void moveCursorRight(); 57 | void pageUp(); 58 | void pageDown(); 59 | void moveCursorToLineStart(); 60 | void moveCursorToLineEnd(); 61 | void scrollUp(int lines = 3); 62 | void scrollDown(int lines = 3); 63 | void validateCursorAndViewport(); 64 | void positionCursor(); 65 | 66 | // Text editing 67 | void insertChar(char ch); 68 | void insertNewline(); 69 | void deleteChar(); 70 | void backspace(); 71 | void deleteLine(); 72 | 73 | // Selection management 74 | void clearSelection(); 75 | void startSelectionIfNeeded(); 76 | void updateSelectionEnd(); 77 | void deleteSelection(); 78 | std::string getSelectedText(); 79 | 80 | // Clipboard operations 81 | void copySelection(); 82 | void cutSelection(); 83 | void pasteFromClipboard(); 84 | void selectAll(); 85 | 86 | // Undo/Redo 87 | 88 | void saveState(); 89 | void undo(); 90 | void redo(); 91 | 92 | // Utility 93 | bool hasUnsavedChanges() const { return isModified; } 94 | void reloadConfig(); 95 | void initializeViewportHighlighting(); 96 | void updateSyntaxHighlighting(); 97 | 98 | // Debug 99 | void debugPrintState(const std::string &context); 100 | bool validateEditorState(); 101 | 102 | // Selection state 103 | int selectionStartLine = 0; 104 | int selectionStartCol = 0; 105 | int selectionEndLine = 0; 106 | int selectionEndCol = 0; 107 | bool hasSelection = false; 108 | bool isSelecting = false; 109 | 110 | // Editor cursor 111 | void setCursorMode(); 112 | CursorMode getCursorMode() const { return currentMode; }; 113 | 114 | // Validation 115 | EditorSnapshot captureSnapshot() const; 116 | ValidationResult validateState(const std::string &context) const; 117 | std::string compareSnapshots(const EditorSnapshot &before, 118 | const EditorSnapshot &after) const; 119 | 120 | // Delta undo configuration 121 | void beginDeltaGroup(); 122 | void setDeltaUndoEnabled(bool enabled) { useDeltaUndo_ = enabled; } 123 | bool isDeltaUndoEnabled() const { return useDeltaUndo_; } 124 | 125 | // Debug/stats 126 | size_t getUndoMemoryUsage() const; 127 | size_t getRedoMemoryUsage() const; 128 | 129 | private: 130 | // Core data 131 | GapBuffer buffer; 132 | std::string filename; 133 | SyntaxHighlighter *syntaxHighlighter; 134 | bool isSaving = false; 135 | 136 | // Delta 137 | bool useDeltaUndo_ = false; // Feature flag - start disabled! 138 | std::stack<DeltaGroup> deltaUndoStack_; 139 | std::stack<DeltaGroup> deltaRedoStack_; 140 | DeltaGroup currentDeltaGroup_; // Accumulates deltas for grouping 141 | 142 | // Delta operations 143 | void addDelta(const EditDelta &delta); 144 | void commitDeltaGroup(); 145 | void applyDeltaForward(const EditDelta &delta); 146 | void applyDeltaReverse(const EditDelta &delta); 147 | // Helper to create delta from current operation 148 | EditDelta createDeltaForInsertChar(char ch); 149 | EditDelta createDeltaForDeleteChar(); 150 | EditDelta createDeltaForBackspace(); 151 | EditDelta createDeltaForNewline(); 152 | EditDelta createDeltaForDeleteSelection(); 153 | // Viewport and cursor 154 | int viewportTop = 0; 155 | int viewportLeft = 0; 156 | int viewportHeight; 157 | int cursorLine = 0; 158 | int cursorCol = 0; 159 | 160 | // Clipboard 161 | std::string clipboard; 162 | 163 | // Undo/Redo 164 | std::chrono::steady_clock::time_point lastEditTime; 165 | static constexpr int UNDO_GROUP_TIMEOUT_MS = 2000; 166 | bool isUndoRedoing = false; // Add this flag 167 | std::stack<EditorState> undoStack; 168 | std::stack<EditorState> redoStack; 169 | static const size_t MAX_UNDO_LEVELS = 100; 170 | 171 | // File state 172 | bool isModified = false; 173 | int tabSize = 4; 174 | 175 | // Private helpers 176 | std::string expandTabs(const std::string &line, int tabSize = 4); 177 | std::string getFileExtension(); 178 | bool isPositionSelected(int line, int col); 179 | bool mouseToFilePos(int mouseRow, int mouseCol, int &fileRow, int &fileCol); 180 | void updateCursorAndViewport(int newLine, int newCol); 181 | void markModified(); 182 | void splitLineAtCursor(); 183 | void joinLineWithNext(); 184 | EditorState getCurrentState(); 185 | void restoreState(const EditorState &state); 186 | void limitUndoStack(); 187 | std::pair<std::pair<int, int>, std::pair<int, int>> getNormalizedSelection(); 188 | 189 | void notifyTreeSitterEdit(const EditDelta &delta, bool isReverse); 190 | void optimizedLineInvalidation(int startLine, int endLine); 191 | 192 | // Cursor Style 193 | CursorMode currentMode = NORMAL; 194 | }; 195 | 196 | #endif // EDITOR_H ``` -------------------------------------------------------------------------------- /src/features/syntax_highlighter.h: -------------------------------------------------------------------------------- ``` 1 | // src/features/syntax_highlighter.h 2 | #pragma once 3 | 4 | #include "src/core/buffer.h" 5 | #include "src/core/config_manager.h" 6 | #include "src/features/markdown_state.h" 7 | #include "src/ui/style_manager.h" 8 | #include "syntax_config_loader.h" 9 | #include <map> 10 | #include <memory> 11 | #include <mutex> 12 | #include <string> 13 | #include <thread> 14 | #include <unordered_map> 15 | #include <unordered_set> 16 | #include <vector> 17 | 18 | #ifdef TREE_SITTER_ENABLED 19 | #include <tree_sitter/api.h> 20 | #endif 21 | 22 | struct ColorSpan 23 | { 24 | int start; 25 | int end; 26 | int colorPair; 27 | int attribute; 28 | int priority; 29 | }; 30 | 31 | class SyntaxHighlighter 32 | { 33 | public: 34 | SyntaxHighlighter(); 35 | ~SyntaxHighlighter(); 36 | 37 | // Initialize with config directory path 38 | bool initialize(const std::string &config_directory = "treesitter/"); 39 | void setSyntaxMode(SyntaxMode mode) { syntax_mode_ = mode; } 40 | int viewport_start_line_ = 0; 41 | bool is_full_parse_ = true; 42 | 43 | // Core functionality 44 | void setLanguage(const std::string &extension); 45 | std::vector<ColorSpan> getHighlightSpans(const std::string &line, int lineNum, 46 | const GapBuffer &buffer) const; 47 | 48 | // Buffer update notification for Tree-sitter 49 | void bufferChanged(const GapBuffer &buffer); 50 | 51 | // Get current language info 52 | std::string getCurrentLanguage() const { return currentLanguage; } 53 | void updateMarkdownState(const GapBuffer &buffer); 54 | std::vector<std::string> getSupportedExtensions() const; 55 | std::string computeBufferHash(const GapBuffer &buffer) const 56 | { 57 | std::string content; 58 | int lineCount = buffer.getLineCount(); 59 | for (int i = 0; i < lineCount; i++) 60 | { 61 | if (i > 0) 62 | content += "\n"; 63 | content += buffer.getLine(i); 64 | } 65 | return std::to_string(std::hash<std::string>{}(content)); 66 | }; 67 | void invalidateLineRange(int startLine, int endLine); 68 | 69 | void invalidateLineCache(int lineNum); 70 | 71 | void notifyEdit(size_t byte_pos, size_t old_byte_len, size_t new_byte_len, 72 | uint32_t start_row, uint32_t start_col, uint32_t old_end_row, 73 | uint32_t old_end_col, uint32_t new_end_row, 74 | uint32_t new_end_col); 75 | void markViewportLines(int startLine, int endLine) const; 76 | bool isLineHighlighted(int lineIndex) const; 77 | void debugTreeSitterState() const; 78 | void updateTreeAfterEdit(const GapBuffer &buffer, size_t byte_pos, 79 | size_t old_byte_len, size_t new_byte_len, 80 | uint32_t start_row, uint32_t start_col, 81 | uint32_t old_end_row, uint32_t old_end_col, 82 | uint32_t new_end_row, uint32_t new_end_col); 83 | void parseViewportOnly(const GapBuffer &buffer, int targetLine); 84 | void scheduleBackgroundParse(const GapBuffer &buffer); 85 | 86 | void forceFullReparse(const GapBuffer &buffer); 87 | void invalidateFromLine(int startLine); 88 | void clearAllCache(); 89 | 90 | private: 91 | // Configuration management 92 | std::unique_ptr<SyntaxConfigLoader> config_loader_; 93 | const LanguageConfig *current_language_config_; 94 | std::string currentLanguage; 95 | SyntaxMode syntax_mode_ = SyntaxMode::VIEWPORT; 96 | mutable bool tree_initialized_ = false; 97 | bool parse_pending_ = true; 98 | std::thread parse_thread_; 99 | std::atomic<bool> is_parsing_{false}; 100 | std::atomic<bool> parse_complete_{false}; 101 | mutable std::mutex tree_mutex_; 102 | mutable bool tree_needs_reparse_ = false; 103 | 104 | std::atomic<uint64_t> tree_version_{0}; 105 | void backgroundParse(const GapBuffer &buffer); 106 | 107 | // Markdown state tracking 108 | std::map<int, MarkdownState> line_states_; 109 | mutable std::map<int, std::vector<ColorSpan>> 110 | line_cache_; // Changed from unordered_map 111 | 112 | mutable std::string last_buffer_hash_; 113 | mutable std::unordered_map<int, bool> line_highlight_pending_; 114 | mutable std::unordered_set<int> priority_lines_; 115 | std::vector<uint32_t> line_byte_offsets_; // Cached byte offsets for each line 116 | 117 | std::chrono::steady_clock::time_point last_parse_time_; 118 | static constexpr int PARSE_DEBOUNCE_MS = 500; 119 | 120 | #ifdef TREE_SITTER_ENABLED 121 | // Tree-sitter state 122 | TSParser *parser_; 123 | TSTree *tree_; 124 | const TSLanguage *current_ts_language_; 125 | TSQuery *current_ts_query_; 126 | std::string current_buffer_content_; 127 | 128 | // NEW: Language function registry (auto-populated from generated header) 129 | std::unordered_map<std::string, const TSLanguage *(*)()> language_registry_; 130 | 131 | void diagnoseGrammar() const; 132 | 133 | // Tree-sitter management methods 134 | bool initializeTreeSitter(); 135 | void cleanupTreeSitter(); 136 | const TSLanguage *getLanguageFunction(const std::string &parser_name); 137 | TSQuery *loadQueryFromFile(const std::string &query_file_path); 138 | void updateTree(const GapBuffer &buffer); 139 | void debugParseTree(const std::string &code) const; 140 | 141 | // Query execution 142 | std::vector<ColorSpan> executeTreeSitterQuery(const std::string &line, 143 | int lineNum) const; 144 | int getColorPairForCapture(const std::string &capture_name) const; 145 | #endif 146 | 147 | // Color and attribute mapping 148 | int getColorPairValue(const std::string &color_name) const; 149 | int getAttributeValue(const std::string &attribute_name) const; 150 | 151 | // Fallback highlighting 152 | std::vector<ColorSpan> getBasicHighlightSpans(const std::string &line) const; 153 | void loadBasicRules(); 154 | }; ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/tree-sitter-markdown/test/corpus/extension_pipe_table.txt: -------------------------------------------------------------------------------- ``` 1 | ================================================================================ 2 | Example 198 - https://github.github.com/gfm/#example-198 3 | ================================================================================ 4 | | foo | bar | 5 | | --- | --- | 6 | | baz | bim | 7 | 8 | -------------------------------------------------------------------------------- 9 | 10 | (document 11 | (section 12 | (pipe_table 13 | (pipe_table_header 14 | (pipe_table_cell) 15 | (pipe_table_cell)) 16 | (pipe_table_delimiter_row 17 | (pipe_table_delimiter_cell) 18 | (pipe_table_delimiter_cell)) 19 | (pipe_table_row 20 | (pipe_table_cell) 21 | (pipe_table_cell))))) 22 | 23 | ================================================================================ 24 | Example 199 - https://github.github.com/gfm/#example-199 25 | ================================================================================ 26 | | abc | defghi | 27 | :-: | -----------: 28 | bar | baz 29 | 30 | -------------------------------------------------------------------------------- 31 | 32 | (document 33 | (section 34 | (pipe_table 35 | (pipe_table_header 36 | (pipe_table_cell) 37 | (pipe_table_cell)) 38 | (pipe_table_delimiter_row 39 | (pipe_table_delimiter_cell 40 | (pipe_table_align_left) 41 | (pipe_table_align_right)) 42 | (pipe_table_delimiter_cell 43 | (pipe_table_align_right))) 44 | (pipe_table_row 45 | (pipe_table_cell) 46 | (pipe_table_cell))))) 47 | 48 | ================================================================================ 49 | Example 200 - https://github.github.com/gfm/#example-200 50 | ================================================================================ 51 | | f\|oo | 52 | | ------ | 53 | | b `\|` az | 54 | | b **\|** im | 55 | 56 | -------------------------------------------------------------------------------- 57 | 58 | (document 59 | (section 60 | (pipe_table 61 | (pipe_table_header 62 | (pipe_table_cell)) 63 | (pipe_table_delimiter_row 64 | (pipe_table_delimiter_cell)) 65 | (pipe_table_row 66 | (pipe_table_cell)) 67 | (pipe_table_row 68 | (pipe_table_cell))))) 69 | 70 | ================================================================================ 71 | Example 201 - https://github.github.com/gfm/#example-201 72 | ================================================================================ 73 | | abc | def | 74 | | --- | --- | 75 | | bar | baz | 76 | > bar 77 | 78 | -------------------------------------------------------------------------------- 79 | 80 | (document 81 | (section 82 | (pipe_table 83 | (pipe_table_header 84 | (pipe_table_cell) 85 | (pipe_table_cell)) 86 | (pipe_table_delimiter_row 87 | (pipe_table_delimiter_cell) 88 | (pipe_table_delimiter_cell)) 89 | (pipe_table_row 90 | (pipe_table_cell) 91 | (pipe_table_cell))) 92 | (block_quote 93 | (block_quote_marker) 94 | (paragraph 95 | (inline))))) 96 | 97 | ================================================================================ 98 | Example 202 - https://github.github.com/gfm/#example-202 99 | ================================================================================ 100 | | abc | def | 101 | | --- | --- | 102 | | bar | baz | 103 | bar 104 | 105 | bar 106 | 107 | -------------------------------------------------------------------------------- 108 | 109 | (document 110 | (section 111 | (pipe_table 112 | (pipe_table_header 113 | (pipe_table_cell) 114 | (pipe_table_cell)) 115 | (pipe_table_delimiter_row 116 | (pipe_table_delimiter_cell) 117 | (pipe_table_delimiter_cell)) 118 | (pipe_table_row 119 | (pipe_table_cell) 120 | (pipe_table_cell)) 121 | (pipe_table_row 122 | (pipe_table_cell))) 123 | (paragraph 124 | (inline)))) 125 | 126 | ================================================================================ 127 | Example 203 - https://github.github.com/gfm/#example-203 128 | ================================================================================ 129 | | abc | def | 130 | | --- | 131 | | bar | 132 | 133 | -------------------------------------------------------------------------------- 134 | 135 | (document 136 | (section 137 | (paragraph 138 | (inline)))) 139 | 140 | ================================================================================ 141 | Example 204 - https://github.github.com/gfm/#example-204 142 | ================================================================================ 143 | | abc | def | 144 | | --- | --- | 145 | | bar | 146 | | bar | baz | boo | 147 | 148 | -------------------------------------------------------------------------------- 149 | 150 | (document 151 | (section 152 | (pipe_table 153 | (pipe_table_header 154 | (pipe_table_cell) 155 | (pipe_table_cell)) 156 | (pipe_table_delimiter_row 157 | (pipe_table_delimiter_cell) 158 | (pipe_table_delimiter_cell)) 159 | (pipe_table_row 160 | (pipe_table_cell)) 161 | (pipe_table_row 162 | (pipe_table_cell) 163 | (pipe_table_cell) 164 | (pipe_table_cell))))) 165 | 166 | ================================================================================ 167 | Example 205 - https://github.github.com/gfm/#example-205 168 | ================================================================================ 169 | | abc | def | 170 | | --- | --- | 171 | 172 | -------------------------------------------------------------------------------- 173 | 174 | (document 175 | (section 176 | (pipe_table 177 | (pipe_table_header 178 | (pipe_table_cell) 179 | (pipe_table_cell)) 180 | (pipe_table_delimiter_row 181 | (pipe_table_delimiter_cell) 182 | (pipe_table_delimiter_cell))))) 183 | 184 | ================================================================================ 185 | #112 - Works with table cells that only contain whitespce 186 | ================================================================================ 187 | | foo | bar | 188 | | --- | --- | 189 | | | bim | 190 | 191 | -------------------------------------------------------------------------------- 192 | 193 | (document 194 | (section 195 | (pipe_table 196 | (pipe_table_header 197 | (pipe_table_cell) 198 | (pipe_table_cell)) 199 | (pipe_table_delimiter_row 200 | (pipe_table_delimiter_cell) 201 | (pipe_table_delimiter_cell)) 202 | (pipe_table_row 203 | (pipe_table_cell) 204 | (pipe_table_cell))))) 205 | ``` -------------------------------------------------------------------------------- /src/ui/style_manager.h: -------------------------------------------------------------------------------- ``` 1 | #pragma once 2 | 3 | #include <map> 4 | #include <string> 5 | 6 | // Platform-specific ncurses includes 7 | #ifdef _WIN32 8 | #include <curses.h> 9 | #else 10 | #include <ncurses.h> 11 | #endif 12 | 13 | // UNIFIED ColorPairs enum - this replaces the one in colors.h 14 | // UNIFIED ColorPairs enum - updated to match the new apply_theme() 15 | // implementation 16 | enum ColorPairs 17 | { 18 | // Reserved pairs 19 | DEFAULT_PAIR = 0, 20 | BACKGROUND_PAIR = 1, 21 | 22 | // UI Elements 23 | LINE_NUMBERS = 2, 24 | LINE_NUMBERS_ACTIVE = 3, 25 | LINE_NUMBERS_DIM = 4, 26 | 27 | // Status bar 28 | STATUS_BAR = 5, 29 | STATUS_BAR_TEXT = 6, 30 | STATUS_BAR_ACTIVE = 7, 31 | STATUS_BAR_CYAN = 8, 32 | STATUS_BAR_YELLOW = 9, 33 | STATUS_BAR_GREEN = 10, 34 | STATUS_BAR_MAGENTA = 11, 35 | STATUS_BAR_DIM = 12, 36 | 37 | // Selection and cursor 38 | CURSOR = 13, 39 | SELECTION = 14, 40 | LINE_HIGHLIGHT = 15, 41 | 42 | // Semantic categories (20-39) 43 | KEYWORD = 20, 44 | STRING_LITERAL = 21, // Keep name for backward compat 45 | NUMBER = 22, 46 | COMMENT = 23, 47 | FUNCTION = 24, 48 | VARIABLE = 25, 49 | TYPE = 26, 50 | OPERATOR = 27, 51 | PUNCTUATION = 28, 52 | CONSTANT = 29, 53 | NAMESPACE = 30, 54 | PROPERTY = 31, 55 | DECORATOR = 32, 56 | MACRO = 33, 57 | LABEL = 34, 58 | 59 | // Markup (50-59) - Keep for Markdown 60 | MARKUP_HEADING = 50, 61 | MARKUP_BOLD = 51, 62 | MARKUP_ITALIC = 52, 63 | MARKUP_CODE = 53, 64 | MARKUP_CODE_BLOCK = 54, 65 | MARKUP_LINK = 55, 66 | MARKUP_URL = 56, 67 | MARKUP_BLOCKQUOTE = 57, 68 | MARKUP_LIST = 58, 69 | MARKUP_TABLE = 59, 70 | MARKUP_STRIKETHROUGH = 60, 71 | MARKUP_QUOTE = 61, 72 | 73 | // Special pairs 74 | ACTIVE_LINE_BG = 70 75 | }; 76 | 77 | // ThemeColor enum - semantic color names (kept for backward compatibility with 78 | // load_default_theme) 79 | enum class ThemeColor 80 | { 81 | TERMINAL, 82 | BLACK, 83 | DARK_GRAY, 84 | GRAY, 85 | LIGHT_GRAY, 86 | WHITE, 87 | RED, 88 | GREEN, 89 | BLUE, 90 | YELLOW, 91 | MAGENTA, 92 | CYAN, 93 | BRIGHT_RED, 94 | BRIGHT_GREEN, 95 | BRIGHT_BLUE, 96 | BRIGHT_YELLOW, 97 | BRIGHT_MAGENTA, 98 | BRIGHT_CYAN 99 | }; 100 | 101 | // RGB color structure for hex parsing 102 | struct RGB 103 | { 104 | int r, g, b; // 0-255 range 105 | RGB() : r(0), g(0), b(0) {} 106 | RGB(int red, int green, int blue) : r(red), g(green), b(blue) {} 107 | }; 108 | 109 | struct NamedTheme 110 | { 111 | std::string name; 112 | std::string background; 113 | std::string foreground; 114 | std::string cursor; 115 | std::string selection; 116 | std::string line_highlight; 117 | std::string line_numbers; 118 | std::string line_numbers_active; 119 | std::string status_bar_bg; 120 | std::string status_bar_fg; 121 | std::string status_bar_active; 122 | 123 | // Semantic categories 124 | std::string keyword; 125 | std::string string_literal; 126 | std::string number; 127 | std::string comment; 128 | std::string function_name; 129 | std::string variable; 130 | std::string type; 131 | std::string operator_color; 132 | std::string punctuation; 133 | std::string constant; 134 | std::string namespace_color; 135 | std::string property; 136 | std::string decorator; 137 | std::string macro; 138 | std::string label; 139 | 140 | // Markup 141 | std::string markup_heading; 142 | std::string markup_bold; 143 | std::string markup_italic; 144 | std::string markup_code; 145 | std::string markup_code_block; 146 | std::string markup_link; 147 | std::string markup_url; 148 | std::string markup_list; 149 | std::string markup_blockquote; 150 | std::string markup_strikethrough; 151 | std::string markup_quote; 152 | }; 153 | 154 | // Legacy Theme struct for compatibility (keep your old colors.h expectations) 155 | struct Theme 156 | { 157 | std::string name; 158 | int line_numbers_fg, line_numbers_bg; 159 | int status_bar_fg, status_bar_bg; 160 | int keyword_fg, keyword_bg; 161 | int string_fg, string_bg; 162 | int comment_fg, comment_bg; 163 | int number_fg, number_bg; 164 | int preprocessor_fg, preprocessor_bg; 165 | int function_fg, function_bg; 166 | int operator_fg, operator_bg; 167 | int markdown_heading_fg, markdown_heading_bg; 168 | int markdown_bold_fg, markdown_bold_bg; 169 | int markdown_italic_fg, markdown_italic_bg; 170 | int markdown_code_fg, markdown_code_bg; 171 | int markdown_link_fg, markdown_link_bg; 172 | }; 173 | 174 | class StyleManager 175 | { 176 | public: 177 | StyleManager(); 178 | 179 | // Core theme system 180 | void initialize(); 181 | bool load_theme_from_file(const std::string &file_path); 182 | bool load_theme_from_yaml(const std::string &yaml_content); 183 | const NamedTheme &get_current_theme() const { return current_theme; } 184 | std::string get_theme_name() const { return current_theme.name; } 185 | bool is_initialized() const { return initialized; } 186 | 187 | // Helper methods (legacy - kept for compatibility) 188 | int theme_color_to_ncurses_color(ThemeColor color) const; 189 | int theme_color_to_ncurses_attr(ThemeColor color) const; 190 | 191 | // Terminal capability detection 192 | bool supports_256_colors() const; 193 | bool supports_true_color() const; 194 | 195 | // Legacy compatibility functions (for old colors.h API) 196 | Theme get_legacy_theme(); 197 | void apply_legacy_theme(const Theme &theme); 198 | void apply_color_pair(int pair_id, ThemeColor theme_color) const; 199 | bool is_light_theme() const; 200 | void optimize_for_wsl(); 201 | bool is_wsl_environment() const; 202 | 203 | private: 204 | bool initialized; 205 | NamedTheme current_theme; 206 | 207 | // New private members for 256-color support 208 | bool supports_256_colors_cache; 209 | std::map<std::string, short> 210 | color_cache; // Maps Hex code to ncurses ID (>= 16) 211 | short next_custom_color_id; // Starts at 16 212 | 213 | void load_default_theme(); 214 | void apply_theme(); 215 | 216 | ThemeColor string_to_theme_color(const std::string &color_name) const; 217 | 218 | // New core color resolution function 219 | short resolve_theme_color(const std::string &config_value); 220 | 221 | // Hex parsing utilities 222 | RGB parse_hex_color(const std::string &hex_str) const; 223 | short find_closest_8color(const RGB &rgb) const; 224 | short find_closest_256color(const RGB &rgb) const; 225 | short rgb_to_xterm256(const RGB &rgb) const; 226 | 227 | std::string trim(const std::string &str); 228 | std::string remove_quotes(const std::string &str); 229 | std::map<std::string, std::string> 230 | parse_yaml(const std::string &yaml_content); 231 | }; 232 | 233 | // Global instance 234 | extern StyleManager g_style_manager; 235 | 236 | // Legacy API functions for backward compatibility 237 | void init_colors(); 238 | void load_default_theme(); 239 | void apply_theme(const Theme &theme); 240 | Theme get_current_theme(); ``` -------------------------------------------------------------------------------- /deps/tree-sitter-markdown/common/common.js: -------------------------------------------------------------------------------- ```javascript 1 | /// <reference types="tree-sitter-cli/dsl" /> 2 | 3 | module.exports.EXTENSION_DEFAULT = !process.env.NO_DEFAULT_EXTENSIONS; 4 | module.exports.EXTENSION_GFM = process.env.EXTENSION_GFM || module.exports.EXTENSION_DEFAULT || process.env.ALL_EXTENSIONS; 5 | module.exports.EXTENSION_TASK_LIST = process.env.EXTENSION_TASK_LIST || module.exports.EXTENSION_GFM || process.env.ALL_EXTENSIONS; 6 | module.exports.EXTENSION_STRIKETHROUGH = process.env.EXTENSION_STRIKETHROUGH || module.exports.EXTENSION_GFM || process.env.ALL_EXTENSIONS; 7 | module.exports.EXTENSION_PIPE_TABLE = process.env.EXTENSION_PIPE_TABLE || module.exports.EXTENSION_GFM || process.env.ALL_EXTENSIONS; 8 | module.exports.EXTENSION_MINUS_METADATA = process.env.EXTENSION_MINUS_METADATA || module.exports.EXTENSION_DEFAULT || process.env.ALL_EXTENSIONS; 9 | module.exports.EXTENSION_PLUS_METADATA = process.env.EXTENSION_PLUS_METADATA || module.exports.EXTENSION_DEFAULT || process.env.ALL_EXTENSIONS; 10 | module.exports.EXTENSION_TAGS = process.env.EXTENSION_TAGS || process.env.ALL_EXTENSIONS; 11 | module.exports.EXTENSION_LATEX = process.env.EXTENSION_LATEX || module.exports.EXTENSION_DEFAULT || process.env.ALL_EXTENSIONS; 12 | module.exports.EXTENSION_WIKI_LINK = process.env.EXTENSION_WIKI_LINK || process.env.ALL_EXTENSIONS; 13 | 14 | const PUNCTUATION_CHARACTERS_REGEX = '!-/:-@\\[-`\\{-~'; 15 | const PUNCTUATION_CHARACTERS_ARRAY = [ 16 | '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', 17 | '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~' 18 | ]; 19 | 20 | const PRECEDENCE_LEVEL_LINK = 10; 21 | 22 | module.exports.PRECEDENCE_LEVEL_LINK = PRECEDENCE_LEVEL_LINK; 23 | 24 | module.exports.PUNCTUATION_CHARACTERS_REGEX = PUNCTUATION_CHARACTERS_REGEX; 25 | 26 | /** @type {Record<string, ($: GrammarSymbols<any>) => RuleOrLiteral>} */ 27 | module.exports.rules = { 28 | 29 | // A backslash escape. This can often be part of different nodes like link labels 30 | // 31 | // https://github.github.com/gfm/#backslash-escapes 32 | backslash_escape: $ => $._backslash_escape, 33 | _backslash_escape: $ => new RegExp('\\\\[' + PUNCTUATION_CHARACTERS_REGEX + ']'), 34 | 35 | // HTML entity and numeric character references. 36 | // 37 | // The regex for entity references are build from the html_entities.json file. 38 | // 39 | // https://github.github.com/gfm/#entity-and-numeric-character-references 40 | entity_reference: $ => html_entity_regex(), 41 | numeric_character_reference: $ => /&#([0-9]{1,7}|[xX][0-9a-fA-F]{1,6});/, 42 | 43 | link_label: $ => seq('[', repeat1(choice( 44 | $._text_inline_no_link, 45 | $.backslash_escape, 46 | $.entity_reference, 47 | $.numeric_character_reference, 48 | $._soft_line_break 49 | )), ']'), 50 | 51 | link_destination: $ => prec.dynamic(PRECEDENCE_LEVEL_LINK, choice( 52 | seq('<', repeat(choice($._text_no_angle, $.backslash_escape, $.entity_reference, $.numeric_character_reference)), '>'), 53 | seq( 54 | choice( // first character is not a '<' 55 | $._word, 56 | punctuation_without($, ['<', '(', ')']), 57 | $.backslash_escape, 58 | $.entity_reference, 59 | $.numeric_character_reference, 60 | $._link_destination_parenthesis 61 | ), 62 | repeat(choice( 63 | $._word, 64 | punctuation_without($, ['(', ')']), 65 | $.backslash_escape, 66 | $.entity_reference, 67 | $.numeric_character_reference, 68 | $._link_destination_parenthesis 69 | )), 70 | ) 71 | )), 72 | _link_destination_parenthesis: $ => seq('(', repeat(choice( 73 | $._word, 74 | punctuation_without($, ['(', ')']), 75 | $.backslash_escape, 76 | $.entity_reference, 77 | $.numeric_character_reference, 78 | $._link_destination_parenthesis 79 | )), ')'), 80 | _text_no_angle: $ => choice($._word, punctuation_without($, ['<', '>']), $._whitespace), 81 | link_title: $ => choice( 82 | seq('"', repeat(choice( 83 | $._word, 84 | punctuation_without($, ['"']), 85 | $._whitespace, 86 | $.backslash_escape, 87 | $.entity_reference, 88 | $.numeric_character_reference, 89 | seq($._soft_line_break, optional(seq($._soft_line_break, $._trigger_error))) 90 | )), '"'), 91 | seq("'", repeat(choice( 92 | $._word, 93 | punctuation_without($, ["'"]), 94 | $._whitespace, 95 | $.backslash_escape, 96 | $.entity_reference, 97 | $.numeric_character_reference, 98 | seq($._soft_line_break, optional(seq($._soft_line_break, $._trigger_error))) 99 | )), "'"), 100 | seq('(', repeat(choice( 101 | $._word, 102 | punctuation_without($, ['(', ')']), 103 | $._whitespace, 104 | $.backslash_escape, 105 | $.entity_reference, 106 | $.numeric_character_reference, 107 | seq($._soft_line_break, optional(seq($._soft_line_break, $._trigger_error))) 108 | )), ')'), 109 | ), 110 | 111 | _newline_token: $ => /\n|\r\n?/, 112 | }; 113 | 114 | // Returns a rule that matches all characters that count as punctuation inside markdown, besides 115 | // a list of excluded punctuation characters. Calling this function with a empty list as the second 116 | // argument returns a rule that matches all punctuation. 117 | function punctuation_without($, chars) { 118 | return seq(choice(...PUNCTUATION_CHARACTERS_ARRAY.filter(c => !chars.includes(c))), optional($._last_token_punctuation)); 119 | } 120 | 121 | module.exports.punctuation_without = punctuation_without; 122 | 123 | // Constructs a regex that matches all html entity references. 124 | function html_entity_regex() { 125 | // A file with all html entities, should be kept up to date with 126 | // https://html.spec.whatwg.org/multipage/entities.json 127 | let html_entities = require("./html_entities.json"); 128 | let s = '&('; 129 | s += Object.keys(html_entities).map(name => name.substring(1, name.length - 1)).join('|'); 130 | s += ');'; 131 | return new RegExp(s); 132 | } 133 | ``` -------------------------------------------------------------------------------- /src/ui/input_handler.cpp: -------------------------------------------------------------------------------- ```cpp 1 | #include "input_handler.h" 2 | #include <fstream> 3 | #include <iostream> 4 | #include <optional> 5 | 6 | // PDCursesMod key code compatibility 7 | #define CTRL(x) ((x) & 0x1f) 8 | #define KEY_TAB 9 9 | #define KEY_ENTER 10 10 | #define KEY_ESC 27 11 | #define KEY_BACKSPACE_ALT 127 12 | 13 | #ifdef _WIN32 14 | #define GETMOUSE_FUNC nc_getmouse 15 | 16 | // PDCursesMod VT-mode extended key codes 17 | #define PDC_KEY_UP 60418 18 | #define PDC_KEY_DOWN 60419 19 | #define PDC_KEY_RIGHT 60420 20 | #define PDC_KEY_LEFT 60421 21 | 22 | #else 23 | #define GETMOUSE_FUNC getmouse 24 | #endif 25 | 26 | InputHandler::InputHandler(Editor &editor) 27 | : editor_(editor), mouse_enabled_(true) 28 | { 29 | } 30 | 31 | InputHandler::KeyResult InputHandler::handleKey(int key) 32 | { 33 | // Handle special events first 34 | if (key == KEY_MOUSE && mouse_enabled_) 35 | { 36 | return handleMouseEvent(); 37 | } 38 | 39 | if (key == KEY_RESIZE) 40 | { 41 | return handleResizeEvent(); 42 | } 43 | 44 | // Global shortcuts (Ctrl+S, Ctrl+Z, etc.) 45 | if (auto result = handleGlobalShortcut(key)) 46 | { 47 | return *result; // Return whatever KeyResult it gave us 48 | } 49 | 50 | // Movement keys (handles both normal and shift+movement for selection) 51 | if (handleMovementKey(key, false)) 52 | { 53 | return KeyResult::REDRAW; 54 | } 55 | 56 | // Editing keys (backspace, delete, enter, tab) 57 | if (handleEditingKey(key)) 58 | { 59 | return KeyResult::REDRAW; 60 | } 61 | 62 | // Insert printable characters 63 | if (isPrintableChar(key)) 64 | { 65 | // Delete selection first if one exists 66 | if (editor_.hasSelection || editor_.isSelecting) 67 | { 68 | editor_.deleteSelection(); 69 | } 70 | 71 | editor_.insertChar(static_cast<char>(key)); 72 | return KeyResult::REDRAW; 73 | } 74 | 75 | return KeyResult::NOT_HANDLED; 76 | } 77 | 78 | std::optional<InputHandler::KeyResult> 79 | InputHandler::handleGlobalShortcut(int key) 80 | { 81 | switch (key) 82 | { 83 | case CTRL('s'): 84 | editor_.saveFile(); 85 | return KeyResult::REDRAW; 86 | 87 | case CTRL('z'): 88 | // std::cerr << "Clicked CTRL+Z!"; 89 | editor_.undo(); 90 | return KeyResult::REDRAW; 91 | 92 | case CTRL('y'): 93 | editor_.redo(); 94 | return KeyResult::REDRAW; 95 | 96 | case CTRL('q'): 97 | // // TODO: Check for unsaved changes and prompt 98 | // if (editor_.hasUnsavedChanges()) 99 | // { 100 | // // For now, just quit - add confirmation dialog later 101 | // exit(0); 102 | // } 103 | // exit(0); 104 | // return true; 105 | return KeyResult::QUIT; 106 | 107 | case CTRL('c'): 108 | editor_.copySelection(); 109 | return KeyResult::REDRAW; 110 | 111 | case CTRL('x'): 112 | editor_.cutSelection(); 113 | return KeyResult::REDRAW; 114 | 115 | case CTRL('v'): 116 | editor_.pasteFromClipboard(); 117 | return KeyResult::REDRAW; 118 | 119 | case CTRL('a'): 120 | editor_.selectAll(); 121 | return KeyResult::REDRAW; 122 | 123 | default: 124 | return std::nullopt; // No shortcut handled 125 | } 126 | } 127 | 128 | bool InputHandler::handleMovementKey(int key, bool shift_held) 129 | { 130 | // Detect if shift is being held for this key 131 | bool extending_selection = false; 132 | 133 | #ifdef _WIN32 134 | if (PDC_get_key_modifiers() & PDC_KEY_MODIFIER_SHIFT) 135 | { 136 | extending_selection = true; 137 | } 138 | #endif 139 | 140 | // Check for shift-modified arrow keys (Unix/ncurses) 141 | switch (key) 142 | { 143 | case KEY_SLEFT: 144 | case KEY_SRIGHT: 145 | case KEY_SR: 146 | case KEY_SF: 147 | #ifdef _WIN32 148 | case KEY_SUP: 149 | case KEY_SDOWN: 150 | #endif 151 | extending_selection = true; 152 | break; 153 | } 154 | 155 | // IMPROVED: Start selection BEFORE movement if shift held 156 | if (extending_selection) 157 | { 158 | editor_.startSelectionIfNeeded(); 159 | } 160 | else 161 | { 162 | // Clear selection BEFORE movement if no shift 163 | editor_.clearSelection(); 164 | } 165 | 166 | // Handle the actual movement 167 | bool moved = false; 168 | 169 | switch (key) 170 | { 171 | case KEY_LEFT: 172 | case KEY_SLEFT: 173 | #ifdef _WIN32 174 | case PDC_KEY_LEFT: 175 | #endif 176 | editor_.moveCursorLeft(); 177 | moved = true; 178 | break; 179 | 180 | case KEY_RIGHT: 181 | case KEY_SRIGHT: 182 | #ifdef _WIN32 183 | case PDC_KEY_RIGHT: 184 | #endif 185 | editor_.moveCursorRight(); 186 | moved = true; 187 | break; 188 | 189 | case KEY_UP: 190 | case KEY_SR: 191 | #ifdef _WIN32 192 | case PDC_KEY_UP: 193 | case KEY_SUP: 194 | #endif 195 | editor_.moveCursorUp(); 196 | moved = true; 197 | break; 198 | 199 | case KEY_DOWN: 200 | case KEY_SF: 201 | #ifdef _WIN32 202 | case PDC_KEY_DOWN: 203 | case KEY_SDOWN: 204 | #endif 205 | editor_.moveCursorDown(); 206 | moved = true; 207 | break; 208 | 209 | case KEY_HOME: 210 | editor_.moveCursorToLineStart(); 211 | moved = true; 212 | break; 213 | 214 | case KEY_END: 215 | editor_.moveCursorToLineEnd(); 216 | moved = true; 217 | break; 218 | 219 | case KEY_PPAGE: 220 | editor_.pageUp(); 221 | moved = true; 222 | break; 223 | 224 | case KEY_NPAGE: 225 | editor_.pageDown(); 226 | moved = true; 227 | break; 228 | 229 | default: 230 | return false; 231 | } 232 | 233 | // IMPROVED: Always update selection end if extending 234 | if (moved && extending_selection) 235 | { 236 | editor_.updateSelectionEnd(); 237 | } 238 | 239 | return moved; 240 | } 241 | bool InputHandler::handleEditingKey(int key) 242 | { 243 | switch (key) 244 | { 245 | case KEY_BACKSPACE: 246 | case KEY_BACKSPACE_ALT: 247 | case 8: // Ctrl+H 248 | // If there's a selection, delete it instead of single backspace 249 | if (editor_.hasSelection || editor_.isSelecting) 250 | { 251 | editor_.deleteSelection(); 252 | } 253 | else 254 | { 255 | editor_.backspace(); 256 | } 257 | return true; 258 | 259 | case KEY_DC: // Delete key 260 | // If there's a selection, delete it 261 | if (editor_.hasSelection || editor_.isSelecting) 262 | { 263 | editor_.deleteSelection(); 264 | } 265 | else 266 | { 267 | editor_.deleteChar(); 268 | } 269 | return true; 270 | 271 | case KEY_ENTER: 272 | case '\r': 273 | // case '\n': 274 | // Delete selection first if one exists 275 | if (editor_.hasSelection || editor_.isSelecting) 276 | { 277 | editor_.deleteSelection(); 278 | } 279 | editor_.insertNewline(); 280 | return true; 281 | 282 | case KEY_TAB: 283 | // Delete selection first if one exists 284 | if (editor_.hasSelection || editor_.isSelecting) 285 | { 286 | editor_.deleteSelection(); 287 | } 288 | // Insert 4 spaces instead of tab 289 | for (int i = 0; i < 4; i++) 290 | { 291 | editor_.insertChar(' '); 292 | } 293 | return true; 294 | 295 | case KEY_ESC: 296 | // Clear selection on escape 297 | editor_.clearSelection(); 298 | return true; 299 | 300 | default: 301 | return false; 302 | } 303 | } 304 | 305 | InputHandler::KeyResult InputHandler::handleMouseEvent() 306 | { 307 | MEVENT event; 308 | if (GETMOUSE_FUNC(&event) == OK) 309 | { 310 | editor_.handleMouse(event); 311 | return KeyResult::REDRAW; 312 | } 313 | return KeyResult::NOT_HANDLED; 314 | } 315 | 316 | InputHandler::KeyResult InputHandler::handleResizeEvent() 317 | { 318 | editor_.handleResize(); 319 | flushinp(); 320 | return KeyResult::REDRAW; 321 | } 322 | 323 | bool InputHandler::isPrintableChar(int key) const 324 | { 325 | return key >= 32 && key <= 126; 326 | } ```