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