Remove zed dependency from docs_preprocessor (#45130)
Closes #ISSUE Uses the existing `--dump-all-actions` arg on the Zed binary to generate an asset of all of our actions so that the `docs_preprocessor` can injest it, rather than depending on the Zed crate itself to collect all action names Release Notes: - N/A *or* Added/Fixed/Improved ... --------- Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
This commit is contained in:
3
.github/workflows/run_tests.yml
vendored
3
.github/workflows/run_tests.yml
vendored
@@ -353,6 +353,9 @@ jobs:
|
|||||||
- name: steps::download_wasi_sdk
|
- name: steps::download_wasi_sdk
|
||||||
run: ./script/download-wasi-sdk
|
run: ./script/download-wasi-sdk
|
||||||
shell: bash -euxo pipefail {0}
|
shell: bash -euxo pipefail {0}
|
||||||
|
- name: ./script/generate-action-metadata
|
||||||
|
run: ./script/generate-action-metadata
|
||||||
|
shell: bash -euxo pipefail {0}
|
||||||
- name: run_tests::check_docs::install_mdbook
|
- name: run_tests::check_docs::install_mdbook
|
||||||
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08
|
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08
|
||||||
with:
|
with:
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -36,6 +36,7 @@
|
|||||||
DerivedData/
|
DerivedData/
|
||||||
Packages
|
Packages
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
|
crates/docs_preprocessor/actions.json
|
||||||
|
|
||||||
# Don't commit any secrets to the repo.
|
# Don't commit any secrets to the repo.
|
||||||
.env
|
.env
|
||||||
|
|||||||
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -5021,8 +5021,6 @@ name = "docs_preprocessor"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"command_palette",
|
|
||||||
"gpui",
|
|
||||||
"mdbook",
|
"mdbook",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -5031,7 +5029,6 @@ dependencies = [
|
|||||||
"task",
|
"task",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
"zed",
|
|
||||||
"zlog",
|
"zlog",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ license = "GPL-3.0-or-later"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
command_palette.workspace = true
|
|
||||||
gpui.workspace = true
|
|
||||||
# We are specifically pinning this version of mdbook, as later versions introduce issues with double-nested subdirectories.
|
# We are specifically pinning this version of mdbook, as later versions introduce issues with double-nested subdirectories.
|
||||||
# Ask @maxdeviant about this before bumping.
|
# Ask @maxdeviant about this before bumping.
|
||||||
mdbook = "= 0.4.40"
|
mdbook = "= 0.4.40"
|
||||||
@@ -17,7 +15,6 @@ serde.workspace = true
|
|||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
zed.workspace = true
|
|
||||||
zlog.workspace = true
|
zlog.workspace = true
|
||||||
task.workspace = true
|
task.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
@@ -27,4 +24,4 @@ workspace = true
|
|||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "docs_preprocessor"
|
name = "docs_preprocessor"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
@@ -22,16 +22,13 @@ static KEYMAP_WINDOWS: LazyLock<KeymapFile> = LazyLock::new(|| {
|
|||||||
load_keymap("keymaps/default-windows.json").expect("Failed to load Windows keymap")
|
load_keymap("keymaps/default-windows.json").expect("Failed to load Windows keymap")
|
||||||
});
|
});
|
||||||
|
|
||||||
static ALL_ACTIONS: LazyLock<Vec<ActionDef>> = LazyLock::new(dump_all_gpui_actions);
|
static ALL_ACTIONS: LazyLock<Vec<ActionDef>> = LazyLock::new(load_all_actions);
|
||||||
|
|
||||||
const FRONT_MATTER_COMMENT: &str = "<!-- ZED_META {} -->";
|
const FRONT_MATTER_COMMENT: &str = "<!-- ZED_META {} -->";
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
zlog::init();
|
zlog::init();
|
||||||
zlog::init_output_stderr();
|
zlog::init_output_stderr();
|
||||||
// call a zed:: function so everything in `zed` crate is linked and
|
|
||||||
// all actions in the actual app are registered
|
|
||||||
zed::stdout_is_a_pty();
|
|
||||||
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||||
|
|
||||||
match args.get(0).map(String::as_str) {
|
match args.get(0).map(String::as_str) {
|
||||||
@@ -72,8 +69,8 @@ enum PreprocessorError {
|
|||||||
impl PreprocessorError {
|
impl PreprocessorError {
|
||||||
fn new_for_not_found_action(action_name: String) -> Self {
|
fn new_for_not_found_action(action_name: String) -> Self {
|
||||||
for action in &*ALL_ACTIONS {
|
for action in &*ALL_ACTIONS {
|
||||||
for alias in action.deprecated_aliases {
|
for alias in &action.deprecated_aliases {
|
||||||
if alias == &action_name {
|
if alias == action_name.as_str() {
|
||||||
return PreprocessorError::DeprecatedActionUsed {
|
return PreprocessorError::DeprecatedActionUsed {
|
||||||
used: action_name,
|
used: action_name,
|
||||||
should_be: action.name.to_string(),
|
should_be: action.name.to_string(),
|
||||||
@@ -214,7 +211,7 @@ fn template_and_validate_keybindings(book: &mut Book, errors: &mut HashSet<Prepr
|
|||||||
chapter.content = regex
|
chapter.content = regex
|
||||||
.replace_all(&chapter.content, |caps: ®ex::Captures| {
|
.replace_all(&chapter.content, |caps: ®ex::Captures| {
|
||||||
let action = caps[1].trim();
|
let action = caps[1].trim();
|
||||||
if find_action_by_name(action).is_none() {
|
if is_missing_action(action) {
|
||||||
errors.insert(PreprocessorError::new_for_not_found_action(
|
errors.insert(PreprocessorError::new_for_not_found_action(
|
||||||
action.to_string(),
|
action.to_string(),
|
||||||
));
|
));
|
||||||
@@ -244,10 +241,12 @@ fn template_and_validate_actions(book: &mut Book, errors: &mut HashSet<Preproces
|
|||||||
.replace_all(&chapter.content, |caps: ®ex::Captures| {
|
.replace_all(&chapter.content, |caps: ®ex::Captures| {
|
||||||
let name = caps[1].trim();
|
let name = caps[1].trim();
|
||||||
let Some(action) = find_action_by_name(name) else {
|
let Some(action) = find_action_by_name(name) else {
|
||||||
errors.insert(PreprocessorError::new_for_not_found_action(
|
if actions_available() {
|
||||||
name.to_string(),
|
errors.insert(PreprocessorError::new_for_not_found_action(
|
||||||
));
|
name.to_string(),
|
||||||
return String::new();
|
));
|
||||||
|
}
|
||||||
|
return format!("<code class=\"hljs\">{}</code>", name);
|
||||||
};
|
};
|
||||||
format!("<code class=\"hljs\">{}</code>", &action.human_name)
|
format!("<code class=\"hljs\">{}</code>", &action.human_name)
|
||||||
})
|
})
|
||||||
@@ -257,11 +256,19 @@ fn template_and_validate_actions(book: &mut Book, errors: &mut HashSet<Preproces
|
|||||||
|
|
||||||
fn find_action_by_name(name: &str) -> Option<&ActionDef> {
|
fn find_action_by_name(name: &str) -> Option<&ActionDef> {
|
||||||
ALL_ACTIONS
|
ALL_ACTIONS
|
||||||
.binary_search_by(|action| action.name.cmp(name))
|
.binary_search_by(|action| action.name.as_str().cmp(name))
|
||||||
.ok()
|
.ok()
|
||||||
.map(|index| &ALL_ACTIONS[index])
|
.map(|index| &ALL_ACTIONS[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn actions_available() -> bool {
|
||||||
|
!ALL_ACTIONS.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_missing_action(name: &str) -> bool {
|
||||||
|
actions_available() && find_action_by_name(name).is_none()
|
||||||
|
}
|
||||||
|
|
||||||
fn find_binding(os: &str, action: &str) -> Option<String> {
|
fn find_binding(os: &str, action: &str) -> Option<String> {
|
||||||
let keymap = match os {
|
let keymap = match os {
|
||||||
"macos" => &KEYMAP_MACOS,
|
"macos" => &KEYMAP_MACOS,
|
||||||
@@ -384,18 +391,13 @@ fn template_and_validate_json_snippets(book: &mut Book, errors: &mut HashSet<Pre
|
|||||||
let keymap = settings::KeymapFile::parse(&snippet_json_fixed)
|
let keymap = settings::KeymapFile::parse(&snippet_json_fixed)
|
||||||
.context("Failed to parse keymap JSON")?;
|
.context("Failed to parse keymap JSON")?;
|
||||||
for section in keymap.sections() {
|
for section in keymap.sections() {
|
||||||
for (keystrokes, action) in section.bindings() {
|
for (_keystrokes, action) in section.bindings() {
|
||||||
keystrokes
|
|
||||||
.split_whitespace()
|
|
||||||
.map(|source| gpui::Keystroke::parse(source))
|
|
||||||
.collect::<std::result::Result<Vec<_>, _>>()
|
|
||||||
.context("Failed to parse keystroke")?;
|
|
||||||
if let Some((action_name, _)) = settings::KeymapFile::parse_action(action)
|
if let Some((action_name, _)) = settings::KeymapFile::parse_action(action)
|
||||||
.map_err(|err| anyhow::format_err!(err))
|
.map_err(|err| anyhow::format_err!(err))
|
||||||
.context("Failed to parse action")?
|
.context("Failed to parse action")?
|
||||||
{
|
{
|
||||||
anyhow::ensure!(
|
anyhow::ensure!(
|
||||||
find_action_by_name(action_name).is_some(),
|
!is_missing_action(action_name),
|
||||||
"Action not found: {}",
|
"Action not found: {}",
|
||||||
action_name
|
action_name
|
||||||
);
|
);
|
||||||
@@ -491,27 +493,35 @@ where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
struct ActionDef {
|
struct ActionDef {
|
||||||
name: &'static str,
|
name: String,
|
||||||
human_name: String,
|
human_name: String,
|
||||||
deprecated_aliases: &'static [&'static str],
|
deprecated_aliases: Vec<String>,
|
||||||
docs: Option<&'static str>,
|
#[serde(rename = "documentation")]
|
||||||
|
docs: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_all_gpui_actions() -> Vec<ActionDef> {
|
fn load_all_actions() -> Vec<ActionDef> {
|
||||||
let mut actions = gpui::generate_list_of_all_registered_actions()
|
let asset_path = concat!(env!("CARGO_MANIFEST_DIR"), "/actions.json");
|
||||||
.map(|action| ActionDef {
|
match std::fs::read_to_string(asset_path) {
|
||||||
name: action.name,
|
Ok(content) => {
|
||||||
human_name: command_palette::humanize_action_name(action.name),
|
let mut actions: Vec<ActionDef> =
|
||||||
deprecated_aliases: action.deprecated_aliases,
|
serde_json::from_str(&content).expect("Failed to parse actions.json");
|
||||||
docs: action.documentation,
|
actions.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
})
|
actions
|
||||||
.collect::<Vec<ActionDef>>();
|
}
|
||||||
|
Err(err) => {
|
||||||
actions.sort_by_key(|a| a.name);
|
if std::env::var("CI").is_ok() {
|
||||||
|
panic!("actions.json not found at {}: {}", asset_path, err);
|
||||||
actions
|
}
|
||||||
|
eprintln!(
|
||||||
|
"Warning: actions.json not found, action validation will be skipped: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_postprocessing() -> Result<()> {
|
fn handle_postprocessing() -> Result<()> {
|
||||||
@@ -647,7 +657,7 @@ fn generate_big_table_of_actions() -> String {
|
|||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
|
|
||||||
let mut actions_sorted = actions.iter().collect::<Vec<_>>();
|
let mut actions_sorted = actions.iter().collect::<Vec<_>>();
|
||||||
actions_sorted.sort_by_key(|a| a.name);
|
actions_sorted.sort_by_key(|a| a.name.as_str());
|
||||||
|
|
||||||
// Start the definition list with custom styling for better spacing
|
// Start the definition list with custom styling for better spacing
|
||||||
output.push_str("<dl style=\"line-height: 1.8;\">\n");
|
output.push_str("<dl style=\"line-height: 1.8;\">\n");
|
||||||
@@ -664,7 +674,7 @@ fn generate_big_table_of_actions() -> String {
|
|||||||
output.push_str("<dd style=\"margin-left: 2em; margin-bottom: 1em;\">\n");
|
output.push_str("<dd style=\"margin-left: 2em; margin-bottom: 1em;\">\n");
|
||||||
|
|
||||||
// Add the description, escaping HTML if needed
|
// Add the description, escaping HTML if needed
|
||||||
if let Some(description) = action.docs {
|
if let Some(description) = action.docs.as_ref() {
|
||||||
output.push_str(
|
output.push_str(
|
||||||
&description
|
&description
|
||||||
.replace("&", "&")
|
.replace("&", "&")
|
||||||
@@ -674,7 +684,7 @@ fn generate_big_table_of_actions() -> String {
|
|||||||
output.push_str("<br>\n");
|
output.push_str("<br>\n");
|
||||||
}
|
}
|
||||||
output.push_str("Keymap Name: <code>");
|
output.push_str("Keymap Name: <code>");
|
||||||
output.push_str(action.name);
|
output.push_str(&action.name);
|
||||||
output.push_str("</code><br>\n");
|
output.push_str("</code><br>\n");
|
||||||
if !action.deprecated_aliases.is_empty() {
|
if !action.deprecated_aliases.is_empty() {
|
||||||
output.push_str("Deprecated Alias(es): ");
|
output.push_str("Deprecated Alias(es): ");
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
use gpui::{Entity, OwnedMenu, OwnedMenuItem};
|
use gpui::{Action, Entity, OwnedMenu, OwnedMenuItem, actions};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
use gpui::{Action, actions};
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@@ -14,18 +9,23 @@ use ui::{ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*};
|
|||||||
|
|
||||||
use crate::title_bar_settings::TitleBarSettings;
|
use crate::title_bar_settings::TitleBarSettings;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
actions!(
|
actions!(
|
||||||
app_menu,
|
app_menu,
|
||||||
[
|
[
|
||||||
/// Navigates to the menu item on the right.
|
/// Activates the menu on the right in the client-side application menu.
|
||||||
|
///
|
||||||
|
/// Does not apply to platform menu bars (e.g. on macOS).
|
||||||
ActivateMenuRight,
|
ActivateMenuRight,
|
||||||
/// Navigates to the menu item on the left.
|
/// Activates the menu on the left in the client-side application menu.
|
||||||
|
///
|
||||||
|
/// Does not apply to platform menu bars (e.g. on macOS).
|
||||||
ActivateMenuLeft
|
ActivateMenuLeft
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
/// Opens the named menu in the client-side application menu.
|
||||||
|
///
|
||||||
|
/// Does not apply to platform menu bars (e.g. on macOS).
|
||||||
#[derive(Clone, Deserialize, JsonSchema, PartialEq, Default, Action)]
|
#[derive(Clone, Deserialize, JsonSchema, PartialEq, Default, Action)]
|
||||||
#[action(namespace = app_menu)]
|
#[action(namespace = app_menu)]
|
||||||
pub struct OpenApplicationMenu(String);
|
pub struct OpenApplicationMenu(String);
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ tracy = ["ztracing/tracy"]
|
|||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
path = "src/zed-main.rs"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "zed"
|
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// Disable command line from opening on release mode
|
||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
mod reliability;
|
mod reliability;
|
||||||
mod zed;
|
mod zed;
|
||||||
|
|
||||||
@@ -163,9 +166,9 @@ fn fail_to_open_window(e: anyhow::Error, _cx: &mut App) {
|
|||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub static STARTUP_TIME: OnceLock<Instant> = OnceLock::new();
|
static STARTUP_TIME: OnceLock<Instant> = OnceLock::new();
|
||||||
|
|
||||||
pub fn main() {
|
fn main() {
|
||||||
STARTUP_TIME.get_or_init(|| Instant::now());
|
STARTUP_TIME.get_or_init(|| Instant::now());
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@@ -1301,7 +1304,7 @@ fn init_paths() -> HashMap<io::ErrorKind, Vec<&'static Path>> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stdout_is_a_pty() -> bool {
|
fn stdout_is_a_pty() -> bool {
|
||||||
std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_none() && io::stdout().is_terminal()
|
std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_none() && io::stdout().is_terminal()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1547,14 +1550,14 @@ fn dump_all_gpui_actions() {
|
|||||||
struct ActionDef {
|
struct ActionDef {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
human_name: String,
|
human_name: String,
|
||||||
aliases: &'static [&'static str],
|
deprecated_aliases: &'static [&'static str],
|
||||||
documentation: Option<&'static str>,
|
documentation: Option<&'static str>,
|
||||||
}
|
}
|
||||||
let mut actions = gpui::generate_list_of_all_registered_actions()
|
let mut actions = gpui::generate_list_of_all_registered_actions()
|
||||||
.map(|action| ActionDef {
|
.map(|action| ActionDef {
|
||||||
name: action.name,
|
name: action.name,
|
||||||
human_name: command_palette::humanize_action_name(action.name),
|
human_name: command_palette::humanize_action_name(action.name),
|
||||||
aliases: action.deprecated_aliases,
|
deprecated_aliases: action.deprecated_aliases,
|
||||||
documentation: action.documentation,
|
documentation: action.documentation,
|
||||||
})
|
})
|
||||||
.collect::<Vec<ActionDef>>();
|
.collect::<Vec<ActionDef>>();
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// Disable command line from opening on release mode
|
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
// separated out so that the file containing the main function can be imported by other crates,
|
|
||||||
// while having all gpui resources that are registered in main (primarily actions) initialized
|
|
||||||
zed::main();
|
|
||||||
}
|
|
||||||
@@ -4780,7 +4780,6 @@ mod tests {
|
|||||||
"activity_indicator",
|
"activity_indicator",
|
||||||
"agent",
|
"agent",
|
||||||
"agents",
|
"agents",
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
"app_menu",
|
"app_menu",
|
||||||
"assistant",
|
"assistant",
|
||||||
"assistant2",
|
"assistant2",
|
||||||
|
|||||||
10
script/generate-action-metadata
Executable file
10
script/generate-action-metadata
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
echo "Generating action metadata..."
|
||||||
|
cargo run -p zed -- --dump-all-actions > crates/docs_preprocessor/actions.json
|
||||||
|
|
||||||
|
echo "Generated crates/docs_preprocessor/actions.json with $(grep -c '"name":' crates/docs_preprocessor/actions.json) actions"
|
||||||
@@ -448,6 +448,7 @@ fn check_docs() -> NamedJob {
|
|||||||
lychee_link_check("./docs/src/**/*"), // check markdown links
|
lychee_link_check("./docs/src/**/*"), // check markdown links
|
||||||
)
|
)
|
||||||
.map(steps::install_linux_dependencies)
|
.map(steps::install_linux_dependencies)
|
||||||
|
.add_step(steps::script("./script/generate-action-metadata"))
|
||||||
.add_step(install_mdbook())
|
.add_step(install_mdbook())
|
||||||
.add_step(build_docs())
|
.add_step(build_docs())
|
||||||
.add_step(
|
.add_step(
|
||||||
|
|||||||
Reference in New Issue
Block a user