Compare commits
6 Commits
thread-edi
...
actions_js
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73f7061b1d | ||
|
|
332660d888 | ||
|
|
a85915486b | ||
|
|
1b73741d6d | ||
|
|
b595445e6a | ||
|
|
33e4300647 |
19
Cargo.lock
generated
19
Cargo.lock
generated
@@ -2,6 +2,20 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actions"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"command_palette",
|
||||||
|
"gpui",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"util",
|
||||||
|
"workspace-hack",
|
||||||
|
"zed",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "activity_indicator"
|
name = "activity_indicator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -4596,18 +4610,17 @@ dependencies = [
|
|||||||
name = "docs_preprocessor"
|
name = "docs_preprocessor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"actions",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"command_palette",
|
|
||||||
"gpui",
|
|
||||||
"mdbook",
|
"mdbook",
|
||||||
"regex",
|
"regex",
|
||||||
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
"util",
|
"util",
|
||||||
"workspace-hack",
|
"workspace-hack",
|
||||||
"zed",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
|
"crates/actions",
|
||||||
"crates/activity_indicator",
|
"crates/activity_indicator",
|
||||||
"crates/agent",
|
"crates/agent",
|
||||||
"crates/agent_settings",
|
"crates/agent_settings",
|
||||||
@@ -200,7 +201,7 @@ members = [
|
|||||||
"tooling/workspace-hack",
|
"tooling/workspace-hack",
|
||||||
"tooling/xtask",
|
"tooling/xtask",
|
||||||
]
|
]
|
||||||
default-members = ["crates/zed"]
|
default-members = ["crates/zed", "crates/actions"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
publish = false
|
publish = false
|
||||||
@@ -212,6 +213,7 @@ edition = "2024"
|
|||||||
# Workspace member crates
|
# Workspace member crates
|
||||||
#
|
#
|
||||||
|
|
||||||
|
actions = { path = "crates/actions" }
|
||||||
activity_indicator = { path = "crates/activity_indicator" }
|
activity_indicator = { path = "crates/activity_indicator" }
|
||||||
agent = { path = "crates/agent" }
|
agent = { path = "crates/agent" }
|
||||||
agent_settings = { path = "crates/agent_settings" }
|
agent_settings = { path = "crates/agent_settings" }
|
||||||
|
|||||||
3774
assets/actions/actions.json
Normal file
3774
assets/actions/actions.json
Normal file
File diff suppressed because it is too large
Load Diff
28
crates/actions/Cargo.toml
Normal file
28
crates/actions/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "actions"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
|
command_palette.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
workspace-hack.workspace = true
|
||||||
|
zed.workspace = true
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
|
command_palette.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
zed.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
59
crates/actions/build.rs
Normal file
59
crates/actions/build.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct ActionDef {
|
||||||
|
name: String,
|
||||||
|
human_name: String,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
deprecated_aliases: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
// 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 actions = dump_all_gpui_actions();
|
||||||
|
|
||||||
|
let out_dir = env::var("CARGO_MANIFEST_DIR")?;
|
||||||
|
let assets_path = Path::new(&out_dir)
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join("assets/actions");
|
||||||
|
|
||||||
|
// Create the actions directory if it doesn't exist
|
||||||
|
fs::create_dir_all(&assets_path)?;
|
||||||
|
|
||||||
|
let json_path = assets_path.join("actions.json");
|
||||||
|
let json_content = serde_json::to_string_pretty(&actions)?;
|
||||||
|
fs::write(&json_path, json_content)?;
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
println!("cargo:rerun-if-changed=../../assets/actions/actions.json");
|
||||||
|
|
||||||
|
// This sucks. We regenerate actions.json on every cargo invocation
|
||||||
|
println!("cargo:rerun-if-changed=*");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_all_gpui_actions() -> Vec<ActionDef> {
|
||||||
|
let mut actions = gpui::generate_list_of_all_registered_actions()
|
||||||
|
.into_iter()
|
||||||
|
.map(|action| ActionDef {
|
||||||
|
name: action.name.to_string(),
|
||||||
|
human_name: command_palette::humanize_action_name(action.name),
|
||||||
|
deprecated_aliases: action.aliases.iter().map(|s| s.to_string()).collect(),
|
||||||
|
})
|
||||||
|
.collect::<Vec<ActionDef>>();
|
||||||
|
|
||||||
|
actions.sort_by_key(|a| a.name.clone());
|
||||||
|
|
||||||
|
actions
|
||||||
|
}
|
||||||
18
crates/actions/src/lib.rs
Normal file
18
crates/actions/src/lib.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//! Actions crate that generates actions.json at build time.
|
||||||
|
//!
|
||||||
|
//! This crate uses build.rs to automatically regenerate the actions.json file
|
||||||
|
//! whenever the crate is built, eliminating the need for manual updates.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Represents an action definition that can be serialized to/from JSON.
|
||||||
|
/// This struct is shared between the build script that generates actions.json
|
||||||
|
/// and other crates that need to read action definitions.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ActionDef {
|
||||||
|
pub name: String,
|
||||||
|
pub human_name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub deprecated_aliases: Vec<String>,
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ publish.workspace = true
|
|||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
actions.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
mdbook = "0.4.40"
|
mdbook = "0.4.40"
|
||||||
@@ -13,11 +14,9 @@ serde.workspace = true
|
|||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
|
rust-embed.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
workspace-hack.workspace = true
|
workspace-hack.workspace = true
|
||||||
zed.workspace = true
|
|
||||||
gpui.workspace = true
|
|
||||||
command_palette.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
|
use actions::ActionDef;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Arg, ArgMatches, Command};
|
use clap::{Arg, ArgMatches, Command};
|
||||||
use mdbook::BookItem;
|
use mdbook::BookItem;
|
||||||
use mdbook::book::{Book, Chapter};
|
use mdbook::book::{Book, Chapter};
|
||||||
use mdbook::preprocess::CmdPreprocessor;
|
use mdbook::preprocess::CmdPreprocessor;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
use settings::KeymapFile;
|
use settings::KeymapFile;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "../../assets"]
|
||||||
|
#[include = "actions/*"]
|
||||||
|
struct Assets;
|
||||||
|
|
||||||
static KEYMAP_MACOS: LazyLock<KeymapFile> = LazyLock::new(|| {
|
static KEYMAP_MACOS: LazyLock<KeymapFile> = LazyLock::new(|| {
|
||||||
load_keymap("keymaps/default-macos.json").expect("Failed to load MacOS keymap")
|
load_keymap("keymaps/default-macos.json").expect("Failed to load MacOS keymap")
|
||||||
});
|
});
|
||||||
@@ -18,7 +25,7 @@ static KEYMAP_LINUX: LazyLock<KeymapFile> = LazyLock::new(|| {
|
|||||||
load_keymap("keymaps/default-linux.json").expect("Failed to load Linux keymap")
|
load_keymap("keymaps/default-linux.json").expect("Failed to load Linux keymap")
|
||||||
});
|
});
|
||||||
|
|
||||||
static ALL_ACTIONS: LazyLock<Vec<ActionDef>> = LazyLock::new(dump_all_gpui_actions);
|
static ALL_ACTIONS: LazyLock<Vec<ActionDef>> = LazyLock::new(load_all_actions);
|
||||||
|
|
||||||
pub fn make_app() -> Command {
|
pub fn make_app() -> Command {
|
||||||
Command::new("zed-docs-preprocessor")
|
Command::new("zed-docs-preprocessor")
|
||||||
@@ -32,9 +39,6 @@ pub fn make_app() -> Command {
|
|||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let matches = make_app().get_matches();
|
let matches = make_app().get_matches();
|
||||||
// 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();
|
|
||||||
|
|
||||||
if let Some(sub_args) = matches.subcommand_matches("supports") {
|
if let Some(sub_args) = matches.subcommand_matches("supports") {
|
||||||
handle_supports(sub_args);
|
handle_supports(sub_args);
|
||||||
@@ -54,7 +58,7 @@ enum Error {
|
|||||||
impl Error {
|
impl Error {
|
||||||
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 {
|
||||||
return Error::DeprecatedActionUsed {
|
return Error::DeprecatedActionUsed {
|
||||||
used: action_name.clone(),
|
used: action_name.clone(),
|
||||||
@@ -163,7 +167,7 @@ fn template_and_validate_actions(book: &mut Book, errors: &mut HashSet<Error>) {
|
|||||||
|
|
||||||
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])
|
||||||
}
|
}
|
||||||
@@ -234,24 +238,80 @@ where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
fn load_all_actions() -> Vec<ActionDef> {
|
||||||
struct ActionDef {
|
let content = util::asset_str::<Assets>("actions/actions.json");
|
||||||
name: &'static str,
|
let mut actions: Vec<ActionDef> =
|
||||||
human_name: String,
|
serde_json::from_str(content.as_ref()).expect("Failed to parse actions.json");
|
||||||
deprecated_aliases: &'static [&'static str],
|
|
||||||
|
actions.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
|
actions
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_all_gpui_actions() -> Vec<ActionDef> {
|
#[cfg(test)]
|
||||||
let mut actions = gpui::generate_list_of_all_registered_actions()
|
mod tests {
|
||||||
.into_iter()
|
use super::*;
|
||||||
.map(|action| ActionDef {
|
|
||||||
name: action.name,
|
|
||||||
human_name: command_palette::humanize_action_name(action.name),
|
|
||||||
deprecated_aliases: action.aliases,
|
|
||||||
})
|
|
||||||
.collect::<Vec<ActionDef>>();
|
|
||||||
|
|
||||||
actions.sort_by_key(|a| a.name);
|
#[test]
|
||||||
|
fn test_load_actions() {
|
||||||
|
let actions = load_all_actions();
|
||||||
|
assert!(!actions.is_empty(), "Actions should not be empty");
|
||||||
|
|
||||||
return actions;
|
// Check that actions are sorted
|
||||||
|
for i in 1..actions.len() {
|
||||||
|
assert!(
|
||||||
|
actions[i - 1].name <= actions[i].name,
|
||||||
|
"Actions should be sorted by name"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we can find a common action
|
||||||
|
assert!(
|
||||||
|
find_action_by_name("editor::Cut").is_some(),
|
||||||
|
"Should be able to find editor::Cut action"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_action_by_name() {
|
||||||
|
// Test finding an action that exists
|
||||||
|
let action = find_action_by_name("editor::Cut");
|
||||||
|
assert!(action.is_some());
|
||||||
|
assert_eq!(action.unwrap().name, "editor::Cut");
|
||||||
|
|
||||||
|
// Test finding an action that doesn't exist
|
||||||
|
let action = find_action_by_name("nonexistent::Action");
|
||||||
|
assert!(action.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_name_for_action() {
|
||||||
|
// Test simple action name
|
||||||
|
assert_eq!(name_for_action("editor::Cut".to_string()), "editor::Cut");
|
||||||
|
|
||||||
|
// Test action with parameters
|
||||||
|
assert_eq!(
|
||||||
|
name_for_action(
|
||||||
|
"\"editor::ToggleComments\", {\"advance_downwards\":false}".to_string()
|
||||||
|
),
|
||||||
|
"editor::ToggleComments"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test action with quotes
|
||||||
|
assert_eq!(
|
||||||
|
name_for_action("\"workspace::NewFile\"".to_string()),
|
||||||
|
"workspace::NewFile"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_creation() {
|
||||||
|
// Test creating error for non-existent action
|
||||||
|
let error = Error::new_for_not_found_action("nonexistent::Action".to_string());
|
||||||
|
match error {
|
||||||
|
Error::ActionNotFound { action_name } => {
|
||||||
|
assert_eq!(action_name, "nonexistent::Action");
|
||||||
|
}
|
||||||
|
_ => panic!("Expected ActionNotFound error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,9 @@ windows.workspace = true
|
|||||||
[target.'cfg(target_os = "windows")'.build-dependencies]
|
[target.'cfg(target_os = "windows")'.build-dependencies]
|
||||||
winresource = "0.1"
|
winresource = "0.1"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
zed_actions.workspace = true
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
|
||||||
ashpd.workspace = true
|
ashpd.workspace = true
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user