From 0a8810426ff94e54c939af02d81624f74ee91e95 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Tue, 20 Jan 2026 21:09:00 -0800 Subject: [PATCH] Move contributor docs editor message structure tree generator from test to tools directory --- .github/workflows/build-dev-and-ci.yml | 6 +- .github/workflows/website.yml | 13 ++- .gitignore | 2 - Cargo.lock | 7 ++ Cargo.toml | 3 +- editor/src/messages/message.rs | 95 +------------------ tools/editor-message-tree/Cargo.toml | 11 +++ tools/editor-message-tree/src/main.rs | 93 ++++++++++++++++++ .../generate-editor-structure.ts | 2 + website/.gitignore | 1 + website/package-lock.json | 8 +- website/package.json | 4 +- website/templates/macros/replacements.html | 8 +- 13 files changed, 142 insertions(+), 111 deletions(-) create mode 100644 tools/editor-message-tree/Cargo.toml create mode 100644 tools/editor-message-tree/src/main.rs diff --git a/.github/workflows/build-dev-and-ci.yml b/.github/workflows/build-dev-and-ci.yml index 365eb76fe8..aee4292435 100644 --- a/.github/workflows/build-dev-and-ci.yml +++ b/.github/workflows/build-dev-and-ci.yml @@ -113,9 +113,11 @@ jobs: - name: 📃 Generate code documentation info for website if: github.ref == 'refs/heads/master' run: | - cargo test --package graphite-editor --lib -- messages::message::test::generate_message_tree + cd tools/editor-message-tree + cargo run + cd ../.. mkdir -p artifacts-generated - mv hierarchical_message_system_tree.txt artifacts-generated/hierarchical_message_system_tree.txt + mv website/generated/hierarchical_message_system_tree.txt artifacts-generated/hierarchical_message_system_tree.txt - name: 💿 Obtain cache of auto-generated code docs artifacts, to check if they've changed if: github.ref == 'refs/heads/master' diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 2586393077..3b8fb54eeb 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -67,20 +67,23 @@ jobs: rustup update stable echo "🦀 Latest updated version of Rust:" rustc --version - cargo test --package graphite-editor --lib -- messages::message::test::generate_message_tree + cd tools/editor-message-tree + cargo run + cd ../.. mkdir artifacts - mv hierarchical_message_system_tree.txt artifacts/hierarchical_message_system_tree.txt + mv website/generated/hierarchical_message_system_tree.txt artifacts/hierarchical_message_system_tree.txt - - name: 🚚 Move `artifacts` contents to the project root + - name: 🚚 Move `artifacts` contents to website/generated run: | - mv artifacts/* . + mkdir -p website/generated + mv artifacts/* website/generated/ - name: 🔧 Build auto-generated code docs artifacts into HTML run: | cd website npm run generate-editor-structure - - name: Generate node catalog documentation + - name: 📃 Generate node catalog documentation run: | cd tools/node-docs cargo run diff --git a/.gitignore b/.gitignore index 34ac06f894..d6cc8a574c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,3 @@ flamegraph.svg .idea/ .direnv .DS_Store -hierarchical_message_system_tree.txt -hierarchical_message_system_tree.html diff --git a/Cargo.lock b/Cargo.lock index 53ea375a56..21876d1c7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1502,6 +1502,13 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "editor-message-tree" +version = "0.0.0" +dependencies = [ + "graphite-editor", +] + [[package]] name = "either" version = "1.15.0" diff --git a/Cargo.toml b/Cargo.toml index b2fd92d7af..91ead4e58b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,8 @@ members = [ "node-graph/preprocessor", "proc-macros", "tools/crate-hierarchy-viz", - "tools/node-docs" + "tools/editor-message-tree", + "tools/node-docs", ] default-members = [ "editor", diff --git a/editor/src/messages/message.rs b/editor/src/messages/message.rs index 5d4dd8bfea..cc24a00afc 100644 --- a/editor/src/messages/message.rs +++ b/editor/src/messages/message.rs @@ -53,97 +53,8 @@ impl specta::Type for MessageDiscriminant { } } -#[cfg(test)] -mod test { - use super::*; - use std::io::Write; - - #[test] - fn generate_message_tree() { - let result = Message::build_message_tree(); - let mut file = std::fs::File::create("../hierarchical_message_system_tree.txt").unwrap(); - file.write_all(format!("{} `{}`\n", result.name(), result.path()).as_bytes()).unwrap(); - if let Some(variants) = result.variants() { - for (i, variant) in variants.iter().enumerate() { - let is_last = i == variants.len() - 1; - print_tree_node(variant, "", is_last, &mut file); - } - } - } - - fn print_tree_node(tree: &DebugMessageTree, prefix: &str, is_last: bool, file: &mut std::fs::File) { - // Print the current node - let (branch, child_prefix) = if tree.message_handler_data_fields().is_some() || tree.message_handler_fields().is_some() { - ("├── ", format!("{prefix}│ ")) - } else if is_last { - ("└── ", format!("{prefix} ")) - } else { - ("├── ", format!("{prefix}│ ")) - }; - - if tree.path().is_empty() { - file.write_all(format!("{}{}{}\n", prefix, branch, tree.name()).as_bytes()).unwrap(); - } else { - file.write_all(format!("{}{}{} `{}`\n", prefix, branch, tree.name(), tree.path()).as_bytes()).unwrap(); - } - - // Print children if any - if let Some(variants) = tree.variants() { - let len = variants.len(); - for (i, variant) in variants.iter().enumerate() { - let is_last_child = i == len - 1; - print_tree_node(variant, &child_prefix, is_last_child, file); - } - } - - // Print message field if any - if let Some(fields) = tree.fields() { - let len = fields.len(); - for (i, field) in fields.iter().enumerate() { - let is_last_field = i == len - 1; - let branch = if is_last_field { "└── " } else { "├── " }; - - file.write_all(format!("{child_prefix}{branch}{field}\n").as_bytes()).unwrap(); - } - } - - // Print handler field if any - if let Some(data) = tree.message_handler_fields() { - let len = data.fields().len(); - let (branch, child_prefix) = if tree.message_handler_data_fields().is_some() { - ("├── ", format!("{prefix}│ ")) - } else { - ("└── ", format!("{prefix} ")) - }; - - const FRONTEND_MESSAGE_STR: &str = "FrontendMessage"; - if data.name().is_empty() && tree.name() != FRONTEND_MESSAGE_STR { - panic!("{}'s MessageHandler is missing #[message_handler_data]", tree.name()); - } else if tree.name() != FRONTEND_MESSAGE_STR { - file.write_all(format!("{}{}{} `{}`\n", prefix, branch, data.name(), data.path()).as_bytes()).unwrap(); - - for (i, field) in data.fields().iter().enumerate() { - let is_last_field = i == len - 1; - let branch = if is_last_field { "└── " } else { "├── " }; - - file.write_all(format!("{}{}{}\n", child_prefix, branch, field.0).as_bytes()).unwrap(); - } - } - } - - // Print data field if any - if let Some(data) = tree.message_handler_data_fields() { - let len = data.fields().len(); - if data.path().is_empty() { - file.write_all(format!("{}{}{}\n", prefix, "└── ", data.name()).as_bytes()).unwrap(); - } else { - file.write_all(format!("{}{}{} `{}`\n", prefix, "└── ", data.name(), data.path()).as_bytes()).unwrap(); - } - for (i, field) in data.fields().iter().enumerate() { - let is_last_field = i == len - 1; - let branch = if is_last_field { "└── " } else { "├── " }; - file.write_all(format!("{}{}{}\n", format!("{} ", prefix), branch, field.0).as_bytes()).unwrap(); - } - } +impl Message { + pub fn message_tree() -> DebugMessageTree { + Self::build_message_tree() } } diff --git a/tools/editor-message-tree/Cargo.toml b/tools/editor-message-tree/Cargo.toml new file mode 100644 index 0000000000..00b5bfcf77 --- /dev/null +++ b/tools/editor-message-tree/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "editor-message-tree" +description = "Tool to generate developer documentation for the editor message system structure" +edition.workspace = true +version.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] +# Local dependencies +editor = { path = "../../editor", package = "graphite-editor" } diff --git a/tools/editor-message-tree/src/main.rs b/tools/editor-message-tree/src/main.rs new file mode 100644 index 0000000000..2933240567 --- /dev/null +++ b/tools/editor-message-tree/src/main.rs @@ -0,0 +1,93 @@ +use editor::messages::message::Message; +use editor::utility_types::DebugMessageTree; +use std::io::Write; + +fn main() { + let result = Message::message_tree(); + std::fs::create_dir_all("../../website/generated").unwrap(); + let mut file = std::fs::File::create("../../website/generated/hierarchical_message_system_tree.txt").unwrap(); + file.write_all(format!("{} `{}`\n", result.name(), result.path()).as_bytes()).unwrap(); + if let Some(variants) = result.variants() { + for (i, variant) in variants.iter().enumerate() { + let is_last = i == variants.len() - 1; + print_tree_node(variant, "", is_last, &mut file); + } + } +} + +fn print_tree_node(tree: &DebugMessageTree, prefix: &str, is_last: bool, file: &mut std::fs::File) { + // Print the current node + let (branch, child_prefix) = if tree.message_handler_data_fields().is_some() || tree.message_handler_fields().is_some() { + ("├── ", format!("{prefix}│ ")) + } else if is_last { + ("└── ", format!("{prefix} ")) + } else { + ("├── ", format!("{prefix}│ ")) + }; + + if tree.path().is_empty() { + file.write_all(format!("{}{}{}\n", prefix, branch, tree.name()).as_bytes()).unwrap(); + } else { + file.write_all(format!("{}{}{} `{}`\n", prefix, branch, tree.name(), tree.path()).as_bytes()).unwrap(); + } + + // Print children if any + if let Some(variants) = tree.variants() { + let len = variants.len(); + for (i, variant) in variants.iter().enumerate() { + let is_last_child = i == len - 1; + print_tree_node(variant, &child_prefix, is_last_child, file); + } + } + + // Print message field if any + if let Some(fields) = tree.fields() { + let len = fields.len(); + for (i, field) in fields.iter().enumerate() { + let is_last_field = i == len - 1; + let branch = if is_last_field { "└── " } else { "├── " }; + + file.write_all(format!("{child_prefix}{branch}{field}\n").as_bytes()).unwrap(); + } + } + + // Print handler field if any + if let Some(data) = tree.message_handler_fields() { + let len = data.fields().len(); + let (branch, child_prefix) = if tree.message_handler_data_fields().is_some() { + ("├── ", format!("{prefix}│ ")) + } else { + ("└── ", format!("{prefix} ")) + }; + + const FRONTEND_MESSAGE_STR: &str = "FrontendMessage"; + if data.name().is_empty() && tree.name() != FRONTEND_MESSAGE_STR { + panic!("{}'s MessageHandler is missing #[message_handler_data]", tree.name()); + } else if tree.name() != FRONTEND_MESSAGE_STR { + file.write_all(format!("{}{}{} `{}`\n", prefix, branch, data.name(), data.path()).as_bytes()).unwrap(); + + for (i, field) in data.fields().iter().enumerate() { + let is_last_field = i == len - 1; + let branch = if is_last_field { "└── " } else { "├── " }; + + file.write_all(format!("{}{}{}\n", child_prefix, branch, field.0).as_bytes()).unwrap(); + } + } + } + + // Print data field if any + if let Some(data) = tree.message_handler_data_fields() { + let len = data.fields().len(); + if data.path().is_empty() { + file.write_all(format!("{}{}{}\n", prefix, "└── ", data.name()).as_bytes()).unwrap(); + } else { + file.write_all(format!("{}{}{} `{}`\n", prefix, "└── ", data.name(), data.path()).as_bytes()).unwrap(); + } + for (i, field) in data.fields().iter().enumerate() { + let is_last_field = i == len - 1; + let branch = if is_last_field { "└── " } else { "├── " }; + let field = &field.0; + file.write_all(format!("{prefix} {branch}{field}\n").as_bytes()).unwrap(); + } + } +} diff --git a/website/.build-scripts/generate-editor-structure.ts b/website/.build-scripts/generate-editor-structure.ts index 6664c084cd..36dc304d73 100644 --- a/website/.build-scripts/generate-editor-structure.ts +++ b/website/.build-scripts/generate-editor-structure.ts @@ -1,3 +1,5 @@ +// TODO: Port this script to Rust as part of `tools/editor-message-tree/src/main.rs` + /* eslint-disable no-console */ import fs from "fs"; diff --git a/website/.gitignore b/website/.gitignore index ccaacaef2c..9d5039e6f5 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -1,5 +1,6 @@ node_modules/ public/ +generated/ static/* !static/js/ content/learn/node-catalog diff --git a/website/package-lock.json b/website/package-lock.json index 7c9c4c57b7..c038887f85 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -23,7 +23,7 @@ "eslint-plugin-prettier": "^5.5.5", "prettier": "^3.8.0", "sass": "1.97.2", - "tar": "^7.5.4", + "tar": "^7.5.6", "typescript-eslint": "^8.53.1" } }, @@ -4122,9 +4122,9 @@ } }, "node_modules/tar": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.4.tgz", - "integrity": "sha512-AN04xbWGrSTDmVwlI4/GTlIIwMFk/XEv7uL8aa57zuvRy6s4hdBed+lVq2fAZ89XDa7Us3ANXcE3Tvqvja1kTA==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz", + "integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { diff --git a/website/package.json b/website/package.json index 870f278d14..88901b0877 100644 --- a/website/package.json +++ b/website/package.json @@ -12,7 +12,7 @@ "type": "module", "scripts": { "postinstall": "node .build-scripts/install.ts", - "generate-editor-structure": "node .build-scripts/generate-editor-structure.ts ../hierarchical_message_system_tree.txt ../hierarchical_message_system_tree.html", + "generate-editor-structure": "node .build-scripts/generate-editor-structure.ts generated/hierarchical_message_system_tree.txt generated/hierarchical_message_system_tree.html", "lint": "eslint . && tsc --noEmit", "lint-fix": "eslint . --fix && tsc --noEmit" }, @@ -28,7 +28,7 @@ "eslint": "^9.39.2", "prettier": "^3.8.0", "sass": "1.97.2", - "tar": "^7.5.4", + "tar": "^7.5.6", "typescript-eslint": "^8.53.1" }, "dependencies": { diff --git a/website/templates/macros/replacements.html b/website/templates/macros/replacements.html index f34f76ca82..6b5f385f58 100644 --- a/website/templates/macros/replacements.html +++ b/website/templates/macros/replacements.html @@ -40,12 +40,14 @@

{{ article.title } {% endmacro text_balancer %} {% macro hierarchical_message_system_tree() %} -{%- set content = load_data(path = "../../hierarchical_message_system_tree.html", format = "plain", required = false) -%} +{%- set content = load_data(path = "../generated/hierarchical_message_system_tree.html", format = "plain", required = false) -%} {%- set fallback = "
THIS CONTENT IS FILLED IN WHEN CI BUILDS THE WEBSITE.
 
-TO TEST IT LOCALLY, FROM THE `website` DIRECTORY, RUN:
+TO TEST IT LOCALLY, FROM THE ROOT OF THE PROJECT, RUN:
 
-cargo test --package graphite-editor --lib -- messages::message::test::generate_message_tree
+cd tools/editor-message-tree
+cargo run
+cd ../../website
 npm run generate-editor-structure
" -%} {{ content | default(value = fallback) | safe }} {% endmacro hierarchical_message_system_tree %}