diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 113fc112..2b2f3eeb 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -39,6 +39,7 @@ jobs: node-version: 20 - name: Run tests run: | + rustup update cd server ./bt install --dev ./bt test diff --git a/.prettierignore b/.prettierignore index 83229509..8e87ea3b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -16,7 +16,8 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # .prettierignore -- files for Prettier to ignore +# `.prettierignore` -- files for Prettier to ignore +# ================================================= # # NPM client/node_modules/ diff --git a/README.md b/README.md index 710feca9..e3da35bd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# Welcome to the CodeChat Editor +Welcome to the CodeChat Editor +============================== The CodeChat Editor is a GUI-based programmer's word processor / [Jupyter](https://jupyter.org/) for software developers. This document describes @@ -6,13 +7,15 @@ its basic features and use. In contrast, the [style guide](docs/style_guide.cpp) provides strategies for effectively employing the CodeChat Editor to improve the software development process. -## Installation +Installation +------------ -Install the -[CodeChat Editor extension for Visual Studio code](extensions/VSCode/README.md). -For developers, see [building from source](docs/design.md). +Install the [CodeChat Editor extension for Visual Studio +code](extensions/VSCode/README.md). For developers, see [building from +source](docs/design.md). -## Structure +Structure +--------- The CodeChat Editor divides source code into code blocks and documentation (doc) blocks.​ These blocks are separated by newlines; the image below shows the @@ -21,32 +24,36 @@ blocks.​ These blocks are separated by newlines; the image below shows the in the CodeChat Editor (using the VSCode extension). Specifically, this screenshot shows: -- : a doc block. Doc blocks must have one - space after the comment delimiter.​ -- : a code block. Comments on the same - line as code are not interpreted as doc blocks.​ -- : varying indents before a doc block. -- : [Markdown](https://commonmark.org/) - in a doc block; see a - [brief overview of Markdown](https://commonmark.org/help/). +* : a doc block. Doc blocks must have + one space after the comment delimiter.​ +* : a code block. Comments on the same + line as code are not interpreted as doc blocks.​ +* : varying indents before a doc block. +* : [Markdown](https://commonmark.org/) + in a doc block; see a [brief overview of + Markdown](https://commonmark.org/help/). -![Image showing code blocks and doc blocks in Visual Studio Code](docs/code-blocks-doc-blocks.png) +![Image showing code blocks and doc blocks in Visual Studio +Code](docs/code-blocks-doc-blocks.png) See the [style guide](docs/style_guide.cpp) for more examples. -## Editing +Editing +------- Edits may be made either in the IDE hosting the CodeChat Editor, or within the CodeChat Editor window itself. Edits made in one place are transferred to the other after a short delay. -## Navigation +Navigation +---------- Switching documents in the IDE likewise switches the document shown in the CodeChat Editor. Likewise, following hyperlinks in the CodeChat Editor to a local file loads that file in the IDE, as well as showing it in the Editor. -## References to other files +References to other files +------------------------- The CodeChat Editor supports hyperlinks to any recognized file type; to refer to another source file, simply insert a hyperlink to it. For example, @@ -68,7 +75,8 @@ docs/ monitor.png ``` -## Images +Images +------ Likewise, the path to local images is relative to the current file's location (see the preceding diagram for the location of `monitor.png`). For example @@ -82,17 +90,19 @@ Although the CodeChat Editor allows drag-and-drop of images, the result is a mess -- the image data is embedded directly in the source file. Avoid this; instead, place images in a separate file, then reference them as shown above. -## Projects +Projects +-------- The CodeChat Editor can either display a single file, or a project. In a project, the table of contents is displayed on the left, while a file within the project is displayed on the right. To create a project, simply place a file named `toc.md` at the root of your project [\[2\]](#notes); its contents define -the table of contents. See the -[new project template](https://github.com/bjones1/CodeChat_Editor/tree/main/new-project-template) +the table of contents. See the [new project +template](https://github.com/bjones1/CodeChat_Editor/tree/main/new-project-template) for a simple example. -## Mathematics +Mathematics +----------- The CodeChat Editor uses [MathJax](https://www.mathjax.org/) to support typeset mathematics. Place the delimiters `$` or `\\(` and `\\)` immediately before and @@ -134,9 +144,9 @@ The CodeChat Editor contains rudimentary support for diagrams created by | -------------------------------------------------------------- | ------------------------------------------------------------ | | `` | | -To edit these diagrams, use an -[HTML entity encoder/decoder](https://mothereff.in/html-entities) and a Graphviz -editor such as [Edotor](https://edotor.net/). +To edit these diagrams, use an [HTML entity +encoder/decoder](https://mothereff.in/html-entities) and a Graphviz editor such +as [Edotor](https://edotor.net/). ### Mermaid @@ -147,9 +157,9 @@ The CodeChat Editor contains rudimentary support for diagrams created by | --------------------------------------------- | ---------------------------------------------- | | `graph TD; A --> B;` | graph TD; A --> B; | -To edit these diagrams, use an -[HTML entity encoder/decoder](https://mothereff.in/html-entities) and the -[Mermaid live editor](https://mermaid.live/). +To edit these diagrams, use an [HTML entity +encoder/decoder](https://mothereff.in/html-entities) and the [Mermaid live +editor](https://mermaid.live/). ### PlantUML @@ -160,40 +170,43 @@ diagram directly to an SVG; for example, | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `![Sample PlantUML diagram](https://www.plantuml.com/plantuml/svg/ SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80)` | ![Sample PlantUML diagram](https://www.plantuml.com/plantuml/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80) | -To edit these diagrams, paste the URL into the -[PlantUML web server](https://www.plantuml.com/plantuml/uml), click Decode URL, -edit, then copy and paste the SVG URL back to this file. - -## Supported languages - -- C/C++ -- C# -- CSS -- Go -- HTML -- Java/Kotlin -- JavaScript/ECMAScript and TypeScript -- JSON with comments ([JSON5](https://json5.org/)) -- Markdown -- MATLAB -- Python -- Rust -- Shell scripts (`.sh`) -- SQL -- Swift -- TOML -- VHDL -- Verilog/SystemVerilog -- Vlang -- YAML - -## Issues and feature requests - -Please report issues and provide suggestions for improvement using the -[Github page for this project](https://github.com/bjones1/CodeChat_Editor). +To edit these diagrams, paste the URL into the [PlantUML web +server](https://www.plantuml.com/plantuml/uml), click Decode URL, edit, then +copy and paste the SVG URL back to this file. + +Supported languages +--------------------------------------------------- + +* C/C++ +* C# +* CSS +* Go +* HTML +* Java/Kotlin +* JavaScript/ECMAScript and TypeScript +* JSON with comments ([JSON5](https://json5.org/)) +* Markdown +* MATLAB +* Python +* Rust +* Shell scripts (`.sh`) +* SQL +* Swift +* TOML +* VHDL +* Verilog/SystemVerilog +* Vlang +* YAML + +Issues and feature requests +--------------------------- + +Please report issues and provide suggestions for improvement using the [Github +page for this project](https://github.com/bjones1/CodeChat_Editor). Contributions to the code are welcome and encouraged! -## License +License +------- Copyright (C) 2022 Bryan A. Jones. @@ -213,11 +226,12 @@ You should have received a [copy](LICENSE.html) of the GNU General Public License along with the CodeChat Editor. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/). -## Notes +Notes +----------------------- -1. The image used comes from - [Monitor icons created by prettycons - Flaticon](https://www.flaticon.com/free-icons/monitor "monitor icons"). +1. The image used comes from [Monitor icons created by prettycons - + Flaticon](https://www.flaticon.com/free-icons/monitor "monitor icons"). 2. Note that the filename for the table of contents is lowercase; while the acronym is TOC, requiring upper-case naming can cause confusion when moving files between case-insensitive filesystems (Windows) and case-sensitive - filesystems (Linux/OS X). + filesystems (Linux/OS X). \ No newline at end of file diff --git a/builder/.gitignore b/builder/.gitignore index 6abdc825..e96184ee 100644 --- a/builder/.gitignore +++ b/builder/.gitignore @@ -16,7 +16,8 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `.gitignore` -- files for Git to ignore +# `.gitignore` -- files for Git to ignore +# ======================================= # # Rust build output target/ diff --git a/builder/Cargo.lock b/builder/Cargo.lock index eb703a84..c6f4a167 100644 --- a/builder/Cargo.lock +++ b/builder/Cargo.lock @@ -47,7 +47,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -58,7 +58,7 @@ checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", "once_cell", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.27" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" dependencies = [ "clap_builder", "clap_derive", @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" dependencies = [ "anstream", "anstyle", @@ -101,9 +101,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", @@ -199,13 +199,13 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -240,9 +240,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "os_pipe" @@ -251,7 +251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -331,9 +331,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -351,9 +351,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "utf8parse" @@ -383,7 +383,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -392,15 +392,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.59.0" diff --git a/builder/Cargo.toml b/builder/Cargo.toml index 645043b9..6acdc3c6 100644 --- a/builder/Cargo.toml +++ b/builder/Cargo.toml @@ -16,11 +16,12 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `Cargo.toml` -- Rust build/package management config for the builder +# `Cargo.toml` -- Rust build/package management config for the builder +# ==================================================================== [package] name = "builder" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] clap = { version = "4.5.19", features = ["derive"] } diff --git a/builder/src/main.rs b/builder/src/main.rs index d1beb707..ea5bae09 100644 --- a/builder/src/main.rs +++ b/builder/src/main.rs @@ -14,7 +14,8 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `main.rs` -- Entrypoint for the CodeChat Editor Builder +// `main.rs` -- Entrypoint for the CodeChat Editor Builder +// ======================================================= // // This code uses [dist](https://opensource.axo.dev/cargo-dist/book/) as a part // of the release process. To update the `./release.yaml` file this tool @@ -26,7 +27,8 @@ // 4. Revert the changes to `server/dist-workspace.toml`. // 5. Test // -// ## Imports +// Imports +// ------- // // ### Standard library use std::{ffi::OsStr, fs, io, path::Path, process::Command}; @@ -41,7 +43,8 @@ use regex::Regex; // // None // -// ## Data structures +// Data structures +// --------------- // // The following defines the command-line interface for the CodeChat Editor. #[derive(Parser)] @@ -87,7 +90,8 @@ enum Commands { }, } -// ## Code +// Code +// ---- // // ### Utilities // @@ -170,8 +174,8 @@ fn quick_copy_dir>(src: P, dest: P, files: Option

) -> io::Res } #[cfg(not(windows))] { - // Create the dest directory, since old CI OSes don't support - // `rsync --mkpath`. + // Create the dest directory, since old CI OSes don't support `rsync + // --mkpath`. run_script( "mkdir", &["-p", dest.as_ref().to_str().unwrap()], @@ -209,8 +213,8 @@ fn quick_copy_dir>(src: P, dest: P, files: Option

) -> io::Res .status()? .code() .expect("Copy process terminated by signal"); - // Per - // [these docs](https://learn.microsoft.com/en-us/troubleshoot/windows-server/backup-and-storage/return-codes-used-robocopy-utility), + // Per [these + // docs](https://learn.microsoft.com/en-us/troubleshoot/windows-server/backup-and-storage/return-codes-used-robocopy-utility), // check the return code. if cfg!(windows) && exit_code >= 8 || !cfg!(windows) && exit_code != 0 { Err(io::Error::new( @@ -254,7 +258,8 @@ fn search_and_replace_file< fs::write(&path, file_contents_replaced.as_bytes()) } -// ## Core routines +// Core routines +// ------------- // // These functions simplify common build-focused development tasks and support // CI builds. @@ -278,8 +283,8 @@ fn patch_file(patch: &str, before_patch: &str, file_path: &str) -> io::Result<() } /// After updating files in the client's Node files, perform some fix-ups. fn patch_client_npm() -> io::Result<()> { - // Apply a the fixes described in - // [issue 27](https://github.com/bjones1/CodeChat_Editor/issues/27). + // Apply a the fixes described in [issue + // 27](https://github.com/bjones1/CodeChat_Editor/issues/27). patch_file( " selectionNotFocus = this.view.state.facet(editable) ? focused : hasSelection(this.dom, this.view.observer.selectionRange)", @@ -287,11 +292,15 @@ fn patch_client_npm() -> io::Result<()> { hasSelection(this.dom, this.view.observer.selectionRange) && !(activeElt && this.dom.contains(activeElt));", "../client/node_modules/@codemirror/view/dist/index.js" )?; - // In [older releases](https://www.tiny.cloud/docs/tinymce/5/6.0-upcoming-changes/#options), TinyMCE allowed users to change `whitespace_elements`; the whitespace inside these isn't removed by TinyMCE. However, this was removed in v6.0. Therefore, manually patch TinyMCE instead. + // In [older + // releases](https://www.tiny.cloud/docs/tinymce/5/6.0-upcoming-changes/#options), + // TinyMCE allowed users to change `whitespace_elements`; the whitespace + // inside these isn't removed by TinyMCE. However, this was removed in v6.0. + // Therefore, manually patch TinyMCE instead. patch_file( " wc-mermaid", "const whitespaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object code", - "../client/node_modules/tinymce/tinymce.js" + "../client/node_modules/tinymce/tinymce.js", )?; // Copy across the parts of MathJax that are needed, since bundling it is @@ -358,13 +367,13 @@ fn run_update() -> io::Result<()> { fn run_test() -> io::Result<()> { // On Windows, `cargo sort --check` fails since it default to LF, not CRLF, // line endings. Work around this by changing this setting only on Windows. - // See the - // [cargo sort config docs](https://github.com/DevinR528/cargo-sort?tab=readme-ov-file#config) - // and the - // [related issue](https://github.com/DevinR528/cargo-sort/issues/85). + // See the [cargo sort config + // docs](https://github.com/DevinR528/cargo-sort?tab=readme-ov-file#config) + // and the [related + // issue](https://github.com/DevinR528/cargo-sort/issues/85). // - // However, this still fails: `cargo sort` uses - // [inconsistent line endings](https://github.com/DevinR528/cargo-sort/issues/86). + // However, this still fails: `cargo sort` uses [inconsistent line + // endings](https://github.com/DevinR528/cargo-sort/issues/86). /*** #[cfg(windows)] { @@ -469,7 +478,8 @@ fn run_postrelease(target: &str) -> io::Result<()> { Ok(()) } -// ## CLI implementation +// CLI implementation +// ------------------ // // The following code implements the command-line interface for the CodeChat // Editor. diff --git a/client/.eslintrc.yml b/client/.eslintrc.yml index b80e1b50..0ef013aa 100644 --- a/client/.eslintrc.yml +++ b/client/.eslintrc.yml @@ -16,17 +16,18 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `.eslintrc.yml` -- Configure ESLint for this project +# `.eslintrc.yml` -- Configure ESLint for this project +# ==================================================== env: browser: true es2020: true extends: - standard - # See the - # [ESLint config prettier docs](https://github.com/prettier/eslint-config-prettier#installation) - # and its parent link, - # [integrating Prettier with linters](https://prettier.io/docs/en/integrating-with-linters.html). + # See the [ESLint config prettier + # docs](https://github.com/prettier/eslint-config-prettier#installation) and + # its parent link, [integrating Prettier with + # linters](https://prettier.io/docs/en/integrating-with-linters.html). - prettier parser: "@typescript-eslint/parser" parserOptions: @@ -36,6 +37,6 @@ plugins: rules: camelcase: off # TypeScript already enforces this; otherwise, eslint complains that - # `NodeJS` is undefined. See - # [this GitHub issue](https://github.com/Chatie/eslint-config/issues/45#issuecomment-1003990077). + # `NodeJS` is undefined. See [this GitHub + # issue](https://github.com/Chatie/eslint-config/issues/45#issuecomment-1003990077). no-undef: off diff --git a/client/.gitignore b/client/.gitignore index 2ad264a0..af96fcdc 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -16,7 +16,8 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `.gitignore` -- files for Git to ignore +# `.gitignore` -- files for Git to ignore +# ======================================= # # NPM node_modules/ diff --git a/client/.prettierrc.json5 b/client/.prettierrc.json5 index fbae4fbd..399e5800 100644 --- a/client/.prettierrc.json5 +++ b/client/.prettierrc.json5 @@ -15,6 +15,7 @@ // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // // `.prettierrc.json5` - Prettier configuration +// ============================================ { tabWidth: 4, } diff --git a/client/package-lock.json b/client/package-lock.json index 020bcc9c..5acb1521 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "codechat-editor-client", - "version": "0.1.9", + "version": "0.1.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codechat-editor-client", - "version": "0.1.9", + "version": "0.1.10", "license": "GPL-3.0-or-later", "dependencies": { "@codemirror/lang-cpp": "^6", @@ -37,7 +37,7 @@ "@typescript-eslint/eslint-plugin": "^8", "@typescript-eslint/parser": "^8", "chai": "^5", - "esbuild": "^0.24", + "esbuild": "^0.25", "eslint": "^9", "eslint-config-prettier": "^10", "eslint-plugin-import": "^2", @@ -47,27 +47,52 @@ } }, "node_modules/@antfu/install-pkg": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.1.tgz", - "integrity": "sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.0.0.tgz", + "integrity": "sha512-xvX6P/lo1B3ej0OsaErAjqgFYzYVcJpamjLAFLYh9vRJngBrMoUG7aVnrGTeqM7yxbyTD5p3F2+0/QUEh8Vzhw==", "license": "MIT", "dependencies": { - "package-manager-detector": "^0.2.0", - "tinyexec": "^0.3.0" + "package-manager-detector": "^0.2.8", + "tinyexec": "^0.3.2" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@antfu/utils": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", - "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" } }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@braintree/sanitize-url": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", @@ -114,9 +139,9 @@ "license": "Apache-2.0" }, "node_modules/@codemirror/autocomplete": { - "version": "6.18.4", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.4.tgz", - "integrity": "sha512-sFAphGQIqyQZfP2ZBsSHV7xQvo9Py0rV0dW7W3IMRdS+zDuNb2l3no78CvUaWKGfzFjI4FTrLdUSj86IGb2hRA==", + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -201,9 +226,9 @@ } }, "node_modules/@codemirror/lang-javascript": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", - "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.3.tgz", + "integrity": "sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==", "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -316,9 +341,9 @@ } }, "node_modules/@codemirror/search": { - "version": "6.5.8", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.8.tgz", - "integrity": "sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==", + "version": "6.5.9", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.9.tgz", + "integrity": "sha512-7DdQ9aaZMMxuWB1u6IIFWWuK9NocVZwvo4nG8QjJTS6oZGvteoLSiXw3EbVZVlO08Ri2ltO89JVInMpfcJxhtg==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -327,18 +352,18 @@ } }, "node_modules/@codemirror/state": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.1.tgz", - "integrity": "sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", "license": "MIT", "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "node_modules/@codemirror/view": { - "version": "6.36.2", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.2.tgz", - "integrity": "sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==", + "version": "6.36.3", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.3.tgz", + "integrity": "sha512-N2bilM47QWC8Hnx0rMdDxO2x2ImJ1FvZWXubwKgjeoOrWwEiFrtpA7SFHcuZ+o2Ze2VzbkgbzWVj4+V18LVkeg==", "license": "MIT", "dependencies": { "@codemirror/state": "^6.5.0", @@ -347,9 +372,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", "cpu": [ "ppc64" ], @@ -364,9 +389,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", "cpu": [ "arm" ], @@ -381,9 +406,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", "cpu": [ "arm64" ], @@ -398,9 +423,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", "cpu": [ "x64" ], @@ -415,9 +440,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", "cpu": [ "arm64" ], @@ -432,9 +457,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", "cpu": [ "x64" ], @@ -449,9 +474,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", "cpu": [ "arm64" ], @@ -466,9 +491,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", "cpu": [ "x64" ], @@ -483,9 +508,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", "cpu": [ "arm" ], @@ -500,9 +525,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", "cpu": [ "arm64" ], @@ -517,9 +542,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", "cpu": [ "ia32" ], @@ -534,9 +559,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", "cpu": [ "loong64" ], @@ -551,9 +576,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", "cpu": [ "mips64el" ], @@ -568,9 +593,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", "cpu": [ "ppc64" ], @@ -585,9 +610,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", "cpu": [ "riscv64" ], @@ -602,9 +627,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", "cpu": [ "s390x" ], @@ -619,9 +644,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", "cpu": [ "x64" ], @@ -636,9 +661,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", "cpu": [ "arm64" ], @@ -653,9 +678,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", "cpu": [ "x64" ], @@ -670,9 +695,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", "cpu": [ "arm64" ], @@ -687,9 +712,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", "cpu": [ "x64" ], @@ -704,9 +729,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", "cpu": [ "x64" ], @@ -721,9 +746,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", "cpu": [ "arm64" ], @@ -738,9 +763,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", "cpu": [ "ia32" ], @@ -755,9 +780,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", "cpu": [ "x64" ], @@ -801,13 +826,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -840,9 +865,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", + "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -901,9 +926,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "version": "9.20.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", + "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", "dev": true, "license": "MIT", "engines": { @@ -911,9 +936,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -921,13 +946,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.6.tgz", + "integrity": "sha512-+0TjwR1eAUdZtvv/ir1mGX+v0tUoR3VEPB8Up0LLJC+whRW0GgBBtpbOkg/a/U4Dxa6l5a3l9AJ1aWIQVyoWJA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.11.0", "levn": "^0.4.1" }, "engines": { @@ -987,9 +1012,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1007,25 +1032,25 @@ "license": "MIT" }, "node_modules/@iconify/utils": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.2.1.tgz", - "integrity": "sha512-0/7J7hk4PqXmxo5PDBDxmnecw5PxklZJfNjIVG9FM0mEfVrvfudS22rYWsqVk6gR3UJ/mSYS90X4R3znXnqfNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", "license": "MIT", "dependencies": { - "@antfu/install-pkg": "^0.4.1", - "@antfu/utils": "^0.7.10", + "@antfu/install-pkg": "^1.0.0", + "@antfu/utils": "^8.1.0", "@iconify/types": "^2.0.0", "debug": "^4.4.0", - "globals": "^15.13.0", + "globals": "^15.14.0", "kolorist": "^1.8.0", - "local-pkg": "^0.5.1", - "mlly": "^1.7.3" + "local-pkg": "^1.0.0", + "mlly": "^1.7.4" } }, "node_modules/@iconify/utils/node_modules/globals": { - "version": "15.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", - "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "license": "MIT", "engines": { "node": ">=18" @@ -1034,6 +1059,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, "node_modules/@lezer/common": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", @@ -1052,9 +1083,9 @@ } }, "node_modules/@lezer/css": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.9.tgz", - "integrity": "sha512-TYwgljcDv+YrV0MZFFvYFQHCfGgbPMR6nuqLabBdmZoFH3EP1gvw8t0vae326Ne3PszQkbXfVBjCnf3ZVCr0bA==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.10.tgz", + "integrity": "sha512-V5/89eDapjeAkWPBpWEfQjZ1Hag3aYUUJOL8213X0dFRuXJ4BXa5NKl9USzOnaLod4AOpmVCkduir2oKwZYZtg==", "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", @@ -1136,13 +1167,14 @@ } }, "node_modules/@lezer/markdown": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.4.0.tgz", - "integrity": "sha512-mk4MYeq6ZQdxgsgRAe0G7kqPRV6Desajfa14TcHoGGXIqqj1/2ARN31VFpmrXDgvXiGBWpA7RXtv0he+UdTkGw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.4.1.tgz", + "integrity": "sha512-Za5okfyWoNaX6sSZ2dm94XegaFXbkQ9UjKJ8hAoZX88XDpbu6DoR63IuSl+dqj1VkVQBQGsdr0JnTcMsogQDdw==", "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0" + "@lezer/highlight": "^1.0.0", + "@marijn/buildtool": "^0.1.6" } }, "node_modules/@lezer/php": { @@ -1189,6 +1221,26 @@ "@lezer/lr": "^1.0.0" } }, + "node_modules/@marijn/buildtool": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@marijn/buildtool/-/buildtool-0.1.6.tgz", + "integrity": "sha512-rcA2wljsM24MFAwx2U5vSBrt7IdIaPh4WPRfJPS8PuCUlbuQ8Pmky4c/ec00v3YFu90rZSbkVLnPuCeb/mUEng==", + "license": "MIT", + "dependencies": { + "@types/mocha": "^9.1.1", + "acorn": "^8.10.0", + "acorn-walk": "^8.2.0", + "rollup": "^3.28.0", + "rollup-plugin-dts": "^5.3.1", + "typescript": "^5.1.6" + } + }, + "node_modules/@marijn/buildtool/node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "license": "MIT" + }, "node_modules/@marijn/find-cluster-break": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", @@ -1435,9 +1487,9 @@ } }, "node_modules/@types/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", "license": "MIT" }, "node_modules/@types/d3-polygon": { @@ -1459,9 +1511,9 @@ "license": "MIT" }, "node_modules/@types/d3-scale": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", - "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", "license": "MIT", "dependencies": { "@types/d3-time": "*" @@ -1540,9 +1592,9 @@ "license": "MIT" }, "node_modules/@types/geojson": { - "version": "7946.0.15", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz", - "integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==", + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", "license": "MIT" }, "node_modules/@types/js-beautify": { @@ -1574,9 +1626,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", - "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "version": "22.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", "dev": true, "license": "MIT", "dependencies": { @@ -1591,21 +1643,21 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", - "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz", + "integrity": "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/type-utils": "8.21.0", - "@typescript-eslint/utils": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/type-utils": "8.24.1", + "@typescript-eslint/utils": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1621,16 +1673,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", - "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.1.tgz", + "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4" }, "engines": { @@ -1646,14 +1698,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", - "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz", + "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0" + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1664,16 +1716,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", - "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.1.tgz", + "integrity": "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/utils": "8.24.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1688,9 +1740,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", - "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz", + "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==", "dev": true, "license": "MIT", "engines": { @@ -1702,20 +1754,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", - "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz", + "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1729,16 +1781,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", - "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz", + "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0" + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1753,13 +1805,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", - "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz", + "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/types": "8.24.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1814,6 +1866,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1983,6 +2047,16 @@ "node": ">=12" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2049,9 +2123,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2090,9 +2164,9 @@ } }, "node_modules/chai": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", - "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", "dev": true, "license": "MIT", "dependencies": { @@ -2898,9 +2972,9 @@ } }, "node_modules/dompurify": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz", - "integrity": "sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -3037,13 +3111,16 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -3065,9 +3142,9 @@ } }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3078,31 +3155,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" } }, "node_modules/escape-string-regexp": { @@ -3119,18 +3196,18 @@ } }, "node_modules/eslint": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", - "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", + "version": "9.20.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", + "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.18.0", + "@eslint/js": "9.20.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -3553,9 +3630,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "dev": true, "license": "ISC", "dependencies": { @@ -3620,20 +3697,40 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/function-bind": { @@ -3929,9 +4026,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3998,12 +4095,13 @@ } }, "node_modules/is-async-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", - "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { + "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", @@ -4033,13 +4131,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -4324,13 +4422,13 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -4370,6 +4468,13 @@ "dev": true, "license": "ISC" }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT", + "optional": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -4500,13 +4605,13 @@ } }, "node_modules/local-pkg": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", - "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.0.0.tgz", + "integrity": "sha512-bbgPw/wmroJsil/GgL4qjDzs5YLTBMQ99weRsok1XCDccQeehbHA/I1oRvk2NPtr7KGZgT/Y5tPRnAtMqeG2Kg==", "license": "MIT", "dependencies": { "mlly": "^1.7.3", - "pkg-types": "^1.2.1" + "pkg-types": "^1.3.0" }, "engines": { "node": ">=14" @@ -4545,12 +4650,21 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", - "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", "dev": true, "license": "MIT" }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/marked": { "version": "13.0.3", "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", @@ -4694,9 +4808,9 @@ "license": "MIT" }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { @@ -4859,9 +4973,9 @@ } }, "node_modules/package-manager-detector": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.8.tgz", - "integrity": "sha512-ts9KSdroZisdvKMWVAVCXiKqnqNfXz4+IbrBG8/BWx/TR5le+jfenvoBuIZ6UWM9nz47W7AbD9qYfAwfWMIwzA==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.9.tgz", + "integrity": "sha512-+vYvA/Y31l8Zk8dwxHhL3JfTuHPm6tlxM2A3GeQyl7ovYnSp1+mzAxClxaOr0qO1TtPxbQxetI7v5XqKLJZk7Q==", "license": "MIT" }, "node_modules/parent-module": { @@ -4911,9 +5025,9 @@ "license": "MIT" }, "node_modules/pathe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", - "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, "node_modules/pathval": { @@ -4926,6 +5040,13 @@ "node": ">= 14.16" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC", + "optional": true + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -4967,9 +5088,9 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", "engines": { @@ -4987,9 +5108,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", + "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", "dev": true, "license": "MIT", "peer": true, @@ -5139,6 +5260,44 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", "license": "Unlicense" }, + "node_modules/rollup": { + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-dts": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-5.3.1.tgz", + "integrity": "sha512-gusMi+Z4gY/JaEQeXnB0RUdU82h1kF0WYzCWgVmV4p3hWXqelaKuCvcJawfeg+EKn2T1Ie+YWF2OiN1/L8bTVg==", + "license": "LGPL-3.0", + "dependencies": { + "magic-string": "^0.30.2" + }, + "engines": { + "node": ">=v14.21.3" + }, + "funding": { + "url": "https://github.com/sponsors/Swatinem" + }, + "optionalDependencies": { + "@babel/code-frame": "^7.22.5" + }, + "peerDependencies": { + "rollup": "^3.0", + "typescript": "^4.1 || ^5.0" + } + }, "node_modules/roughjs": { "version": "4.6.6", "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", @@ -5254,9 +5413,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -5503,9 +5662,9 @@ "license": "MIT" }, "node_modules/stylis": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.5.tgz", - "integrity": "sha512-K7npNOKGRYuhAFFzkzMGfxFDpN6gDwf8hcMiE+uveTVbBgm93HrNP3ZDUpKqzZ4pG7TP6fmb+EMAQPjq9FqqvA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", "license": "MIT" }, "node_modules/supports-color": { @@ -5558,9 +5717,9 @@ "license": "MIT" }, "node_modules/tinymce": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-7.6.0.tgz", - "integrity": "sha512-kUrklnD7H8JbpSDEGRh51GKK6Mrf+pR9neSDzUHvXKV+2oRtMB7sqfAtEOnM0/WKdstwaX0qoNCZNo2H1Y0EFA==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-7.7.0.tgz", + "integrity": "sha512-zDFPXeje5fQIxuYzv9fE6YmjFBQmeB9SU+85ofizIHqCCkbBnDdEWhD5R8rBu9MwdjIQP3aNohgGe9D6JOhNaw==", "license": "GPL-2.0-or-later" }, "node_modules/to-regex-range": { @@ -5577,9 +5736,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", - "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, "license": "MIT", "engines": { @@ -5713,7 +5872,6 @@ "version": "5.7.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/client/package.json b/client/package.json index 66187278..fae32f7d 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "codechat-editor-client", - "version": "0.1.9", + "version": "0.1.10", "description": "The CodeChat Editor Client, part of a web-based literate programming editor (the CodeChat Editor).", "homepage": "https://github.com/bjones1/CodeChat_Editor", "type": "module", @@ -24,7 +24,7 @@ "@typescript-eslint/eslint-plugin": "^8", "@typescript-eslint/parser": "^8", "chai": "^5", - "esbuild": "^0.24", + "esbuild": "^0.25", "eslint": "^9", "eslint-config-prettier": "^10", "eslint-plugin-import": "^2", diff --git a/client/src/CodeChatEditor-test.mts b/client/src/CodeChatEditor-test.mts index 4d57913a..60d68118 100644 --- a/client/src/CodeChatEditor-test.mts +++ b/client/src/CodeChatEditor-test.mts @@ -14,12 +14,14 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `CodeChatEditor-test.mts` -- Tests for the CodeChat Editor client +// `CodeChatEditor-test.mts` -- Tests for the CodeChat Editor client +// ================================================================= // // To run tests, add a `?test` to any web page served by the CodeChat Editor // server. // -// ## Imports +// Imports +// ------- // // I can't get Mocha to work with ESBuild, so I import it using a script tag. import { assert } from "chai"; @@ -33,7 +35,8 @@ export { page_init }; // Provide convenient access to all functions tested here. const { codechat_html_to_markdown } = exportedForTesting; -// ## Tests +// Tests +// ----- // // Defining this global variable signals the // CodeChat Editor to [run tests](CodeChatEditor.mts#CodeChatEditor_test). diff --git a/client/src/CodeChatEditor.mts b/client/src/CodeChatEditor.mts index 7f20d98d..a9be067b 100644 --- a/client/src/CodeChatEditor.mts +++ b/client/src/CodeChatEditor.mts @@ -11,7 +11,8 @@ // details. // // You should have received a copy of the GNU General Public License along with -// the CodeChat Editor. If not, see[http://www.gnu.org/licenses](http://www.gnu.org/licenses). +// the CodeChat Editor. If not, +// see[http://www.gnu.org/licenses](http://www.gnu.org/licenses). // // `CodeChatEditor.mts` -- the CodeChat Editor Client // ================================================== @@ -55,8 +56,8 @@ import { } from "./CodeMirror-integration.mjs"; import "./EditorComponents.mjs"; import "./graphviz-webcomponent-setup.mts"; -// This must be imported*after* the previous setup import, so it's placed here, instead of in the -// third-party category above. +// This must be imported*after* the previous setup import, so it's placed here, +// instead of in the third-party category above. import "graphviz-webcomponent"; import { tinymce, init, Editor } from "./tinymce-config.mjs"; @@ -66,7 +67,9 @@ import "./css/CodeChatEditor.css"; // Data structures // --------------- // -// Define all possible editor modes; these are passed as a[query string](https://en.wikipedia.org/wiki/Query_string) (`http://path/to/foo.py?mode=toc`, for example) to the page's URL. +// Define all possible editor modes; these are passed as +// a[query string](https://en.wikipedia.org/wiki/Query_string) +// (`http://path/to/foo.py?mode=toc`, for example) to the page's URL. enum EditorMode { // Display the source code using CodeChat, but disallow editing. view, @@ -79,7 +82,8 @@ enum EditorMode { raw, } -// Since this is experimental, TypeScript doesn't define it. See the[docs](https://developer.mozilla.org/en-US/docs/Web/API/NavigateEvent). +// Since this is experimental, TypeScript doesn't define it. See +// the[docs](https://developer.mozilla.org/en-US/docs/Web/API/NavigateEvent). interface NavigateEvent extends Event { canIntercept: boolean; destination: any; @@ -120,7 +124,8 @@ let autosaveEnabled = true; // Store the lexer info for the currently-loaded language. // -// This mirrors the data provided by the server -- see[SourceFileMetadata](../../server/src/webserver.rs#SourceFileMetadata). +// This mirrors the data provided by the server -- +// see[SourceFileMetadata](../../server/src/webserver.rs#SourceFileMetadata). let current_metadata: { mode: string; }; @@ -130,7 +135,8 @@ let is_dirty = false; // ### Markdown to HTML conversion // -// Instantiate[turndown](https://github.com/mixmark-io/turndown) for HTML to Markdown conversion +// Instantiate[turndown](https://github.com/mixmark-io/turndown) for HTML to +// Markdown conversion const turndownService = new TurndownService({ br: "\\", codeBlockStyle: "fenced", @@ -138,7 +144,9 @@ const turndownService = new TurndownService({ wordWrap: [80, 40], }); -// Add the plugins from[turndown-plugin-gfm](https://github.com/laurent22/joplin/tree/dev/packages/turndown-plugin-gfm) to enable conversions for tables, task lists, and strikethroughs. +// Add the plugins +// from[turndown-plugin-gfm](https://github.com/laurent22/joplin/tree/dev/packages/turndown-plugin-gfm) +// to enable conversions for tables, task lists, and strikethroughs. turndownService.use(gfm); // Page initialization @@ -172,7 +180,8 @@ export const set_is_dirty = (value: boolean = true) => { is_dirty = value; }; -// This is copied from[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event#checking_whether_loading_is_already_complete). +// This is copied +// from[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event#checking_whether_loading_is_already_complete). const on_dom_content_loaded = (on_load_func: () => void) => { if (document.readyState === "loading") { // Loading hasn't finished yet. @@ -204,14 +213,20 @@ const _open_lp = async ( // associated metadata. See[`AllSource`](#AllSource). all_source: CodeChatForWeb, ) => { - // Use[URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) to parse out the search parameters of this window's URL. + // Use[URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) + // to parse out the search parameters of this window's URL. const urlParams = new URLSearchParams(window.location.search); - // Get the mode from the page's query parameters. Default to edit using the[nullish coalescing operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator). This works, but TypeScript marks it as an error. Ignore this error by - // including the[@ts-ignore directive](https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html#ts-check). + // Get the mode from the page's query parameters. Default to edit using + // the[nullish coalescing + // operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator). + // This works, but TypeScript marks it as an error. Ignore this error by + // including the[@ts-ignore + // directive](https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html#ts-check). /// @ts-ignore const editorMode = EditorMode[urlParams.get("mode") ?? "edit"]; - // Get thecurrent_metadata from the provided`all_source` struct and store it as a global variable. + // Get thecurrent_metadata from + // the provided`all_source` struct and store it as a global variable. current_metadata = all_source["metadata"]; const source = all_source["source"]; const codechat_body = document.getElementById( @@ -222,22 +237,31 @@ const _open_lp = async ( clearAutosaveTimer(); // Before calling any MathJax, make sure it's fully loaded. await window.MathJax.startup.promise; - // Per the[docs](https://docs.mathjax.org/en/latest/web/typeset.html#updating-previously-typeset-content), "If you modify the page to remove content that contains typeset + // Per + // the[docs](https://docs.mathjax.org/en/latest/web/typeset.html#updating-previously-typeset-content), + // "If you modify the page to remove content that contains typeset // mathematics, you will need to tell MathJax about that so that it knows // the typeset math that you are removing is no longer on the page." window.MathJax.typesetClear(codechat_body); if (is_doc_only()) { if (tinymce.activeEditor === null) { - // Special case: a CodeChat Editor document's HTML is stored in`source.doc`. We don't need the CodeMirror editor at all; instead, treat it - // like a single doc block contents div. + // Special case: a CodeChat Editor document's HTML is stored + // in`source.doc`. We don't need the CodeMirror editor at all; + // instead, treat it like a single doc block contents div. codechat_body.innerHTML = `

${source.doc}
`; await init({ selector: ".CodeChat-doc-contents", // In the doc-only mode, add autosave functionality. While there - // is an[autosave plugin](https://www.tiny.cloud/docs/tinymce/6/autosave/), this autosave functionality is completely different from - // the autosave provided here. Per[handling editor events](https://www.tiny.cloud/docs/tinymce/6/events/#handling-editor-events), this is how to create a TinyMCE event handler. + // is an[autosave + // plugin](https://www.tiny.cloud/docs/tinymce/6/autosave/), + // this autosave functionality is completely different from the + // autosave provided here. Per[handling editor + // events](https://www.tiny.cloud/docs/tinymce/6/events/#handling-editor-events), + // this is how to create a TinyMCE event handler. setup: (editor: Editor) => { - // The[editor core events list](https://www.tiny.cloud/docs/tinymce/6/events/#editor-core-events) includes the`Dirty` event. + // The[editor core events + // list](https://www.tiny.cloud/docs/tinymce/6/events/#editor-core-events) + // includes the`Dirty` event. editor.on("Dirty", (_event: Event) => { is_dirty = true; startAutosaveTimer(); @@ -246,7 +270,9 @@ const _open_lp = async ( }); tinymce.activeEditor!.focus(); } else { - // Save and restore cursor/scroll location after an update per the[docs](https://www.tiny.cloud/docs/tinymce/6/apis/tinymce.dom.bookmarkmanager). However, this doesn't seem to work for the cursor location. + // Save and restore cursor/scroll location after an update per + // the[docs](https://www.tiny.cloud/docs/tinymce/6/apis/tinymce.dom.bookmarkmanager). + // However, this doesn't seem to work for the cursor location. // Perhaps when TinyMCE normalizes the document, this gets lost? const bm = tinymce.activeEditor!.selection.getBookmark(); tinymce.activeEditor!.setContent(source.doc); @@ -258,7 +284,10 @@ const _open_lp = async ( } autosaveEnabled = true; - // If tests should be run, then the[following global variable](CodeChatEditor-test.mts#CodeChatEditor_test) is function that runs them. + // If tests should be run, then + // the[following global + // variable](CodeChatEditor-test.mts#CodeChatEditor_test) is function that + // runs them. if (typeof window.CodeChatEditor_test === "function") { window.CodeChatEditor_test(); } @@ -303,7 +332,8 @@ const save_lp = async () => { return update; }; -// Per[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform#examples), here's the least bad way to choose between the control key and the command +// Per[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform#examples), +// here's the least bad way to choose between the control key and the command // key. const os_is_osx = navigator.platform.indexOf("Mac") === 0 || navigator.platform === "iPhone" @@ -315,8 +345,8 @@ const on_save = async (only_if_dirty: boolean = false) => { if (only_if_dirty && !is_dirty) { return; } - // Save the provided contents back to the filesystem, by sending an update - // message over the websocket. + // Save the provided contents back to the filesystem, by + // sending an update message over the websocket. const webSocketComm = parent.window.CodeChatEditorFramework.webSocketComm; console.log("Sent Update - saving document."); await new Promise(async (resolve) => { @@ -374,7 +404,10 @@ const clearAutosaveTimer = () => { // current document before navigating. const on_navigate = (navigateEvent: NavigateEvent) => { if ( - // Some of this was copied from[Modern client-side routing: the Navigation API](https://developer.chrome.com/docs/web-platform/navigation-api/#deciding_how_to_handle_a_navigation). If we're navigating within the document, ignore this. + // Some of this was copied from[Modern client-side routing: the + // Navigation + // API](https://developer.chrome.com/docs/web-platform/navigation-api/#deciding_how_to_handle_a_navigation). + // If we're navigating within the document, ignore this. navigateEvent.hashChange || // If this is a download, let the browser perform the download. navigateEvent.downloadRequest || @@ -391,7 +424,8 @@ const on_navigate = (navigateEvent: NavigateEvent) => { return; } - // If the IDE initiated this navigation via a`CurrentFile` message, then allow it. + // If the IDE initiated this navigation via a`CurrentFile` message, then + // allow it. if (window.CodeChatEditor.allow_navigation) { // We don't need to reset this flag, since this window will be reloaded. return; @@ -456,7 +490,8 @@ const save_then_navigate = (codeChatEditorUrl: URL) => { // Testing // ------- // -// A great and simple idea taken from[SO](https://stackoverflow.com/a/54116079): wrap all testing exports in a single variable. This avoids namespace +// A great and simple idea taken from[SO](https://stackoverflow.com/a/54116079): +// wrap all testing exports in a single variable. This avoids namespace // pollution, since only one name is exported, and it's clearly marked for // testing only. Test code still gets access to everything it needs. export const exportedForTesting = { diff --git a/client/src/CodeChatEditorFramework.mts b/client/src/CodeChatEditorFramework.mts index 79565a58..c99f354e 100644 --- a/client/src/CodeChatEditorFramework.mts +++ b/client/src/CodeChatEditorFramework.mts @@ -14,7 +14,8 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `CodeChatEditorFramework.mts` -- the CodeChat Editor Client Framework +// `CodeChatEditorFramework.mts` -- the CodeChat Editor Client Framework +// ===================================================================== // // This maintains a websocket connection between the CodeChat Editor Server. The // accompanying HTML is a full-screen iframe, allowing the Framework to change @@ -22,14 +23,16 @@ // to report navigation events to as a websocket message when the iframe's // location changes. // -// ## Imports +// Imports +// ------- // // ### JavaScript/TypeScript // // #### Third-party import ReconnectingWebSocket from "./ReconnectingWebSocket.cjs"; -// ## Websocket +// Websocket +// --------- // // This code communicates with the CodeChat Editor Server via its websocket // interface. @@ -311,9 +314,9 @@ let testMode = false; // Load the dynamic content into the static page. export const page_init = ( // The pathname for the websocket to use. The remainder of the URL is - // derived from the hosting page's URL. See the - // [Location docs](https://developer.mozilla.org/en-US/docs/Web/API/Location) - // for a nice, interactive definition of the components of a URL. + // derived from the hosting page's URL. See the [Location + // docs](https://developer.mozilla.org/en-US/docs/Web/API/Location) for a + // nice, interactive definition of the components of a URL. ws_pathname: string, // Test mode flag testMode_: boolean, diff --git a/client/src/CodeMirror-integration.mts b/client/src/CodeMirror-integration.mts index 0cfaba06..76d87f48 100644 --- a/client/src/CodeMirror-integration.mts +++ b/client/src/CodeMirror-integration.mts @@ -14,7 +14,8 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `CodeMirror-integration.mts` -- integrate CodeMirror into the CodeChat Editor +// `CodeMirror-integration.mts` -- integrate CodeMirror into the CodeChat Editor +// ============================================================================= // // This file assumes the server has parsed the source. For example given the // following original Python source code: @@ -44,7 +45,8 @@ // contents are focused, apply the TinyMCE instance to those contents. // 5. Define a set of StateEffects to add/update/etc. doc blocks. // -// ## Imports +// Imports +// ------- // // ### Third-party import { basicSetup } from "codemirror"; @@ -80,7 +82,8 @@ import { Editor, init, tinymce } from "./tinymce-config.mjs"; // ### Local import { set_is_dirty, startAutosaveTimer } from "./CodeChatEditor.mjs"; -// ## Globals +// Globals +// ------- let current_view: EditorView; let tinymce_singleton: Editor | undefined; let ignore_next_dirty = false; @@ -91,7 +94,8 @@ declare global { } } -// ## Doc blocks in CodeMirror +// Doc blocks in CodeMirror +// ------------------------ // // The goal: given a [Range](https://codemirror.net/docs/ref/#state.Range) of // lines containing a doc block (a delimiter, indent, and contents) residing at @@ -360,8 +364,9 @@ class DocBlockWidget extends WidgetType { } ignoreEvent(event: Event) { - // Avoid handling other events, since this causes - // [weird problems with event routing](https://discuss.codemirror.net/t/how-to-get-focusin-events-on-a-custom-widget-decoration/6792). + // Avoid handling other events, since this causes [weird problems with + // event + // routing](https://discuss.codemirror.net/t/how-to-get-focusin-events-on-a-custom-widget-decoration/6792). if (event.type === "focusin" || event.type === "input") { return false; } else { @@ -389,8 +394,8 @@ class DocBlockWidget extends WidgetType { } } -// Typeset the provided node; taken from the -// [MathJax docs](https://docs.mathjax.org/en/latest/web/typeset.html#handling-asynchronous-typesetting). +// Typeset the provided node; taken from the [MathJax +// docs](https://docs.mathjax.org/en/latest/web/typeset.html#handling-asynchronous-typesetting). export const mathJaxTypeset = (node: HTMLElement) => { window.MathJax.typesetPromise([node]).catch((err: any) => console.log("Typeset failed: " + err.message), @@ -632,7 +637,8 @@ const DocBlockPlugin = ViewPlugin.fromClass( }, ); -// ## UI +// UI +// -- // // Allow only spaces and delete/backspaces when editing the indent of a doc // block. @@ -653,16 +659,16 @@ const doc_block_indent_on_before_input = (event_: Event) => { }; // There doesn't seem to be any tracking of a dirty/clean flag built into -// CodeMirror v6 (although -// [v5 does](https://codemirror.net/5/doc/manual.html#isClean)). The best I've -// found is a -// [forum post](https://discuss.codemirror.net/t/codemirror-6-proper-way-to-listen-for-changes/2395/11) +// CodeMirror v6 (although [v5 +// does](https://codemirror.net/5/doc/manual.html#isClean)). The best I've found +// is a [forum +// post](https://discuss.codemirror.net/t/codemirror-6-proper-way-to-listen-for-changes/2395/11) // showing code to do this, which I use below. // // How this works: the // [EditorView.updateListener](https://codemirror.net/docs/ref/#codemirror) is a -// [Facet](https://codemirror.net/docs/ref/#state.Facet) with an -// [of function](https://codemirror.net/docs/ref/#state.Facet.of) that creates a +// [Facet](https://codemirror.net/docs/ref/#state.Facet) with an [of +// function](https://codemirror.net/docs/ref/#state.Facet.of) that creates a // CodeMirror extension. const autosaveExtension = EditorView.updateListener.of( // CodeMirror passes this function a diff --git a/client/src/EditorComponents.mts b/client/src/EditorComponents.mts index b5650338..4c87f6a8 100644 --- a/client/src/EditorComponents.mts +++ b/client/src/EditorComponents.mts @@ -14,7 +14,9 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `EditorComponents.mts` -- Custom HTML tags which provide authoring support for the CodeChat Editor +// `EditorComponents.mts` -- Custom HTML tags which provide authoring support +// for the CodeChat Editor +// ========================================================================== // // Create a combined editor/renderer component. It's not currently used, since // TinyMCE doesn't allow the editor to be focused. diff --git a/client/src/HashReader.mts b/client/src/HashReader.mts index dc091af0..60948bb7 100644 --- a/client/src/HashReader.mts +++ b/client/src/HashReader.mts @@ -14,7 +14,8 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `HashReader.mts` -- post-process esbuild output +// `HashReader.mts` -- post-process esbuild output +// =============================================== // // This script reads the output produced by esbuild to determine the location of // the bundled files, which have hashes in their file names. It writes these diff --git a/client/src/css/CodeChatEditor.css b/client/src/css/CodeChatEditor.css index 9576cf6f..8808a667 100644 --- a/client/src/css/CodeChatEditor.css +++ b/client/src/css/CodeChatEditor.css @@ -16,23 +16,26 @@ the CodeChat Editor. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). - # `CodeChatEditor.css` -- Styles for the CodeChat Editor + `CodeChatEditor.css` -- Styles for the CodeChat Editor + ====================================================== This style sheet is used by the HTML generated by - [CodeChatEditor.mts](../../src/CodeChatEditor.mts). + [CodeChatEditor.mts](../CodeChatEditor.mts). TODO: do a much better job of grouping common styles. Rename styles based on whether they style a code or doc block. - ## Import a theme - Eventually, this will be a user-configurable setting. - */ + Import a theme + -------------- + + Eventually, this will be a user-configurable setting. */ @import url('themes/light.css'); -/* ## Styles for the entire page layout +/* Styles for the entire page layout + --------------------------------- - This is used only to store a reused variable value. See the - [CSS docs](https://drafts.csswg.org/css-variables/). */ + This is used only to store a reused variable value. See the [CSS + docs](https://drafts.csswg.org/css-variables/). */ :root { --top-height: 6.7rem; --body-padding: 0.2rem; @@ -69,18 +72,20 @@ body { overflow: auto; } } -/* ## Misc styling +/* Misc styling + ------------ - Make the filename compact. */ + Make the filename compact. */ #CodeChat-filename p { margin: 0px; white-space: nowrap; } -/* ## Doc block styling */ +/* Doc block styling + ----------------- */ .CodeChat-doc { - /* Use - [flexbox layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) + /* Use [flexbox + layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) to style doc blocks. The goal of this layout is: \ \ \` contains \ - - `
` contains \ - - `
` contains \ - - `
` contains the \ */ + * `
` contains \ + * `
` contains \ + * `
` contains \ + * `
` contains the \ */ display: flex; padding: 0px 2px 0px 6px; } @@ -124,7 +129,8 @@ body { outline-width: 0px; } -/* ## Combined code/doc block styling +/* Combined code/doc block styling + ------------------------------- Remove space between a code block followed by a doc block. Doc block elements typically have top margin and/or padding that diff --git a/client/src/css/CodeChatEditorProject.css b/client/src/css/CodeChatEditorProject.css index 0eb50fe6..e964b521 100644 --- a/client/src/css/CodeChatEditorProject.css +++ b/client/src/css/CodeChatEditorProject.css @@ -16,7 +16,8 @@ the CodeChat Editor. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). - # `CodeChatEditorProject.css` -- Styles for the CodeChat Editor for projects */ + `CodeChatEditorProject.css` -- Styles for the CodeChat Editor for projects + ========================================================================== */ :root { --sidebar-width: 15rem; } @@ -25,7 +26,8 @@ body { overflow: hidden; } -/* TODO: This is a overly simple, non-responsive layout to create a sidebar containing the table of contents. Fix. */ +/* TODO: This is a overly simple, non-responsive layout to create a sidebar + containing the table of contents. Fix. */ #CodeChat-sidebar { width: var(--sidebar-width); height: calc(100vh - 2 * var(--body-padding)); diff --git a/client/src/css/themes/light.css b/client/src/css/themes/light.css index b1289370..05e529ec 100644 --- a/client/src/css/themes/light.css +++ b/client/src/css/themes/light.css @@ -16,10 +16,11 @@ the CodeChat Editor. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). - # `light.css` -- Styles for the light theme + `light.css` -- Styles for the light theme + ========================================= - Use - [CSS nesting](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting) + Use [CSS + nesting](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting) to make everything in this style sheet apply only to the light theme. */ body.CodeChat-theme-light { /* Style code blocks so that they are clearly separated from doc blocks. Per diff --git a/client/src/graphviz-webcomponent-setup.mts b/client/src/graphviz-webcomponent-setup.mts index 739328ca..640132b5 100644 --- a/client/src/graphviz-webcomponent-setup.mts +++ b/client/src/graphviz-webcomponent-setup.mts @@ -14,15 +14,16 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `graphviz-webcomponent-setup.mts` -- Configure graphviz webcomponent options +// `graphviz-webcomponent-setup.mts` -- Configure graphviz webcomponent options +// ============================================================================ // // Configure the Graphviz web component to load the (large) renderer only when a // Graphviz web component is found on a page. See the // [docs](https://github.com/prantlf/graphviz-webcomponent#configuration). // // Note that this must be in a separate module which is imported before the -// graphviz webcomponent; see the -// [ESBuild docs](https://esbuild.github.io/content-types/#real-esm-imports). +// graphviz webcomponent; see the [ESBuild +// docs](https://esbuild.github.io/content-types/#real-esm-imports). (window as any).graphvizWebComponent = { rendererUrl: "/static/graphviz-webcomponent/renderer.min.js", delayWorkerLoading: true, diff --git a/client/src/tinymce-config.mts b/client/src/tinymce-config.mts index 0c6022c9..9214e1bf 100644 --- a/client/src/tinymce-config.mts +++ b/client/src/tinymce-config.mts @@ -14,7 +14,9 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `tinymce-webpack.ts` -- integrate and configure the TinyMCE editor for use with the CodeChat Editor +// `tinymce-webpack.ts` -- integrate and configure the TinyMCE editor for use +// with the CodeChat Editor +// ========================================================================== // // Import TinyMCE. import { @@ -89,15 +91,15 @@ export const init = async ( // The imports above apply the skins; don't try to dynamically load // the skin's CSS. skin: false, - // Enable the - // [browser-supplied spellchecker](https://www.tiny.cloud/docs/tinymce/6/spelling/#browser_spellcheck), + // Enable the [browser-supplied + // spellchecker](https://www.tiny.cloud/docs/tinymce/6/spelling/#browser_spellcheck), // since TinyMCE's spellchecker is a premium feature. browser_spellcheck: true, - // Put more buttons on the - // [quick toolbar](https://www.tiny.cloud/docs/tinymce/6/quickbars/) - // that appears when text is selected. TODO: add a button for code - // format (can't find this one -- it's only on the - // [list of menu items](https://www.tiny.cloud/docs/tinymce/6/available-menu-items/#the-core-menu-items) + // Put more buttons on the [quick + // toolbar](https://www.tiny.cloud/docs/tinymce/6/quickbars/) that + // appears when text is selected. TODO: add a button for code format + // (can't find this one -- it's only on the [list of menu + // items](https://www.tiny.cloud/docs/tinymce/6/available-menu-items/#the-core-menu-items) // as `codeformat`). quickbars_selection_toolbar: "align | bold italic underline | quicklink h2 h3 blockquote", @@ -110,18 +112,18 @@ export const init = async ( // When true, this still prevents hyperlinks to anchors on the // current page from working correctly. There's an onClick handler // that prevents links in the current page from working -- need to - // look into this. See also - // [a related GitHub issue](https://github.com/tinymce/tinymce/issues/3836). + // look into this. See also [a related GitHub + // issue](https://github.com/tinymce/tinymce/issues/3836). //readonly: true // Per the comment above, this is commented out. // TODO: Notes on this setting. relative_urls: true, - // This combines the - // [default TinyMCE toolbar buttons](https://www.tiny.cloud/blog/tinymce-toolbar/) - // with a few more from plugins. I like the default, so this is - // currently disabled. + // This combines the [default TinyMCE toolbar + // buttons](https://www.tiny.cloud/blog/tinymce-toolbar/) with a few + // more from plugins. I like the default, so this is currently + // disabled. //toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | ltr rtl | help', - // See - // [License key](https://www.tiny.cloud/docs/tinymce/latest/license-key). + // See [License + // key](https://www.tiny.cloud/docs/tinymce/latest/license-key). license_key: "gpl", // Settings for plugins diff --git a/client/src/typings.d.ts b/client/src/typings.d.ts index 3320b2e3..c464083d 100644 --- a/client/src/typings.d.ts +++ b/client/src/typings.d.ts @@ -14,7 +14,8 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `typing.d.ts` -- Global type definitions +// `typing.d.ts` -- Global type definitions +// ======================================== // // The server passes this to the client to load a file. See // [LexedSourceFile](../../server/src/webserver.rs#LexedSourceFile). diff --git a/client/src/wc-mermaid/developer.md b/client/src/wc-mermaid/developer.md index 1d319497..b25ea38e 100644 --- a/client/src/wc-mermaid/developer.md +++ b/client/src/wc-mermaid/developer.md @@ -1,3 +1,7 @@ -# Mermaid support +Mermaid support +=============== -This file is based on [wc-mermaid](https://github.com/manolakis/wc-mermaid). The code here was modified to allow a dynamic import of Mermaid and updated to support modern Mermaid. This software is licensed separately under the [Mermaid license](LICENSE.md). +This file is based on [wc-mermaid](https://github.com/manolakis/wc-mermaid). The +code here was modified to allow a dynamic import of Mermaid and updated to +support modern Mermaid. This software is licensed separately under the [Mermaid +license](LICENSE.md). \ No newline at end of file diff --git a/client/static/.gitignore b/client/static/.gitignore index 71444ec7..ec6aa808 100644 --- a/client/static/.gitignore +++ b/client/static/.gitignore @@ -16,7 +16,8 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `.gitignore` -- files for Git to ignore +# `.gitignore` -- files for Git to ignore +# ======================================= bundled/ mathjax/ mathjax-modern-font/ diff --git a/client/tsconfig.json b/client/tsconfig.json index 2feac8ce..bbffbf1d 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -14,19 +14,20 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # tsconfig.json -- TypeScript configuration +// tsconfig.json -- TypeScript configuration +// ========================================= { "compilerOptions": { "allowJs": true, // Required by ESBuild per their // [docs](https://esbuild.github.io/content-types/#es-module-interop); - // see also - // [the TypeScript docs](https://www.typescriptlang.org/tsconfig#esModuleInterop). + // see also [the TypeScript + // docs](https://www.typescriptlang.org/tsconfig#esModuleInterop). "esModuleInterop": true, // Required by ESBuild per their // [docs](https://esbuild.github.io/content-types/#isolated-modules); - // see also - // [the TypeScript docs](https://www.typescriptlang.org/tsconfig#isolatedModules). + // see also [the TypeScript + // docs](https://www.typescriptlang.org/tsconfig#isolatedModules). "isolatedModules": true, "module": "nodenext", "moduleResolution": "nodenext", diff --git a/docs/changelog.md b/docs/changelog.md index 9436f120..0087bd17 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -21,6 +21,10 @@ Changelog * [Github master](https://github.com/bjones1/CodeChat_Editor): * No changes. +* v0.1.10, 2025-Feb-20:  + * Update to the 2024 editing of Rust. + * Update dependencies. + * Update source formatting using current CodeChat Editor. * v0.1.9, 2025-Jan-20: * Correct word wrapping inside Mermaid diagrams. * Correct translation after adding newlines to code blocks in the Editor. diff --git a/docs/design.md b/docs/design.md index f7df3447..7187138d 100644 --- a/docs/design.md +++ b/docs/design.md @@ -1,12 +1,15 @@ -# CodeChat Editor design +CodeChat Editor design +====================== -## To build from source +To build from source +-------------------- 1. Clone or download the repository. 2. [Install the Rust language](https://www.rust-lang.org/tools/install). I recommend the 64-bit toolset for Windows. -3. [Install NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) - (the Node.js package manager). +3. [Install + NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) (the + Node.js package manager). 4. In the `server/` directory: 1. Run `./bt install --dev`. 2. Run `./bt build`. @@ -16,70 +19,72 @@ Use `./bt` tool's options update all libraries (`updated`), run all tests (`check`), and more. -## Vision +Vision +------------------------- These form a set of high-level requirements to guide the project. -- View source code as - [code blocks and doc blocks](index.md#code-blocks-and-doc-blocks). - Doc blocks are lines of source which contain only correctly-formatted - comments. -- Provide support for a - [wide variety of programming languages](index.md#programming-language-support). -- Provide integration with a - [wide variety of IDEs/text editors](index.md#ide-integration). -- Load a document from source code, allow edits in a GUI, then save it back to - source code. - - Provide word processor GUI tools (insert hyperlink, images, headings, change - font, etc.) for doc blocks. - - Provide text editor/IDE tools (syntax highlighting, line numbers, show - linter feedback) for code blocks. -- Zero build: eliminate the traditional project build process -- make it almost - instantaneous. -- Doc block markup should be readable and well-known: markdown. -- Support both a single-file mode and a project mode. - - A project is a specific directory tree, identified by the presence of a TOC. - A TOC is just a plain Markdown file with a specific name. - - A page in a project build is a single-file page plus: - - A TOC, along with previous/next/up navigation. The TOC is synchronized to - the current page. - - Numbering comes from the current page's location within the TOC. Pages not - in the TOC aren't numbered. -- Provide - [authoring support](index.md#authoring-support), which allows authors to - easily create book/project-like features. In particular: - - Counters for numbering figures, tables, equations, etc. All counters are - page-local (no global counters). - - Auto-titled links: the link text is automatically derived from the link's - destination (the heading text at the link's destination; a figure/table - caption, etc.). - - Auto-generated back links: anchors support auto-generated links back to all - their referents, which can be used for footnotes, endnotes, citations, and - indices. To enable this, all forward links must include an anchor and - optionally the text to display at the target. - - TOC support which, given some file(s), expands to a nested list of headings - in the file(s). Authors may specify the depth of headings to include. - - Autogenerated anchors for all anchors (headings, hyperlinks, etc.) - - Hyperlinks to identifiers in code (use - [ctags](https://github.com/universal-ctags/ctags)). - - Substitutions. - - Files/anchors can be freely moved without breaking links. This requires all - anchors to be globally unique. HTML allows upper/lowercase ASCII plus the - hyphen and underscore for IDs, meaning that a 5-character string - provides >250 million unique anchors. -- Make picking a file/anchor easy: provide a searchable, expanded TOC listing - every anchor. -- Provide edit and view options. (Rely on an IDE to edit raw source.) +* View source code as [code + blocks and doc blocks](index.md#code-blocks-and-doc-blocks). Doc blocks are + lines of source which contain only correctly-formatted comments. +* Provide support for a [wide + variety of programming languages](index.md#programming-language-support). +* Provide integration with a [wide variety + of IDEs/text editors](index.md#ide-integration). +* Load a document from source code, allow edits in a GUI, then save it + back to source code. + * Provide word processor GUI tools (insert hyperlink, images, headings, + change font, etc.) for doc blocks. + * Provide text editor/IDE tools (syntax highlighting, line numbers, show + linter feedback) for code blocks. +* Zero build: eliminate the traditional project build process -- make it + almost instantaneous. +* Doc block markup should be readable and well-known: markdown. +* Support both a single-file mode and a project mode. + * A project is a specific directory tree, identified by the presence of a + TOC. A TOC is just a plain Markdown file with a specific name. + * A page in a project build is a single-file page plus: + * A TOC, along with previous/next/up navigation. The TOC is + synchronized to the current page. + * Numbering comes from the current page's location within the TOC. + Pages not in the TOC aren't numbered. +* Provide [authoring + support](index.md#authoring-support), which allows authors to easily + create book/project-like features. In particular: + * Counters for numbering figures, tables, equations, etc. All counters are + page-local (no global counters). + * Auto-titled links: the link text is automatically derived from the + link's destination (the heading text at the link's destination; a + figure/table caption, etc.). + * Auto-generated back links: anchors support auto-generated links back to + all their referents, which can be used for footnotes, endnotes, + citations, and indices. To enable this, all forward links must include + an anchor and optionally the text to display at the target. + * TOC support which, given some file(s), expands to a nested list of + headings in the file(s). Authors may specify the depth of headings to + include. + * Autogenerated anchors for all anchors (headings, hyperlinks, etc.) + * Hyperlinks to identifiers in code (use + [ctags](https://github.com/universal-ctags/ctags)). + * Substitutions. + * Files/anchors can be freely moved without breaking links. This requires + all anchors to be globally unique. HTML allows upper/lowercase ASCII + plus the hyphen and underscore for IDs, meaning that a 5-character + string provides >250 million unique anchors. +* Make picking a file/anchor easy: provide a searchable, expanded TOC listing + every anchor. +* Provide edit and view options. (Rely on an IDE to edit raw source.) ### Nice to have features -- Simple to install locally; provide a template CodeSpaces repo for web-based - editing. -- Support a static build: producing a set of view-only HTML files which don't - need a server for a project, or a single HTML file outside a project. -- An API-only view (Doxygen/Javadoc like feature). +* Simple to install locally; provide a template CodeSpaces repo for web-based + editing. +* Support a static build: producing a set of view-only HTML files which don't + need a server for a project, or a single HTML file outside a project. +* An API-only view (Doxygen/Javadoc like feature). -## Requirements +Requirements +-------------------------------------- The requirements expand on the vision by providing additional details. @@ -111,43 +116,46 @@ indents are combined into a single, larger doc block.
// This is all one doc block, since only the preceding
// whitespace (there is none) matters, not the amount of
// whitespace following the opening comment delimiters.
// This is the beginning of a different doc
// block, since the indent is different.
// Here's a third doc block; inline and block comments
/* combine as long as the whitespace preceding the comment
delimiters is identical. Whitespace inside the comment doesn't affect
the classification. */
// These are two separate doc blocks,
void foo();
// since they are separated by a code block.
-### [Programming language support](index.md#programming-language-support) +### [Programming language +support](index.md#programming-language-support) Initial targets come from the Stack Overflow Developer Survey 2022's section on -[programming, scripting, and markup languages](https://survey.stackoverflow.co/2022/#section-most-popular-technologies-programming-scripting-and-markup-languages) -and IEEE Spectrum's -[Top Programming Languages 2022](https://spectrum.ieee.org/top-programming-languages-2022). +[programming, scripting, and markup +languages](https://survey.stackoverflow.co/2022/#section-most-popular-technologies-programming-scripting-and-markup-languages) +and IEEE Spectrum's [Top Programming Languages +2022](https://spectrum.ieee.org/top-programming-languages-2022). ### IDE/text editor integration Initial targets come from the Stack Overflow Developer Survey 2022's section on -[integrated development environments](https://survey.stackoverflow.co/2022/#section-most-popular-technologies-integrated-development-environment). +[integrated development +environments](https://survey.stackoverflow.co/2022/#section-most-popular-technologies-integrated-development-environment). There are two basic approaches: -- Sync with current window (simplest): have an additional IDE window open that - displays the file currently being edited. This requires: - - Auto-save: the CodeChat Editor autosaves any changes made, to keep files - synced. Have the host IDE auto-save, so that updates get pushed quickly. - - Auto-reload: if a the currently-opened file changes, then automatically - reload it. Have the host IDE do the same. - - Current file sync: when the current tab changes, update the CodeChat Editor - with the new file. Ideally, also sync the cursor position. -- Switchable editor (better, complex): provide a command to switch the current - editor with the CodeChat Editor and vice versa. This requires: - - To switch from the IDE editor to CodeChat, need to send the text of the - IDE's editor to CodeChat. For the opposite, need to get the CodeChat Editor - text and send that to the IDE's editor. - - Need to preserve the current cursor location across switches. This is harder - inside a doc block. An approximate find might be a good option. +* Sync with current window (simplest): have an additional IDE window open + that displays the file currently being edited. This requires: + * Auto-save: the CodeChat Editor autosaves any changes made, to keep files + synced. Have the host IDE auto-save, so that updates get pushed quickly. + * Auto-reload: if a the currently-opened file changes, then automatically + reload it. Have the host IDE do the same. + * Current file sync: when the current tab changes, update the CodeChat + Editor with the new file. Ideally, also sync the cursor position. +* Switchable editor (better, complex): provide a command to switch the + current editor with the CodeChat Editor and vice versa. This requires: + * To switch from the IDE editor to CodeChat, need to send the text of the + IDE's editor to CodeChat. For the opposite, need to get the CodeChat + Editor text and send that to the IDE's editor. + * Need to preserve the current cursor location across switches. This is + harder inside a doc block. An approximate find might be a good option. Additional features: -- Smart navigation: following links to a locally-editable file will open that - file in the current editor, saving any edits before navigating away. Following - non-local links opens the file in an external browser. -- Memory: the editor remembers the last cursor location for recently-opened - files, restoring that on the next file open. +* Smart navigation: following links to a locally-editable file will open that + file in the current editor, saving any edits before navigating away. + Following non-local links opens the file in an external browser. +* Memory: the editor remembers the last cursor location for recently-opened + files, restoring that on the next file open. ### Zero-build support @@ -163,7 +171,8 @@ instead providing an option to edit the underlying tag that produced the text. When a new tag is inserted, any tag-produced content should be immediately added. -## License +License +------- Copyright (C) 2022 Bryan A. Jones. @@ -181,4 +190,4 @@ details. You should have received a [copy](LICENSE.html) of the GNU General Public License along with the CodeChat Editor. If not, see -[https://www.gnu.org/licenses/](https://www.gnu.org/licenses/). +[https://www.gnu.org/licenses/](https://www.gnu.org/licenses/). \ No newline at end of file diff --git a/docs/implementation.md b/docs/implementation.md index 59c0a909..4777dfcf 100644 --- a/docs/implementation.md +++ b/docs/implementation.md @@ -16,9 +16,11 @@ You should have received a copy of the GNU General Public License along with the CodeChat Editor. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). -# Implementation +Implementation +============== -## Architecture +Architecture +------------------------------------------ ### Client/server partitioning @@ -31,89 +33,90 @@ elements, such as a cross-reference tag, depend on information from other pages ability to access other files, while the server has direct access to these files. Therefore, the overall strategy is: -- On page load, the server transforms custom tags which depend on information - from other pages into tags which include this information. For example, a - cross-reference tag might be transformed into a hyperlink whose link text - comes from the cross-reference on another page. -- The client them defines a set of - [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components) - which implement custom tags which only need local information. For example, a - GraphViz custom tag renders graphs based on a description of the graph inside - the tag. -- On save, the client sends its text back to the server, which de-transforms - custom tags which depend on information from other pages. If de-transforms - disagree with the provided text, then re-load the updated text after the save - is complete. For example, after inserting an auto-titled link, the auto-titled - text is missing; a save/reload fixes this. +* On page load, the server transforms custom tags which depend on information + from other pages into tags which include this information. For example, a + cross-reference tag might be transformed into a hyperlink whose link text + comes from the cross-reference on another page. +* The client them defines a set of [Web + Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components) + which implement custom tags which only need local information. For example, + a GraphViz custom tag renders graphs based on a description of the graph + inside the tag. +* On save, the client sends its text back to the server, which de-transforms + custom tags which depend on information from other pages. If de-transforms + disagree with the provided text, then re-load the updated text after the + save is complete. For example, after inserting an auto-titled link, the + auto-titled text is missing; a save/reload fixes this. ### Page processing pipeline On load: -- Classify the file; input are mutable global state (which, if present, - indicates this is a project build), if the file is a TOC, the file's binary - data, and the file's path. Output of the classification: binary, raw text, a - CodeChat document (a Markdown file), or a CodeChat file. The load processing - pipelines For CodeChat files: -- (CodeChat files only) Run pre-parse hooks: they receive source code, file - metadata. Examples: code formatters. Skip if cache is up to date. -- (CodeChat files only) Lex the file into code and doc blocks. -- Run post-parse hooks: they receive an array of code and doc blocks. - - Transform Markdown to HTML. -- Run HTML hooks: - - Update the cache for the current file only if the current file's cache is - stale. To do this, walk the DOM of each doc block. The hook specifies which - tags it wants, and the tree walker calls the hook when it encounters these. - If this requires adding/changing anything (anchors, for example), mark the - document as dirty. - - Update tags whose contents depend on data from other files. Hooks work the - same as the cache updates, but have a different role. They're always run, - while the cache update is skipped when the cache is current. -- Determine next/prev/up hyperlinks based on this file's location in the TOC. -- Transform the code and doc blocks into CodeMirror's format. +* Classify the file; input are mutable global state (which, if present, + indicates this is a project build), if the file is a TOC, the file's binary + data, and the file's path. Output of the classification: binary, raw text, a + CodeChat document (a Markdown file), or a CodeChat file. The load processing + pipelines For CodeChat files: +* (CodeChat files only) Run pre-parse hooks: they receive source code, file + metadata. Examples: code formatters. Skip if cache is up to date. +* (CodeChat files only) Lex the file into code and doc blocks. +* Run post-parse hooks: they receive an array of code and doc blocks. + * Transform Markdown to HTML. +* Run HTML hooks: + * Update the cache for the current file only if the current file's cache + is stale. To do this, walk the DOM of each doc block. The hook specifies + which tags it wants, and the tree walker calls the hook when it + encounters these. If this requires adding/changing anything (anchors, + for example), mark the document as dirty. + * Update tags whose contents depend on data from other files. Hooks work + the same as the cache updates, but have a different role. They're always + run, while the cache update is skipped when the cache is current. +* Determine next/prev/up hyperlinks based on this file's location in the TOC. +* Transform the code and doc blocks into CodeMirror's format. We want a clean separate between the webserver and the processing pipeline. The webserver should provide I/O between the local file system and the client, but do little processing. The processing pipeline should not perform I/O. Therefore: -- On load, the webserver receives a request for a file. It should gather and - pass the following to the page processor: - - The loaded file as text (or an Err result). - - The global state (empty if this isn't a project build). - - The pathname of the file. - - If this file should be processed as a TOC or not. -- The page processor returns: - - An Enum with the file's contents: +* On load, the webserver receives a request for a file. It should gather + and pass the following to the page processor: + * The loaded file as text (or an Err result). + * The global state (empty if this isn't a project build). + * The pathname of the file. + * If this file should be processed as a TOC or not. +* The page processor returns: + * An Enum with the file's contents: On save: -- Transform the CodeMirror format back to code and doc blocks. -- Run HTML hooks: - - Update the cache for the current file. Mark the file as "dirty" (reload - needed) if any changes are made. - - Check tags whose contents depend on data from other files; if the contents - differ, mark the file as dirty. - - Transform HTML to Markdown. -- Run post-parse hooks; mark the file as dirty if any changes are made. -- De-lex the file into source code. -- Run pre-parse hooks; mark the file as dirty if any changes are made. -- Save the file to disk. -- If dirty, re-load the file. +* Transform the CodeMirror format back to code and doc blocks. +* Run HTML hooks: + * Update the cache for the current file. Mark the file as "dirty" (reload + needed) if any changes are made. + * Check tags whose contents depend on data from other files; if the + contents differ, mark the file as dirty. + * Transform HTML to Markdown. +* Run post-parse hooks; mark the file as dirty if any changes are made. +* De-lex the file into source code. +* Run pre-parse hooks; mark the file as dirty if any changes are made. +* Save the file to disk. +* If dirty, re-load the file. #### HTML to Markdown transformation Currently, Turndown translates HTML to Markdown, then Prettier word-wraps the result. This has several problems: -- There are several bugs/open issues in Turndown; however, this package is no - longer maintained. -- Turndown doesn't have a good way to deal with raw HTML intermingled with - Markdown; since raw HTML can change the meaning of HTML through styles, this - is hard to avoid. But it still produces ugly results. -- Prettier translates setext-style headings to ATX headings, which I don't like. -- Because both packages are written in Javascript, they run in the browser. - However, we need to run processing at the HTML level on the server first, - requiring some round trips between client and sever in the future. +* There are several bugs/open issues in Turndown; however, this package is no + longer maintained. +* Turndown doesn't have a good way to deal with raw HTML intermingled with + Markdown; since raw HTML can change the meaning of HTML through styles, this + is hard to avoid. But it still produces ugly results. +* Prettier translates setext-style headings to ATX headings, which I don't + like. +* Because both packages are written in Javascript, they run in the browser. + However, we need to run processing at the HTML level on the server first, + requiring some round trips between client and sever in the future. To build Turndown, simply execute `npm run build` or `npm run test`. @@ -139,27 +142,30 @@ editor-overlay filesystem. #### Network interfaces between the Client, Server, and IDE -- The startup phase loads the Client framework into a browser:\ - ![Startup diagram](https://www.plantuml.com/plantuml/svg/PP3HIiOW583lVOfpwIwY-u4ng62ZHR7P0rYUOkJKZcUDthwPiVh_tOZ8vtS-RH8RucLsGYaOV_OHb1BTpIrSNC68z8bKmqD4ZrPs5lLNn4gKyqniO0q3fiMn79ac_xRHTmVYsatekUNPxLIhxti814z3NvtFEmfpNww0PmfhGW9PbF1APiOrqFk9CB_1XH05_8x-Rs-rVWJ2ZmKJoyl4XgUNaW7mrrtkxNIAmIVSSMlOL0Az5Sssv0_y1W00) -- If the current file in the IDE changes (including the initial startup, when - the change is from no file to the current file), or a link is followed in the - Client's iframe:\ - ![Load diagram](https://www.plantuml.com/plantuml/svg/hLBRJiCm37tlL_XnVO0Fw0EQD40W9fXs-O2mkX0fJKBY8ktlYTVGahQQEeayjBdOvnmVU-b9E6fgbTdmbqTfXIPuldz8pZjqt-YIgvMIg2aJwXmDoeZIGoKLPd2-kBcB8GMi6kV2vZ4yBdRafFueO2Fe4qm5jP3wrfxoa6LiWAfY5fJcsDIyaHvAwUYK0Q_u7E2PfO23BGNri4UZAJpx59hNKDGMlJNQu-BjXIDGbzaGF8r1vJ46fDMcIPFL7hRhHD5bDQob0utU5_2qts_0uLU3dXP3m3Qeqx0E-X81bkqcqoT4_WZUyuyokSX9MtFk_U-9kuIb9F7EbaJOli1EVMJvWnSZyiciUTqTUpLehZB6PX3MF5TzOwrn52ZRwgLEPscMsMESDfbDsuq86AcVqqkTISoRffZb_sK87lQHJ6teIgclHcF-3wAWSgO-x_p94zPHf2wpzijokr5acTVFOZfK3BeCdwPMFm00) -- If the current file's contents in the IDE are edited:\ - ![Edit diagram](https://www.plantuml.com/plantuml/svg/XT1DQiCm40NWlKunItlH2tXH36vBInCI_7C09NeE0cNaIEFa-ed1OCVaPp_l6zxBe-WW_T6flwzl-lYa2k6Ca57J6Ir8AWcM3nanBhJtB629gT9EQAqjKsiTo4Q2iQ9t3ef6OA0APy7oXeABkBVOosklw4C0ouzr4zgKA_BjpANnVDxfjwwt573g4ILP9Xw-6XEnynoVDc2Zfb-t6JCgbudDVwfoi1c6lW80) -- If the current file's contents in the Client are edited, the Client sends the - IDE an `Update` with the revised contents. -- When the PC goes to sleep then wakes up, the IDE client and the Editor client - both reconnect to the websocket URL containing their assigned ID. -- If the Editor client or the IDE client are closed, they close their websocket, - which send a `Close` message to the other websocket, causes it to also close - and ending the session. -- If the server is stopped (or crashes), both clients shut down after several - reconnect retries. - -Note: to edit these diagrams, paste the URL into the -[PlantUML web server](https://www.plantuml.com/plantuml/uml), click Decode URL, -edit, then copy and paste the SVG URL back to this file. +* The startup phase loads the Client framework into a browser:\ + ![Startup + diagram](https://www.plantuml.com/plantuml/svg/PP3HIiOW583lVOfpwIwY-u4ng62ZHR7P0rYUOkJKZcUDthwPiVh_tOZ8vtS-RH8RucLsGYaOV_OHb1BTpIrSNC68z8bKmqD4ZrPs5lLNn4gKyqniO0q3fiMn79ac_xRHTmVYsatekUNPxLIhxti814z3NvtFEmfpNww0PmfhGW9PbF1APiOrqFk9CB_1XH05_8x-Rs-rVWJ2ZmKJoyl4XgUNaW7mrrtkxNIAmIVSSMlOL0Az5Sssv0_y1W00) +* If the current file in the IDE changes (including the initial startup, when + the change is from no file to the current file), or a link is followed in + the Client's iframe:\ + ![Load + diagram](https://www.plantuml.com/plantuml/svg/hLBRJiCm37tlL_XnVO0Fw0EQD40W9fXs-O2mkX0fJKBY8ktlYTVGahQQEeayjBdOvnmVU-b9E6fgbTdmbqTfXIPuldz8pZjqt-YIgvMIg2aJwXmDoeZIGoKLPd2-kBcB8GMi6kV2vZ4yBdRafFueO2Fe4qm5jP3wrfxoa6LiWAfY5fJcsDIyaHvAwUYK0Q_u7E2PfO23BGNri4UZAJpx59hNKDGMlJNQu-BjXIDGbzaGF8r1vJ46fDMcIPFL7hRhHD5bDQob0utU5_2qts_0uLU3dXP3m3Qeqx0E-X81bkqcqoT4_WZUyuyokSX9MtFk_U-9kuIb9F7EbaJOli1EVMJvWnSZyiciUTqTUpLehZB6PX3MF5TzOwrn52ZRwgLEPscMsMESDfbDsuq86AcVqqkTISoRffZb_sK87lQHJ6teIgclHcF-3wAWSgO-x_p94zPHf2wpzijokr5acTVFOZfK3BeCdwPMFm00) +* If the current file's contents in the IDE are edited:\ + ![Edit + diagram](https://www.plantuml.com/plantuml/svg/XT1DQiCm40NWlKunItlH2tXH36vBInCI_7C09NeE0cNaIEFa-ed1OCVaPp_l6zxBe-WW_T6flwzl-lYa2k6Ca57J6Ir8AWcM3nanBhJtB629gT9EQAqjKsiTo4Q2iQ9t3ef6OA0APy7oXeABkBVOosklw4C0ouzr4zgKA_BjpANnVDxfjwwt573g4ILP9Xw-6XEnynoVDc2Zfb-t6JCgbudDVwfoi1c6lW80) +* If the current file's contents in the Client are edited, the Client sends + the IDE an `Update` with the revised contents. +* When the PC goes to sleep then wakes up, the IDE client and the Editor + client both reconnect to the websocket URL containing their assigned ID. +* If the Editor client or the IDE client are closed, they close their + websocket, which send a `Close` message to the other websocket, causes it to + also close and ending the session. +* If the server is stopped (or crashes), both clients shut down after several + reconnect retries. + +Note: to edit these diagrams, paste the URL into the [PlantUML web +server](https://www.plantuml.com/plantuml/uml), click Decode URL, edit, then +copy and paste the SVG URL back to this file. #### Message IDs @@ -169,7 +175,8 @@ Response message in return. Therefore, we need globally unique IDs for each message. To achieve this, the Server uses IDs that are multiples of 3 (0, 3, 6, ...), the Client multiples of 3 + 1 (1, 4, 7, ...) and the IDE multiples of 3 + 2 (2, 5, 8, ...). A double-precision floating point number (the standard -[numeric type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#number_type) +[numeric +type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#number_type) in JavaScript) has a 53-bit mantissa, meaning IDs won't wrap around for a very long time. @@ -181,19 +188,19 @@ activity from the core processing needed to translate source code between a CodeChat Editor Client and an IDE client. Specifically, one task handles the receive and transmit function for the websocket: -- The task sends a periodic ping to the CodeChat Editor Client or the IDE - client, then waits for a pong, closing the connection if the pong isn't - received in a timely manner. This helps detect a broken websocket connection - produced when a computer is put to sleep then wakes back up. -- Likewise, the task responds to a ping message from the CodeChat Editor Client - by sending a pong in response. -- It tracks messages sent and produces an error message if a sent message isn't - acknowledged within a timeout window. -- If the websocket is closed without warning, the websocket stores the relevant - data so that it can resume when the client reconnects to it. -- If the websocket is closed purposefully (for example, by closing a CodeChat - Editor Client tab in a web browser), the receive task detects this and shuts - down the websocket along with the associated IDE client tasks. +* The task sends a periodic ping to the CodeChat Editor Client or the IDE + client, then waits for a pong, closing the connection if the pong isn't + received in a timely manner. This helps detect a broken websocket connection + produced when a computer is put to sleep then wakes back up. +* Likewise, the task responds to a ping message from the CodeChat Editor + Client by sending a pong in response. +* It tracks messages sent and produces an error message if a sent message + isn't acknowledged within a timeout window. +* If the websocket is closed without warning, the websocket stores the + relevant data so that it can resume when the client reconnects to it. +* If the websocket is closed purposefully (for example, by closing a CodeChat + Editor Client tab in a web browser), the receive task detects this and shuts + down the websocket along with the associated IDE client tasks. To decouple these low-level websocket details from high-level processing (such as translating between source code and its web equivalent), the websocket tasks @@ -201,7 +208,39 @@ enqueue all high-level messages to the processing task; they listen to any enqueued messages in the client or ide queue, passing these on via the websocket connection. The following diagram illustrates this approach: - + The queues use multiple-sender, single receiver (mpsc) types; hence, a single task in the diagram receives data from a queue, while multiple tasks send data @@ -217,58 +256,67 @@ this specific request instance. The endpoint then returns the provided response. Simplest non-IDE integration: the file watcher. -- On startup, it sends the current file to the CodeChat Editor. -- It uses a file watcher to send update commands when the current file changes. -- It writes a file to disk when it receives an update command. -- It closes the editor if the file is deleted or moved. +* On startup, it sends the current file to the CodeChat Editor. +* It uses a file watcher to send update commands when the current file + changes. +* It writes a file to disk when it receives an update command. +* It closes the editor if the file is deleted or moved. Simplest IDE integration: -- On startup, it sends the current file to the CodeChat Editor. -- It sends update commands if edits are made in the IDE, when scrolling, or when - the active editor changes. -- It updates the IDE contents or opens a new file when it receives a update - command. +* On startup, it sends the current file to the CodeChat Editor. +* It sends update commands if edits are made in the IDE, when scrolling, or + when the active editor changes. +* It updates the IDE contents or opens a new file when it receives a update + command. More complex IDE integration: everything that the simple IDE does, plus the ability to toggle between the IDE's editor and the CodeChat Editor. -## Build system +Build system +------------ The app needs build support because of complexity: -- The client's NPM libraries need patching and some partial copying. -- After building a release for a platform, client/server binaries must be copied - to the VSCode extension, then a release published for that platform. +* The client's NPM libraries need patching and some partial copying. +* After building a release for a platform, client/server binaries must be + copied to the VSCode extension, then a release published for that platform. So, this project contains Rust code to automate this process -- see the [builder](../builder/Cargo.toml). -## Future work +Future work +----------- ### Table of contents -- While the TOC file must be placed in the root of the project, it will be - served alongside pages served from subdirectories. Therefore, place this in an - iframe to avoid regenerating it for every page. -- The TOC is just HTML. Numbered sections are expressed as nested ordered lists, - with links to each section inside these lists. -- All numbering is stored internally as a number, instead of the corresponding - marker (I, II, III, etc.). This allows styles to customize numbering easily. - - Given an `a` element in the TOC, looking through its parents provides the - section number. Given an array of section numbers, use CSS to style all the - headings. Implement numbering using CSS variables, which makes it easy for a - style sheet to include or exclude section numbers: - - `:root {`\ -   `--section-counter-reset: s1 4 s2 5;`\ -   `--section-counter-content: counter(s1, numeric) '-' counter(s2, numeric);`\ - `}` - - `h1::before {`\ -   `counter-reset: var(--section-counter-reset);`\ -   `content: var(--section-counter-content);`\ - `}` +* While the TOC file must be placed in the root of the project, it will be + served alongside pages served from subdirectories. Therefore, place this in + an iframe to avoid regenerating it for every page. +* The TOC is just HTML. Numbered sections are expressed as nested ordered + lists, with links to each section inside these lists. +* All numbering is stored internally as a number, instead of the + corresponding marker (I, II, III, etc.). This allows styles to customize + numbering easily. + * Given an `a` element in the TOC, looking through its parents provides + the section number. Given an array of section numbers, use CSS to style + all the headings. Implement numbering using CSS variables, which makes + it easy for a style sheet to include or exclude section numbers: + + `:root {`\ +   + `--section-counter-reset: s1 4 s2 5;`\ +   + `--section-counter-content: counter(s1, numeric) '-' counter(s2, + numeric);`\ + `}` + + `h1::before {`\ +   + `counter-reset: var(--section-counter-reset);`\ +   + `content: var(--section-counter-content);`\ + `}` ### Cached state @@ -279,39 +327,41 @@ server, all needed hydration data should be stored in the cache. What we need to know: -- To generate the TOC, we need a way to find the linked file, then get a list of - all its headings. - - Problem: files can be moved. Better would be an invariant anchor, stored in - the file, which doesn't change. It would make sense to link to the only - \

element...but there may not be one, or it may not be at the top of the - file. The easy solution would be an anchor tag at the beginning of the - file...but this would break shell scripts, for example. Another is including - an anchor tag somewhere in each document, but need authors to understand - what it is (and not delete it). Another possibility is to link to any anchor - in the file with a special query identifying it as link to the underlying - file. -- To auto-title a link, need to look up an anchor and get its location (page - number, section number) and title. -- For back links, need to look up all links to the given anchor, then get the - location and title of each link. -- To generate the TOC containing all anchors, we need a list of all anchors on a - given page. +* To generate the TOC, we need a way to find the linked file, then get a + list of all its headings. + * Problem: files can be moved. Better would be an invariant anchor, stored + in the file, which doesn't change. It would make sense to link to the + only \

element...but there may not be one, or it may not be at the + top of the file. The easy solution would be an anchor tag at the + beginning of the file...but this would break shell scripts, for example. + Another is including an anchor tag somewhere in each document, but need + authors to understand what it is (and not delete it). Another + possibility is to link to any anchor in the file with a special query + identifying it as link to the underlying file. +* To auto-title a link, need to look up an anchor and get its location (page + number, section number) and title. +* For back links, need to look up all links to the given anchor, then get the + location and title of each link. +* To generate the TOC containing all anchors, we need a list of all anchors on + a given page. Therefore, the cache must contain a `FileAnchor`, an enum of: -- A `PlainFileAnchor` (a non-HTML file -- an image, PDF, etc.). Generate an ID - based on a checksum of the file. Basically, this provides some way to find the - (unmodified) file if it's moved/renamed. Cache data: |path, ID, file's - metadata|. -- An `HtmlFileAnchor` (an HTML file). Store an ID as a comment in it somewhere, - probably at the end of the file. Cache data: |path, ID, file's metadata|, TOC - numbering, a vector of `HeadingAnchor`s, a vector of `NonHeadingAnchor`s: - - A `HeadingAnchor` in an HTML file: |weak ref to the containing - `HtmlFileAnchor`, ID, anchor's inner HTML, optional hyperlink|, numbering on - this page, a vector of `NonHeadingAnchors` contained in this heading. - - A `NonHeadingAnchor` in an HTML file: |weak ref to the containing - `HtmlFileAnchor`, ID, anchor's inner HTML, optional hyperlink|, optional - parent heading, snippet of surrounding text, numbering group, number. +* A `PlainFileAnchor` (a non-HTML file -- an image, PDF, etc.). Generate an ID + based on a checksum of the file. Basically, this provides some way to find + the (unmodified) file if it's moved/renamed. Cache data: |path, ID, file's + metadata|. +* An `HtmlFileAnchor` (an HTML file). Store an ID as a comment in it + somewhere, probably at the end of the file. Cache data: |path, ID, + file's metadata|, TOC numbering, a vector of `HeadingAnchor`s, a vector + of `NonHeadingAnchor`s: + * A `HeadingAnchor` in an HTML file: |weak ref to the containing + `HtmlFileAnchor`, ID, anchor's inner HTML, optional hyperlink|, + numbering on this page, a vector of `NonHeadingAnchors` contained in + this heading. + * A `NonHeadingAnchor` in an HTML file: |weak ref to the containing + `HtmlFileAnchor`, ID, anchor's inner HTML, optional hyperlink|, optional + parent heading, snippet of surrounding text, numbering group, number. A `Hyperlink` consists of a path and ID the link refers to.\ An `HtmlAnchor` is an enum of `HeadingAnchor` and `NonHeadingAnchor`.\ @@ -319,8 +369,8 @@ An `Anchor` is an enum of a `FileAnchor` and an `HtmlAnchor`. Globals: -- A map of `PathBuf`s to `FileAnchors`. -- A map of IDs to (`Anchor`, set of IDs of referring links) +* A map of `PathBuf`s to `FileAnchors`. +* A map of IDs to (`Anchor`, set of IDs of referring links) How to keep the sets of referring links up to date? If a link is deleted, we won't know until that file is removed. To fix, add a validate() function that @@ -353,8 +403,8 @@ flush, simply set the date/time stamp of that file to something old/invalid. Options: -- Path to linked file -- Depth of numbering +* Path to linked file +* Depth of numbering #### Example @@ -378,18 +428,18 @@ make it easy to add more config values. Settings should also be available for plug-ins. Store the config values in a bare JSON file; provide a web-based GUI with descriptions of each setting. -- Files/directories to process/ignore -- Header/footer info (name, version, copyright, etc.) -- The programming language, markup language, and spellchecker language for each - source file. -- Text wrap width when saving. -- Visual styling (theme/style sheets, color, fonts, size of TOC sidebar, - location of sidebar, etc.). -- HTML `` modifications: CSS/JS to add to all pages/a set of pages. -- Depth of headings to include in the page-local TOC. -- Auto-reload if modified externally or not -- Tabs vs spaces; newline type -- Substitutions +* Files/directories to process/ignore +* Header/footer info (name, version, copyright, etc.) +* The programming language, markup language, and spellchecker language for + each source file. +* Text wrap width when saving. +* Visual styling (theme/style sheets, color, fonts, size of TOC sidebar, + location of sidebar, etc.). +* HTML `` modifications: CSS/JS to add to all pages/a set of pages. +* Depth of headings to include in the page-local TOC. +* Auto-reload if modified externally or not +* Tabs vs spaces; newline type +* Substitutions ### Core development priorities @@ -399,8 +449,8 @@ with descriptions of each setting. ### Next steps 1. Refactor the webserver to pull out the processing step (converting source - code to code/doc blocks). Run this in a separate thread -- see the - [Tokio docs](https://docs.rs/tokio/latest/tokio/#cpu-bound-tasks-and-blocking-code) + code to code/doc blocks). Run this in a separate thread -- see the [Tokio + docs](https://docs.rs/tokio/latest/tokio/#cpu-bound-tasks-and-blocking-code) on how to await a task running in another thread. 2. Implement caching for all anchors/headings. 3. Implement author support: TOC, auto-titled links. @@ -420,27 +470,29 @@ with descriptions of each setting. ### Open questions -- I'd like to be able to wrap a heading and associated content in a `
` - tag. This is hard to do -- if a heading appears in the middle of an indented - comment, then need special processing (close the section, then the indent, - then restart a new indent and section). In addition, it requires that code is - nested inside doc blocks, which is tricky. However, I would like to do this. -- How to handle images/videos/PDFs/etc. when file are moved? Currently, we - expect the user to move them as well. There's not an easy way to tag them with - an unique ID, then refer to them using that ID than I can think of. -- Config file format: I really like and prefer Python's strictyaml. Basically, I - want something that includes type validation and allows comments within the - config file. Perhaps JSON with a pre-parse step to discard comments then - [JSON Typedef](https://jsontypedef.com/)? Possibly, vlang can do this - somewhat, since it wants to decode JSON into a V struct.) - -## Organization +* I'd like to be able to wrap a heading and associated content in a + `
` tag. This is hard to do -- if a heading appears in the middle of + an indented comment, then need special processing (close the section, then + the indent, then restart a new indent and section). In addition, it requires + that code is nested inside doc blocks, which is tricky. However, I would + like to do this. +* How to handle images/videos/PDFs/etc. when file are moved? Currently, we + expect the user to move them as well. There's not an easy way to tag them + with an unique ID, then refer to them using that ID than I can think of. +* Config file format: I really like and prefer Python's strictyaml. Basically, + I want something that includes type validation and allows comments within + the config file. Perhaps JSON with a pre-parse step to discard comments then + [JSON Typedef](https://jsontypedef.com/)? Possibly, vlang can do this + somewhat, since it wants to decode JSON into a V struct.) + +Organization +------------ ### Client As shown in the figure below, the CodeChat Editor Client starts with `client/package.json`, which tells -[NPM]() which JavaScript libraries +[NPM](https://en.wikipedia.org/wiki/Npm_\(software\)) which JavaScript libraries are used in this project. Running `npm update` copies these libraries and all their dependencies to the `client/node_modules` directory. The CodeChat Editor Client source code (see [CodeChatEditor.mts](../client/src/CodeChatEditor.mts)) @@ -453,28 +505,68 @@ a smaller set of files. At a user's request, the CodeChat Editor Server generates HTML which creates an editor around the user-requested file. This HTML loads the packaged dependencies to create the CodeChat Editor Client webpage. - - -Note: to edit these diagrams, use an -[HTML entity encoder/decoder](https://mothereff.in/html-entities) and a Graphviz -editor such as [Edotor](https://edotor.net/). + + +Note: to edit these diagrams, use an [HTML entity +encoder/decoder](https://mothereff.in/html-entities) and a Graphviz editor such +as [Edotor](https://edotor.net/). However, esbuild's code splitting doesn't work with dynamic imports -- the splitter always picks Node-style default imports, while the Ace editor expects Babel-style imports. -TODO: GUIs using TinyMCE. See the -[how-to guide](https://www.tiny.cloud/docs/tinymce/6/dialog-components/#panel-components). +TODO: GUIs using TinyMCE. See the [how-to +guide](https://www.tiny.cloud/docs/tinymce/6/dialog-components/#panel-components). ### System architecture - - -## Code style + + +Code style +---------- JavaScript functions are a [disaster](https://dmitripavlutin.com/differences-between-arrow-and-regular-functions/). Therefore, we use only arrow functions for this codebase. -Other than that, follow the -[MDN style guide](https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Writing_style_guide/Code_style_guide/JavaScript). +Other than that, follow the [MDN style +guide](https://developer.mozilla.org/en-US/docs/MDN/Writing_guidelines/Writing_style_guide/Code_style_guide/JavaScript). \ No newline at end of file diff --git a/docs/style_guide.cpp b/docs/style_guide.cpp index 6b443874..9a423549 100644 --- a/docs/style_guide.cpp +++ b/docs/style_guide.cpp @@ -1,4 +1,5 @@ -// # `style_guide.cpp` - Literate programming using the CodeChat Editor +// `style_guide.cpp` - Literate programming using the CodeChat Editor +// ================================================================== // // This document, written as a C++ source file, primarily demonstrates the use // of the CodeChat Editor in literate programming. It should be viewed using the @@ -20,7 +21,8 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// ## Introduction +// Introduction +// ------------ // // This document provides a style guide for literate programming using the // CodeChat Editor. For basic use, see the [user manual](../README.md). @@ -39,12 +41,13 @@ const char* CODE_BLOCK = // doc blocks with differing indents cannot be combined. /* Doc blocks may use either inline comments (`//` in C++) or block comments (like this comment). Doc blocks with differing delimiters cannot be combined. */ -// Doc blocks are interpreted using Markdown -// (specifically, [CommonMark](https://commonmark.org/)), enabling the use of -// headings, _emphasis_, **strong emphasis**, `monospaced fonts`, and much more; -// see a [brief overview of Markdown](https://commonmark.org/help/). +// Doc blocks are interpreted using Markdown (specifically,  +// [CommonMark](https://commonmark.org/)), enabling the use of headings, +// *emphasis*, **strong emphasis**, `monospaced fonts`, and much more; see a +// [brief overview of Markdown](https://commonmark.org/help/). // -// ## Approach +// Approach +// -------- // // Viewing a program as a document defines the heart of the literate programming // paradigm. A program/document -- constructed as a series of code blocks and @@ -70,23 +73,24 @@ const char* CODE_BLOCK = // sense?​ Update your overall approach based on what you discover. Get another // person to review what you wrote, then implement their ideas and suggestions. // -// ## Organization +// Organization +// ------------------------------------- // // The program should use headings to appropriately organize the contents. Near // the top of the file, include a single level-1 heading, providing the title of -// the file; per the HTML spec, there should be -// [only one level-1 heading](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements#avoid_using_multiple_h1_elements_on_one_page). +// the file; per the HTML spec, there should be [only one level-1 +// heading](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements#avoid_using_multiple_h1_elements_on_one_page). // For source files, include the file name at the beginning of the title, in a // monospaced font. // -// Following the title, include additional heading levels; -// [don't skip levels](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements#navigation), +// Following the title, include additional heading levels; [don't skip +// levels](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements#navigation), // e.g. by placing a level-3 heading immediately following a level-1 heading. -// Use headings to provide a natural outline of your program. The -// [end of this document](#org-style) provides the recommended organizational -// style. +// Use headings to provide a natural outline of your program. The [end of this +// document](#org-style) provides the recommended organizational style. // -// ## Location +// Location +// -------- // // In general, place documentation before the corresponding code. For example: // @@ -105,7 +109,8 @@ class LedBlinker { ); }; -// ## Use of mathematics +// Use of mathematics +// ------------------ // // Formulas should be placed near code that implements them, along with good // explanations of the equations used. For example: @@ -120,17 +125,18 @@ double accurate_g( // Height above sea level, in meters. double height_meters ) { - // This text comes from the - // [SensorsOne Local Gravity Calculator](https://www.sensorsone.com/local-gravity-calculator/). - // For more detail, see - // [Theoretical Gravity](https://en.wikipedia.org/wiki/Theoretical_gravity). + // This text comes from the [SensorsOne Local Gravity + // Calculator](https://www.sensorsone.com/local-gravity-calculator/). For + // more detail, see [Theoretical + // Gravity](https://en.wikipedia.org/wiki/Theoretical_gravity). // - // The formulas used by this function are based on - // the [International Gravity Formula IGF) 1980](https://en.wikipedia.org/wiki/Normal_gravity_formula#International_gravity_formula_1980) from - // the parameters of - // the [Geodetic Reference System 1980 (GRS80)](https://en.wikipedia.org/wiki/GRS_80), - // which determines the gravity from the position of latitude, and - // the [Free Air Correction (FAC)](https://en.wikipedia.org/wiki/Gravity_of_Earth#Free_air_correction) + // The formulas used by this function are based on the [International + // Gravity Formula IGF) + // 1980](https://en.wikipedia.org/wiki/Normal_gravity_formula#International_gravity_formula_1980)  + // from the parameters of the [Geodetic Reference System 1980 + // (GRS80)](https://en.wikipedia.org/wiki/GRS_80), which determines the + // gravity from the position of latitude, and the [Free Air Correction + // (FAC)](https://en.wikipedia.org/wiki/Gravity_of_Earth#Free_air_correction) // which corrects for height above and below mean sea level in free air. // // Compute the International Gravity Formula (IGF):\ @@ -146,67 +152,70 @@ double accurate_g( return IGF + FAC; // Symbols: // - // - $g$ = Theoretical local gravity, in $m/s^2$. - // - $\\phi$ = Latitude, in decimal degrees. - // - $h$ = Height relative to sea level, in $m$. + // * $g$ = Theoretical local gravity, in $m/s^2$. + // * $\\phi$ = Latitude, in decimal degrees. + // * $h$ = Height relative to sea level, in $m$. } -// ## Excellence in code +// Excellence in code +// ------------------ // // Literate programming should be accompanied by excellence in authoring code. // Specifically: // -// - Use meaningful, descriptive names for variables, classes, functions, etc. -// Doc blocks should only supply what -// [self-documenting code](https://en.wikipedia.org/wiki/Self-documenting_code) -// cannot -- design choices, purpose, etc. -// - Be consistent; preferably, use a -// [code formatter](https://en.wikipedia.org/wiki/Prettyprint#Programming_code_formatting) -// to ensure this consistency. -// - Employ [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) -// principles. -// - Address warnings, not only errors; preferably, use -// a [linter](). -// - Write automated tests; employ -// [test-driven development](https://en.wikipedia.org/wiki/Test-driven_development). -// -// ## Editor configuration +// * Use meaningful, descriptive names for variables, classes, functions, etc. +// Doc blocks should only supply what [self-documenting +// code](https://en.wikipedia.org/wiki/Self-documenting_code) cannot -- +// design choices, purpose, etc. +// * Be consistent; preferably, use a [code +// formatter](https://en.wikipedia.org/wiki/Prettyprint#Programming_code_formatting) +// to ensure this consistency. +// * Employ [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) +// principles. +// * Address warnings, not only errors; preferably, use a  +// [linter](https://en.wikipedia.org/wiki/Lint_\(software\)). +// * Write automated tests; employ [test-driven +// development](https://en.wikipedia.org/wiki/Test-driven_development). +// +// Editor configuration +// -------------------- // // Properly configuring the text editor used with the CodeChat Editor // significantly improves the authoring process. Recommended settings: // -// - Enable word wrap: -// [vscode](https://learn.microsoft.com/en-us/visualstudio/ide/reference/how-to-manage-word-wrap-in-the-editor?view=vs-2022) -// - Use spaces, not tabs​, for indentation: -// [vscode](https://code.visualstudio.com/docs/editor/codebasics#_indentation) -// - Enable auto-save: -// [vscode](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) -// - Auto-reload enabled​: default in vscode -// - On save, remove trailing whitespace​: -// [vscode](https://stackoverflow.com/a/53663494/16038919) -// - Use a spell checker: -// [vscode](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) -// - On a big monitor, place your IDE side by side with the CodeChat Editor. -// -// ## Common problems -// -// - Don't drag and drop an image into the Editor – this creates a mess. -// Instead, save all images to a file, then use an SVG or PNG image for -// text/line art​ or a JPEG image for photos​. The Markdown syntax to insert -// an image is `![Alt text](https://url.to/image.svg)`. -// - Indent your comments to match the indentation of nearby code; don't -// purposelessly vary the comment indentation. -// - Avoid inserting a one-line empty code block (a blank line) between -// paragraphs in a doc block; instead, use a single doc block to store -// multiple paragraphs. -// - Use minimal formatting. Markdown is a simple, rather limited syntax; -// however, it is very easy to use and read. While the CodeChat Editor will -// happily replace simple Markdown constructs with verbose HTML to accomplish -// the formatting you specify, avoid the resulting -// messy syntax produced by this process. -// Pasting from an HTML source (such as Word or a web page) directly to the -// CodeChat Editor likewise produces a lot of messy syntax; consider pasting -// text only, then reformatting as necessary. +// * Enable word wrap: +// [vscode](https://learn.microsoft.com/en-us/visualstudio/ide/reference/how-to-manage-word-wrap-in-the-editor?view=vs-2022) +// * Use spaces, not tabs​, for indentation: +// [vscode](https://code.visualstudio.com/docs/editor/codebasics#_indentation) +// * Enable auto-save: +// [vscode](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) +// * Auto-reload enabled​: default in vscode +// * On save, remove trailing whitespace​: +// [vscode](https://stackoverflow.com/a/53663494/16038919) +// * Use a spell checker: +// [vscode](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) +// * On a big monitor, place your IDE side by side with the CodeChat Editor. +// +// Common problems +// --------------- +// +// * Don't drag and drop an image into the Editor – this creates a mess. +// Instead, save all images to a file, then use an SVG or PNG image for +// text/line art​ or a JPEG image for photos​. The Markdown syntax to insert +// an image is `![Alt text](https://url.to/image.svg)`. +// * Indent your comments to match the indentation of nearby code; don't +// purposelessly vary the comment indentation. +// * Avoid inserting a one-line empty code block (a blank line) between +// paragraphs in a doc block; instead, use a single doc block to store +// multiple paragraphs. +// * Use minimal formatting. Markdown is a simple, rather limited syntax; +// however, it is very easy to use and read. While the CodeChat Editor will +// happily replace simple Markdown constructs with verbose HTML to +// accomplish the formatting you specify, avoid the resulting messy syntax produced by this process. +// Pasting from an HTML source (such as Word or a web page) directly to the +// CodeChat Editor likewise produces a lot of messy syntax; consider pasting +// text only, then reformatting as necessary. // // ### Commenting out code // @@ -220,16 +229,18 @@ double accurate_g( // improved alternative to commenting out code using preprocessor directives for // C/C++. // -// ## Example structure +// Example structure +// ----------------- // // As discussed in [organization](#organization), the remainder of this document // presents the preferred use of headings to organize source code. // -// ## Includes +// Includes +// ------------------------------ // // Include files (in Python, imports; Rust, use statements; JavaScript, -// require/import, etc.) should be organized by category; for example, -// [PEP 8](https://peps.python.org/pep-0008/#imports) recommends the following +// require/import, etc.) should be organized by category; for example, [PEP +// 8](https://peps.python.org/pep-0008/#imports) recommends the following // categories: // // ### Standard library @@ -244,20 +255,24 @@ double accurate_g( // Note: This is a fictitious file, here for example only. #include -// ## Global variables/constants +// Global variables/constants +// -------------------------- // // Use units when describing physical quantities. For example, this gives the // acceleration due to gravity in $m/s^2$. const double accel_m_s2 = 9.8067; -// ## Macros +// Macros +// ------ #define LED1 (LATB16) -// ## Structures/classes +// Structures/classes +// ------------------ class BlinkLed { }; -// ## Code +// Code +// ---- int main(int argc, char* argv[]) { // Here's an example of commenting code out when using the CodeChat Editor: /** diff --git a/extensions/VSCode/.eslintrc.yml b/extensions/VSCode/.eslintrc.yml index 45b12690..f2853c81 100644 --- a/extensions/VSCode/.eslintrc.yml +++ b/extensions/VSCode/.eslintrc.yml @@ -16,16 +16,17 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `.eslintrc.yml` - Configure ESLint for this project +# `.eslintrc.yml` - Configure ESLint for this project +# =================================================== env: commonjs: true node: true extends: - standard - # See the - # [ESLint config prettier docs](https://github.com/prettier/eslint-config-prettier#installation) - # and its parent link, - # [integrating Prettier with linters](https://prettier.io/docs/en/integrating-with-linters.html). + # See the [ESLint config prettier + # docs](https://github.com/prettier/eslint-config-prettier#installation) and + # its parent link, [integrating Prettier with + # linters](https://prettier.io/docs/en/integrating-with-linters.html). - prettier parser: "@typescript-eslint/parser" parserOptions: @@ -35,6 +36,6 @@ plugins: rules: camelcase: off # TypeScript already enforces this; otherwise, eslint complains that - # `NodeJS` is undefined. See - # [this GitHub issue](https://github.com/Chatie/eslint-config/issues/45#issuecomment-1003990077). + # `NodeJS` is undefined. See [this GitHub + # issue](https://github.com/Chatie/eslint-config/issues/45#issuecomment-1003990077). no-undef: off diff --git a/extensions/VSCode/.gitignore b/extensions/VSCode/.gitignore index cfa81522..acdac515 100644 --- a/extensions/VSCode/.gitignore +++ b/extensions/VSCode/.gitignore @@ -16,7 +16,8 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `.gitignore` -- files for Git to ignore +# `.gitignore` -- files for Git to ignore +# ======================================= # # NPM node_modules/ diff --git a/extensions/VSCode/.vscodeignore b/extensions/VSCode/.vscodeignore index 5b08932e..8af78d46 100644 --- a/extensions/VSCode/.vscodeignore +++ b/extensions/VSCode/.vscodeignore @@ -16,8 +16,11 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `vscodeignore` - Files not to package -# See https://code.visualstudio.com/api/working-with-extensions/publishing-extension#.vscodeignore. +# `vscodeignore` - Files not to package +# ===================================== +# +# See +# [Using .vscodeignore](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#using-.vscodeignore). # # Omit all TypeScript source. src/** @@ -25,7 +28,8 @@ src/** # Omit VSCode config. .vscode/** -# Omit target-specific binaries. (Comment out when publishing for a specific target.) +# Omit target-specific binaries. (Comment out when publishing for a specific +# target.) #server/** # Misc files not needed in a package. diff --git a/extensions/VSCode/README.md b/extensions/VSCode/README.md index c74e91cd..9141ccd5 100644 --- a/extensions/VSCode/README.md +++ b/extensions/VSCode/README.md @@ -1,26 +1,32 @@ -# The CodeChat Editor extension for Visual Studio Code +The CodeChat Editor extension for Visual Studio Code +==================================================== This extension provides the CodeChat Editor's capabilities within the Visual Studio Code IDE. -![Screenshot of the CodeChat Editor extension](https://github.com/bjones1/CodeChat_Editor/blob/main/extensions/VSCode/screenshot.png?raw=true) +![Screenshot of the CodeChat Editor +extension](https://github.com/bjones1/CodeChat_Editor/blob/main/extensions/VSCode/screenshot.png?raw=true) -## Installation +Installation +------------ First, install [Visual Studio Code](https://code.visualstudio.com/). Next: -1. [Install the CodeChat Editor extension](https://marketplace.visualstudio.com/items?itemName=CodeChat.codechat-editor-client). -2. (Recommended) - [switch to a light theme](https://code.visualstudio.com/docs/getstarted/themes), - since the CodeChat Editor only provides a light theme. +1. [Install the CodeChat Editor + extension](https://marketplace.visualstudio.com/items?itemName=CodeChat.codechat-editor-client). +2. (Recommended) [switch to a light + theme](https://code.visualstudio.com/docs/getstarted/themes), since the + CodeChat Editor only provides a light theme. -## Running +Running +------- 1. Open a file that the CodeChat Editor [supports](https://github.com/bjones1/CodeChat_Editor/blob/main/README.md#supported-languages) (many source files, along with Markdown files). -2. Open the - [Visual Studio Code command palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) + +2. Open the [Visual Studio Code command + palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) by pressing `Ctrl+Shift+P`. Type `CodeChat`, select "Enable the CodeChat Editor", then press enter to run the extension. After a moment, the rendered file should load. If it doesn't: @@ -32,7 +38,8 @@ First, install [Visual Studio Code](https://code.visualstudio.com/). Next: 2. Run the extension again (close the existing window, type `Ctrl+Shift+P` then select Enable the CodeChat Editor). -## Additional documentation +Additional documentation +------------------------ -See the -[user manual](https://github.com/bjones1/CodeChat_Editor/blob/main/README.md). +See the [user +manual](https://github.com/bjones1/CodeChat_Editor/blob/main/README.md). \ No newline at end of file diff --git a/extensions/VSCode/developer.md b/extensions/VSCode/developer.md index 7c00aa17..f0412d46 100644 --- a/extensions/VSCode/developer.md +++ b/extensions/VSCode/developer.md @@ -1,42 +1,49 @@ -# Developer documentation +Developer documentation +======================= -## From source +From source +----------- To install from source: -- Install [npm](https://nodejs.org/en/). -- Install this extension's manifest - ([package.json](https://code.visualstudio.com/api/references/extension-manifest)): - from this directory, open a command prompt/terminal then execute:: - - ``` - npm install - ``` - -## Debugging the extension - -- From VSCode, select File | Add Folder to Workspace... then choose the folder - containing this file. -- Press ctrl+shift+B to compile the extension. -- Press F5 or click start debugging under the Debug menu. -- A new instance of VSCode will start in a special mode (Extension Development - Host) which contains the CodeChat extension. -- Open any source code, then press Ctrl+Shift+P and type "CodeChat" to run the - CodeChat extension. You will be able to see the rendered version of your - active window. - -## Release procedure - -- In the Client: - - Update the version of the plugin in `package.json`. -- In the Server: - - Update the version in `cargo.toml`. -- Here: - - Update the version of the plugin in `package.json`. - - Run `cargo run -- release` on each platform, which produces a `.vsix` file for that platform - - Run `npx vsce publish --packagePath blah`. - ([docs](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#platformspecific-extensions)) - -## Tests - -TODO: tests are missing. +* Install [npm](https://nodejs.org/en/). + +* Install this extension's manifest + ([package.json](https://code.visualstudio.com/api/references/extension-manifest)): + from this directory, open a command prompt/terminal then execute:: + + ``` + npm install + ``` + +Debugging the extension +----------------------- + +* From VSCode, select File | Add Folder to Workspace... then choose the folder + containing this file. +* Press ctrl+shift+B to compile the extension. +* Press F5 or click start debugging under the Debug menu. +* A new instance of VSCode will start in a special mode (Extension Development + Host) which contains the CodeChat extension. +* Open any source code, then press Ctrl+Shift+P and type "CodeChat" to run the + CodeChat extension. You will be able to see the rendered version of your + active window. + +Release procedure +----------------- + +* In the Client: + * Update the version of the plugin in `package.json`. +* In the Server: + * Update the version in `cargo.toml`. +* Here: + * Update the version of the plugin in `package.json`. + * Run `cargo run -- release` on each platform, which produces a `.vsix` + file for that platform + * Run `npx vsce publish --packagePath blah`. + ([docs](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#platformspecific-extensions)) + +Tests +----- + +TODO: tests are missing. \ No newline at end of file diff --git a/extensions/VSCode/jsconfig.json b/extensions/VSCode/jsconfig.json index b06629a3..09fe5253 100644 --- a/extensions/VSCode/jsconfig.json +++ b/extensions/VSCode/jsconfig.json @@ -14,12 +14,14 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// `jsconfig.json` is a configuration file that controls the behavior of ??? +// `jsconfig.json` -- JavaScript configuration +// =========================================== { "compilerOptions": { "module": "NodeNext", "target": "es6", - "checkJs": true, /* Typecheck .js files. */ + // Typecheck `.js` files. + "checkJs": true, "lib": [ "es6" ] diff --git a/extensions/VSCode/package-lock.json b/extensions/VSCode/package-lock.json index dc1fb8f8..6351b240 100644 --- a/extensions/VSCode/package-lock.json +++ b/extensions/VSCode/package-lock.json @@ -1,12 +1,12 @@ { "name": "codechat-editor-client", - "version": "0.1.9", + "version": "0.1.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codechat-editor-client", - "version": "0.1.9", + "version": "0.1.10", "license": "GPL-3.0-only", "dependencies": { "escape-html": "^1", @@ -81,9 +81,9 @@ } }, "node_modules/@azure/core-rest-pipeline": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.2.tgz", - "integrity": "sha512-IkTf/DWKyCklEtN/WYW3lqEsIaUDshlzWRlZNNwSYtFcCBQz++OtOjxNpm8rr1VcbMS6RpjybQa3u6B6nG0zNw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.19.0.tgz", + "integrity": "sha512-bM3308LRyg5g7r3Twprtqww0R/r7+GyVxj4BafcmVPo4WQoGt5JXuaqxHEFjw2o3rvFZcUPiqJMg6WuvEEeVUA==", "dev": true, "license": "MIT", "dependencies": { @@ -128,9 +128,9 @@ } }, "node_modules/@azure/identity": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.6.0.tgz", - "integrity": "sha512-ANpO1iAvcZmpD4QY7/kaE/P2n66pRXsDp3nMUC6Ow3c9KfXOZF7qMU9VgqPw8m7adP7TVIbVyrCEmD9cth3KQQ==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.7.0.tgz", + "integrity": "sha512-6z/S2KorkbKaZ0DgZFVRdu7RCuATmMSTjKpuhj7YpjxkJ0vnJ7kTM3cpNgzFgk9OPYfZ31wrBEtC/iwAS4jQDA==", "dev": true, "license": "MIT", "dependencies": { @@ -141,11 +141,11 @@ "@azure/core-tracing": "^1.0.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^4.0.1", - "@azure/msal-node": "^2.15.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.2.1", "events": "^3.0.0", "jws": "^4.0.0", - "open": "^8.0.0", + "open": "^10.1.0", "stoppable": "^1.1.0", "tslib": "^2.2.0" }, @@ -167,22 +167,22 @@ } }, "node_modules/@azure/msal-browser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.0.1.tgz", - "integrity": "sha512-jqiwVJPArnEOUhmc+dvo481OP8b2PMcsu3EtGtxt7sxmKgFtdQyGDCndj+2me62JVG/HEgArEgKyMA7L0aNhdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.4.0.tgz", + "integrity": "sha512-rU6juYXk67CKQmpgi6fDgZoPQ9InZ1760z1BSAH7RbeIc4lHZM/Tu+H0CyRk7cnrfvTkexyYE4pjYhMghpzheA==", "dev": true, "license": "MIT", "dependencies": { - "@azure/msal-common": "15.0.1" + "@azure/msal-common": "15.2.0" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.0.1.tgz", - "integrity": "sha512-JELxEK3Pnc4Rq8u+mI9u6o37auSpSOPCB7jaq7QziOAKi9WliWEmZZORCFHPbwf2xKitpHBXTz/0uerj17NsSQ==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.2.0.tgz", + "integrity": "sha512-HiYfGAKthisUYqHG1nImCf/uzcyS31wng3o+CycWLIM9chnYJ9Lk6jZ30Y6YiYYpTQ9+z/FGUpiKKekd3Arc0A==", "dev": true, "license": "MIT", "engines": { @@ -190,13 +190,13 @@ } }, "node_modules/@azure/msal-node": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", - "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.2.3.tgz", + "integrity": "sha512-0eaPqBIWEAizeYiXdeHb09Iq0tvHJ17ztvNEaLdr/KcJJhJxbpkkEQf09DB+vKlFE0tzYi7j4rYLTXtES/InEQ==", "dev": true, "license": "MIT", "dependencies": { - "@azure/msal-common": "14.16.0", + "@azure/msal-common": "15.2.0", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, @@ -204,16 +204,6 @@ "node": ">=16" } }, - "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { - "version": "14.16.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", - "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -244,13 +234,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -283,9 +273,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", + "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -344,9 +334,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "version": "9.20.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", + "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", "dev": true, "license": "MIT", "engines": { @@ -354,9 +344,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -364,13 +354,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.6.tgz", + "integrity": "sha512-+0TjwR1eAUdZtvv/ir1mGX+v0tUoR3VEPB8Up0LLJC+whRW0GgBBtpbOkg/a/U4Dxa6l5a3l9AJ1aWIQVyoWJA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.11.0", "levn": "^0.4.1" }, "engines": { @@ -430,9 +420,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -535,9 +525,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", - "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "version": "22.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", "dev": true, "license": "MIT", "dependencies": { @@ -545,16 +535,16 @@ } }, "node_modules/@types/vscode": { - "version": "1.96.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz", - "integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==", + "version": "1.97.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.97.0.tgz", + "integrity": "sha512-ueE73loeOTe7olaVyqP9mrRI54kVPJifUPjblZo9fYcv1CuVLPOEKEkqW0GkqPC454+nCEoigLWnC2Pp7prZ9w==", "dev": true, "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.5.13", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", - "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", "dev": true, "license": "MIT", "dependencies": { @@ -562,21 +552,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", - "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz", + "integrity": "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/type-utils": "8.21.0", - "@typescript-eslint/utils": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/type-utils": "8.24.1", + "@typescript-eslint/utils": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -592,16 +582,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", - "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.1.tgz", + "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4" }, "engines": { @@ -617,14 +607,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", - "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz", + "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0" + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -635,16 +625,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", - "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.1.tgz", + "integrity": "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/utils": "8.24.1", "debug": "^4.3.4", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -659,9 +649,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", - "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz", + "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==", "dev": true, "license": "MIT", "engines": { @@ -673,20 +663,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", - "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz", + "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -700,16 +690,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", - "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz", + "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0" + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -724,13 +714,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", - "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz", + "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/types": "8.24.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -755,9 +745,9 @@ } }, "node_modules/@vscode/vsce": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.1.tgz", - "integrity": "sha512-AY9vBjwExakK1c0cI/3NN2Ey0EgiKLBye/fxl/ue+o4q6RZ7N+xzd1jAD6eI6eBeMVANi617+V2rxIAkDPco2Q==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.2.tgz", + "integrity": "sha512-4TqdUq/yKlQTHcQMk/DamR632bq/+IJDomSbexOMee/UAYWqYm0XHWA6scGslsCpzY+sCWEhhl0nqdOB0XW1kw==", "dev": true, "license": "MIT", "dependencies": { @@ -767,7 +757,7 @@ "chalk": "^2.4.2", "cheerio": "^1.0.0-rc.9", "cockatiel": "^3.1.2", - "commander": "^6.2.1", + "commander": "^12.1.0", "form-data": "^4.0.0", "glob": "^11.0.0", "hosted-git-info": "^4.0.2", @@ -1167,6 +1157,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1330,6 +1330,22 @@ "node": ">=6.14.2" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -1350,9 +1366,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1505,13 +1521,13 @@ } }, "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=18" } }, "node_modules/concat-map": { @@ -1673,6 +1689,36 @@ "dev": true, "license": "MIT" }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -1692,13 +1738,16 @@ } }, "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { @@ -2005,13 +2054,16 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -2049,18 +2101,18 @@ } }, "node_modules/eslint": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", - "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", + "version": "9.20.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", + "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.18.0", + "@eslint/js": "9.20.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2656,9 +2708,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "dev": true, "license": "ISC", "dependencies": { @@ -2733,9 +2785,9 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, @@ -2761,13 +2813,19 @@ } }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { @@ -2788,14 +2846,15 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -3220,9 +3279,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3296,12 +3355,13 @@ } }, "node_modules/is-async-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", - "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { + "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", @@ -3331,13 +3391,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -3425,16 +3485,16 @@ } }, "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, "license": "MIT", "bin": { "is-docker": "cli.js" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3508,6 +3568,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -3661,13 +3740,13 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -3694,16 +3773,19 @@ } }, "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" + "is-inside-container": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isarray": { @@ -3721,9 +3803,9 @@ "license": "ISC" }, "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.3.tgz", + "integrity": "sha512-oSwM7q8PTHQWuZAlp995iPpPJ4Vkl7qT0ZRD+9duL9j2oBy6KcTfyxc8mEuHJYC+z/kbps80aJLkaNzTOrf/kw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -4169,9 +4251,9 @@ "license": "ISC" }, "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "dev": true, "license": "MIT", "optional": true @@ -4184,9 +4266,9 @@ "license": "MIT" }, "node_modules/node-abi": { - "version": "3.73.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.73.0.tgz", - "integrity": "sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg==", + "version": "3.74.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", + "integrity": "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==", "dev": true, "license": "MIT", "optional": true, @@ -4231,9 +4313,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { @@ -4339,18 +4421,19 @@ } }, "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", "dev": true, "license": "MIT", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4397,6 +4480,16 @@ "node": ">= 20" } }, + "node_modules/ovsx/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/ovsx/node_modules/yauzl": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", @@ -4616,9 +4709,9 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", "engines": { @@ -4626,9 +4719,9 @@ } }, "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", "dev": true, "license": "MIT", "optional": true, @@ -4638,7 +4731,7 @@ "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", @@ -4888,6 +4981,19 @@ "node": ">=0.10.0" } }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5003,9 +5109,9 @@ "license": "ISC" }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -5515,9 +5621,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", - "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, "license": "MIT", "engines": { diff --git a/extensions/VSCode/package.json b/extensions/VSCode/package.json index 2f1d558e..1c760bc0 100644 --- a/extensions/VSCode/package.json +++ b/extensions/VSCode/package.json @@ -1,6 +1,6 @@ { "name": "codechat-editor-client", - "version": "0.1.9", + "version": "0.1.10", "publisher": "CodeChat", "engines": { "vscode": "^1.61.0" diff --git a/extensions/VSCode/src/extension.ts b/extensions/VSCode/src/extension.ts index 5197b7cf..88017351 100644 --- a/extensions/VSCode/src/extension.ts +++ b/extensions/VSCode/src/extension.ts @@ -14,12 +14,14 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `extension.ts` - The CodeChat Editor Visual Studio Code extension +// `extension.ts` - The CodeChat Editor Visual Studio Code extension +// ================================================================= // // This extension creates a webview, then uses a websocket connection to the // CodeChat Editor Server and Client to render editor text in that webview. // -// ## Imports +// Imports +// ------- // // ### Node.js packages import assert from "assert"; @@ -35,7 +37,8 @@ import { WebSocket } from "ws"; // // None. // -// ## Globals +// Globals +// ------- enum CodeChatEditorClientLocation { html, browser, @@ -135,7 +138,8 @@ interface JointMessage { message: JointMessageContents; } -// ## Activation/deactivation +// Activation/deactivation +// ----------------------- // // This is invoked when the extension is activated. It either creates a new // CodeChat Editor Server instance or reveals the currently running one. @@ -154,8 +158,8 @@ export const activate = (context: vscode.ExtensionContext) => { subscribed = true; // Render when the text is changed by listening for the - // correct - // `event `\_. + // correct `event + // `\_. context.subscriptions.push( vscode.workspace.onDidChangeTextDocument((event) => { // VSCode sends empty change events -- ignore these. @@ -325,8 +329,8 @@ export const activate = (context: vscode.ExtensionContext) => { show_error( `Error communicating with the CodeChat Editor Server: ${err.message}. Re-run the CodeChat Editor extension to restart it.` ); - // The close event will be - // [emitted next](https://nodejs.org/api/net.html#net_event_error_1); + // The close event will be [emitted + // next](https://nodejs.org/api/net.html#net_event_error_1); // that will handle cleanup. }); @@ -335,8 +339,8 @@ export const activate = (context: vscode.ExtensionContext) => { "CodeChat Editor extension: closing websocket connection." ); // If there was an error, the event handler above - // already provided the message. Note: the - // [parameter hadError](https://nodejs.org/api/net.html#net_event_close_1) + // already provided the message. Note: the [parameter + // hadError](https://nodejs.org/api/net.html#net_event_close_1) // only applies to transmission errors, not to any other // errors which trigger the error callback. Therefore, // I'm using the `was_error` flag instead to catch @@ -458,7 +462,8 @@ export const activate = (context: vscode.ExtensionContext) => { const { timer_id, callback } = pending_messages[id]; clearTimeout(timer_id); - // eslint-disable-next-line n/no-callback-literal + // eslint-disable-next-line + // n/no-callback-literal callback(true); delete pending_messages[id]; } @@ -531,7 +536,8 @@ export const deactivate = async () => { console.log("CodeChat extension: deactivated."); }; -// ## Supporting functions +// Supporting functions +// -------------------- // // Send a message expecting a result to the server. const send_message = ( @@ -695,8 +701,8 @@ const get_document = (file_path: string) => { // are case-insensitive; I don't know how to easily determine the // case-sensitivity of the current filesystem without extra probing code // (write a file in mixed case, try to open it in another mixed case.) - // Per - // [How to Work with Different Filesystems](https://nodejs.org/en/learn/manipulating-files/working-with-different-filesystems#filesystem-behavior), + // Per [How to Work with Different + // Filesystems](https://nodejs.org/en/learn/manipulating-files/working-with-different-filesystems#filesystem-behavior), // "Be wary of inferring filesystem behavior from `process.platform`. // For example, do not assume that because your program is running on // Darwin that you are therefore working on a case-insensitive diff --git a/extensions/VSCode/tsconfig.json b/extensions/VSCode/tsconfig.json index 01a27c6d..de8cd19a 100644 --- a/extensions/VSCode/tsconfig.json +++ b/extensions/VSCode/tsconfig.json @@ -14,6 +14,8 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // +// tsconfig.json -- TypeScript configuration +// ========================================= { "compilerOptions": { "module": "nodenext", diff --git a/new-project-template/README.md b/new-project-template/README.md index ce3c96b6..e956b622 100644 --- a/new-project-template/README.md +++ b/new-project-template/README.md @@ -1,7 +1,8 @@ -# New project template +New project template +==================== -This directory contains an example of a CodeChat Editor project. To create a -new project using this template: +This directory contains an example of a CodeChat Editor project. To create a new +project using this template: 1. Copy this file and [toc.md](toc.md) to a newly-created directory of your choice. @@ -10,5 +11,5 @@ new project using this template: 3. Open this file and start editing. (If you don't want a readme file, you may delete it; only [toc.md](toc.md)  is required for a CodeChat Editor project.) You may add any source files you like in this directory or in any - subdirectory. When you add or delete a file, update the - [table of contents](toc.md) to make navigation easier. + subdirectory. When you add or delete a file, update the [table of + contents](toc.md) to make navigation easier. \ No newline at end of file diff --git a/new-project-template/toc.md b/new-project-template/toc.md index 248f08c4..a522f5dc 100644 --- a/new-project-template/toc.md +++ b/new-project-template/toc.md @@ -1,8 +1,10 @@ -# New project template +New project template +==================== This file provides a table of contents for a CodeChat Editor project. -## Contents +Contents +-------- 1. [Readme](README.md) -2. [Table of contents](toc.md) +2. [Table of contents](toc.md) \ No newline at end of file diff --git a/server/.cargo/config.toml b/server/.cargo/config.toml index 3d284ba3..7f2396d3 100644 --- a/server/.cargo/config.toml +++ b/server/.cargo/config.toml @@ -1,26 +1,31 @@ -# # `config` - a Cargo configuration file +# `config` - a Cargo configuration file +# ===================================== # # See the [docs](https://doc.rust-lang.org/cargo/reference/config.html) for this # file. # # Code coverage, the manual way: # -# 1. Run `cargo install rustfilt` per the -# [code coverage docs](https://doc.rust-lang.org/rustc/instrument-coverage.html#building-the-demangler). +# 1. Run `cargo install rustfilt` per the [code coverage +# docs](https://doc.rust-lang.org/rustc/instrument-coverage.html#building-the-demangler). # 2. You must manually run `rustup component add llvm-tools-preview` following -# the -# [coverge docs](https://doc.rust-lang.org/rustc/instrument-coverage.html#installing-llvm-coverage-tools). +# the [coverge +# docs](https://doc.rust-lang.org/rustc/instrument-coverage.html#installing-llvm-coverage-tools). # Per some searching, also run `cargo install cargo-binutils` to put these # tools in the path. -# 3. In Powershell, `$Env:RUSTFLAGS = "-C instrument-coverage"` then -# `cargo test`. When the tests run, record the name of the test binary. +# 3. In Powershell, `$Env:RUSTFLAGS = "-C instrument-coverage"` then `cargo +# test`. When the tests run, record the name of the test binary. # 4. `rust-profdata merge -sparse default_*.profraw -o default.profdata`. -# 5. `rust-cov show --Xdemangler=rustfilt target\debug\deps\code_chat_editor-4dbe5c7815a53cd9.exe --instr-profile=default.profdata --ignore-filename-regex=\\.cargo\\registry --format=html --output-dir=coverage`, -# replacing the binary path with the one recorded in step 3. +# 5. `rust-cov show --Xdemangler=rustfilt +# target\debug\deps\code_chat_editor-4dbe5c7815a53cd9.exe +# --instr-profile=default.profdata +# --ignore-filename-regex=\\.cargo\\registry --format=html +# --output-dir=coverage`, replacing the binary path with the one recorded in +# step 3. # 6. Open the file `coverage\index.html`. # -# Or, `cargo install cargo-tarpaulin` then -# `cargo tarpaulin --ignore-panics --out=html --skip-clean`. +# Or, `cargo install cargo-tarpaulin` then `cargo tarpaulin --ignore-panics +# --out=html --skip-clean`. [build] # Set these to match the output from `cargo tarpaulin --print-rust-flags` to @@ -28,5 +33,3 @@ # # This is commented out; for development, uncomment this. ##rustflags = ["-Cdebuginfo=2", "-Cstrip=none", "--cfg=tarpaulin", "-Cinstrument-coverage"] - -# CodeChat Editor lexer: python. diff --git a/server/.gitignore b/server/.gitignore index 8e188289..633877c8 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -16,7 +16,8 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `.gitignore` -- files for Git to ignore +# `.gitignore` -- files for Git to ignore +# ======================================= # # Rust build output target/ diff --git a/server/Cargo.lock b/server/Cargo.lock index 14b9c1b5..fe7cb4d6 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -72,7 +72,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand", + "rand 0.8.5", "sha1", "smallvec", "tokio", @@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -206,7 +206,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -245,10 +245,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -342,9 +342,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "arc-swap" @@ -391,13 +391,13 @@ checksum = "0082388b8564898f945b04215e800e800a164af15307d8dfe714b02cc69356e9" [[package]] name = "async-trait" -version = "0.1.85" +version = "0.1.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -461,9 +461,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -482,9 +482,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -494,9 +494,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" dependencies = [ "serde", ] @@ -512,9 +512,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.10" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "jobserver", "libc", @@ -543,9 +543,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.27" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" dependencies = [ "clap_builder", "clap_derive", @@ -553,9 +553,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" dependencies = [ "anstream", "anstyle", @@ -565,14 +565,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -583,7 +583,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "codechat-editor-server" -version = "0.1.9" +version = "0.1.10" dependencies = [ "actix-files", "actix-http", @@ -661,9 +661,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -714,9 +714,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" [[package]] name = "deranged" @@ -740,15 +740,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -782,7 +782,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -808,9 +808,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -922,7 +922,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -970,7 +970,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", ] [[package]] @@ -1073,9 +1085,9 @@ checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -1227,7 +1239,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1479,7 +1491,7 @@ dependencies = [ "log-mdc", "once_cell", "parking_lot", - "rand", + "rand 0.8.5", "serde", "serde-value", "serde_json", @@ -1524,18 +1536,18 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" dependencies = [ "adler2", ] [[package]] name = "minreq" -version = "2.13.0" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a8e50e917e18a37d500d27d40b7bc7d127e71c0c94fb2d83f43b4afd308390" +checksum = "da0c420feb01b9fb5061f8c8f452534361dd783756dcf38ec45191ce55e7a161" dependencies = [ "log", ] @@ -1548,7 +1560,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1622,9 +1634,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "open" @@ -1724,7 +1736,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1776,9 +1788,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "postgres-protocol" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" +checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" dependencies = [ "base64", "byteorder", @@ -1787,16 +1799,16 @@ dependencies = [ "hmac", "md-5", "memchr", - "rand", + "rand 0.9.0", "sha2", "stringprep", ] [[package]] name = "postgres-types" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" +checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" dependencies = [ "bytes", "chrono", @@ -1816,7 +1828,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -1860,9 +1872,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.12.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" dependencies = [ "bitflags 2.8.0", "memchr", @@ -1892,8 +1904,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.1", + "zerocopy 0.8.20", ] [[package]] @@ -1903,7 +1926,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.1", ] [[package]] @@ -1912,7 +1945,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.20", ] [[package]] @@ -1976,9 +2019,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.8.0", "errno", @@ -1995,9 +2038,9 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -2022,9 +2065,9 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] @@ -2041,20 +2084,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "serde_json" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -2141,9 +2184,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" @@ -2197,9 +2240,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2214,18 +2257,18 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "tempfile" -version = "3.15.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", - "getrandom", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2263,7 +2306,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2274,7 +2317,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2369,14 +2412,14 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "tokio-postgres" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" +checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" dependencies = [ "async-trait", "byteorder", @@ -2391,7 +2434,7 @@ dependencies = [ "pin-project-lite", "postgres-protocol", "postgres-types", - "rand", + "rand 0.9.0", "socket2", "tokio", "tokio-util", @@ -2400,9 +2443,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4bf6fecd69fcdede0ec680aaf474cdab988f9de6bc73d3758f0160e3b7025a" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" dependencies = [ "futures-util", "log", @@ -2445,17 +2488,16 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ - "byteorder", "bytes", "data-encoding", "http 1.2.0", "httparse", "log", - "rand", + "rand 0.9.0", "sha1", "thiserror 2.0.11", "utf-8", @@ -2472,9 +2514,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ucd-trie" @@ -2496,9 +2538,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-normalization" @@ -2585,9 +2627,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] @@ -2608,6 +2650,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasite" version = "0.1.0" @@ -2636,7 +2687,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -2658,7 +2709,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2851,6 +2902,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -2883,7 +2943,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure", ] @@ -2894,7 +2954,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c" +dependencies = [ + "zerocopy-derive 0.8.20", ] [[package]] @@ -2905,7 +2974,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -2925,7 +3005,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure", ] @@ -2948,32 +3028,32 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "zstd" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.14+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5" dependencies = [ "cc", "pkg-config", diff --git a/server/Cargo.toml b/server/Cargo.toml index d9de16f7..0fa0450f 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -16,26 +16,29 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `Cargo.toml` -- Rust build/package management config for the server +# `Cargo.toml` -- Rust build/package management config for the server +# =================================================================== # -# ## General package configurations +# General package configurations +# ------------------------------ [package] authors = ["Bryan A. Jones", "Peter Loux"] categories = ["development-tools", "text-editors"] description = "A programmer's word processor." -edition = "2021" +edition = "2024" keywords = ["literate programming"] license = "GPL-3.0-only" name = "codechat-editor-server" readme = "../README.md" repository = "https://github.com/bjones1/CodeChat_Editor" -version = "0.1.9" +version = "0.1.10" # This library allows other packages to use core CodeChat Editor features. [lib] name = "code_chat_editor" -# ## Dependencies +# Dependencies +# ------------ [dependencies] actix-files = "0.6" actix-rt = "2.9.0" @@ -61,7 +64,7 @@ pest = "2.7.14" pest_derive = "2.7.14" # Per the [docs](https://docs.rs/crate/pulldown-cmark/latest), skip building the # binary. -pulldown-cmark = { version = "0.12", default-features = false, features = ["html"] } +pulldown-cmark = { version = "0.13", default-features = false, features = ["html"] } regex = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -70,7 +73,8 @@ tokio-postgres = { version = "0.7", features = ["with-chrono-0_4"] } url = "2.5.2" urlencoding = "2" -# [Windows-only dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies). +# [Windows-only +# dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies). [target.'cfg(windows)'.dependencies] win_partitions = "0.3.0" @@ -89,7 +93,8 @@ tokio-tungstenite = "0.26" #actix = { path = "../../actix/actix" } #actix-rt = { path = "../../actix-net/actix-rt" } -# ## Release +# Release +# ------- # # Specify release-only features for pulldown. See the # [docs](https://docs.rs/crate/pulldown-cmark/latest). @@ -98,7 +103,8 @@ lto = true codegen-units = 1 panic = "abort" -# ## Features +# Features +# -------- # # See the [docs](https://doc.rust-lang.org/cargo/reference/features.html). [features] @@ -110,7 +116,8 @@ lexer_explain = [] # Avoid a lint about tarpaulin. unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tarpaulin_include)'] } -# ## Distribution +# Distribution +# ------------ # # This uses [cargo dist](https://opensource.axo.dev/cargo-dist) to build # binaries across multiple platforms using github's CI/CD. diff --git a/server/dist-workspace.toml b/server/dist-workspace.toml index f90aeff1..d1281ada 100644 --- a/server/dist-workspace.toml +++ b/server/dist-workspace.toml @@ -16,7 +16,9 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# `dist-workspace.toml' - Configure [cargo-dist](https://opensource.axo.dev/cargo-dist/) +# `dist-workspace.toml` - Configure +# [cargo-dist](https://opensource.axo.dev/cargo-dist/) +# ==================================================== [workspace] members = ["cargo:."] @@ -24,7 +26,8 @@ members = ["cargo:."] [dist] # The preferred dist version to use in CI (Cargo.toml SemVer syntax) cargo-dist-version = "0.28.0" -# Extra static files to include in each App (path relative to this Cargo.toml's dir) +# Extra static files to include in each App (path relative to this Cargo.toml's +# dir) include = ["log4rs.yml", "hashLocations.json", "../client/static"] # The installers to generate for each app installers = [] diff --git a/server/log4rs.yml b/server/log4rs.yml index 11c1fe31..a5bb18a4 100644 --- a/server/log4rs.yml +++ b/server/log4rs.yml @@ -16,7 +16,8 @@ # the CodeChat Editor. If not, see # [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # -# # `log4rs.yml` - configure logging +# `log4rs.yml` - configure logging +# ================================ # # This file configures log4rs for this application. appenders: diff --git a/server/src/lexer.rs b/server/src/lexer.rs index 87f31b93..deecab05 100644 --- a/server/src/lexer.rs +++ b/server/src/lexer.rs @@ -14,11 +14,14 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). mod pest_parser; -/// # `lexer.rs` -- Lex source code into code and doc blocks -// ## Submodule definitions +/// `lexer.rs` -- Lex source code into code and doc blocks +/// ====================================================== +// Submodule definitions +// --------------------- pub mod supported_languages; -// ## Imports +// Imports +// ------- // // ### Standard library #[cfg(feature = "lexer_explain")] @@ -32,25 +35,27 @@ use regex::Regex; // ### Local use supported_languages::get_language_lexer_vec; -/// ## Data structures +/// Data structures +/// --------------- /// /// ### Language definition /// /// These data structures define everything the lexer needs in order to analyze /// a programming language: /// -/// - It defines block and inline comment delimiters; these (when correctly -/// formatted) become doc blocks. -/// - It defines strings: what is the escape character? Are newlines allowed? If -/// so, must newlines be escaped? -/// - It defines heredocs in a flexible form (see `HeredocDelim` for more -/// details). -/// - It associates an Ace mode and filename extensions with the lexer. +/// * It defines block and inline comment delimiters; these (when correctly +/// formatted) become doc blocks. +/// * It defines strings: what is the escape character? Are newlines allowed? +/// If so, must newlines be escaped? +/// * It defines heredocs in a flexible form (see `HeredocDelim` for more +/// details). +/// * It associates an Ace mode and filename extensions with the lexer. /// /// This lexer ignores line continuation characters; in C/C++/Python, it's a `\` -/// character followed immediately by a newline -/// ([C reference](https://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf#page22), -/// [Python reference](https://docs.python.org/3/reference/lexical_analysis.html#explicit-line-joining)). +/// character followed immediately by a newline ([C +/// reference](https://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf#page22), +/// [Python +/// reference](https://docs.python.org/3/reference/lexical_analysis.html#explicit-line-joining)). /// From a lexer perspective, supporting these adds little value: /// /// 1. It would allow the lexer to recognize the following C/C++ snippet as a @@ -124,14 +129,15 @@ struct HeredocDelim { enum SpecialCase { /// There are no special cases for this language. None, - /// [Template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) + /// [Template + /// literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) /// support (for languages such as JavaScript, TypeScript, etc.). TemplateLiteral, - /// C#'s verbatim string literal -- see - /// [6.4.5.6 String literals](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#6456-string-literals). + /// C#'s verbatim string literal -- see [6.4.5.6 String + /// literals](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#6456-string-literals). CSharpVerbatimStringLiteral, - /// MATLAB - /// [block comments](https://www.mathworks.com/help/matlab/matlab_prog/comments.html) + /// MATLAB [block + /// comments](https://www.mathworks.com/help/matlab/matlab_prog/comments.html) /// must start and end on a blank line. Matlab, } @@ -258,18 +264,20 @@ pub enum CodeDocBlock { DocBlock(DocBlock), } -// ## Globals +// Globals +// ------- // -// Create constant regexes needed by the lexer, following the -// [Regex docs recommendation](https://docs.rs/regex/1.6.0/regex/index.html#example-avoid-compiling-the-same-regex-in-a-loop). +// Create constant regexes needed by the lexer, following the [Regex docs +// recommendation](https://docs.rs/regex/1.6.0/regex/index.html#example-avoid-compiling-the-same-regex-in-a-loop). lazy_static! { static ref WHITESPACE_ONLY_REGEX: Regex = Regex::new("^[[:space:]]*$").unwrap(); /// TODO: This regex should also allow termination on an unescaped `${` /// sequence, which then must count matching braces to find the end of the /// expression. static ref TEMPLATE_LITERAL_CLOSING_REGEX: Regex = Regex::new( - // Allow `.` to match _any_ character, including a newline. See the - // [regex docs](https://docs.rs/regex/1.6.0/regex/index.html#grouping-and-flags). + // Allow `.` to match *any* character, including a newline. See the + // [regex + // docs](https://docs.rs/regex/1.6.0/regex/index.html#grouping-and-flags). &("(?s)".to_string() + // Start at the beginning of the string, and require a match of every // character. Allowing the regex to start matching in the middle means @@ -403,9 +411,9 @@ fn build_lexer_regex( // terminated by an unescaped newline or an unescaped delimiter. // Escaped newlines or terminators should be included in the string. (true, NewlineSupport::Escaped) => Regex::new( - // Allow `.` to match _any_ character, including a newline. See - // the - // [regex docs](https://docs.rs/regex/1.6.0/regex/index.html#grouping-and-flags). + // Allow `.` to match *any* character, including a newline. See + // the [regex + // docs](https://docs.rs/regex/1.6.0/regex/index.html#grouping-and-flags). &("(?s)".to_string() + // Start at the beginning of the string, and require a match of // every character. Allowing the regex to start matching in the @@ -453,9 +461,9 @@ fn build_lexer_regex( // Even simpler: look for an unescaped string delimiter. (true, NewlineSupport::Unescaped) => Regex::new( - // Allow `.` to match _any_ character, including a newline. See - // the - // [regex docs](https://docs.rs/regex/1.6.0/regex/index.html#grouping-and-flags). + // Allow `.` to match *any* character, including a newline. See + // the [regex + // docs](https://docs.rs/regex/1.6.0/regex/index.html#grouping-and-flags). &("(?s)".to_string() + // Start at the beginning of the string, and require a match of // every character. Allowing the regex to start matching in the @@ -539,8 +547,8 @@ fn build_lexer_regex( // To match on a line which consists only of leading and // trailing whitespace plus the opening comment delimiter, put // these inside a `(?m:exp)` block, so that `^` and `$` will - // match on any newline in the string; see the - // [regex docs](https://docs.rs/regex/latest/regex/#grouping-and-flags). + // match on any newline in the string; see the [regex + // docs](https://docs.rs/regex/latest/regex/#grouping-and-flags). // This also functions as a non-capturing group, to avoid // whitespace capture as discussed earlier. "(?m:" + @@ -593,7 +601,8 @@ fn build_lexer_regex( } } -// ## Compile lexers +// Compile lexers +// -------------- pub fn compile_lexers(language_lexer_arr: Vec) -> LanguageLexersCompiled { let mut language_lexers_compiled = LanguageLexersCompiled { language_lexer_compiled_vec: Vec::new(), @@ -630,7 +639,8 @@ pub fn compile_lexers(language_lexer_arr: Vec) -> LanguageLexersC language_lexers_compiled } -/// ## Source lexer +/// Source lexer +/// ------------ /// /// This lexer categorizes source code into code blocks or doc blocks. /// @@ -647,15 +657,16 @@ pub fn source_lexer( // to categorize all the source code into code blocks or doc blocks. To do // it, it only needs to: // - // - Recognize where comments can't be—inside strings or string-like syntax, - // such as [here text](https://en.wikipedia.org/wiki/Here_document) or - // [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals). - // These are always part of a code block and can never contain a comment - // or (by implication) a doc block. - // - Outside of these special cases, look for inline or block comments, - // categorizing everything else as plain code. - // - After finding either an inline or block comment, determine if this is a - // doc block. + // * Recognize where comments can't be—inside strings or string-like + // syntax, such as [here + // text](https://en.wikipedia.org/wiki/Here_document) or [template + // literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals). + // These are always part of a code block and can never contain a comment + // or (by implication) a doc block. + // * Outside of these special cases, look for inline or block comments, + // categorizing everything else as plain code. + // * After finding either an inline or block comment, determine if this is + // a doc block. // // ### Lexer operation // @@ -665,18 +676,18 @@ pub fn source_lexer( // `language_lexer_compiled.map`. These divides source code into two // categories: plain code and special cases. The special cases consist of: // - // - String-like code (strings, here text, template literals). In this case, - // the lexer must find the end of the string-like element before it can - // return to plain code. - // - Comments (inline or block). In this case, the lexer must find the end - // of the comment before it can return to plain code. + // * String-like code (strings, here text, template literals). In this + // case, the lexer must find the end of the string-like element before + // it can return to plain code. + // * Comments (inline or block). In this case, the lexer must find the end + // of the comment before it can return to plain code. // // This regex assumes the string it analyzes was preceded by plain code; its // purpose is to identify the start of the next special case. **This code // makes heavy use of regexes -- read the previous link thoroughly.** // - // To better explain the operation of the lexer, see the - // [lexer walkthrough](lexer/lexer-walkthrough.md). + // To better explain the operation of the lexer, see the [lexer + // walkthrough](lexer/lexer-walkthrough.md). // // ### Helper function // @@ -758,19 +769,22 @@ pub fn source_lexer( #[cfg(feature = "lexer_explain")] println!( "Searching the following source_code using the pattern {:?}:\n'{}'\n\nThe current code block is '{}'\n", - language_lexer_compiled.next_token, &source_code[source_code_unlexed_index..], &source_code[current_code_block_index..source_code_unlexed_index] + language_lexer_compiled.next_token, + &source_code[source_code_unlexed_index..], + &source_code[current_code_block_index..source_code_unlexed_index] ); // #### Find the next token // // Look for the next special case. Per the earlier discussion, this // assumes that the text immediately preceding `source_code` was plain // code. - if let Some(classify_match) = language_lexer_compiled + match language_lexer_compiled .next_token .captures(&source_code[source_code_unlexed_index..]) { - // Find the first group in the regex that matched. - let matching_group_index = classify_match + Some(classify_match) => { + // Find the first group in the regex that matched. + let matching_group_index = classify_match .iter() // Group 0 is the entire match, which is always true. Skip this // group. @@ -779,23 +793,25 @@ pub fn source_lexer( .unwrap() // Correct the resulting group index, since we skipped group 0. + 1; - let matching_group_str = &classify_match[matching_group_index]; - - // Move everything preceding this match from `source_code` to the - // current code block, since per the assumptions this is code. - source_code_unlexed_index += classify_match.get(matching_group_index).unwrap().start(); - - #[cfg(feature = "lexer_explain")] - println!( - "Matched the string {} in group {}. The current_code_block is now\n'{}'\n", - matching_group_str, - matching_group_index, - &source_code[current_code_block_index..source_code_unlexed_index] - ); + let matching_group_str = &classify_match[matching_group_index]; + + // Move everything preceding this match from `source_code` to + // the current code block, since per the assumptions this is + // code. + source_code_unlexed_index += + classify_match.get(matching_group_index).unwrap().start(); - // This helper function moves code from unlexed source code to the - // current code block based on the provided regex. - let mut append_code = + #[cfg(feature = "lexer_explain")] + println!( + "Matched the string {} in group {}. The current_code_block is now\n'{}'\n", + matching_group_str, + matching_group_index, + &source_code[current_code_block_index..source_code_unlexed_index] + ); + + // This helper function moves code from unlexed source code to + // the current code block based on the provided regex. + let mut append_code = // The regex; code up to the end of this // match will be appended to the current code // block. @@ -825,76 +841,76 @@ pub fn source_lexer( }; - // In the map, index 0 refers to group 1 (since group 0 matches are - // skipped). Adjust the index for this. - match &language_lexer_compiled.map[matching_group_index - 1] { - // #### Inline comment - RegexDelimType::InlineComment => { - // **First**, find the end of this comment: a newline. - let end_of_comment_rel_index = - source_code[source_code_unlexed_index..].find('\n'); - - // Assign `full_comment` to contain the entire comment - // (excluding the inline comment delimiter) until the - // newline which ends the comment. - let full_comment_start_index = - source_code_unlexed_index + matching_group_str.len(); - - // The current code block contains preceding code (which - // might be multiple lines) until the inline comment - // delimiter. Split this on newlines, grouping all the lines - // before the last line into `code_lines_before_comment` - // (which is all code), and everything else (from the - // beginning of the last line to where the inline comment - // delimiter appears) into `comment_line_prefix`. For - // example, consider the fragment `a = 1\nb = 2 // Doc`. - // After processing, - // `code_lines_before_comment == "a = 1\n"` and - // `comment_line_prefix == "b = 2 "`. - let current_code_block = - &source_code[current_code_block_index..source_code_unlexed_index]; - let comment_line_prefix = current_code_block.rsplit('\n').next().unwrap(); - let code_lines_before_comment = - ¤t_code_block[..current_code_block.len() - comment_line_prefix.len()]; - - // Move to the next block of source code to be lexed. No - // matching newline means we're at the end of the file, so - // the comment is all the remaining `source_code`. - source_code_unlexed_index = if let Some(index) = end_of_comment_rel_index { - // Note that `index` is the index of the newline; add 1 - // to include that newline in the comment. - source_code_unlexed_index + index + 1 - } else { - source_code.len() - }; - let full_comment = - &source_code[full_comment_start_index..source_code_unlexed_index]; + // In the map, index 0 refers to group 1 (since group 0 matches + // are skipped). Adjust the index for this. + match &language_lexer_compiled.map[matching_group_index - 1] { + // #### Inline comment + RegexDelimType::InlineComment => { + // **First**, find the end of this comment: a newline. + let end_of_comment_rel_index = + source_code[source_code_unlexed_index..].find('\n'); + + // Assign `full_comment` to contain the entire comment + // (excluding the inline comment delimiter) until the + // newline which ends the comment. + let full_comment_start_index = + source_code_unlexed_index + matching_group_str.len(); + + // The current code block contains preceding code (which + // might be multiple lines) until the inline comment + // delimiter. Split this on newlines, grouping all the + // lines before the last line into + // `code_lines_before_comment` (which is all code), and + // everything else (from the beginning of the last line + // to where the inline comment delimiter appears) into + // `comment_line_prefix`. For example, consider the + // fragment `a = 1\nb = 2 // Doc`. After processing, + // `code_lines_before_comment == "a = 1\n"` and + // `comment_line_prefix == "b = 2 "`. + let current_code_block = + &source_code[current_code_block_index..source_code_unlexed_index]; + let comment_line_prefix = current_code_block.rsplit('\n').next().unwrap(); + let code_lines_before_comment = ¤t_code_block + [..current_code_block.len() - comment_line_prefix.len()]; + + // Move to the next block of source code to be lexed. No + // matching newline means we're at the end of the file, + // so the comment is all the remaining `source_code`. + source_code_unlexed_index = if let Some(index) = end_of_comment_rel_index { + // Note that `index` is the index of the newline; + // add 1 to include that newline in the comment. + source_code_unlexed_index + index + 1 + } else { + source_code.len() + }; + let full_comment = + &source_code[full_comment_start_index..source_code_unlexed_index]; - #[cfg(feature = "lexer_explain")] - println!( - "This is an inline comment. Source code before the line containing this comment is:\n'{}'\n\ + #[cfg(feature = "lexer_explain")] + println!( + "This is an inline comment. Source code before the line containing this comment is:\n'{}'\n\ The text preceding this comment is: '{}'.\n\ The comment is: '{}'\n", - code_lines_before_comment, comment_line_prefix, full_comment - ); - - // **Next**, determine if this comment is a doc block. - // Criteria for doc blocks for an inline comment: - // - // 1. All characters preceding the comment on the line - // containing the comment must be whitespace. - // 2. Either: - // 1. The inline comment delimiter is immediately - // followed by a space, or - // 2. the inline comment delimiter is followed by a - // newline or the end of the file. - // - // With this last line located, apply the doc block - // criteria. - let ws_only = WHITESPACE_ONLY_REGEX.is_match(comment_line_prefix); - let has_space_after_comment = full_comment.starts_with(' '); - // Criteria 1 -- the whitespace matched. - if ws_only && + code_lines_before_comment, comment_line_prefix, full_comment + ); + + // **Next**, determine if this comment is a doc block. + // Criteria for doc blocks for an inline comment: + // + // 1. All characters preceding the comment on the line + // containing the comment must be whitespace. + // 2. Either: + // 1. The inline comment delimiter is immediately + // followed by a space, or + // 2. the inline comment delimiter is followed by a + // newline or the end of the file. + // + // With this last line located, apply the doc block + // criteria. + let ws_only = WHITESPACE_ONLY_REGEX.is_match(comment_line_prefix); + let has_space_after_comment = full_comment.starts_with(' '); + // Criteria 1 -- the whitespace matched. + if ws_only && // TODO: generalize this to specific lines that are // never doc blocks. full_comment != " prettier-ignore\n" @@ -906,132 +922,144 @@ pub fn source_lexer( // Criteria 2.2b -- end of file means the comment is // empty. full_comment.is_empty()) - ) - { - // This is a doc block. Transition from the preceding - // code block to this doc block. - append_code_doc_block("", "", code_lines_before_comment); - - // Add this doc block by pushing the array \[whitespace - // before the inline comment, inline comment contents, - // inline comment delimiter\]. Since it's a doc block, - // then `comment_line_prefix` contains the whitespace - // before this comment and `matching_group_string` - // contains the inline comment delimiter. For the - // contents, omit the leading space if it's there (this - // might be just a newline or an EOF). - let contents = &full_comment[if has_space_after_comment { 1 } else { 0 }..]; - append_code_doc_block(comment_line_prefix, matching_group_str, contents); + ) { + // This is a doc block. Transition from the + // preceding code block to this doc block. + append_code_doc_block("", "", code_lines_before_comment); + + // Add this doc block by pushing the array + // \[whitespace before the inline comment, inline + // comment contents, inline comment delimiter\]. + // Since it's a doc block, then + // `comment_line_prefix` contains the whitespace + // before this comment and `matching_group_string` + // contains the inline comment delimiter. For the + // contents, omit the leading space if it's there + // (this might be just a newline or an EOF). + let contents = + &full_comment[if has_space_after_comment { 1 } else { 0 }..]; + append_code_doc_block( + comment_line_prefix, + matching_group_str, + contents, + ); - #[cfg(feature = "lexer_explain")] - println!( - "This is a doc block. Possibly added the preceding code block\n\ + #[cfg(feature = "lexer_explain")] + println!( + "This is a doc block. Possibly added the preceding code block\n\ '{}'.\n\ Added a doc block with indent = '{}', delimiter = '{}', and contents =\n\ '{}'.\n", - current_code_block, comment_line_prefix, matching_group_str, contents - ); + current_code_block, + comment_line_prefix, + matching_group_str, + contents + ); - // We've now stored the current code block (which was - // classified as a doc block) in `classified_lines`. - // Make the current code block empty by moving its index - // up to the unlexed code. - current_code_block_index = source_code_unlexed_index; - } else { - // This comment is not a doc block; instead, treat it as - // code. This code is already in the current code block, - // so we're done. + // We've now stored the current code block (which + // was classified as a doc block) in + // `classified_lines`. Make the current code block + // empty by moving its index up to the unlexed code. + current_code_block_index = source_code_unlexed_index; + } else { + // This comment is not a doc block; instead, treat + // it as code. This code is already in the current + // code block, so we're done. + } } - } - // #### Block comment - RegexDelimType::BlockComment(comment_delim_regex) => 'block_comment: { - #[cfg(feature = "lexer_explain")] - println!("Block Comment Found."); + // #### Block comment + RegexDelimType::BlockComment(comment_delim_regex) => 'block_comment: { + #[cfg(feature = "lexer_explain")] + println!("Block Comment Found."); - // Determine the location of the beginning of this block - // comment's content. - let mut comment_start_index = - source_code_unlexed_index + matching_group_str.len(); + // Determine the location of the beginning of this block + // comment's content. + let mut comment_start_index = + source_code_unlexed_index + matching_group_str.len(); - #[cfg(feature = "lexer_explain")] - println!( - "The opening delimiter is '{}', and the closing delimiter regex is '{}'.", - matching_group_str, comment_delim_regex - ); - - // For nested comments, only treat the innermost comment as - // a potential doc block; everything else is treated as - // code. The rationale: - // - // 1. Typically, nested comments are used to comment out a - // block of code, which may already contain "real" - // comments (as opposed to commented-out code). - // Therefore, we assume that only these innermost - // comments are true comments, while everything else is - // code. I can't think of any reason to nest true - // comments. Assuming a legitimate use for nested - // comments, what criteria would distinguish a nested - // comment from a commented-out code block? - // 2. The CodeChat Editor data structures don't support - // nested doc blocks. So, while we might be able to - // correctly parse nested comments as doc blocks, the - // code that transforms these back to code would remove - // the nesting. - // 3. We lack criteria that would distinguish a nested doc - // block from commented-out code. - // - // With these assumptions, we need to know if the current - // comment is the innermost or not. If the last block - // comment delimiter encountered was an opening comment, and - // the current block comment delimiter is a closing block - // comment, then this is an innermost comment which could be - // a doc block. Otherwise, treat the text as a code block. - let mut last_delimiter_was_opening = true; - // To correctly handle nested block comments, we must avoid - // any other parsing (recognizing strings/heredocs, in - // particular) until we leave the nested comment block. - // Therefore, keep track of the nesting depth; when this - // returns to 0, we've found outermost closing block comment - // delimiter, and can return to normal parsing. At this - // point in the code, we've found one opening block comment - // delimiter, so the nesting depth starts at 1. - let mut nesting_depth = 1; - let mut loop_count = 0; - // Loop until we've outside all nested block comments. - while nesting_depth != 0 && loop_count < 10 { - loop_count += 1; - // Get the index of the next block comment delimiter. #[cfg(feature = "lexer_explain")] println!( - "Looking for a block comment delimiter in '{}'.", - &source_code[comment_start_index - ..min(comment_start_index + 30, source_code.len())] + "The opening delimiter is '{}', and the closing delimiter regex is '{}'.", + matching_group_str, comment_delim_regex ); - let delimiter_captures_wrapped = - comment_delim_regex.captures(&source_code[comment_start_index..]); - if delimiter_captures_wrapped.is_none() { + + // For nested comments, only treat the innermost comment + // as a potential doc block; everything else is treated + // as code. The rationale: + // + // 1. Typically, nested comments are used to comment + // out a block of code, which may already contain + // "real" comments (as opposed to commented-out + // code). Therefore, we assume that only these + // innermost comments are true comments, while + // everything else is code. I can't think of any + // reason to nest true comments. Assuming a + // legitimate use for nested comments, what criteria + // would distinguish a nested comment from a + // commented-out code block? + // 2. The CodeChat Editor data structures don't support + // nested doc blocks. So, while we might be able to + // correctly parse nested comments as doc blocks, + // the code that transforms these back to code would + // remove the nesting. + // 3. We lack criteria that would distinguish a nested + // doc block from commented-out code. + // + // With these assumptions, we need to know if the + // current comment is the innermost or not. If the last + // block comment delimiter encountered was an opening + // comment, and the current block comment delimiter is a + // closing block comment, then this is an innermost + // comment which could be a doc block. Otherwise, treat + // the text as a code block. + let mut last_delimiter_was_opening = true; + // To correctly handle nested block comments, we must + // avoid any other parsing (recognizing + // strings/heredocs, in particular) until we leave the + // nested comment block. Therefore, keep track of the + // nesting depth; when this returns to 0, we've found + // outermost closing block comment delimiter, and can + // return to normal parsing. At this point in the code, + // we've found one opening block comment delimiter, so + // the nesting depth starts at 1. + let mut nesting_depth = 1; + let mut loop_count = 0; + // Loop until we've outside all nested block comments. + while nesting_depth != 0 && loop_count < 10 { + loop_count += 1; + // Get the index of the next block comment + // delimiter. #[cfg(feature = "lexer_explain")] - println!("The closing comment delimiter wasn't found."); - // If there's no closing delimiter, this is not a - // doc block; it's a syntax error. The safe route is - // to assume the rest of the contents are code, - // which this program won't edit; it does edit - // comments by cleaning up HTML tags, word-wrapping, - // etc. which would be a disaster if this was - // applied to code. - source_code_unlexed_index = source_code.len(); - // Exit the block comment processing code here. - break 'block_comment; - } - let delimiter_captures = delimiter_captures_wrapped.unwrap(); - // Sanity check: - assert!( - // either this language doesn't support nested - // comments, so only the overall match group (a - // closing block comment delimiter) was captured, - // or... - delimiter_captures.len() == 1 + println!( + "Looking for a block comment delimiter in '{}'.", + &source_code[comment_start_index + ..min(comment_start_index + 30, source_code.len())] + ); + let delimiter_captures_wrapped = + comment_delim_regex.captures(&source_code[comment_start_index..]); + if delimiter_captures_wrapped.is_none() { + #[cfg(feature = "lexer_explain")] + println!("The closing comment delimiter wasn't found."); + // If there's no closing delimiter, this is not + // a doc block; it's a syntax error. The safe + // route is to assume the rest of the contents + // are code, which this program won't edit; it + // does edit comments by cleaning up HTML tags, + // word-wrapping, etc. which would be a disaster + // if this was applied to code. + source_code_unlexed_index = source_code.len(); + // Exit the block comment processing code here. + break 'block_comment; + } + let delimiter_captures = delimiter_captures_wrapped.unwrap(); + // Sanity check: + assert!( + // either this language doesn't support nested + // comments, so only the overall match group (a + // closing block comment delimiter) was + // captured, or... + delimiter_captures.len() == 1 // ...for languages that support nested // comments, there are two capture groups // (in addition to capture group 0, the @@ -1045,356 +1073,370 @@ pub fn source_lexer( && delimiter_captures.get(2).is_none()) || (delimiter_captures.get(1).is_none() && delimiter_captures.get(2).is_some()))) - ); - // Is this an opening comment delimiter? - if let Some(opening_delimiter) = delimiter_captures.get(1) { - // Yes. - last_delimiter_was_opening = true; - nesting_depth += 1; - // Mark all previous text as code, then continue the - // loop. - #[cfg(feature = "lexer_explain")] - println!( - "opening_delimiter.start() = {}, opening_delimiter.len() = {}", - opening_delimiter.start(), - opening_delimiter.len() - ); - source_code_unlexed_index += - comment_start_index + opening_delimiter.start(); - comment_start_index = - source_code_unlexed_index + opening_delimiter.len(); - #[cfg(feature = "lexer_explain")] - println!( - "Found a nested opening block comment delimiter. Nesting depth: {}", - &nesting_depth ); - continue; - } else { - // This is a closing comment delimiter. - nesting_depth -= 1; - assert!(nesting_depth >= 0); - let closing_delimiter_match = if delimiter_captures.len() == 3 { - delimiter_captures.get(2).unwrap() - } else { - delimiter_captures.get(0).unwrap() - }; - - // If `last_delimiter_was_opening` was false, then - // mark this text as code and continue the loop. - if !last_delimiter_was_opening { - source_code_unlexed_index += comment_start_index - + closing_delimiter_match.start() - + closing_delimiter_match.len(); - last_delimiter_was_opening = false; + // Is this an opening comment delimiter? + if let Some(opening_delimiter) = delimiter_captures.get(1) { + // Yes. + last_delimiter_was_opening = true; + nesting_depth += 1; + // Mark all previous text as code, then continue + // the loop. #[cfg(feature = "lexer_explain")] - println!("Found a non-innermost closing block comment delimiter. Nesting depth: {}", &nesting_depth); + println!( + "opening_delimiter.start() = {}, opening_delimiter.len() = {}", + opening_delimiter.start(), + opening_delimiter.len() + ); + source_code_unlexed_index += + comment_start_index + opening_delimiter.start(); + comment_start_index = + source_code_unlexed_index + opening_delimiter.len(); + #[cfg(feature = "lexer_explain")] + println!( + "Found a nested opening block comment delimiter. Nesting depth: {}", + &nesting_depth + ); continue; - } + } else { + // This is a closing comment delimiter. + nesting_depth -= 1; + assert!(nesting_depth >= 0); + let closing_delimiter_match = if delimiter_captures.len() == 3 { + delimiter_captures.get(2).unwrap() + } else { + delimiter_captures.get(0).unwrap() + }; + + // If `last_delimiter_was_opening` was false, + // then mark this text as code and continue the + // loop. + if !last_delimiter_was_opening { + source_code_unlexed_index += comment_start_index + + closing_delimiter_match.start() + + closing_delimiter_match.len(); + last_delimiter_was_opening = false; + #[cfg(feature = "lexer_explain")] + println!( + "Found a non-innermost closing block comment delimiter. Nesting depth: {}", + &nesting_depth + ); + continue; + } - // Otherwise, this is a potential doc block: it's an - // innermost nested block comment. See if this - // qualifies as a doc block. - let closing_delimiter_start_index = - closing_delimiter_match.start() + comment_start_index; - let closing_delimiter_end_index = - closing_delimiter_match.end() + comment_start_index; + // Otherwise, this is a potential doc block: + // it's an innermost nested block comment. See + // if this qualifies as a doc block. + let closing_delimiter_start_index = + closing_delimiter_match.start() + comment_start_index; + let closing_delimiter_end_index = + closing_delimiter_match.end() + comment_start_index; - // Capture the body of the comment -- everything but - // the opening and closing delimiters. - let comment_body = - &source_code[comment_start_index..closing_delimiter_start_index]; + // Capture the body of the comment -- everything + // but the opening and closing delimiters. + let comment_body = &source_code + [comment_start_index..closing_delimiter_start_index]; - #[cfg(feature = "lexer_explain")] - println!( - "The comment body is\n\ + #[cfg(feature = "lexer_explain")] + println!( + "The comment body is\n\ '{}'.\n\ The closing delimiter is '{}'.", - comment_body, - closing_delimiter_match.as_str() - ); - // Find the first \\n after the closing delimiter. - // If there is a newline after the closing - // delimiter, set - // `newline_or_eof_after_closing_delimiter_index` to - // the index of the first newline after the closing - // delimiter else set it to the end of the file. - let newline_or_eof_after_closing_delimiter_index = - match source_code[closing_delimiter_end_index..].find('\n') { - // The + 1 includes the newline in the - // resulting index. - Some(index) => index + closing_delimiter_end_index + 1, - None => source_code.len(), - }; + comment_body, + closing_delimiter_match.as_str() + ); + // Find the first \\n after the closing + // delimiter. If there is a newline after the + // closing delimiter, set + // `newline_or_eof_after_closing_delimiter_index` + // to the index of the first newline after the + // closing delimiter else set it to the end of + // the file. + let newline_or_eof_after_closing_delimiter_index = + match source_code[closing_delimiter_end_index..].find('\n') { + // The + 1 includes the newline in the + // resulting index. + Some(index) => index + closing_delimiter_end_index + 1, + None => source_code.len(), + }; + + // Capture the line which begins after the + // closing delimiter and ends at the next + // newline/EOF. + let post_closing_delimiter_line = &source_code + [closing_delimiter_end_index + ..newline_or_eof_after_closing_delimiter_index]; - // Capture the line which begins after the closing - // delimiter and ends at the next newline/EOF. - let post_closing_delimiter_line = &source_code - [closing_delimiter_end_index - ..newline_or_eof_after_closing_delimiter_index]; - - #[cfg(feature = "lexer_explain")] - println!( - "The post-comment line is '{}'.", - post_closing_delimiter_line - ); - - // Set the `current_code_block` to contain preceding - // code (which might be multiple lines) until the - // block comment delimiter. Split this on newlines, - // grouping all the lines before the last line into - // `code_lines_before_comment` (which is all code), - // and everything else (from the beginning of the - // last line to where the block comment delimiter - // appears) into `comment_line_prefix`. For example, - // consider the fragment: - // `a = 1\nb = 2 /* comment */`. After processing, - // `code_lines_before_comment` will be "`a = 1\n`" - // and `comment_line_prefix` will be "`b = 2` ". - let current_code_block = - &source_code[current_code_block_index..source_code_unlexed_index]; - let comment_line_prefix = - current_code_block.rsplit('\n').next().unwrap(); - let code_lines_before_comment = ¤t_code_block - [..current_code_block.len() - comment_line_prefix.len()]; - - // Move to the next block of source code to be - // lexed. - source_code_unlexed_index = - newline_or_eof_after_closing_delimiter_index; + #[cfg(feature = "lexer_explain")] + println!( + "The post-comment line is '{}'.", + post_closing_delimiter_line + ); + + // Set the `current_code_block` to contain + // preceding code (which might be multiple + // lines) until the block comment delimiter. + // Split this on newlines, grouping all the + // lines before the last line into + // `code_lines_before_comment` (which is all + // code), and everything else (from the + // beginning of the last line to where the block + // comment delimiter appears) into + // `comment_line_prefix`. For example, consider + // the fragment: `a = 1\nb = 2 /* comment */`. + // After processing, `code_lines_before_comment` + // will be "`a = 1\n`" and `comment_line_prefix` + // will be "`b = 2` ". + let current_code_block = &source_code + [current_code_block_index..source_code_unlexed_index]; + let comment_line_prefix = + current_code_block.rsplit('\n').next().unwrap(); + let code_lines_before_comment = ¤t_code_block + [..current_code_block.len() - comment_line_prefix.len()]; + + // Move to the next block of source code to be + // lexed. + source_code_unlexed_index = + newline_or_eof_after_closing_delimiter_index; - #[cfg(feature = "lexer_explain")] - println!( - "current_code_block is '{}'\n\ + #[cfg(feature = "lexer_explain")] + println!( + "current_code_block is '{}'\n\ comment_line_prefix is '{}'\n\ code_lines_before_comment is '{}'", - current_code_block, comment_line_prefix, code_lines_before_comment - ); + current_code_block, + comment_line_prefix, + code_lines_before_comment + ); - // Next, determine if this is a doc block. Criteria - // for doc blocks for a block comment: - // - // 1. Must have a space or newline after the - // opening delimiter. - // 2. Must not have anything besides whitespace - // before the opening comment delimiter on the - // same line. This whitespace becomes the - // indent. - // 3. Must not have anything besides whitespace - // after the closing comment delimiter on the - // same line. This whitespace is included, as if - // it were inside the block comment. Rationale: - // this avoids deleting text (or, in this case, - // whitespace); moving that whitespace around - // seems like a better alternative than deleting - // it. - if (comment_body.starts_with(' ') || comment_body.starts_with('\n')) - && WHITESPACE_ONLY_REGEX.is_match(comment_line_prefix) - && WHITESPACE_ONLY_REGEX.is_match(post_closing_delimiter_line) - { - // Put the `code_lines_before_comment` into the - // code block. - append_code_doc_block("", "", code_lines_before_comment); - - // If there's a space at the end of the comment - // body, remove it; also remove the initial - // space/newline at the beginning of the comment - // body. + // Next, determine if this is a doc block. + // Criteria for doc blocks for a block comment: // - // This `unwrap()` is always safe, since we know - // that `comment_body` starts with a space or - // newline. - let last_char = comment_body.chars().last().unwrap(); - let ends_with_space = last_char == ' ' && + // 1. Must have a space or newline after the + // opening delimiter. + // 2. Must not have anything besides whitespace + // before the opening comment delimiter on + // the same line. This whitespace becomes + // the indent. + // 3. Must not have anything besides whitespace + // after the closing comment delimiter on + // the same line. This whitespace is + // included, as if it were inside the block + // comment. Rationale: this avoids deleting + // text (or, in this case, whitespace); + // moving that whitespace around seems like + // a better alternative than deleting it. + if (comment_body.starts_with(' ') || comment_body.starts_with('\n')) + && WHITESPACE_ONLY_REGEX.is_match(comment_line_prefix) + && WHITESPACE_ONLY_REGEX.is_match(post_closing_delimiter_line) + { + // Put the `code_lines_before_comment` into + // the code block. + append_code_doc_block("", "", code_lines_before_comment); + + // If there's a space at the end of the + // comment body, remove it; also remove the + // initial space/newline at the beginning of + // the comment body. + // + // This `unwrap()` is always safe, since we + // know that `comment_body` starts with a + // space or newline. + let last_char = comment_body.chars().last().unwrap(); + let ends_with_space = last_char == ' ' && // Don't remove a space at the end of the // comment body when it's also the space at // the beginning of the comment body // (meaning it's a single-character comment // body). comment_body.len() > 1; - let trimmed_comment_body = &comment_body - [1..comment_body.len() - if ends_with_space { 1 } else { 0 }]; - // The contents of the doc block are the trimmed - // comment body plus any whitespace after the - // closing comment delimiter. - let contents = &(trimmed_comment_body.to_string() - + post_closing_delimiter_line); - // The indent is the whitespace before the - // opening comment delimiter. - let indent = comment_line_prefix; - // The opening comment delimiter was captured in - // the initial match. - let delimiter = matching_group_str; - - // #### Block comment indentation processing - // - // There are several cases: - // - // - A single line: `/* comment */`. No special - // handling needed. - // - Multiple lines, in two styles. - // - Each line of the comment is not - // consistently whitespace-indented. No - // special handling needed. For example: - // - // ```C - // /* This is - // not - // consistently indented. */ - // ``` - // - // - Each line of the comment is consistently - // whitespace-indented; for example: - // - // ```C - // /* This is - // consistently indented. */ - // ``` - // - // Consistently indented means the first - // non-whitespace character on a line aligns - // with, but never comes before, the - // comment's start. Another example: - // - // ```C - // /* This is - // correct - // - // indentation. - // */ - // ``` - // - // Note that the third (blank) line doesn't - // have an indent; since that line consists - // only of whitespace, this is OK. Likewise, - // the last line (containing the closing - // comment delimiter of `*/`) consists only - // of whitespace after the comment - // delimiters are removed. - // - // Determine if this comment is indented. - // - // Determine the starting column of the indent - // (assuming this block comment has a valid - // indent). The +1 represents the space after - // the opening delimiter. - let indent_column = indent.len() + delimiter.len() + 1; - let split_contents: Vec<&str> = - contents.split_inclusive('\n').collect(); - // We need at least two lines of comment - // contents to look for an indent. This is just - // a first guess at `is_indented`, not the final - // value. - let mut is_indented = split_contents.len() > 1; - if is_indented { - // Ignore the first line, since the indent - // and delimiter have already been split out - // for that line. - for line in &split_contents[1..] { - let this_line_indent = if line.len() < indent_column { - line - } else { - &line[..indent_column] - }; - if !WHITESPACE_ONLY_REGEX.is_match(this_line_indent) { - is_indented = false; - break; + let trimmed_comment_body = &comment_body[1..comment_body.len() + - if ends_with_space { 1 } else { 0 }]; + // The contents of the doc block are the + // trimmed comment body plus any whitespace + // after the closing comment delimiter. + let contents = &(trimmed_comment_body.to_string() + + post_closing_delimiter_line); + // The indent is the whitespace before the + // opening comment delimiter. + let indent = comment_line_prefix; + // The opening comment delimiter was + // captured in the initial match. + let delimiter = matching_group_str; + + // #### Block comment indentation processing + // + // There are several cases: + // + // * A single line: `/* comment */`. No + // special handling needed. + // * Multiple lines, in two styles. + // * Each line of the comment is not + // consistently whitespace-indented. No + // special handling needed. For example: + // + // ```C + // /* This is + // not + // consistently indented. */ + // ``` + // + // * Each line of the comment is consistently + // whitespace-indented; for example: + // + // ```C + // /* This is + // consistently indented. */ + // ``` + // + // Consistently indented means the first + // non-whitespace character on a line + // aligns with, but never comes before, the + // comment's start. Another example: + // + // ```C + // /* This is + // correct + // + // indentation. + // */ + // ``` + // + // Note that the third (blank) line doesn't + // have an indent; since that line consists + // only of whitespace, this is OK. + // Likewise, the last line (containing the + // closing comment delimiter of `*/`) + // consists only of whitespace after the + // comment delimiters are removed. + // + // Determine if this comment is indented. + // + // Determine the starting column of the + // indent (assuming this block comment has a + // valid indent). The +1 represents the + // space after the opening delimiter. + let indent_column = indent.len() + delimiter.len() + 1; + let split_contents: Vec<&str> = + contents.split_inclusive('\n').collect(); + // We need at least two lines of comment + // contents to look for an indent. This is + // just a first guess at `is_indented`, not + // the final value. + let mut is_indented = split_contents.len() > 1; + if is_indented { + // Ignore the first line, since the indent + // and delimiter have already been split + // out for that line. + for line in &split_contents[1..] { + let this_line_indent = if line.len() < indent_column { + line + } else { + &line[..indent_column] + }; + if !WHITESPACE_ONLY_REGEX.is_match(this_line_indent) { + is_indented = false; + break; + } } } - } - // If the comment was indented, dedent it; - // otherwise, leave it unchanged. - let mut buf = String::new(); - let dedented_contents = if is_indented { - // If this is indented, then the first line - // must exist. - buf += split_contents[0]; - for line in &split_contents[1..] { - // Remove the indent, unless this line - // didn't have an indent (just whitespace). - buf += if line.len() < indent_column { - // Tricky case: in the middle of a comment, - // every line always ends with a newline; - // if there's not enough whitespace to - // remove the indent, then replace that - // with just a newline. At the end of a - // comment which is the last line of a - // file, a lack of whitespace shouldn't be - // replaced with a newline, since it's not - // there in the original. - if line.ends_with('\n') { - "\n" + // If the comment was indented, dedent it; + // otherwise, leave it unchanged. + let mut buf = String::new(); + let dedented_contents = if is_indented { + // If this is indented, then the first line + // must exist. + buf += split_contents[0]; + for line in &split_contents[1..] { + // Remove the indent, unless this line + // didn't have an indent (just whitespace). + buf += if line.len() < indent_column { + // Tricky case: in the middle of a comment, + // every line always ends with a newline; + // if there's not enough whitespace to + // remove the indent, then replace that + // with just a newline. At the end of a + // comment which is the last line of a + // file, a lack of whitespace shouldn't be + // replaced with a newline, since it's not + // there in the original. + if line.ends_with('\n') { "\n" } else { "" } } else { - "" - } - } else { - &line[indent_column..] - }; - } - &buf + &line[indent_column..] + }; + } + &buf + } else { + contents + }; + + // Add this doc block: + append_code_doc_block(indent, delimiter, dedented_contents); + + // print the doc block + #[cfg(feature = "lexer_explain")] + println!( + "Appending a doc block with indent '{}', delimiter '{}', and contents '{}'.", + &comment_line_prefix, matching_group_str, contents + ); + + // advance `current_code_block_index` to + // `source_code_unlexed_index`, since we've + // moved everything in the current code + // block into the `classified_source`. + current_code_block_index = source_code_unlexed_index; + // Likewise, move the `comment_start_index` + // up, since everything before + // `source_code_unlexed_index` has been + // classified. + comment_start_index = source_code_unlexed_index; } else { - contents - }; - - // Add this doc block: - append_code_doc_block(indent, delimiter, dedented_contents); - - // print the doc block - #[cfg(feature = "lexer_explain")] - println!("Appending a doc block with indent '{}', delimiter '{}', and contents '{}'.", &comment_line_prefix, matching_group_str, contents); - - // advance `current_code_block_index` to - // `source_code_unlexed_index`, since we've - // moved everything in the current code block - // into the `classified_source`. - current_code_block_index = source_code_unlexed_index; - // Likewise, move the `comment_start_index` up, - // since everything before - // `source_code_unlexed_index` has been - // classified. - comment_start_index = source_code_unlexed_index; - } else { - // Nothing to do -- the comment was simply added - // to the current code block already. + // Nothing to do -- the comment was simply + // added to the current code block already. + } } } } - } - // #### String-like syntax - RegexDelimType::String(closing_regex) => { - #[cfg(feature = "lexer_explain")] - print!("This is a string. "); - append_code(closing_regex) - } + // #### String-like syntax + RegexDelimType::String(closing_regex) => { + #[cfg(feature = "lexer_explain")] + print!("This is a string. "); + append_code(closing_regex) + } - RegexDelimType::TemplateLiteral => { - #[cfg(feature = "lexer_explain")] - print!("This is a template literal. "); - append_code(&TEMPLATE_LITERAL_CLOSING_REGEX); - } + RegexDelimType::TemplateLiteral => { + #[cfg(feature = "lexer_explain")] + print!("This is a template literal. "); + append_code(&TEMPLATE_LITERAL_CLOSING_REGEX); + } - RegexDelimType::Heredoc(stop_prefix, stop_suffix) => { - #[cfg(feature = "lexer_explain")] - print!("This is a heredoc. "); - - // Get the string from the source code which (along with the - // stop prefix/suffix) defines the end of the heredoc. - let heredoc_string = &classify_match[language_lexer_compiled.map.len() + 1]; - // Make a regex from it. - let closing_regex = Regex::new( - &(stop_prefix.to_owned() + ®ex::escape(heredoc_string) + stop_suffix), - ) - .unwrap(); - // Use this to find the end of the heredoc and add that to - // `current_source_code`. - append_code(&closing_regex); + RegexDelimType::Heredoc(stop_prefix, stop_suffix) => { + #[cfg(feature = "lexer_explain")] + print!("This is a heredoc. "); + + // Get the string from the source code which (along with + // the stop prefix/suffix) defines the end of the + // heredoc. + let heredoc_string = &classify_match[language_lexer_compiled.map.len() + 1]; + // Make a regex from it. + let closing_regex = Regex::new( + &(stop_prefix.to_owned() + + ®ex::escape(heredoc_string) + + stop_suffix), + ) + .unwrap(); + // Use this to find the end of the heredoc and add that + // to `current_source_code`. + append_code(&closing_regex); + } } } - } else { - // There's no match, so the rest of the source code belongs in the - // current code block. - source_code_unlexed_index = source_code.len(); + _ => { + // There's no match, so the rest of the source code belongs in + // the current code block. + source_code_unlexed_index = source_code.len(); + } } } @@ -1404,10 +1446,11 @@ pub fn source_lexer( classified_source } -// ## Tests +// Tests +// ----- // -// Rust -// [almost mandates](https://doc.rust-lang.org/book/ch11-03-test-organization.html) +// Rust [almost +// mandates](https://doc.rust-lang.org/book/ch11-03-test-organization.html) // putting tests in the same file as the source, which I dislike. Here's a way // to place them in a separate file. #[cfg(test)] diff --git a/server/src/lexer/lexer-walkthrough.md b/server/src/lexer/lexer-walkthrough.md index aa73ca40..bfb69d4b 100644 --- a/server/src/lexer/lexer-walkthrough.md +++ b/server/src/lexer/lexer-walkthrough.md @@ -16,72 +16,79 @@ You should have received a [copy](LICENSE.html) of the GNU General Public License along with the CodeChat Editor. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/). -# Lexer walkthrough +Lexer walkthrough +================= This walkthrough shows how the lexer parses the following Python code fragment: -print("""¶\ -# This is not a comment! It's a multi-line +print("""¶\ +# This is not a comment! It's a multi-line string.¶\ -"""\ -# This is a comment. +"""\ +# This is a comment. Paragraph marks (the ¶ character) are included to show how the lexer handles -newlines. To explain the operation of the lexer, the code will be highlighted -in yellow to represent the -unlexed source code, -represented by the contents of the -variable `source_code[source_code_unlexed_index..]` and in green for the -current code block, -defined by `source_code[current_code_block_index..source_code_unlexed_index]`. -Code that is classified by the lexer will be placed in the `classified_code` -array. - -## Start of parse - -The unlexed source -code holds all the code (everything is highlighted in yellow); the -current code block -is empty (there is no green highlight). - -print("""¶\ -# +newlines. To explain the operation of the lexer, the code will be highlighted in +yellow to represent the unlexed source +code, represented by the contents of the variable  +`source_code[source_code_unlexed_index..]` and in green for the current code block, defined by +`source_code[current_code_block_index..source_code_unlexed_index]`. Code that is +classified by the lexer will be placed in the `classified_code` array. + +Start of parse +-------------- + +The unlexed source code holds +all the code (everything is highlighted in yellow); the current code block is empty (there is +no green highlight). + +print("""¶\ +# This is not a comment! It's a multi-line string.¶\ -"""\ -  # -This is a comment. +"""\ +  +  # This is a comment. ``` classified_code = [ ] ``` -## Search for a token +Search for a token +------------------ The lexer begins by searching for the regex in `language_lexer_compiled.next_token`, which is `(\#)|(""")|(''')|(")|(')`. The -first token found is -""". Everything up -to the match is moved from the unlexed source code to the current code block, -giving: +first token found is """. +Everything up to the match is moved from the unlexed source code to the current +code block, giving: -print("""¶\ -# +print("""¶\ +# This is not a comment! It's a multi-line string.¶\ -"""\ -  # -This is a comment. +"""\ +  +  # This is a comment. ``` classified_code = [ ] ``` -## String processing +String processing +----------------- The regex is accompanied by a map named `language_lexer_compiled.map`, which -connects the mapped group to which token it matched (see -`struct RegexDelimType`): +connects the mapped group to which token it matched (see `struct +RegexDelimType`): ``` Regex:         (#)       |  (""") | (''')  |  (")   |  (') @@ -91,81 +98,92 @@ Group:      1            2     3       4        5 Since group 2 matched, looking up this group in the map tells the lexer it’s a string, and also gives a regex which identifies the end of the string . This -regex identifies the end of the string, moving it from the -(unclassified) source -code to the (classified) -current code block. +regex identifies the end of the string, moving it from the (unclassified) source code to the +(classified) current code block. It correctly skips what looks like a comment but is not a comment. After this step, the lexer’s state is: -print("""¶\ -# +print("""¶\ +# This is not a comment! It's a multi-line string.¶\ -"""\ -  # -This is a comment. +"""\ +  +  # This is a comment. ``` classified_code = [ ] ``` -## Search for a token (second time) +Search for a token (second time) +-------------------------------- Now, the lexer is back to its state of looking through code (as opposed to looking inside a string, comment, etc.). It uses the `next_token` regex as -before to identify the next token -# and moves all the -preceding characters from source code to the current code block. The lexer -state is now: +before to identify the next token # and moves all the preceding characters from +source code to the current code block. The lexer state is now: -print("""¶\ -# +print("""¶\ +# This is not a comment! It's a multi-line string.¶\ -"""\ -  # -This is a comment. +"""\ +  +  # This is a +comment. ``` classified_code = [ ] ``` -## Inline comment lex +Inline comment lex +------------------ Based on the map, the lexer identifies this as an inline comment. The inline comment lexer first identifies the end of the comment (the next newline or, as -in this case, the end of the file), putting the entire inline comment except -for the comment opening delimiter -# into -full_comment. -It then splits the current code block into two -groups: code_lines_before_comment -(lines in the current code block which come before the current line) and the -comment_line_prefix -(the current line up to the start of the comment). The classification is: - -print("""¶\ -# +in this case, the end of the file), putting the entire inline comment except for +the comment opening delimiter # into full_comment. It then splits the current code +block into two groups: code_lines_before_comment (lines in the current +code block which come before the current line) and the comment_line_prefix (the +current line up to the start of the comment). The classification is: + +print("""¶\ +# This is not a comment! It's a multi-line string.¶\ -"""\ -  # -This is a comment. +"""\ +  +  # This is a +comment. ``` classified_code = [ ] ``` -## Code/doc block classification +Code/doc block classification +----------------------------- -Because -comment_line_prefix -contains only whitespace and -full_comment has a -space after the comment delimiter, the lexer classifies this as a doc block. It -adds code_lines_before_comment -as a code block, then the text of the comment as a doc block: +Because comment_line_prefix contains only whitespace and full_comment has a space after the +comment delimiter, the lexer classifies this as a doc block. It adds code_lines_before_comment as a code +block, then the text of the comment as a doc block: ``` classified_code = [ @@ -180,8 +198,8 @@ classified_code = [ ] ``` -## Done +Done +---- -After this, the unlexed source code is empty since the inline comment -classified moved the remainder of its contents into `classified_code`. The -function exits. +After this, the unlexed source code is empty since the inline comment classified +moved the remainder of its contents into `classified_code`. The function exits. \ No newline at end of file diff --git a/server/src/lexer/pest/c.pest b/server/src/lexer/pest/c.pest index 578c2f03..4891c9a6 100644 --- a/server/src/lexer/pest/c.pest +++ b/server/src/lexer/pest/c.pest @@ -14,21 +14,23 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `c.pest` - Pest parser definition for the C language +// `c.pest` - Pest parser definition for the C language +// ==================================================== // -// ## Comments +// Comments +// -------- doc_block = _{ inline_comment | block_comment } -// Per the -// [C standard, section 6.4.3](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#page=65), +// Per the [C standard, section +// 6.4.3](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#page=65), // "white-space consists of: (space, horizontal tab, new-line, vertical tab, and // form-feed)." Omit newlines, since the rest of this parser uses these. vertical_tab = { "\x0B" } form_feed = { "\x0C" } white_space = { (" " | "\t" | vertical_tab | form_feed)* } -// The -// [C standard, section 6.4.9](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#page=65), +// The [C standard, section +// 6.4.9](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#page=65), // defines inline and block comments. // // ### Inline comments @@ -47,10 +49,11 @@ block_comment_closing_delim_0 = { "*/" } block_comment_closing_delim_1 = { unused } block_comment_closing_delim_2 = { unused } -// ## Code +// Code +// ---- // -// Per the -// [C standard, section 5.1.1.2](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#page=24), +// Per the [C standard, section +// 5.1.1.2](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#page=24), // if a line of code ends with a backslash, it continues on the next line. This // is a logical line; treat it as a single line. Therefore, consider a // backslash-newline (or anything that's not a newline) a part of the current @@ -59,11 +62,12 @@ block_comment_closing_delim_2 = { unused } // // 1. Comments continued onto another line don't look like a comment; this // would confuse most developers. -// 2. The backslash-newline in a comment creates a -// [hard line break](https://spec.commonmark.org/0.31.2/#hard-line-breaks) -// in Markdown, which means inserting a hard line break this way in an -// inline comment requires the next line to omit the inline comment -// delimiters. For example:  +// +// 2. The backslash-newline in a comment creates a [hard line +// break](https://spec.commonmark.org/0.31.2/#hard-line-breaks) in Markdown, +// which means inserting a hard line break this way in an inline comment +// requires the next line to omit the inline comment delimiters. For +// example:  // // ```C // // This is a hard line break\ @@ -71,65 +75,69 @@ block_comment_closing_delim_2 = { unused } // // delimiter on the line after the line break, but which must // include them on following lines. // ``` +// // 3. The CodeChat Editor web-to-code function produces incorrect results in // this case, adding a comment delimiter when it shouldn't. To fix this, it // would have to look for a backslash newline only in C/C++-like languages. logical_line_char = _{ ("\\" ~ NEWLINE) | not_newline } code_line_token = _{ logical_line_char } -// ## Dedenter +// Dedenter +// -------- // // This parser runs separately; it dedents block comments. There are several // cases: // -// - A single line: `/* comment */`. No special handling needed. -// - Multiple lines, in two styles. -// - Each line of the comment is not consistently whitespace-indented. No -// special handling needed. For example: -// -// ```C -// /* This is -// not -// consistently indented. */ -// ``` -// -// - Each line of the comment is consistently whitespace-indented; for -// example: -// -// ```C -// /* This is -// consistently indented. */ -// ``` -// -// Consistently indented means the first non-whitespace character on a line -// aligns with, but never comes before, the comment's start. Another -// example: -// -// ```C -// /* This is -// correct -// -// indentation. -// */ -// ``` -// -// Note that the third (blank) line doesn't have an indent; since that line -// consists only of whitespace, this is OK. Likewise, the last line -// (containing the closing comment delimiter of `*/`) consists only of -// whitespace after the comment delimiters are removed. -// -// - Each line of the comment is consistently asterisk-indented; for example: -// -// ```C -// /* This is -// * correct -// * -// * indentation. -// */ -// ``` -// -// Note that in this case, no whitespace-only lines are allowed. Instead, -// the special case is lines which have a newline immediately after the `*`. +// * A single line: `/* comment */`. No special handling needed. +// * Multiple lines, in two styles. +// * Each line of the comment is not consistently whitespace-indented. No +// special handling needed. For example: +// +// ```C +// /* This is +// not +// consistently indented. */ +// ``` +// +// * Each line of the comment is consistently whitespace-indented; for +// example: +// +// ```C +// /* This is +// consistently indented. */ +// ``` +// +// Consistently indented means the first non-whitespace character on a +// line aligns with, but never comes before, the comment's start. +// Another example: +// +// ```C +// /* This is +// correct +// +// indentation. +// */ +// ``` +// +// Note that the third (blank) line doesn't have an indent; since that +// line consists only of whitespace, this is OK. Likewise, the last line +// (containing the closing comment delimiter of `*/`) consists only of +// whitespace after the comment delimiters are removed. +// +// * Each line of the comment is consistently asterisk-indented; for +// example: +// +// ```C +// /* This is +// * correct +// * +// * indentation. +// */ +// ``` +// +// Note that in this case, no whitespace-only lines are allowed. +// Instead, the special case is lines which have a newline immediately +// after the `*`. // // To implement this dedenting, we must have two paths to accepting the contents // of a block comment. Otherwise, this parser rejects the block (it cannot be @@ -162,4 +170,4 @@ vis_newline = { NEWLINE } not_newline_eoi = _{ !(NEWLINE ~ EOI) } star_dedent = _{ PEEK ~ " * " } -// CodeChat Editor lexer: c_cpp. +/// CodeChat Editor lexer: c_cpp. diff --git a/server/src/lexer/pest/parser_design.md b/server/src/lexer/pest/parser_design.md index d5a5d40b..3b10ac41 100644 --- a/server/src/lexer/pest/parser_design.md +++ b/server/src/lexer/pest/parser_design.md @@ -1,4 +1,5 @@ -# Parser design +Parser design +============= The CodeChat Editor uses the [Pest parser](https://pest.rs/), a Rust implementation of a parsing expression grammar (or PEG). The purpose of the @@ -6,39 +7,46 @@ parser from a CodeChat Editor perspective is to classify a source file into code blocks and doc blocks. To accomplish this goal, grammar files (`.pest`) are divided into: -- A shared grammar ([shared.pest](shared.pest)), which contains basic - definitions applicable to all languages; -- A language-specific grammar, which builds on these shared definitions by - providing necessary language-specific customizations. +* A shared grammar ([shared.pest](shared.pest)), which contains basic + definitions applicable to all languages; +* A language-specific grammar, which builds on these shared definitions by + providing necessary language-specific customizations. In particular, a language-specific grammar must provide: -- The definition of a `doc_block`; for most languages, - `doc_block = _{ inline_comment | block_comment }`. However, languages which - lack an inline comment (such as CSS) or a block comment (such as Python) would - contain only the appropriate comment type. -- Inline comment definitions: - - Opening inline delimiter(s) supported by the language. Three inline comment - delimiters must be defined for a language. For C, this is: +* The definition of a `doc_block`; for most languages, `doc_block = _{ + inline_comment | block_comment }`. However, languages which lack an inline + comment (such as CSS) or a block comment (such as Python) would contain only + the appropriate comment type. +* Inline comment definitions: + * Opening inline delimiter(s) supported by the language. Three inline + comment delimiters must be defined for a language. For C, this is: + + ``` + inline_comment_delims = _{ inline_comment_delim_0 } + inline_comment_delim_0 = { "//" } + inline_comment_delim_1 = { unused } + inline_comment_delim_2 = { unused } + ``` + + * A token which defines characters in the body of on an inline comment. + For Python, this is: + + ``` + inline_comment_char = { not_newline } + ``` + +* Block comment definitions: provide opening and closing delimiter + definitions. For C, this is: ``` - inline_comment_delims = _{ inline_comment_delim_0 } - inline_comment_delim_0 = { "//" } - inline_comment_delim_1 = { unused } - inline_comment_delim_2 = { unused } - ``` - - A token which defines characters in the body of on an inline comment. For Python, this is: - ``` - inline_comment_char = { not_newline } + block_comment = { block_comment_0 } + block_comment_opening_delim_0 = { "/*" } + block_comment_opening_delim_1 = { unused } + block_comment_opening_delim_2 = { unused } + block_comment_closing_delim_0 = { "*/" } + block_comment_closing_delim_1 = { unused } + block_comment_closing_delim_2 = { unused } ``` -- Block comment definitions: provide opening and closing delimiter definitions. For C, this is: - ``` - block_comment = { block_comment_0 } - block_comment_opening_delim_0 = { "/*" } - block_comment_opening_delim_1 = { unused } - block_comment_opening_delim_2 = { unused } - block_comment_closing_delim_0 = { "*/" } - block_comment_closing_delim_1 = { unused } - block_comment_closing_delim_2 = { unused } - ``` -- `code_line_token`, a token used to recognize tokens in a code line. + +* `code_line_token`, a token used to recognize tokens in a code line. \ No newline at end of file diff --git a/server/src/lexer/pest/python.pest b/server/src/lexer/pest/python.pest index cdf0dbc1..0e96d79e 100644 --- a/server/src/lexer/pest/python.pest +++ b/server/src/lexer/pest/python.pest @@ -14,28 +14,30 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `python.pest` - Pest parser definition for Python +// `python.pest` - Pest parser definition for Python +// ================================================= doc_block = _{ inline_comment } -// Per the -// [Python language reference](https://docs.python.org/3/reference/lexical_analysis.html#indentation), +// Per the [Python language +// reference](https://docs.python.org/3/reference/lexical_analysis.html#indentation), // leading whitespace used to determine the indentation level consists of spaces // and tabs. white_space = { (" " | "\t")* } -// ## Inline comments -// +// Inline comments +// --------------- inline_comment_delims = _{ inline_comment_delim_0 } inline_comment_delim_0 = { "#" } inline_comment_delim_1 = { unused } inline_comment_delim_2 = { unused } -// Per the -// [Python language reference, section 2.1.3](https://docs.python.org/3/reference/lexical_analysis.html#comments), +// Per the [Python language reference, section +// 2.1.3](https://docs.python.org/3/reference/lexical_analysis.html#comments), // comments end at the end of a physical line. There's no C-like backslash that // can join physical lines into logical lines for comments. inline_comment_char = { not_newline } -// ## Block comments +// Block comments +// -------------- // // Other languages support block comments; even though Python doesn't, the // following must be defined. Block comments never combine. @@ -47,7 +49,8 @@ block_comment_closing_delim_0 = { unused } block_comment_closing_delim_1 = { unused } block_comment_closing_delim_2 = { unused } -// ## Code blocks +// Code blocks +// ----------- code_line_token = _{ long_string | short_string | not_newline } long_string = _{ // The opening string delimiter. @@ -69,7 +72,8 @@ short_string = _{ (POP | ((EOI | &NEWLINE) ~ DROP) ) } -// ## Dedenter +// Dedenter +// -------- dedenter = { unused } -// CodeChat Editor lexer: c_cpp. +/// CodeChat Editor lexer: c_cpp. diff --git a/server/src/lexer/pest/shared.pest b/server/src/lexer/pest/shared.pest index cc9e2544..1f8b5911 100644 --- a/server/src/lexer/pest/shared.pest +++ b/server/src/lexer/pest/shared.pest @@ -14,10 +14,12 @@ // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). // -// # `shared.pest` - Pest parser definition shared by all languages +// `shared.pest` - Pest parser definition shared by all languages +// ============================================================== file = { SOI ~ (doc_block | code_block)* ~ EOI } -// ## Inline comments +// Inline comments +// --------------- // // Use this approach to match a group of inline comments with the same // whitespace indentation. @@ -39,11 +41,12 @@ inline_comment_line = { (" " ~ inline_comment_body) | newline_eoi } // // becomes: // -// - inline_comment_body > logical_line: `a\n` -// - inline_comment_body: `\n` +// * inline\_comment\_body > logical\_line: `a\n` +// * inline\_comment\_body: `\n` inline_comment_body = { inline_comment_char* ~ newline_eoi } -// ## Block comments +// Block comments +// -------------- // // Support multiple opening and closing delimiters using some repetition. block_comment_0 = _{ @@ -68,14 +71,16 @@ optional_space = { " "? } // Use this so that the ending (usually a newline) gets captured in a token. block_comment_ending = { newline_eoi } -// ## Code blocks +// Code blocks +// ----------- code_block = { code_line+ } code_line = _{ (!doc_block ~ code_line_token* ~ NEWLINE) | (!doc_block ~ code_line_token+ ~ EOI) } -// ## Other commonly-used tokens +// Other commonly-used tokens +// -------------------------- newline_eoi = _{ NEWLINE | EOI } not_newline = _{ !NEWLINE ~ ANY } // Indicates this token isn't used by the parser. unused = { "unused" } -// CodeChat Editor lexer: c_cpp. +/// CodeChat Editor lexer: c_cpp. diff --git a/server/src/lexer/pest_parser.rs b/server/src/lexer/pest_parser.rs index 7594c872..5b14e766 100644 --- a/server/src/lexer/pest_parser.rs +++ b/server/src/lexer/pest_parser.rs @@ -13,8 +13,12 @@ // You should have received a copy of the GNU General Public License along with // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). -// # `pest_parser.rs` -- Lex source code into code and doc blocks -// ## Imports +// +// `pest_parser.rs` -- Lex source code into code and doc blocks +// ============================================================ +// +// Imports +// ------- // // ### Standard library // @@ -27,12 +31,13 @@ // ### Local // // None. -// -/// ## Parser generator -/// This macro generates a parser function that converts the provided string into -/// a series of code and doc blocks. I'd prefer to use traits, but don't see a -/// way to pass the `Rule` types as a usable. (Using `RuleType` means we can't -/// access `Rule::file`, etc.) +/// Parser generator +/// ---------------- +/// +/// This macro generates a parser function that converts the provided string +/// into a series of code and doc blocks. I'd prefer to use traits, but don't +/// see a way to pass the `Rule` types as a usable. (Using `RuleType` means we +/// can't access `Rule::file`, etc.) #[macro_export] macro_rules! make_parse_to_code_doc_blocks { ($parser: ty) => { @@ -45,22 +50,23 @@ macro_rules! make_parse_to_code_doc_blocks { &String::from_iter(normalize_line_endings::normalized(input.chars())); let pairs = match <$parser>::parse(Rule::file, normalized_input) { Ok(pairs) => pairs, - Err(e) => - panic!("Parse error: {e:#?}") + Err(e) => panic!("Parse error: {e:#?}"), } - // The first (and only) element is the `file` token. - .next() - .unwrap() - // Return the contents of this token (code and doc block - // tokens). - .into_inner(); + // The first (and only) element is the `file` token. + .next() + .unwrap() + // Return the contents of this token (code and doc block tokens). + .into_inner(); // For debugging, print out the parse tree. //println!("{:#?}", pairs); // The last token is the `EOI` token. assert_eq!(pairs.clone().last().unwrap().as_rule(), Rule::EOI); // Transform these tokens into code and doc blocks; ignore the last // token (EOI). - pairs.rev().skip(1).rev() + pairs + .rev() + .skip(1) + .rev() .map(|block| match block.as_rule() { Rule::inline_comment => { // Gather all tokens in the inline comment. @@ -70,29 +76,35 @@ macro_rules! make_parse_to_code_doc_blocks { let whitespace = whitespace_pair.as_str(); let inline_comment_delim = inline_comment.next().unwrap(); // Combine the text of all the inline comments. - let comment = &mut inline_comment.fold(String::new(), |mut acc, inline_comment_body| { - assert_eq!(inline_comment_body.as_rule(), Rule::inline_comment_line); - let s = inline_comment_body.as_str(); - let inner = &mut inline_comment_body.into_inner(); - // See the notes on inline comments in - // [c.pest](pest/c.pest) for the expected structure - // of the `inline_comment_body`. - let contents = if let Some(inline_comment_contents) = inner.next() { - // For comments which contains contents, include - // that. - inline_comment_contents.as_str() - } else { - // For comments which are just a newline, include - // that. - s - }; - assert!(inner.next().is_none()); + let comment = &mut inline_comment.fold( + String::new(), + |mut acc, inline_comment_body| { + assert_eq!( + inline_comment_body.as_rule(), + Rule::inline_comment_line + ); + let s = inline_comment_body.as_str(); + let inner = &mut inline_comment_body.into_inner(); + // See the notes on inline comments in + // [c.pest](pest/c.pest) for the expected + // structure of the `inline_comment_body`. + let contents = if let Some(inline_comment_contents) = inner.next() { + // For comments which contains contents, + // include that. + inline_comment_contents.as_str() + } else { + // For comments which are just a newline, + // include that. + s + }; + assert!(inner.next().is_none()); - // Add this string (the raw newline, or the comment - // contents) to the accumulator. - acc.push_str(contents); - acc - }); + // Add this string (the raw newline, or the + // comment contents) to the accumulator. + acc.push_str(contents); + acc + }, + ); // Determine which opening delimiter was used. let _delimiter_index = match inline_comment_delim.as_rule() { @@ -124,19 +136,22 @@ macro_rules! make_parse_to_code_doc_blocks { let block_comment_pre = block_comment_pre_pair.as_str(); let comment_pair = block_comment.next().unwrap(); assert!( - comment_pair.as_rule() == Rule::contents_0 || - comment_pair.as_rule() == Rule::contents_1 || - comment_pair.as_rule() == Rule::contents_2 + comment_pair.as_rule() == Rule::contents_0 + || comment_pair.as_rule() == Rule::contents_1 + || comment_pair.as_rule() == Rule::contents_2 ); let comment = comment_pair.as_str(); let optional_space_pair = block_comment.next().unwrap(); assert_eq!(optional_space_pair.as_rule(), Rule::optional_space); let optional_space = optional_space_pair.as_str(); - let block_comment_closing_delim_rule = block_comment.next().unwrap().as_rule(); + let block_comment_closing_delim_rule = + block_comment.next().unwrap().as_rule(); assert!( - block_comment_closing_delim_rule == Rule::block_comment_closing_delim_0 || - block_comment_closing_delim_rule == Rule::block_comment_closing_delim_1 || - block_comment_closing_delim_rule == Rule::block_comment_closing_delim_2 + block_comment_closing_delim_rule == Rule::block_comment_closing_delim_0 + || block_comment_closing_delim_rule + == Rule::block_comment_closing_delim_1 + || block_comment_closing_delim_rule + == Rule::block_comment_closing_delim_2 ); let post_whitespace_pair = block_comment.next().unwrap(); assert_eq!(post_whitespace_pair.as_rule(), Rule::white_space); @@ -145,7 +160,10 @@ macro_rules! make_parse_to_code_doc_blocks { // is exactly what we want. Otherwise, use the newline // provided by the `block_comment_ending` token. let block_comment_ending_pair = block_comment.next().unwrap(); - assert_eq!(block_comment_ending_pair.as_rule(), Rule::block_comment_ending); + assert_eq!( + block_comment_ending_pair.as_rule(), + Rule::block_comment_ending + ); let block_comment_ending = block_comment_ending_pair.as_str(); assert!(block_comment.next().is_none()); @@ -232,7 +250,8 @@ macro_rules! make_parse_block_comment { }; } -// ## Parsers +// Parsers +// ------- // // Each parser is kept in a separate module to avoid name conflicts, since Pest // generates a `Rule` enum for each grammar. @@ -260,7 +279,8 @@ pub mod python { make_parse_block_comment!(ThisParser); } -// ## Tests +// Tests +// ----- #[cfg(test)] mod test { use indoc::indoc; diff --git a/server/src/lexer/supported_languages.rs b/server/src/lexer/supported_languages.rs index bd4c105f..ba4a5573 100644 --- a/server/src/lexer/supported_languages.rs +++ b/server/src/lexer/supported_languages.rs @@ -14,12 +14,14 @@ /// the CodeChat Editor. If not, see /// [http://www.gnu.org/licenses](http://www.gnu.org/licenses). /// -/// # `supported_languages.rs` - Provide lexer info for all supported languages +/// `supported_languages.rs` - Provide lexer info for all supported languages +/// ========================================================================= /// /// This file contains a data structure which describes all supported languages; /// the [lexer](../lexer.rs) uses this lex a given language. /// -/// ## Lexer implementation +/// Lexer implementation +/// -------------------- /// /// Ordering matters: all these delimiters end up in a large regex separated by /// an or operator. The regex or operator matches from left to right. So, longer @@ -42,18 +44,20 @@ /// strings. In this case, they would be `'She'` and `'s here.'`. While this /// doesn't parse the string correctly, it does correctly identify where /// comments can't be, which is all that the lexer needs to do. -// ## Imports +// Imports +// ------- // // ### Standard library use std::sync::Arc; // ### Local use super::{ - pest_parser, BlockCommentDelim, CodeDocBlock, HeredocDelim, LanguageLexer, NewlineSupport, - SpecialCase, StringDelimiterSpec, + BlockCommentDelim, CodeDocBlock, HeredocDelim, LanguageLexer, NewlineSupport, SpecialCase, + StringDelimiterSpec, pest_parser, }; -// ## Helper functions +// Helper functions +// ---------------- // // These functions simplify the syntax needed to create a `LanguageLexer`. #[allow(clippy::too_many_arguments)] @@ -118,7 +122,8 @@ fn make_block_comment_delim(opening: &str, closing: &str, is_nestable: bool) -> } } -// ## Define lexers for each supported language. +// Define lexers for each supported language. +// ------------------------------------------ pub fn get_language_lexer_vec() -> Vec { vec![ // ### Linux shell scripts @@ -143,11 +148,11 @@ pub fn get_language_lexer_vec() -> Vec { "c_cpp", // Unusual extensions: // - // - The `.ino` extension is for Arduino source files. - // - The `.pest` extension is for - // [Pest grammars](https://docs.rs/pest/latest/pest/#grammar). - // TODO: Pest doesn't support line continuations and allows - // multiline strings, so this is a inaccurate. + // * The `.ino` extension is for Arduino source files. + // * The `.pest` extension is for [Pest + // grammars](https://docs.rs/pest/latest/pest/#grammar). TODO: + // Pest doesn't support line continuations and allows multiline + // strings, so this is a inaccurate. &["c", "cc", "cpp", "h", "hh", "hpp", "ino", "pest"], &["//"], &[make_block_comment_delim("/*", "*/", false)], @@ -159,8 +164,9 @@ pub fn get_language_lexer_vec() -> Vec { // Note: the C/C++ support expects C++11 or newer. Don't worry about // supporting C or older C++ using another lexer entry, since the // raw string syntax in C++11 and newer is IMHO so rare we won't - // encounter it in older code. See the C++ - // [string literals docs for the reasoning behind the start body regex.](https://en.cppreference.com/w/cpp/language/string_literal) + // encounter it in older code. See the C++ [string literals docs for + // the reasoning behind the start body + // regex.](https://en.cppreference.com/w/cpp/language/string_literal) make_heredoc_delim("R\"", "[^()\\\\[[:space:]]]*", "(", ")", "\""), SpecialCase::None, Some(pest_parser::c::parse_to_code_doc_blocks), @@ -169,18 +175,18 @@ pub fn get_language_lexer_vec() -> Vec { make_language_lexer( "csharp", &["cs"], - // See - // [6.3.3 Comments](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#633-comments). - // Also provide support for - // [documentation comments](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments). + // See [6.3.3 + // Comments](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#633-comments). + // Also provide support for [documentation + // comments](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments). &["//", "///"], &[ make_block_comment_delim("/*", "*/", false), make_block_comment_delim("/**", "*/", false), ], &[make_string_delimiter_spec( - // See - // [6.4.5.6 String literals](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#6456-string-literals). + // See [6.4.5.6 String + // literals](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#6456-string-literals). "\"", "\\", NewlineSupport::None, @@ -207,9 +213,9 @@ pub fn get_language_lexer_vec() -> Vec { make_language_lexer( "golang", &["go"], - // See - // [The Go Programming Language Specification](https://go.dev/ref/spec) - // on [Comments](https://go.dev/ref/spec#Comments). + // See [The Go Programming Language + // Specification](https://go.dev/ref/spec) on + // [Comments](https://go.dev/ref/spec#Comments). &[], &[make_block_comment_delim("/*", "*/", false)], // See [String literals](https://go.dev/ref/spec#String_literals). @@ -239,16 +245,17 @@ pub fn get_language_lexer_vec() -> Vec { make_language_lexer( "java", &["java", "kt"], - // See the - // [Java Language Specification, Java SE 19 edition](https://docs.oracle.com/javase/specs/jls/se19/html/index.html), - // [§3.7. Comments](https://docs.oracle.com/javase/specs/jls/se19/html/jls-3.html#jls-3.7). + // See the [Java Language Specification, Java SE 19 + // edition](https://docs.oracle.com/javase/specs/jls/se19/html/index.html), + // [§3.7. + // Comments](https://docs.oracle.com/javase/specs/jls/se19/html/jls-3.html#jls-3.7). // The end of this section notes that comments do not occur // within character literals, string literals, or text blocks, // which describes the approach of this lexer nicely. &["//"], &[make_block_comment_delim("/*", "*/", false)], - // See - // [§3.10.5. String Literals](https://docs.oracle.com/javase/specs/jls/se19/html/jls-3.html#jls-3.10.5). + // See [§3.10.5. String + // Literals](https://docs.oracle.com/javase/specs/jls/se19/html/jls-3.html#jls-3.10.5). &[ make_string_delimiter_spec( "\"", @@ -258,8 +265,8 @@ pub fn get_language_lexer_vec() -> Vec { // and before the matching closing "." NewlineSupport::None, ), - // See - // [§3.10.6. Text Blocks](https://docs.oracle.com/javase/specs/jls/se19/html/jls-3.html#jls-3.10.6). + // See [§3.10.6. Text + // Blocks](https://docs.oracle.com/javase/specs/jls/se19/html/jls-3.html#jls-3.10.6). make_string_delimiter_spec("\"\"\"", "\\", NewlineSupport::Unescaped), ], None, @@ -271,19 +278,19 @@ pub fn get_language_lexer_vec() -> Vec { "javascript", &[ "js", "mjs", - // Note that - // [Qt's QML language](https://doc.qt.io/qt-6/qtqml-syntax-basics.html) - // is basically JSON with some embedded JavaScript. Treat it as + // Note that [Qt's QML + // language](https://doc.qt.io/qt-6/qtqml-syntax-basics.html) is + // basically JSON with some embedded JavaScript. Treat it as // JavaScript, since those rules include template literals. "qml", ], - // See - // [§12.4 Comments](https://262.ecma-international.org/13.0/#sec-comments) + // See [§12.4 + // Comments](https://262.ecma-international.org/13.0/#sec-comments) &["//"], &[make_block_comment_delim("/*", "*/", false)], &[ - // See - // [§12.8.4 String Literals](https://262.ecma-international.org/13.0/#prod-StringLiteral). + // See [§12.8.4 String + // Literals](https://262.ecma-international.org/13.0/#prod-StringLiteral). make_string_delimiter_spec("\"", "\\", NewlineSupport::Escaped), make_string_delimiter_spec("'", "\\", NewlineSupport::Escaped), ], @@ -309,15 +316,16 @@ pub fn get_language_lexer_vec() -> Vec { make_language_lexer( "matlab", &["m"], - // See the - // [MATLAB docs on comments](https://www.mathworks.com/help/matlab/matlab_prog/comments.html). + // See the [MATLAB docs on + // comments](https://www.mathworks.com/help/matlab/matlab_prog/comments.html). // Block comments are a special case, so they're not included here. &["%", "..."], &[], - // Per the - // [MATLAB docs](https://www.mathworks.com/help/matlab/matlab_prog/represent-text-with-character-and-string-arrays.html), + // Per the [MATLAB + // docs](https://www.mathworks.com/help/matlab/matlab_prog/represent-text-with-character-and-string-arrays.html), // there are two types of strings. Although MATLAB supports - // [standard escape sequences](https://www.mathworks.com/help/matlab/matlab_prog/matlab-operators-and-special-characters.html#bvg44q6) + // [standard escape + // sequences](https://www.mathworks.com/help/matlab/matlab_prog/matlab-operators-and-special-characters.html#bvg44q6) // (scroll to the bottom of the page), these don't affect quotes; // instead, doubled quotes are used to insert a single quote. See // [string delimiter doubling](#string_delimiter_doubling). @@ -337,8 +345,8 @@ pub fn get_language_lexer_vec() -> Vec { &[], &[ // Note that raw strings still allow escaping the single/double - // quote. See the - // [language reference](https://docs.python.org/3/reference/lexical_analysis.html#literals). + // quote. See the [language + // reference](https://docs.python.org/3/reference/lexical_analysis.html#literals). make_string_delimiter_spec("\"\"\"", "\\", NewlineSupport::Unescaped), make_string_delimiter_spec("'''", "\\", NewlineSupport::Unescaped), make_string_delimiter_spec("\"", "\\", NewlineSupport::Escaped), @@ -371,10 +379,11 @@ pub fn get_language_lexer_vec() -> Vec { &["sql"], // See // [Wikipedia](https://en.wikipedia.org/wiki/SQL_syntax#Comments). - // The - // [SQL specification isn't free](https://en.wikipedia.org/wiki/SQL#Standardization_history), + // The [SQL specification isn't + // free](https://en.wikipedia.org/wiki/SQL#Standardization_history), // sadly. Oracle publishes their flavor of the 2016 spec; see - // [Comments within SQL statements](https://docs.oracle.com/database/121/SQLRF/sql_elements006.htm#SQLRF51099). + // [Comments within SQL + // statements](https://docs.oracle.com/database/121/SQLRF/sql_elements006.htm#SQLRF51099). // Postgresql defines // [comments](https://www.postgresql.org/docs/15/sql-syntax-lexical.html#SQL-SYNTAX-COMMENTS) // as well. @@ -382,11 +391,11 @@ pub fn get_language_lexer_vec() -> Vec { &[make_block_comment_delim("/*", "*/", false)], &[ // SQL standard strings allow newlines and don't provide an - // escape character. This language uses - // [string delimiter doubling](#string_delimiter_doubling). - // Unfortunately, each variant of SQL also supports their custom - // definition of strings; these must be handled by - // vendor-specific flavors of this basic lexer definition. + // escape character. This language uses [string delimiter + // doubling](#string_delimiter_doubling). Unfortunately, each + // variant of SQL also supports their custom definition of + // strings; these must be handled by vendor-specific flavors of + // this basic lexer definition. make_string_delimiter_spec("'", "", NewlineSupport::Unescaped), ], None, @@ -401,8 +410,8 @@ pub fn get_language_lexer_vec() -> Vec { // [comments](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics#Comments). &["//"], &[make_block_comment_delim("/*", "*/", true)], - // See - // [Strings and Characters](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters). + // See [Strings and + // Characters](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters). &[ // Technically, this would include optional whitespace after the // triple quotes then a newlines then end with a newline before @@ -411,14 +420,14 @@ pub fn get_language_lexer_vec() -> Vec { make_string_delimiter_spec("\"\"\"", "\\", NewlineSupport::Unescaped), make_string_delimiter_spec("\"", "\\", NewlineSupport::None), ], - // Swift supports - // [extended string delimiters](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters#Extended-String-Delimiters) + // Swift supports [extended string + // delimiters](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/stringsandcharacters#Extended-String-Delimiters) // in both string literal and multiline string flavors. Since this // parser only supports a single heredoc type, we ignore the string - // literal flavor. This is a bug: consider the string - // `#"Not a comment "/*"#`. This would parse as a code block - // containing just `#`, then the string `"Not a comment "` then a - // comment starting with `/*"#`. + // literal flavor. This is a bug: consider the string `#"Not a + // comment "/*"#`. This would parse as a code block containing just + // `#`, then the string `"Not a comment "` then a comment starting + // with `/*"#`. make_heredoc_delim("", "#+", "\"\"\"", "\"\"\"", ""), SpecialCase::None, None, @@ -468,8 +477,8 @@ pub fn get_language_lexer_vec() -> Vec { &["--"], &[make_block_comment_delim("/*", "*/", false)], // Per section 15.7 of the standard, strings may not contain - // newlines. This language uses - // [string delimiter doubling](#string_delimiter_doubling). + // newlines. This language uses [string delimiter + // doubling](#string_delimiter_doubling). &[make_string_delimiter_spec("\"", "", NewlineSupport::None)], None, SpecialCase::None, @@ -516,19 +525,19 @@ pub fn get_language_lexer_vec() -> Vec { &["#"], &[], &[ - // See - // [double-quoted style](https://yaml.org/spec/1.2.2/#double-quoted-style). + // See [double-quoted + // style](https://yaml.org/spec/1.2.2/#double-quoted-style). // Something I don't understand and will probably ignore: // "Single- and double-quoted scalars are restricted to a single // line when contained inside an implicit key." make_string_delimiter_spec("\"", "\\", NewlineSupport::Unescaped), - // See - // [single-quoted style](https://yaml.org/spec/1.2.2/#single-quoted-style). + // See [single-quoted + // style](https://yaml.org/spec/1.2.2/#single-quoted-style). // Single-quoted strings escape a single quote by repeating it // twice: `'That''s unusual.'` Rather than try to parse this, - // treat it as two back-to-back strings: `'That'` and - // `'s unusual.'` We don't care about getting the correct value - // for strings; the only purpose is to avoid interpreting string + // treat it as two back-to-back strings: `'That'` and `'s + // unusual.'` We don't care about getting the correct value for + // strings; the only purpose is to avoid interpreting string // contents as inline or block comments. make_string_delimiter_spec("'", "", NewlineSupport::Unescaped), ], diff --git a/server/src/lexer/tests.rs b/server/src/lexer/tests.rs index 55a4b299..2a4480d8 100644 --- a/server/src/lexer/tests.rs +++ b/server/src/lexer/tests.rs @@ -14,14 +14,17 @@ /// the CodeChat Editor. If not, see /// [http://www.gnu.org/licenses](http://www.gnu.org/licenses). /// -/// # `test.rs` -- Unit tests for the lexer -// ## Imports +/// `test.rs` -- Unit tests for the lexer +/// ===================================== +// Imports +// ------- use super::supported_languages::get_language_lexer_vec; -use super::{compile_lexers, source_lexer, CodeDocBlock, DocBlock}; +use super::{CodeDocBlock, DocBlock, compile_lexers, source_lexer}; use crate::test_utils::stringit; use indoc::indoc; -// ## Utilities +// Utilities +// --------- // // Provide a compact way to create a `CodeDocBlock`. fn build_doc_block(indent: &str, delimiter: &str, contents: &str) -> CodeDocBlock { diff --git a/server/src/lib.rs b/server/src/lib.rs index f44971c9..78cc30c1 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -15,7 +15,8 @@ pub mod capture; /// the CodeChat Editor. If not, see /// [http://www.gnu.org/licenses](http://www.gnu.org/licenses). /// -/// # `lib.rs` -- Define library modules for the CodeChat Editor Server +/// `lib.rs` -- Define library modules for the CodeChat Editor Server +/// ================================================================= /// /// TODO: Add the ability to use /// [plugins](https://zicklag.github.io/rust-tutorials/rust-plugins.html). diff --git a/server/src/main.rs b/server/src/main.rs index c7bd3e00..8e90cbc8 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -13,8 +13,10 @@ // You should have received a copy of the GNU General Public License along with // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). -/// # `main.rs` -- Entrypoint for the CodeChat Editor Server -// ## Imports +/// `main.rs` -- Entrypoint for the CodeChat Editor Server +/// ====================================================== +// Imports +// ------- // // ### Standard library use std::{ @@ -33,7 +35,8 @@ use log::LevelFilter; // ### Local use code_chat_editor::webserver::{self, IP_ADDRESS}; -// ## Data structures +// Data structures +// --------------- // // ### Command-line interface // @@ -75,7 +78,8 @@ enum Commands { Stop, } -// ## Code +// Code +// ---- // // The following code implements the command-line interface for the CodeChat // Editor. @@ -120,7 +124,10 @@ impl Cli { } match process { - // If the process isn't started, then do so. We wait to here to start the process, in case the server was already running; in this case, the ping above will see the running server then exit. + // If the process isn't started, then do so. We wait to + // here to start the process, in case the server was + // already running; in this case, the ping above will + // see the running server then exit. None => { println!("Starting server in background..."); let current_exe = match env::current_exe() { @@ -128,7 +135,7 @@ impl Cli { Err(e) => { return Err( format!("Failed to get current executable: {e}").into() - ) + ); } }; #[cfg(not(debug_assertions))] @@ -148,10 +155,11 @@ impl Cli { } process = match cmd .args(["--port", &self.port.to_string(), "serve", "--log", "off"]) - // Subtle: the default of `stdout(Stdio::inherit())` causes - // a parent process to block, since the child process - // inherits the parent's stdout. So, use the pipes to avoid - // blocking. + // Subtle: the default of + // `stdout(Stdio::inherit())` causes a parent + // process to block, since the child process + // inherits the parent's stdout. So, use the + // pipes to avoid blocking. .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -174,9 +182,9 @@ impl Cli { stdout.read_to_string(&mut stdout_buf).unwrap(); stderr.read_to_string(&mut stderr_buf).unwrap(); return Err(format!( - "Server failed to start: {status:?}\n{stdout_buf}\n{stderr_buf}" - ) - .into()); + "Server failed to start: {status:?}\n{stdout_buf}\n{stderr_buf}" + ) + .into()); } Ok(None) => {} Err(e) => return Err(format!("Error starting server: {e}").into()), diff --git a/server/src/processing.rs b/server/src/processing.rs index 661936e0..2ec12cdb 100644 --- a/server/src/processing.rs +++ b/server/src/processing.rs @@ -14,8 +14,11 @@ /// the CodeChat Editor. If not, see /// [http://www.gnu.org/licenses](http://www.gnu.org/licenses). /// -/// # `processing.rs` -- Transform source code to its web-editable equivalent and back -// ## Imports +/// `processing.rs` -- Transform source code to its web-editable equivalent and +/// back +/// =========================================================================== +// Imports +// ------- // // ### Standard library // @@ -34,17 +37,19 @@ use std::path::PathBuf; // ### Third-party use lazy_static::lazy_static; -use pulldown_cmark::{html, Options, Parser}; +use pulldown_cmark::{Options, Parser, html}; use regex::Regex; use serde::{Deserialize, Serialize}; use crate::lexer::LEXERS; // ### Local -use crate::lexer::{source_lexer, CodeDocBlock, DocBlock, LanguageLexerCompiled}; +use crate::lexer::{CodeDocBlock, DocBlock, LanguageLexerCompiled, source_lexer}; -// ## Data structures +// Data structures +// --------------- // -// ### Translation between a local (traditional) source file and its web-editable, client-side representation +// ### Translation between a local (traditional) source file and its web-editable, +// client-side representation // // There are three ways that a source file is represented: // @@ -132,7 +137,8 @@ pub enum TranslationResultsString { // On save, the process is CodeChatForWeb -> Vec\ -> source code. // -// ## Globals +// Globals +// ------- lazy_static! { /// Match the lexer directive in a source file. static ref LEXER_DIRECTIVE: Regex = Regex::new(r"CodeChat Editor lexer: (\w+)").unwrap(); @@ -143,7 +149,8 @@ lazy_static! { const DOC_BLOCK_SEPARATOR_STRING: &str = "\n\n\n"; -// ## Determine if the provided file is part of a project. +// Determine if the provided file is part of a project. +// ---------------------------------------------------- pub fn find_path_to_toc(file_path: &Path) -> Option { // To determine if this source code is part of a project, look for a project // file by searching the current directory, then all its parents, for a file @@ -166,9 +173,10 @@ pub fn find_path_to_toc(file_path: &Path) -> Option { } } -// ## Transform `CodeChatForWeb` to source code -/// This function takes in a source file in web-editable format -/// (the `CodeChatForWeb` struct) and transforms it into source code. +// Transform `CodeChatForWeb` to source code +// ----------------------------------------- +/// This function takes in a source file in web-editable format (the +/// `CodeChatForWeb` struct) and transforms it into source code. pub fn codechat_for_web_to_source( // The file to save plus metadata, stored in the `LexedSourceFile` codechat_for_web: &CodeChatForWeb, @@ -270,8 +278,8 @@ fn code_doc_block_vec_to_source( // `split_inclusive` becomes an empty list, not `[""]`. Note // that this mirrors what Python's // [splitlines](https://docs.python.org/3/library/stdtypes.html#str.splitlines) - // does, and is also the subject of a - // [Rust bug report](https://github.com/rust-lang/rust/issues/111457). + // does, and is also the subject of a [Rust bug + // report](https://github.com/rust-lang/rust/issues/111457). let lines: Vec<_> = doc_block.contents.split_inclusive('\n').collect(); let lines_fixed = if lines.is_empty() { vec![""] } else { lines }; for content_line in lines_fixed { @@ -293,7 +301,7 @@ fn code_doc_block_vec_to_source( return Err(format!( "Unknown comment opening delimiter '{}'.", doc_block.delimiter - )) + )); } }; @@ -347,12 +355,12 @@ fn code_doc_block_vec_to_source( ); // Since this isn't a first line: } else { - // - If this line is just a newline, include just - // the newline. + // * If this line is just a newline, include just + // the newline. if *content_line == "\n" { append_doc_block("", "", "\n"); - // - Otherwise, include spaces in place of the - // delimiter. + // * Otherwise, include spaces in place of the + // delimiter. } else { append_doc_block( &doc_block.indent, @@ -376,7 +384,8 @@ fn code_doc_block_vec_to_source( Ok(file_contents) } -// ## Transform from source code to `CodeChatForWeb` +// Transform from source code to `CodeChatForWeb` +// ---------------------------------------------- // // Given the contents of a file, classify it and (for CodeChat Editor files) // convert it to the `CodeChatForWeb` format. @@ -401,16 +410,17 @@ pub fn source_to_codechat_for_web( return TranslationResults::Err(format!( "

Unknown lexer type {}.

", &lexer_name - )) + )); } } } else { // Otherwise, look up the lexer by the file's extension. - if let Some(llc) = LEXERS.map_ext_to_lexer_vec.get(file_ext) { - llc.first().unwrap() - } else { - // The file type is unknown; treat it as plain text. - return TranslationResults::Unknown; + match LEXERS.map_ext_to_lexer_vec.get(file_ext) { + Some(llc) => llc.first().unwrap(), + _ => { + // The file type is unknown; treat it as plain text. + return TranslationResults::Unknown; + } } }; @@ -444,8 +454,8 @@ pub fn source_to_codechat_for_web( // Combine all the doc blocks into a single string, separated by a // delimiter. Transform this to markdown, then split the transformed // content back into the doc blocks they came from. This is - // necessary to allow - // [link reference definitions](https://spec.commonmark.org/0.31.2/#link-reference-definitions) + // necessary to allow [link reference + // definitions](https://spec.commonmark.org/0.31.2/#link-reference-definitions) // between doc blocks to work; for example, `[Link][1]` in one doc // block, then `[1]: http:/foo.org` in another doc block requires // both to be in the same Markdown document to translate correctly. @@ -574,59 +584,61 @@ fn markdown_to_html(markdown: &str) -> String { // load/save, then do some accesses during those processes. // // Top-level data structures: a file HashSet and an id -// HashMap}>. Some FileAnchors in the file +// HashMap}>. Some FileAnchors in the file // HashSet are also in a pending load list. // -// - To update a file: -// - Remove the old file from the file HasHMap. Add an empty FileAnchor to the -// file HashMap. -// - For each id, see if that id already exists. -// - If the id exists: if it refers to an id in the old FileAnchor, replace -// it with the new one. If not, need to perform resolution on this id (we -// have a non-unique id; how to fix?). -// - If the id doesn't exist: create a new one. -// - For each hyperlink, see if that id already exists. -// - If so, upsert the referring id. Check the metadata on the id to make -// sure that data is current. If not, add this to the pending hyperlinks -// list. If the file is missing, delete it from the cache. -// - If not, create a new entry in the id HashSet and add the referring id -// to the HashSet. Add the file to a pending hyperlinks list. -// - When the file is processed: -// - Look for all entries in the pending file list that refer to the current -// file and resolve these. Start another task to load in all pending -// files. -// - Look at the old file; remove each id that's still in the id HashMap. If -// the id was in the HashMap and it also was a Hyperlink, remove that from -// the HashSet. -// - To remove a file from the HashMap: -// - Remove it from the file HashMap. -// - For each hyperlink, remove it from the HashSet of referring links (if -// that id still exists). -// - For each id, remove it from the id HashMap. -// - To add a file from the HashSet: -// - Perform an update with an empty FileAnchor. +// * To update a file: +// * Remove the old file from the file HasHMap. Add an empty FileAnchor to +// the file HashMap. +// * For each id, see if that id already exists. +// * If the id exists: if it refers to an id in the old FileAnchor, +// replace it with the new one. If not, need to perform resolution +// on this id (we have a non-unique id; how to fix?). +// * If the id doesn't exist: create a new one. +// * For each hyperlink, see if that id already exists. +// * If so, upsert the referring id. Check the metadata on the id to +// make sure that data is current. If not, add this to the pending +// hyperlinks list. If the file is missing, delete it from the +// cache. +// * If not, create a new entry in the id HashSet and add the +// referring id to the HashSet. Add the file to a pending hyperlinks +// list. +// * When the file is processed: +// * Look for all entries in the pending file list that refer to the +// current file and resolve these. Start another task to load in all +// pending files. +// * Look at the old file; remove each id that's still in the id +// HashMap. If the id was in the HashMap and it also was a +// Hyperlink, remove that from the HashSet. +// * To remove a file from the HashMap: +// * Remove it from the file HashMap. +// * For each hyperlink, remove it from the HashSet of referring links (if +// that id still exists). +// * For each id, remove it from the id HashMap. +// * To add a file from the HashSet: +// * Perform an update with an empty FileAnchor. // // Pending hyperlinks list: for each hyperlink, // -// - check if the id is now current in the cache. If so, add the referring id to -// the HashSet then move to the next hyperlink. -// - check if the file is now current in the cache. If not, load the file and -// update the cache, then go to step 1. -// - The id was not found, even in the expected file. Add the hyperlink to a -// broken links set? +// * check if the id is now current in the cache. If so, add the referring id +// to the HashSet then move to the next hyperlink. +// * check if the file is now current in the cache. If not, load the file and +// update the cache, then go to step 1. +// * The id was not found, even in the expected file. Add the hyperlink to a +// broken links set? // // Global operations: // -// - Scan all files, then perform add/upsert/removes based on differences with -// the cache. +// * Scan all files, then perform add/upsert/removes based on differences with +// the cache. // // Functions: // -// - Upsert an Anchor. -// - Upsert a Hyperlink. -// - Upsert a file. -// - Remove a file. -/* +// * Upsert an Anchor. +// * Upsert a Hyperlink. +// * Upsert a file. +// * Remove a file. +/*x /// There are two types of files that can serve as an anchor: these are file /// anchor targets. enum FileAnchor { @@ -780,17 +792,18 @@ fn html_analyze( } */ -// ## Tests +// Tests +// ----- #[cfg(test)] mod tests { use std::{path::PathBuf, str::FromStr}; use predicates::prelude::predicate::str; - use super::{find_path_to_toc, TranslationResults}; use super::{CodeChatForWeb, CodeMirror, CodeMirrorDocBlocks, SourceFileMetadata}; + use super::{TranslationResults, find_path_to_toc}; use crate::lexer::{ - compile_lexers, supported_languages::get_language_lexer_vec, CodeDocBlock, DocBlock, + CodeDocBlock, DocBlock, compile_lexers, supported_languages::get_language_lexer_vec, }; use crate::processing::{ code_doc_block_vec_to_source, code_mirror_to_code_doc_blocks, codechat_for_web_to_source, @@ -1331,9 +1344,9 @@ mod tests { // Trigger special cases: // - // - An empty doc block at the beginning of the file. - // - A doc block in the middle of the file - // - A doc block with no trailing newline at the end of the file. + // * An empty doc block at the beginning of the file. + // * A doc block in the middle of the file + // * A doc block with no trailing newline at the end of the file. assert_eq!( source_to_codechat_for_web("//\n\n//\n\n//", &"cpp".to_string(), false, false), TranslationResults::CodeChat(build_codechat_for_web( diff --git a/server/src/test_utils.rs b/server/src/test_utils.rs index 46576ecf..53034de2 100644 --- a/server/src/test_utils.rs +++ b/server/src/test_utils.rs @@ -14,43 +14,45 @@ /// the CodeChat Editor. If not, see /// [http://www.gnu.org/licenses](http://www.gnu.org/licenses). /// -/// # `test_utils.rs` -- Reusable routines for testing +/// `test_utils.rs` -- Reusable routines for testing +/// ================================================ /// /// Placing this file in the `tests/` directory prevents me from importing it /// outside that directory tree; the desire was to import this for unit tests in /// the `src/` directory tree. So, it's instead placed here, then conditionally /// imported in `lib.rs`. -// ## Imports +// Imports +// ------- // // ### Standard library use std::env; -use std::path::PathBuf; use std::path::MAIN_SEPARATOR_STR; +use std::path::PathBuf; // ### Third-party -use assert_fs::fixture::PathCopy; use assert_fs::TempDir; +use assert_fs::fixture::PathCopy; use assertables::assert_le; use log::Level; // ### Local use crate::testing_logger; -// ## Macros +// Macros +// ------ // -// Extract a known enum variant or fail. More concise than the alternative -// (`if let``, or` let -// else`). From [SO](https://stackoverflow.com/a/69324393). The macro does not handle nested pattern like` -// Some(Animal(cat))\`. +// Extract a known enum variant or fail. More concise than the alternative (`if +// let``, or` let else`). From [SO](https://stackoverflow.com/a/69324393). The +// macro does not handle nested pattern like` Some(Animal(cat))\`. #[macro_export] macro_rules! cast { - ($target: expr, $pat: path) => {{ + ($target: expr_2021, $pat: path) => {{ // The if let exploits recent Rust compiler's smart pattern matching. - // Contrary to other solutions like - // `into_variant`` and friends, this one macro covers all ownership usage like` - // self``, `&self`` and `&mut self``. On the other hand` - // {into,as,as_mut}\_{variant}\`\` solution usually needs 3 \* N method - // definitions where N is the number of variants. + // Contrary to other solutions like `into_variant`` and friends, this + // one macro covers all ownership usage like` self``, `&self`` and `&mut + // self``. On the other hand` {into,as,as\_mut}\_{variant}\`\` solution + // usually needs 3 \* N method definitions where N is the number of + // variants. if let $pat(a) = $target { a } else { @@ -87,7 +89,8 @@ macro_rules! prep_test_dir { }}; } -// ## Code +// Code +// ---- // // Use the `tests/fixtures` path (relative to the root of this Rust project) to // store files for testing. A subdirectory tree, named by the module path then @@ -111,8 +114,9 @@ pub fn _prep_test_dir( // Switch from `::` to a filesystem path separator. let test_path = &test_path.replace("::", MAIN_SEPARATOR_STR); - // First, get the project root directory, based on the - // [location of the cargo.toml file](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates). + // First, get the project root directory, based on the [location of the + // cargo.toml + // file](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates). let root_dir = &env::var("CARGO_MANIFEST_DIR") .expect("Environment variable CARGO_MANIFEST_DIR not defined."); let mut source_path = PathBuf::from(root_dir); @@ -127,7 +131,7 @@ pub fn _prep_test_dir( source_path.pop(); // For debugging, append - // [.into_persistent()](https://docs.rs/assert_fs/latest/assert_fs/fixture/struct.TempDir.html#method.into_persistent). + // [.into\_persistent()](https://docs.rs/assert_fs/latest/assert_fs/fixture/struct.TempDir.html#method.into_persistent). let temp_dir = TempDir::new().unwrap(); // Create a temporary directory, then copy everything needed for this test // to it. Since the `patterns` parameter is a glob, append `/**` to the diff --git a/server/src/webserver.rs b/server/src/webserver.rs index 212c19a0..adc19a26 100644 --- a/server/src/webserver.rs +++ b/server/src/webserver.rs @@ -13,20 +13,23 @@ // You should have received a copy of the GNU General Public License along with // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). -/// # `webserver.rs` -- Serve CodeChat Editor Client webpages -// ## Submodules +/// `webserver.rs` -- Serve CodeChat Editor Client webpages +/// ======================================================= +// Submodules +// ---------- mod filewatcher; #[cfg(test)] pub mod tests; mod vscode; -// ## Imports +// Imports +// ------- // // ### Standard library use std::{ collections::{HashMap, HashSet}, env, fs, - path::{self, Path, PathBuf, MAIN_SEPARATOR_STR}, + path::{self, MAIN_SEPARATOR_STR, Path, PathBuf}, str::FromStr, sync::{Arc, Mutex}, time::Duration, @@ -35,11 +38,12 @@ use std::{ // ### Third-party use actix_files; use actix_web::{ + App, HttpRequest, HttpResponse, HttpServer, dev::{ServerHandle, ServiceFactory, ServiceRequest}, error::Error, get, http::header::ContentType, - web, App, HttpRequest, HttpResponse, HttpServer, + web, }; use actix_ws::AggregatedMessage; use bytes::Bytes; @@ -47,7 +51,7 @@ use dunce::simplified; use futures_util::StreamExt; use indoc::{formatdoc, indoc}; use lazy_static::lazy_static; -use log::{error, info, warn, LevelFilter}; +use log::{LevelFilter, error, info, warn}; use log4rs; use mime::Mime; use mime_guess; @@ -73,16 +77,18 @@ use vscode::{ // ### Local //use crate::capture::EventCapture; use crate::processing::{ - source_to_codechat_for_web_string, CodeChatForWeb, TranslationResultsString, + CodeChatForWeb, TranslationResultsString, source_to_codechat_for_web_string, }; use filewatcher::{ filewatcher_browser_endpoint, filewatcher_client_endpoint, filewatcher_root_fs_redirect, filewatcher_websocket, }; -// ## Data structures +// Data structures +// --------------- // -// ### Data structures supporting a websocket connection between the IDE, this server, and the CodeChat Editor Client +// ### Data structures supporting a websocket connection between the IDE, this +// server, and the CodeChat Editor Client /// Provide queues which send data to the IDE and the CodeChat Editor Client. #[derive(Debug)] struct WebsocketQueues { @@ -251,18 +257,19 @@ pub struct AppState { vscode_connection_id: Arc>>, } -// ## Macros +// Macros +// ------ /// Create a macro to report an error when enqueueing an item. #[macro_export] macro_rules! oneshot_send { // Provide two options: `break` or `break 'label`. - ($tx: expr) => { + ($tx: expr_2021) => { if let Err(err) = $tx { error!("Unable to enqueue: {err:?}"); break; } }; - ($tx: expr, $label: tt) => { + ($tx: expr_2021, $label: tt) => { if let Err(err) = $tx { error!("Unable to enqueue: {err:?}"); break $label; @@ -272,15 +279,16 @@ macro_rules! oneshot_send { #[macro_export] macro_rules! queue_send { - ($tx: expr) => { + ($tx: expr_2021) => { $crate::oneshot_send!($tx.await) }; - ($tx: expr, $label: tt) => { + ($tx: expr_2021, $label: tt) => { $crate::oneshot_send!($tx.await, $label) }; } -/// ## Globals +/// Globals +/// ------- /// /// The IP address on which the server listens for incoming connections. pub const IP_ADDRESS: &str = "127.0.0.1"; @@ -360,7 +368,8 @@ lazy_static! { } -// ## Webserver functionality +// Webserver functionality +// ----------------------- #[get("/ping")] async fn ping() -> HttpResponse { HttpResponse::Ok().body("pong") @@ -435,7 +444,7 @@ fn get_client_framework( Err(err) => { return Err(format!( "Unable to encode websocket URL for {ide_path}, id {connection_id}: {err}" - )) + )); } }; let codechat_editor_framework_js = BUNDLED_FILES_MAP.get("CodeChatEditorFramework.js").unwrap(); @@ -711,7 +720,7 @@ async fn serve_file( } // Report a lexer error. TranslationResultsString::Err(err_string) => { - return (SimpleHttpResponse::Err(err_string), None) + return (SimpleHttpResponse::Err(err_string), None); } // This is a CodeChat file. The following code wraps the CodeChat for // web results in a CodeChat Editor Client webpage. @@ -811,7 +820,8 @@ async fn serve_file( ) } -/// ## Websockets +/// Websockets +/// ---------- /// /// Each CodeChat Editor IDE instance pairs with a CodeChat Editor Client /// through the CodeChat Editor Server. Together, these form a joint editor, @@ -869,8 +879,9 @@ async fn client_websocket( // sent), the Client closes the websocket. The rest of this // sequence is covered in the next case. // 2. Either websocket is closed. In this case, the other websocket - // should be immediately closed; there's no longer the opportunity - // to perform a more controlled shutdown (see the first case). + // should be immediately closed; there's no longer the + // opportunity to perform a more controlled shutdown (see the + // first case). // 1. The websocket which closed enqueues a `Closed` message for // the other websocket. // 2. When the other websocket receives this message, it closes. @@ -1073,7 +1084,8 @@ async fn client_websocket( Ok(response) } -// ## Webserver core +// Webserver core +// -------------- #[actix_web::main] pub async fn main(port: u16) -> std::io::Result<()> { run_server(port).await @@ -1114,7 +1126,7 @@ pub fn configure_logger(level: LevelFilter) { } // Quoting the [docs](https://actix.rs/docs/application#shared-mutable-state), -// "To achieve _globally_ shared state, it must be created **outside** of the +// "To achieve *globally* shared state, it must be created **outside** of the // closure passed to `HttpServer::new` and moved/cloned in." Putting this code // inside `configure_app` places it inside the closure which calls // `configure_app`, preventing globally shared state. @@ -1163,7 +1175,8 @@ where .route("/fw/fsb", web::get().to(filewatcher_root_fs_redirect)) } -// ## Utilities +// Utilities +// --------- // // Send a response to the client after processing a message from the client. async fn send_response(client_tx: &Sender, id: f64, result: MessageResult) { diff --git a/server/src/webserver/filewatcher.rs b/server/src/webserver/filewatcher.rs index 8a30ae44..76f93dc8 100644 --- a/server/src/webserver/filewatcher.rs +++ b/server/src/webserver/filewatcher.rs @@ -13,8 +13,10 @@ // You should have received a copy of the GNU General Public License along with // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). -/// # `filewatcher.rs` -- Implement the File Watcher "IDE" -// ## Imports +/// `filewatcher.rs` -- Implement the File Watcher "IDE" +/// ==================================================== +// Imports +// ------- // // ### Standard library use std::{ @@ -24,19 +26,19 @@ use std::{ // ### Third-party use actix_web::{ + HttpRequest, HttpResponse, Responder, error::Error, get, http::header::{self, ContentType}, - web, HttpRequest, HttpResponse, Responder, + web, }; use dunce::simplified; use indoc::formatdoc; use lazy_static::lazy_static; use log::{error, info, warn}; use notify_debouncer_full::{ - new_debouncer, + DebounceEventResult, new_debouncer, notify::{EventKind, RecursiveMode}, - DebounceEventResult, }; use regex::Regex; use tokio::{ @@ -52,23 +54,24 @@ use win_partitions::win_api::get_logical_drive; // ### Local use super::{ + AppState, EditorMessage, EditorMessageContents, UpdateMessageContents, WebsocketQueues, client_websocket, escape_html, get_client_framework, get_connection_id, html_not_found, - html_wrapper, path_display, send_response, AppState, EditorMessage, EditorMessageContents, - UpdateMessageContents, WebsocketQueues, + html_wrapper, path_display, send_response, }; use crate::{ oneshot_send, processing::{ - codechat_for_web_to_source, source_to_codechat_for_web_string, TranslationResultsString, + TranslationResultsString, codechat_for_web_to_source, source_to_codechat_for_web_string, }, queue_send, webserver::{ - filesystem_endpoint, get_test_mode, make_simple_http_response, path_to_url, url_to_path, - ResultOkTypes, + ResultOkTypes, filesystem_endpoint, get_test_mode, make_simple_http_response, path_to_url, + url_to_path, }, }; -// ## Globals +// Globals +// ------- lazy_static! { /// Matches a bare drive letter. Only needed on Windows. static ref DRIVE_LETTER_REGEX: Regex = Regex::new("^[a-zA-Z]:$").unwrap(); @@ -76,7 +79,8 @@ lazy_static! { pub const FILEWATCHER_PATH_PREFIX: &[&str] = &["fw", "fsc"]; -/// ## File browser endpoints +/// File browser endpoints +/// ---------------------- /// /// The file browser provides a very crude interface, allowing a user to select /// a file from the local filesystem for editing. Long term, this should be @@ -131,7 +135,7 @@ async fn filewatcher_browser_endpoint( Err(err) => { return html_not_found(&format!( "

The requested path {fixed_path} is not valid: {err}.

" - )) + )); } }; if canon_path.is_dir() { @@ -200,7 +204,7 @@ async fn dir_listing(web_path: &str, dir_path: &Path) -> HttpResponse { return html_not_found(&format!( "

Unable to list the directory {}: {err}/

", path_display(dir_path) - )) + )); } }; @@ -217,7 +221,7 @@ async fn dir_listing(web_path: &str, dir_path: &Path) -> HttpResponse { return html_not_found(&format!( "

Unable to determine the type of {}: {err}.", path_display(&dir_entry.path()), - )) + )); } }; if file_type.is_file() { @@ -231,7 +235,7 @@ async fn dir_listing(web_path: &str, dir_path: &Path) -> HttpResponse { } } Err(err) => { - return html_not_found(&format!("

Unable to read file in directory: {err}.")) + return html_not_found(&format!("

Unable to read file in directory: {err}.")); } }; } @@ -253,7 +257,7 @@ async fn dir_listing(web_path: &str, dir_path: &Path) -> HttpResponse { Err(err) => { return html_not_found(&format!( "

Unable to decode directory name '{err:?}' as UTF-8." - )) + )); } }; let encoded_dir = urlencoding::encode(&dir_name); @@ -280,7 +284,9 @@ async fn dir_listing(web_path: &str, dir_path: &Path) -> HttpResponse { let file_name = match file.file_name().into_string() { Ok(v) => v, Err(err) => { - return html_not_found(&format!("

Unable to decode file name {err:?} as UTF-8.",)) + return html_not_found( + &format!("

Unable to decode file name {err:?} as UTF-8.",), + ); } }; let encoded_file = urlencoding::encode(&file_name); @@ -396,8 +402,8 @@ async fn processing_task(file_path: &Path, app_state: web::Data, conne message: EditorMessageContents::CurrentFile(url_pathbuf) }), 'task); // Note: it's OK to postpone the increment to here; if the - // `queue_send` exits before this runs, the message didn't get sent, - // so the ID wasn't used. + // `queue_send` exits before this runs, the message didn't get + // sent, so the ID wasn't used. id += 1.0; }; @@ -430,7 +436,8 @@ async fn processing_task(file_path: &Path, app_state: web::Data, conne Ok(debounced_event_vec) => { for debounced_event in debounced_event_vec { let is_modify = match debounced_event.event.kind { - // On OS X, we get a `Create` event when a file is modified. + // On OS X, we get a `Create` event when a + // file is modified. EventKind::Create(_create_kind) => true, // On Windows, the `_modify_kind` is `Any`; // therefore; ignore it rather than trying @@ -444,7 +451,7 @@ async fn processing_task(file_path: &Path, app_state: web::Data, conne }; if is_modify { if debounced_event.event.paths.len() != 1 || - current_filepath.as_ref().map_or(true, |cfp| cfp != &debounced_event.event.paths[0]) + current_filepath.as_ref().is_none_or(|cfp| cfp != &debounced_event.event.paths[0]) { warn!("Modification to different file {}.", debounced_event.event.paths[0].to_string_lossy()); } else { @@ -523,7 +530,8 @@ async fn processing_task(file_path: &Path, app_state: web::Data, conne } Some(http_request) = from_http_rx.recv() => { - // If there's no current file, replace it with an empty file, which will still produce an error. + // If there's no current file, replace it with an empty + // file, which will still produce an error. let empty_path = PathBuf::new(); let cfp = current_filepath.as_ref().unwrap_or(&empty_path); let (simple_http_response, option_update) = make_simple_http_response(&http_request, cfp).await; @@ -539,7 +547,9 @@ async fn processing_task(file_path: &Path, app_state: web::Data, conne match m.message { EditorMessageContents::Update(update_message_contents) => { let result = 'process: { - // Check that the file path matches the current file. If `canonicalize` fails, then the files don't match. + // Check that the file path matches the + // current file. If `canonicalize` fails, + // then the files don't match. if Some(Path::new(&update_message_contents.file_path).to_path_buf()) != current_filepath { break 'process Err(format!( "Update for file '{}' doesn't match current file '{current_filepath:?}'.", @@ -567,7 +577,9 @@ async fn processing_task(file_path: &Path, app_state: web::Data, conne }; let cfp = current_filepath.as_ref().unwrap(); - // Unwrap the file, write to it, then rewatch it, in order to avoid a watch notification from this write. + // Unwrap the file, write to it, then + // rewatch it, in order to avoid a watch + // notification from this write. if let Err(err) = debounced_watcher.unwatch(cfp) { let msg = format!( "Unable to unwatch file '{}': {err}.", @@ -697,7 +709,8 @@ pub async fn filewatcher_websocket( .await } -// ## Tests +// Tests +// ----- #[cfg(test)] mod tests { use std::{ @@ -709,9 +722,10 @@ mod tests { use actix_http::Request; use actix_web::{ + App, body::BoxBody, dev::{Service, ServiceResponse}, - test, web, App, + test, web, }; use assertables::assert_starts_with; use dunce::simplified; @@ -720,17 +734,17 @@ mod tests { use url::Url; use super::{ - super::{configure_app, make_app_data, WebsocketQueues}, - send_response, AppState, EditorMessage, EditorMessageContents, UpdateMessageContents, + super::{WebsocketQueues, configure_app, make_app_data}, + AppState, EditorMessage, EditorMessageContents, UpdateMessageContents, send_response, }; use crate::{ cast, prep_test_dir, processing::{ - source_to_codechat_for_web, CodeChatForWeb, CodeMirror, SourceFileMetadata, - TranslationResults, + CodeChatForWeb, CodeMirror, SourceFileMetadata, TranslationResults, + source_to_codechat_for_web, }, test_utils::{check_logger_errors, configure_testing_logger}, - webserver::{tests::IP_PORT, IdeType, ResultOkTypes}, + webserver::{IdeType, ResultOkTypes, tests::IP_PORT}, }; async fn get_websocket_queues( @@ -738,7 +752,7 @@ mod tests { test_dir: &Path, ) -> ( WebsocketQueues, - impl Service, Error = actix_web::Error>, + impl Service, Error = actix_web::Error> + use<>, ) { let app_data = make_app_data(IP_PORT); let app = test::init_service(configure_app(App::new(), &app_data)).await; @@ -777,7 +791,7 @@ mod tests { } macro_rules! get_message_as { - ($client_rx: expr, $cast_type: ty) => {{ + ($client_rx: expr_2021, $cast_type: ty) => {{ let m = get_message(&mut $client_rx).await; (m.id, cast!(m.message, $cast_type)) }}; diff --git a/server/src/webserver/tests.rs b/server/src/webserver/tests.rs index 7c13a259..cfa010e1 100644 --- a/server/src/webserver/tests.rs +++ b/server/src/webserver/tests.rs @@ -13,10 +13,12 @@ // You should have received a copy of the GNU General Public License along with // the CodeChat Editor. If not, see // [http://www.gnu.org/licenses](http://www.gnu.org/licenses). -/// # `test.rs` -- Unit tests for the webserver -// ## Imports +/// `test.rs` -- Unit tests for the webserver +/// ========================================= +// Imports +// ------- use std::{ - path::{PathBuf, MAIN_SEPARATOR_STR}, + path::{MAIN_SEPARATOR_STR, PathBuf}, thread::{self, sleep}, time::Duration, }; @@ -27,16 +29,19 @@ use assertables::{assert_ends_with, assert_starts_with}; use super::{filewatcher::FILEWATCHER_PATH_PREFIX, path_to_url, url_to_path}; use crate::prep_test_dir; -// ## Constants +// Constants +// --------- /// The default port on which the server listens for incoming connections. pub const IP_PORT: u16 = 8080; -// ## Support functions +// Support functions +// ----------------- fn get_server() -> Command { Command::cargo_bin("codechat-editor-server").unwrap() } -// ## Tests +// Tests +// ----- #[test] fn test_url_to_path() { let (temp_dir, test_dir) = prep_test_dir!(); diff --git a/server/src/webserver/vscode.rs b/server/src/webserver/vscode.rs index 5493a6d2..ecb2f744 100644 --- a/server/src/webserver/vscode.rs +++ b/server/src/webserver/vscode.rs @@ -14,16 +14,20 @@ /// the CodeChat Editor. If not, see /// [http://www.gnu.org/licenses](http://www.gnu.org/licenses). /// -/// # `vscode.rs` -- Implement server-side functionality for the Visual Studio Code IDE -// ## Imports +/// `vscode.rs` -- Implement server-side functionality for the Visual Studio +/// Code IDE +/// ======================================================================== +// Imports +// ------- // // ### Standard library use std::{cmp::min, collections::HashMap, path::PathBuf}; // ### Third-party use actix_web::{ + HttpRequest, HttpResponse, error::{Error, ErrorBadRequest}, - get, web, HttpRequest, HttpResponse, + get, web, }; use indoc::formatdoc; use log::{debug, error, warn}; @@ -32,29 +36,32 @@ use tokio::{select, sync::mpsc}; // ### Local use super::{ - client_websocket, get_client_framework, send_response, AppState, EditorMessage, - EditorMessageContents, IdeType, WebsocketQueues, IP_ADDRESS, + AppState, EditorMessage, EditorMessageContents, IP_ADDRESS, IdeType, WebsocketQueues, + client_websocket, get_client_framework, send_response, }; use crate::{ oneshot_send, processing::{ - codechat_for_web_to_source, source_to_codechat_for_web_string, CodeChatForWeb, CodeMirror, - TranslationResultsString, + CodeChatForWeb, CodeMirror, TranslationResultsString, codechat_for_web_to_source, + source_to_codechat_for_web_string, }, queue_send, webserver::{ - escape_html, filesystem_endpoint, html_wrapper, make_simple_http_response, path_to_url, - text_file_to_response, try_canonicalize, url_to_path, ProcessingTaskHttpRequest, - ResultOkTypes, UpdateMessageContents, INITIAL_MESSAGE_ID, MESSAGE_ID_INCREMENT, + INITIAL_MESSAGE_ID, MESSAGE_ID_INCREMENT, ProcessingTaskHttpRequest, ResultOkTypes, + UpdateMessageContents, escape_html, filesystem_endpoint, html_wrapper, + make_simple_http_response, path_to_url, text_file_to_response, try_canonicalize, + url_to_path, }, }; -// ## Globals +// Globals +// ------- const VSCODE_PATH_PREFIX: &[&str] = &["vsc", "fs"]; // The max length of a message to show in the console. const MAX_MESSAGE_LENGTH: usize = 200; -// ## Code +// Code +// ---- // // This is the processing task for the Visual Studio Code IDE. It handles all // the core logic to moving data between the IDE and the client. @@ -108,32 +115,36 @@ pub async fn vscode_ide_websocket( // connections. let (from_ide_tx, mut from_ide_rx) = mpsc::channel(10); let (to_ide_tx, to_ide_rx) = mpsc::channel(10); - assert!(app_state - .vscode_ide_queues - .lock() - .unwrap() - .insert( - connection_id_str.clone(), - WebsocketQueues { - from_websocket_tx: from_ide_tx, - to_websocket_rx: to_ide_rx, - }, - ) - .is_none()); + assert!( + app_state + .vscode_ide_queues + .lock() + .unwrap() + .insert( + connection_id_str.clone(), + WebsocketQueues { + from_websocket_tx: from_ide_tx, + to_websocket_rx: to_ide_rx, + }, + ) + .is_none() + ); let (from_client_tx, mut from_client_rx) = mpsc::channel(10); let (to_client_tx, to_client_rx) = mpsc::channel(10); - assert!(app_state - .vscode_client_queues - .lock() - .unwrap() - .insert( - connection_id_str.clone(), - WebsocketQueues { - from_websocket_tx: from_client_tx, - to_websocket_rx: to_client_rx, - }, - ) - .is_none()); + assert!( + app_state + .vscode_client_queues + .lock() + .unwrap() + .insert( + connection_id_str.clone(), + WebsocketQueues { + from_websocket_tx: from_client_tx, + to_websocket_rx: to_client_rx, + }, + ) + .is_none() + ); app_state .vscode_connection_id .lock() @@ -146,8 +157,8 @@ pub async fn vscode_ide_websocket( // Start the processing task. actix_rt::spawn(async move { - // Use a - // [labeled block expression](https://doc.rust-lang.org/reference/expressions/loop-expr.html#labelled-block-expressions) + // Use a [labeled block + // expression](https://doc.rust-lang.org/reference/expressions/loop-expr.html#labelled-block-expressions) // to provide a way to exit the current task. 'task: { let mut current_file = PathBuf::new(); @@ -568,7 +579,9 @@ pub async fn vscode_ide_websocket( .remove(&connection_id_task) .is_none() { - error!("Unable to remove connection ID {connection_id_task} from processing task queue."); + error!( + "Unable to remove connection ID {connection_id_task} from processing task queue." + ); } if app_state_task .vscode_client_queues @@ -661,7 +674,8 @@ async fn serve_vscode_fs( filesystem_endpoint(request_path, &req, &app_state).await } -// ## Tests +// Tests +// ----- #[cfg(test)] mod test { use std::{ @@ -687,13 +701,12 @@ mod test { time::sleep, }; use tokio_tungstenite::{ - connect_async, + MaybeTlsStream, WebSocketStream, connect_async, tungstenite::{http::StatusCode, protocol::Message}, - MaybeTlsStream, WebSocketStream, }; use super::super::{ - run_server, tests::IP_PORT, EditorMessage, EditorMessageContents, IdeType, IP_ADDRESS, + EditorMessage, EditorMessageContents, IP_ADDRESS, IdeType, run_server, tests::IP_PORT, }; use crate::{ cast, diff --git a/server/tests/cli.rs b/server/tests/cli.rs index 984314c8..c55efe3d 100644 --- a/server/tests/cli.rs +++ b/server/tests/cli.rs @@ -1,6 +1,8 @@ -// # `cli.rs` - Test the CLI interface +// `cli.rs` - Test the CLI interface +// ================================= // -// ## Imports +// Imports +// ------- // // ### Standard library // @@ -15,12 +17,14 @@ use predicates::{prelude::predicate, str::contains}; use code_chat_editor::webserver::IP_ADDRESS; use tokio::task::spawn_blocking; -// ## Support functions +// Support functions +// ----------------- fn get_server() -> Command { Command::cargo_bin("codechat-editor-server").unwrap() } -// ## Tests +// Tests +// ----- #[test] fn test_start_not_found() { let mut cmd = get_server(); diff --git a/toc.md b/toc.md index 3ce38be7..7be984fc 100644 --- a/toc.md +++ b/toc.md @@ -1,25 +1,30 @@ -# The CodeChat Editor +The CodeChat Editor +=================== -## User documentation +User documentation +------------------ 1. [The CodeChat Editor manual](README.md) -2. [The CodeChat Editor extension for Visual Studio Code manual](extensions/VSCode/README.md) +2. [The CodeChat Editor extension for Visual Studio Code + manual](extensions/VSCode/README.md) 3. [Literate programming using the CodeChat Editor](docs/style_guide.cpp) -## Design +Design +------ 1. [CodeChat Editor Design](docs/design.md) 2. [Implementation](docs/implementation.md) -## Implementation +Implementation +-------------- 1. Server 1. [main.rs](server/src/main.rs) 2. [lib.rs](server/src/lib.rs) 3. [lexer.rs](server/src/lexer.rs) 1. [Lexer walkthrough](server/src/lexer/lexer-walkthrough.md) - 2. [supported_languages.rs](server/src/lexer/supported_languages.rs) - 3. [pest_parser.rs](server/src/lexer/pest_parser.rs) + 2. [supported\_languages.rs](server/src/lexer/supported_languages.rs) + 3. [pest\_parser.rs](server/src/lexer/pest_parser.rs) 1. [Parser design](server/src/lexer/pest/parser_design.md) 2. [shared.pest](server/src/lexer/pest/shared.pest) 3. [c.pest](server/src/lexer/pest/c.pest) @@ -30,7 +35,7 @@ 3. [log4rs.yml](server/log4rs.yml) 5. [processing.rs](server/src/processing.rs) 6. Tests - 1. [test_utils.rs](server/src/test_utils.rs) + 1. [test\_utils.rs](server/src/test_utils.rs) 2. Lexer [tests.rs](server/src/lexer/tests.rs) 3. Webserver [tests.rs](server/src/webserver/tests.rs) 4. [cli.rs](server/tests/cli.rs) @@ -46,10 +51,10 @@ 5. [Mermaid](client/src/wc-mermaid/developer.md) 6. [typings.d.ts](client/src/typings.d.ts) 2. Styles - 1. [CodeChatEditor.css](client/static/css/CodeChatEditor.css) - 2. [CodeChatEditorProject.css](client/static/css/CodeChatEditorProject.css) + 1. [CodeChatEditor.css](client/src/css/CodeChatEditor.css) + 2. [CodeChatEditorProject.css](client/src/css/CodeChatEditorProject.css) 3. Themes - 1. [light.css](client/static/css/themes/light.css) + 1. [light.css](client/src/css/themes/light.css) 3. Tests 1. [CodeChatEditor-test.mts](client/src/CodeChatEditor-test.mts) 1. [Run tests](server/src/lib.rs?test) @@ -84,17 +89,20 @@ 2. [dist-workspace.toml](server/dist-workspace.toml) - cargo-dist configuration -## Misc +Misc +---- -- New - project template -- [Table of contents](toc.md) -- [Changelog](docs/CHANGELOG.md) -- [Index](docs/index.md) +* New + project template +* [Table of contents](toc.md) +* [Changelog](docs/CHANGELOG.md) +* [Index](docs/index.md) -## Notes +Notes +----- -- TODO: all links here should be auto-titled and - autogenerate the page-local TOC. +* TODO: all links here should be auto-titled and + autogenerate the page-local TOC. -## [License](LICENSE.md) +[License](LICENSE.md) +--------------------- \ No newline at end of file