address most of the comments

pull/8675/merge^2
Matt Paras 2025-07-17 21:37:02 -07:00
parent bafe5bdb0e
commit 81e8ba82e4
12 changed files with 67 additions and 79 deletions

View File

@ -18,8 +18,8 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p
# the event registry is essentially read only but must be an rwlock so we can # the event registry is essentially read only but must be an rwlock so we can
# setup new events on intalization, hardware-lock-elision hugnly benefits this case # setup new events on intalization, hardware-lock-elision hugnly benefits this case
# as is essentially makes the lock entirely free as long as there is no writes # as is essentially makes the lock entirely free as long as there is no writes
once_cell = "1.21"
parking_lot = { workspace = true, features = ["hardware-lock-elision"] } parking_lot = { workspace = true, features = ["hardware-lock-elision"] }
once_cell = "1.21"
anyhow = "1" anyhow = "1"
log = "0.4" log = "0.4"

View File

@ -95,7 +95,6 @@ grep-searcher = "0.1.14"
# plugin support # plugin support
steel-core = { workspace = true, optional = true } steel-core = { workspace = true, optional = true }
steel-doc = { git = "https://github.com/mattwparas/steel.git", version = "0.7.0" } steel-doc = { git = "https://github.com/mattwparas/steel.git", version = "0.7.0" }
# steel-doc = { path = "/home/matt/code/scratch/steel/crates/steel-doc", version = "0.6.0" }
globset = "0.4.16" globset = "0.4.16"

View File

@ -15,9 +15,6 @@ pub use lsp::*;
pub use engine::ScriptingEngine; pub use engine::ScriptingEngine;
#[cfg(feature = "steel")]
pub use engine::steel::{helix_module_file, steel_init_file};
use tui::{ use tui::{
text::{Span, Spans}, text::{Span, Spans},
widgets::Cell, widgets::Cell,
@ -252,7 +249,6 @@ impl MappableCommand {
pub fn execute(&self, cx: &mut Context) { pub fn execute(&self, cx: &mut Context) {
match &self { match &self {
Self::Typable { name, args, doc: _ } => { Self::Typable { name, args, doc: _ } => {
// TODO: Swap the order to allow overriding the existing commands?
if let Some(command) = typed::TYPABLE_COMMAND_MAP.get(name.as_str()) { if let Some(command) = typed::TYPABLE_COMMAND_MAP.get(name.as_str()) {
let mut cx = compositor::Context { let mut cx = compositor::Context {
editor: cx.editor, editor: cx.editor,
@ -265,7 +261,6 @@ impl MappableCommand {
cx.editor.set_error(format!("{}", e)); cx.editor.set_error(format!("{}", e));
} }
} else { } else {
// TODO: Update this
let args = args.split_whitespace().map(Cow::from).collect(); let args = args.split_whitespace().map(Cow::from).collect();
if !ScriptingEngine::call_function_by_name(cx, name, args) { if !ScriptingEngine::call_function_by_name(cx, name, args) {
cx.editor.set_error(format!("no such command: '{name}'")); cx.editor.set_error(format!("no such command: '{name}'"));
@ -3162,11 +3157,9 @@ fn file_explorer_in_current_directory(cx: &mut Context) {
fn buffer_picker(cx: &mut Context) { fn buffer_picker(cx: &mut Context) {
let current = view!(cx.editor).doc; let current = view!(cx.editor).doc;
#[allow(unused)]
struct BufferMeta { struct BufferMeta {
id: DocumentId, id: DocumentId,
path: Option<PathBuf>, path: Option<PathBuf>,
name: Option<String>,
is_modified: bool, is_modified: bool,
is_current: bool, is_current: bool,
focused_at: std::time::Instant, focused_at: std::time::Instant,
@ -3175,7 +3168,6 @@ fn buffer_picker(cx: &mut Context) {
let new_meta = |doc: &Document| BufferMeta { let new_meta = |doc: &Document| BufferMeta {
id: doc.id(), id: doc.id(),
path: doc.path().cloned(), path: doc.path().cloned(),
name: doc.name.clone(),
is_modified: doc.is_modified(), is_modified: doc.is_modified(),
is_current: doc.id() == current, is_current: doc.id() == current,
focused_at: doc.focused_at, focused_at: doc.focused_at,
@ -4171,18 +4163,6 @@ pub mod insert {
helix_event::dispatch(PostInsertChar { c, cx }); helix_event::dispatch(PostInsertChar { c, cx });
} }
pub fn insert_string(cx: &mut Context, string: String) {
let (view, doc) = current!(cx.editor);
let indent = Tendril::from(string);
let transaction = Transaction::insert(
doc.text(),
&doc.selection(view.id).clone().cursors(doc.text().slice(..)),
indent,
);
doc.apply(&transaction, view.id);
}
pub fn smart_tab(cx: &mut Context) { pub fn smart_tab(cx: &mut Context) {
let (view, doc) = current_ref!(cx.editor); let (view, doc) = current_ref!(cx.editor);
let view_id = view.id; let view_id = view.id;

View File

@ -5,13 +5,15 @@ use helix_core::{
diagnostic::Severity, diagnostic::Severity,
extensions::steel_implementations::{rope_module, SteelRopeSlice}, extensions::steel_implementations::{rope_module, SteelRopeSlice},
find_workspace, graphemes, find_workspace, graphemes,
syntax::config::{ syntax::{
default_timeout, AutoPairConfig, LanguageConfiguration, LanguageServerConfiguration, self,
SoftWrap, config::{
default_timeout, AutoPairConfig, LanguageConfiguration, LanguageServerConfiguration,
SoftWrap,
},
}, },
syntax::{self},
text_annotations::InlineAnnotation, text_annotations::InlineAnnotation,
Range, Selection, Tendril, Range, Selection, Tendril, Transaction,
}; };
use helix_event::register_hook; use helix_event::register_hook;
use helix_view::{ use helix_view::{
@ -71,7 +73,7 @@ use super::{
components::{self, helix_component_module}, components::{self, helix_component_module},
Context, MappableCommand, TYPABLE_COMMAND_LIST, Context, MappableCommand, TYPABLE_COMMAND_LIST,
}; };
use insert::{insert_char, insert_string}; use insert::insert_char;
pub static INTERRUPT_HANDLER: OnceCell<InterruptHandler> = OnceCell::new(); pub static INTERRUPT_HANDLER: OnceCell<InterruptHandler> = OnceCell::new();
@ -1586,9 +1588,8 @@ fn theme_from_json_string(name: String, value: SteelVal) -> Result<SteelTheme, a
// Mutate the theme? // Mutate the theme?
fn add_theme(cx: &mut Context, theme: SteelTheme) { fn add_theme(cx: &mut Context, theme: SteelTheme) {
cx.editor Arc::make_mut(&mut cx.editor.theme_loader)
.user_defined_themes .add_dynamic_theme(theme.0.name().to_owned(), theme.0);
.insert(theme.0.name().to_owned(), theme.0);
} }
fn get_style(theme: &SteelTheme, name: SteelString) -> helix_view::theme::Style { fn get_style(theme: &SteelTheme, name: SteelString) -> helix_view::theme::Style {
@ -1726,7 +1727,10 @@ Get the `Rect` associated with the currently focused buffer.
); );
register_0!( register_0!(
"selected-register!", "selected-register!",
|cx: &mut Context| cx.editor.selected_register.unwrap_or(cx.editor.config().default_yank_register), |cx: &mut Context| cx
.editor
.selected_register
.unwrap_or(cx.editor.config().default_yank_register),
r#"Get currently selected register"# r#"Get currently selected register"#
); );
@ -5312,3 +5316,15 @@ pub fn remove_inlay_hint(cx: &mut Context, char_index: usize, _completion: Steel
doc.set_inlay_hints(view_id, new_inlay_hints); doc.set_inlay_hints(view_id, new_inlay_hints);
true true
} }
pub fn insert_string(cx: &mut Context, string: SteelString) {
let (view, doc) = current!(cx.editor);
let indent = Tendril::from(string.as_str());
let transaction = Transaction::insert(
doc.text(),
&doc.selection(view.id).clone().cursors(doc.text().slice(..)),
indent,
);
doc.apply(&transaction, view.id);
}

View File

@ -1000,20 +1000,7 @@ fn theme(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow
// Ensures that a preview theme gets cleaned up if the user backspaces until the prompt is empty. // Ensures that a preview theme gets cleaned up if the user backspaces until the prompt is empty.
cx.editor.unset_theme_preview(); cx.editor.unset_theme_preview();
} else if let Some(theme_name) = args.first() { } else if let Some(theme_name) = args.first() {
// if let Ok(theme) = cx.editor.theme_loader.load(theme_name) { if let Ok(theme) = cx.editor.theme_loader.load(theme_name) {
// if !(true_color || theme.is_16_color()) {
// bail!("Unsupported theme: theme requires true color support");
// }
// cx.editor.set_theme_preview(theme);
// };
if let Ok(theme) = cx.editor.theme_loader.load(theme_name).or_else(|_| {
cx.editor
.user_defined_themes
.get(theme_name)
.ok_or_else(|| anyhow::anyhow!("Could not load theme"))
.cloned()
}) {
if !(true_color || theme.is_16_color()) { if !(true_color || theme.is_16_color()) {
bail!("Unsupported theme: theme requires true color support"); bail!("Unsupported theme: theme requires true color support");
} }
@ -1023,19 +1010,12 @@ fn theme(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow
} }
PromptEvent::Validate => { PromptEvent::Validate => {
if let Some(theme_name) = args.first() { if let Some(theme_name) = args.first() {
let theme = cx.editor.theme_loader.load(theme_name).or_else(|_| { let theme = cx
cx.editor .editor
.user_defined_themes .theme_loader
.get(theme_name) .load(theme_name)
.ok_or_else(|| anyhow::anyhow!("Could not load theme")) .map_err(|err| anyhow::anyhow!("could not load theme: {}", err))?;
.cloned()
})?;
// let theme = cx
// .editor
// .theme_loader
// .load(theme_name)
// .map_err(|err| anyhow::anyhow!("Could not load theme: {}", err))?;
if !(true_color || theme.is_16_color()) { if !(true_color || theme.is_16_color()) {
bail!("Unsupported theme: theme requires true color support"); bail!("Unsupported theme: theme requires true color support");
} }

View File

@ -3,9 +3,17 @@ mod steel_implementations {
use crate::{ use crate::{
compositor::Component, compositor::Component,
ui::{Popup, Text}, ui::{overlay::Overlay, Popup, Prompt, Text},
}; };
impl steel::rvals::Custom for Text {} impl steel::rvals::Custom for Text {}
impl<T: steel::rvals::IntoSteelVal + Component> steel::rvals::Custom for Popup<T> {} impl<T: steel::rvals::IntoSteelVal + Component> steel::rvals::Custom for Popup<T> {}
// TODO: For this to be sound, all of the various functions
// have to now be marked as send + sync + 'static. Annoying,
// and something I'll look into with steel.
unsafe impl<T> Send for Overlay<T> {}
unsafe impl<T> Sync for Overlay<T> {}
unsafe impl Send for Prompt {}
unsafe impl Sync for Prompt {}
} }

View File

@ -412,7 +412,7 @@ pub mod completers {
names.push("base16_default".into()); names.push("base16_default".into());
// Include any user defined themes as well // Include any user defined themes as well
names.extend(editor.user_defined_themes.keys().map(|x| x.into())); names.extend(editor.theme_loader.dynamic_themes().map(|x| x.into()));
names.sort(); names.sort();
names.dedup(); names.dedup();

View File

@ -15,12 +15,6 @@ pub struct Overlay<T> {
pub calc_child_size: Box<dyn Fn(Rect) -> Rect>, pub calc_child_size: Box<dyn Fn(Rect) -> Rect>,
} }
// TODO: For this to be sound, all of the various functions
// have to now be marked as send + sync + 'static. Annoying,
// and something I'll look into with steel.
unsafe impl<T> Send for Overlay<T> {}
unsafe impl<T> Sync for Overlay<T> {}
/// Surrounds the component with a margin of 5% on each side, and an additional 2 rows at the bottom /// Surrounds the component with a margin of 5% on each side, and an additional 2 rows at the bottom
pub fn overlaid<T>(content: T) -> Overlay<T> { pub fn overlaid<T>(content: T) -> Overlay<T> {
Overlay { Overlay {

View File

@ -49,12 +49,6 @@ pub struct Prompt {
language: Option<(&'static str, Arc<ArcSwap<syntax::Loader>>)>, language: Option<(&'static str, Arc<ArcSwap<syntax::Loader>>)>,
} }
// TODO: For this to be sound, all of the various functions
// have to now be marked as send + sync + 'static. Annoying,
// and something I'll look into with steel.
unsafe impl Send for Prompt {}
unsafe impl Sync for Prompt {}
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum PromptEvent { pub enum PromptEvent {
/// The prompt input has been updated. /// The prompt input has been updated.

View File

@ -1142,7 +1142,6 @@ pub struct Editor {
pub cursor_cache: CursorCache, pub cursor_cache: CursorCache,
pub editor_clipping: ClippingConfiguration, pub editor_clipping: ClippingConfiguration,
pub user_defined_themes: HashMap<String, Theme>,
} }
#[derive(Default)] #[derive(Default)]
@ -1274,7 +1273,6 @@ impl Editor {
mouse_down_range: None, mouse_down_range: None,
cursor_cache: CursorCache::default(), cursor_cache: CursorCache::default(),
editor_clipping: ClippingConfiguration::default(), editor_clipping: ClippingConfiguration::default(),
user_defined_themes: Default::default(),
} }
} }

View File

@ -39,6 +39,9 @@ pub static BASE16_DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| Theme {
pub struct Loader { pub struct Loader {
/// Theme directories to search from highest to lowest priority /// Theme directories to search from highest to lowest priority
theme_dirs: Vec<PathBuf>, theme_dirs: Vec<PathBuf>,
/// Themes which are dynamically created at runtime
dynamic_themes: HashMap<String, Theme>,
} }
impl Loader { impl Loader {
/// Creates a new loader that can load themes from multiple directories. /// Creates a new loader that can load themes from multiple directories.
@ -48,18 +51,34 @@ impl Loader {
pub fn new(dirs: &[PathBuf]) -> Self { pub fn new(dirs: &[PathBuf]) -> Self {
Self { Self {
theme_dirs: dirs.iter().map(|p| p.join("themes")).collect(), theme_dirs: dirs.iter().map(|p| p.join("themes")).collect(),
dynamic_themes: HashMap::new(),
} }
} }
pub fn dynamic_themes(&self) -> impl Iterator<Item = &String> {
self.dynamic_themes.keys()
}
/// Loads a theme searching directories in priority order. /// Loads a theme searching directories in priority order.
pub fn load(&self, name: &str) -> Result<Theme> { pub fn load(&self, name: &str) -> Result<Theme> {
let (theme, warnings) = self.load_with_warnings(name)?; match self.load_with_warnings(name) {
Ok((theme, warnings)) => {
for warning in warnings {
warn!("Theme '{}': {}", name, warning);
}
for warning in warnings { Ok(theme)
warn!("Theme '{}': {}", name, warning); }
Err(_) => self
.dynamic_themes
.get(name)
.ok_or_else(|| anyhow::anyhow!("Could not load theme: {}", name))
.cloned(),
} }
}
Ok(theme) pub fn add_dynamic_theme(&mut self, name: String, theme: Theme) {
self.dynamic_themes.insert(name, theme);
} }
/// Loads a theme searching directories in priority order, returning any warnings /// Loads a theme searching directories in priority order, returning any warnings

View File

@ -145,7 +145,7 @@ pub mod tasks {
Usage: Run with `cargo xtask <task>`, eg. `cargo xtask docgen`. Usage: Run with `cargo xtask <task>`, eg. `cargo xtask docgen`.
Tasks: Tasks:
steel: Install steel steel Install steel
docgen Generate files to be included in the mdbook output. docgen Generate files to be included in the mdbook output.
query-check [languages] Check that tree-sitter queries are valid for the given query-check [languages] Check that tree-sitter queries are valid for the given
languages, or all languages if none are specified. languages, or all languages if none are specified.