mirror of https://github.com/helix-editor/helix
address most of the comments
parent
bafe5bdb0e
commit
81e8ba82e4
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue