Add live preview to theme picker (#1798)
* Add theme picker with live preview * Add live theme preview to :theme command * cargo fmt * Fix clippy warnings * Remove picker variant * Remove unused import * Cleanup * Change current_theme to last_theme * Fix accidental comment flash deletion * Typo * Remove theme cache * Add some comments * Refactor some theme handling TIL flatmap on Option is called and_then * Remove unnecessary renames * Constrain last_theme theme preview lifecycle * Switch to bitflag implementation * Better handling of last_theme * Sort theme names * Better memory juggling * Missed a branch * Remove name from theme, switch bitand to & * cargo fmt * Update helix-view/src/editor.rs * Switch boolean to enum * Remove bitflag impl * cargo fmt * Remove un-needed type arg * cargo fmt
This commit is contained in:
parent
85411bed83
commit
b26e7e2e8f
6 changed files with 421 additions and 117 deletions
|
@ -108,13 +108,7 @@ impl Application {
|
|||
.ok()
|
||||
.filter(|theme| (true_color || theme.is_16_color()))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
if true_color {
|
||||
theme_loader.default()
|
||||
} else {
|
||||
theme_loader.base16_default()
|
||||
}
|
||||
});
|
||||
.unwrap_or_else(|| theme_loader.default_theme(true_color));
|
||||
|
||||
let syn_loader_conf = user_syntax_loader().unwrap_or_else(|err| {
|
||||
eprintln!("Bad language config: {}", err);
|
||||
|
@ -373,13 +367,7 @@ impl Application {
|
|||
})
|
||||
.ok()
|
||||
.filter(|theme| (true_color || theme.is_16_color()))
|
||||
.unwrap_or_else(|| {
|
||||
if true_color {
|
||||
self.theme_loader.default()
|
||||
} else {
|
||||
self.theme_loader.base16_default()
|
||||
}
|
||||
}),
|
||||
.unwrap_or_else(|| self.theme_loader.default_theme(true_color)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@ pub struct TypableCommand {
|
|||
pub completer: Option<Completer>,
|
||||
}
|
||||
|
||||
fn quit(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
fn quit(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ensure!(args.is_empty(), ":quit takes no arguments");
|
||||
|
||||
// last view and we have unsaved changes
|
||||
|
@ -35,8 +35,12 @@ fn quit(
|
|||
fn force_quit(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ensure!(args.is_empty(), ":quit! takes no arguments");
|
||||
|
||||
cx.editor.close(view!(cx.editor).id);
|
||||
|
@ -44,11 +48,11 @@ fn force_quit(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn open(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
fn open(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ensure!(!args.is_empty(), "wrong argument count");
|
||||
for arg in args {
|
||||
let (path, pos) = args::parse_file(arg);
|
||||
|
@ -114,8 +118,12 @@ fn buffer_gather_paths_impl(editor: &mut Editor, args: &[Cow<str>]) -> Vec<Docum
|
|||
fn buffer_close(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let document_ids = buffer_gather_paths_impl(cx.editor, args);
|
||||
buffer_close_by_ids_impl(cx.editor, &document_ids, false)
|
||||
}
|
||||
|
@ -123,8 +131,12 @@ fn buffer_close(
|
|||
fn force_buffer_close(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let document_ids = buffer_gather_paths_impl(cx.editor, args);
|
||||
buffer_close_by_ids_impl(cx.editor, &document_ids, true)
|
||||
}
|
||||
|
@ -141,8 +153,12 @@ fn buffer_gather_others_impl(editor: &mut Editor) -> Vec<DocumentId> {
|
|||
fn buffer_close_others(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let document_ids = buffer_gather_others_impl(cx.editor);
|
||||
buffer_close_by_ids_impl(cx.editor, &document_ids, false)
|
||||
}
|
||||
|
@ -150,8 +166,12 @@ fn buffer_close_others(
|
|||
fn force_buffer_close_others(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let document_ids = buffer_gather_others_impl(cx.editor);
|
||||
buffer_close_by_ids_impl(cx.editor, &document_ids, true)
|
||||
}
|
||||
|
@ -163,8 +183,12 @@ fn buffer_gather_all_impl(editor: &mut Editor) -> Vec<DocumentId> {
|
|||
fn buffer_close_all(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let document_ids = buffer_gather_all_impl(cx.editor);
|
||||
buffer_close_by_ids_impl(cx.editor, &document_ids, false)
|
||||
}
|
||||
|
@ -172,8 +196,12 @@ fn buffer_close_all(
|
|||
fn force_buffer_close_all(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let document_ids = buffer_gather_all_impl(cx.editor);
|
||||
buffer_close_by_ids_impl(cx.editor, &document_ids, true)
|
||||
}
|
||||
|
@ -181,8 +209,12 @@ fn force_buffer_close_all(
|
|||
fn buffer_next(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
goto_buffer(cx.editor, Direction::Forward);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -190,8 +222,12 @@ fn buffer_next(
|
|||
fn buffer_previous(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
goto_buffer(cx.editor, Direction::Backward);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -242,24 +278,36 @@ fn write_impl(
|
|||
fn write(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write_impl(cx, args.first(), false)
|
||||
}
|
||||
|
||||
fn force_write(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write_impl(cx, args.first(), true)
|
||||
}
|
||||
|
||||
fn new_file(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
cx.editor.new_file(Action::Replace);
|
||||
|
||||
Ok(())
|
||||
|
@ -268,8 +316,12 @@ fn new_file(
|
|||
fn format(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let doc = doc!(cx.editor);
|
||||
if let Some(format) = doc.format() {
|
||||
let callback =
|
||||
|
@ -282,8 +334,12 @@ fn format(
|
|||
fn set_indent_style(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
use IndentStyle::*;
|
||||
|
||||
// If no argument, report current indent style.
|
||||
|
@ -321,8 +377,12 @@ fn set_indent_style(
|
|||
fn set_line_ending(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
use LineEnding::*;
|
||||
|
||||
// If no argument, report current line ending setting.
|
||||
|
@ -391,8 +451,12 @@ fn set_line_ending(
|
|||
fn earlier(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
|
||||
|
||||
let (view, doc) = current!(cx.editor);
|
||||
|
@ -407,8 +471,12 @@ fn earlier(
|
|||
fn later(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
|
||||
let (view, doc) = current!(cx.editor);
|
||||
let success = doc.later(view.id, uk);
|
||||
|
@ -424,6 +492,10 @@ fn write_quit(
|
|||
args: &[Cow<str>],
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write_impl(cx, args.first(), false)?;
|
||||
helix_lsp::block_on(cx.jobs.finish())?;
|
||||
quit(cx, &[], event)
|
||||
|
@ -434,6 +506,10 @@ fn force_write_quit(
|
|||
args: &[Cow<str>],
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write_impl(cx, args.first(), true)?;
|
||||
force_quit(cx, &[], event)
|
||||
}
|
||||
|
@ -463,10 +539,14 @@ pub(super) fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()>
|
|||
fn write_all_impl(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
quit: bool,
|
||||
force: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut errors = String::new();
|
||||
let auto_format = cx.editor.config().auto_format;
|
||||
let jobs = &mut cx.jobs;
|
||||
|
@ -520,6 +600,10 @@ fn write_all(
|
|||
args: &[Cow<str>],
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write_all_impl(cx, args, event, false, false)
|
||||
}
|
||||
|
||||
|
@ -528,6 +612,10 @@ fn write_all_quit(
|
|||
args: &[Cow<str>],
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write_all_impl(cx, args, event, true, false)
|
||||
}
|
||||
|
||||
|
@ -536,6 +624,10 @@ fn force_write_all_quit(
|
|||
args: &[Cow<str>],
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write_all_impl(cx, args, event, true, true)
|
||||
}
|
||||
|
||||
|
@ -556,24 +648,36 @@ fn quit_all_impl(editor: &mut Editor, force: bool) -> anyhow::Result<()> {
|
|||
fn quit_all(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
quit_all_impl(cx.editor, false)
|
||||
}
|
||||
|
||||
fn force_quit_all(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
quit_all_impl(cx.editor, true)
|
||||
}
|
||||
|
||||
fn cquit(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let exit_code = args
|
||||
.first()
|
||||
.and_then(|code| code.parse::<i32>().ok())
|
||||
|
@ -586,8 +690,12 @@ fn cquit(
|
|||
fn force_cquit(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let exit_code = args
|
||||
.first()
|
||||
.and_then(|code| code.parse::<i32>().ok())
|
||||
|
@ -600,35 +708,61 @@ fn force_cquit(
|
|||
fn theme(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
let theme = args.first().context("Theme not provided")?;
|
||||
let theme = cx
|
||||
.editor
|
||||
.theme_loader
|
||||
.load(theme)
|
||||
.with_context(|| format!("Failed setting theme {}", theme))?;
|
||||
let true_color = cx.editor.config().true_color || crate::true_color();
|
||||
if !(true_color || theme.is_16_color()) {
|
||||
bail!("Unsupported theme: theme requires true color support");
|
||||
}
|
||||
cx.editor.set_theme(theme);
|
||||
let true_color = cx.editor.config.load().true_color || crate::true_color();
|
||||
match event {
|
||||
PromptEvent::Abort => {
|
||||
cx.editor.unset_theme_preview();
|
||||
}
|
||||
PromptEvent::Update => {
|
||||
if let Some(theme_name) = args.first() {
|
||||
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);
|
||||
};
|
||||
};
|
||||
}
|
||||
PromptEvent::Validate => {
|
||||
let theme_name = args.first().with_context(|| "Theme name not provided")?;
|
||||
let theme = cx
|
||||
.editor
|
||||
.theme_loader
|
||||
.load(theme_name)
|
||||
.with_context(|| "Theme does not exist")?;
|
||||
if !(true_color || theme.is_16_color()) {
|
||||
bail!("Unsupported theme: theme requires true color support");
|
||||
}
|
||||
cx.editor.set_theme(theme);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn yank_main_selection_to_clipboard(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard)
|
||||
}
|
||||
|
||||
fn yank_joined_to_clipboard(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let doc = doc!(cx.editor);
|
||||
let default_sep = Cow::Borrowed(doc.line_ending.as_str());
|
||||
let separator = args.first().unwrap_or(&default_sep);
|
||||
|
@ -638,16 +772,24 @@ fn yank_joined_to_clipboard(
|
|||
fn yank_main_selection_to_primary_clipboard(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection)
|
||||
}
|
||||
|
||||
fn yank_joined_to_primary_clipboard(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let doc = doc!(cx.editor);
|
||||
let default_sep = Cow::Borrowed(doc.line_ending.as_str());
|
||||
let separator = args.first().unwrap_or(&default_sep);
|
||||
|
@ -657,32 +799,48 @@ fn yank_joined_to_primary_clipboard(
|
|||
fn paste_clipboard_after(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard, 1)
|
||||
}
|
||||
|
||||
fn paste_clipboard_before(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Clipboard, 1)
|
||||
}
|
||||
|
||||
fn paste_primary_clipboard_after(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection, 1)
|
||||
}
|
||||
|
||||
fn paste_primary_clipboard_before(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Selection, 1)
|
||||
}
|
||||
|
||||
|
@ -710,24 +868,36 @@ fn replace_selections_with_clipboard_impl(
|
|||
fn replace_selections_with_clipboard(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard)
|
||||
}
|
||||
|
||||
fn replace_selections_with_primary_clipboard(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
replace_selections_with_clipboard_impl(cx, ClipboardType::Selection)
|
||||
}
|
||||
|
||||
fn show_clipboard_provider(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
cx.editor
|
||||
.set_status(cx.editor.clipboard_provider.name().to_string());
|
||||
Ok(())
|
||||
|
@ -736,8 +906,12 @@ fn show_clipboard_provider(
|
|||
fn change_current_directory(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let dir = helix_core::path::expand_tilde(
|
||||
args.first()
|
||||
.context("target directory not provided")?
|
||||
|
@ -760,8 +934,12 @@ fn change_current_directory(
|
|||
fn show_current_directory(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let cwd = std::env::current_dir().context("Couldn't get the new working directory")?;
|
||||
cx.editor
|
||||
.set_status(format!("Current working directory is {}", cwd.display()));
|
||||
|
@ -772,8 +950,12 @@ fn show_current_directory(
|
|||
fn set_encoding(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let doc = doc_mut!(cx.editor);
|
||||
if let Some(label) = args.first() {
|
||||
doc.set_encoding(label)
|
||||
|
@ -788,8 +970,12 @@ fn set_encoding(
|
|||
fn reload(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let scrolloff = cx.editor.config().scrolloff;
|
||||
let (view, doc) = current!(cx.editor);
|
||||
doc.reload(view.id).map(|_| {
|
||||
|
@ -800,8 +986,12 @@ fn reload(
|
|||
fn tree_sitter_scopes(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (view, doc) = current!(cx.editor);
|
||||
let text = doc.text().slice(..);
|
||||
|
||||
|
@ -814,8 +1004,12 @@ fn tree_sitter_scopes(
|
|||
fn vsplit(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let id = view!(cx.editor).doc;
|
||||
|
||||
if args.is_empty() {
|
||||
|
@ -833,8 +1027,12 @@ fn vsplit(
|
|||
fn hsplit(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let id = view!(cx.editor).doc;
|
||||
|
||||
if args.is_empty() {
|
||||
|
@ -852,8 +1050,12 @@ fn hsplit(
|
|||
fn vsplit_new(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
cx.editor.new_file(Action::VerticalSplit);
|
||||
|
||||
Ok(())
|
||||
|
@ -862,8 +1064,12 @@ fn vsplit_new(
|
|||
fn hsplit_new(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
cx.editor.new_file(Action::HorizontalSplit);
|
||||
|
||||
Ok(())
|
||||
|
@ -872,8 +1078,12 @@ fn hsplit_new(
|
|||
fn debug_eval(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(debugger) = cx.editor.debugger.as_mut() {
|
||||
let (frame, thread_id) = match (debugger.active_frame, debugger.thread_id) {
|
||||
(Some(frame), Some(thread_id)) => (frame, thread_id),
|
||||
|
@ -894,8 +1104,12 @@ fn debug_eval(
|
|||
fn debug_start(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut args = args.to_owned();
|
||||
let name = match args.len() {
|
||||
0 => None,
|
||||
|
@ -907,8 +1121,12 @@ fn debug_start(
|
|||
fn debug_remote(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut args = args.to_owned();
|
||||
let address = match args.len() {
|
||||
0 => None,
|
||||
|
@ -924,8 +1142,12 @@ fn debug_remote(
|
|||
fn tutor(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let path = helix_loader::runtime_dir().join("tutor.txt");
|
||||
cx.editor.open(&path, Action::Replace)?;
|
||||
// Unset path to prevent accidentally saving to the original tutor file.
|
||||
|
@ -936,8 +1158,12 @@ fn tutor(
|
|||
pub(super) fn goto_line_number(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ensure!(!args.is_empty(), "Line number required");
|
||||
|
||||
let line = args[0].parse::<usize>()?;
|
||||
|
@ -954,8 +1180,12 @@ pub(super) fn goto_line_number(
|
|||
fn get_option(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if args.len() != 1 {
|
||||
anyhow::bail!("Bad arguments. Usage: `:get key`");
|
||||
}
|
||||
|
@ -976,8 +1206,12 @@ fn get_option(
|
|||
fn set_option(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if args.len() != 2 {
|
||||
anyhow::bail!("Bad arguments. Usage: `:set key field`");
|
||||
}
|
||||
|
@ -1009,8 +1243,12 @@ fn set_option(
|
|||
fn language(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if args.len() != 1 {
|
||||
anyhow::bail!("Bad arguments. Usage: `:set-language language`");
|
||||
}
|
||||
|
@ -1023,19 +1261,23 @@ fn language(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn sort(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
fn sort(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
sort_impl(cx, args, false)
|
||||
}
|
||||
|
||||
fn sort_reverse(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
sort_impl(cx, args, true)
|
||||
}
|
||||
|
||||
|
@ -1076,8 +1318,12 @@ fn sort_impl(
|
|||
fn reflow(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (view, doc) = current!(cx.editor);
|
||||
|
||||
const DEFAULT_MAX_LEN: usize = 79;
|
||||
|
@ -1115,8 +1361,12 @@ fn reflow(
|
|||
fn tree_sitter_subtree(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (view, doc) = current!(cx.editor);
|
||||
|
||||
if let Some(syntax) = doc.syntax() {
|
||||
|
@ -1151,8 +1401,12 @@ fn tree_sitter_subtree(
|
|||
fn open_config(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
cx.editor
|
||||
.open(&helix_loader::config_file(), Action::Replace)?;
|
||||
Ok(())
|
||||
|
@ -1161,8 +1415,12 @@ fn open_config(
|
|||
fn open_log(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
cx.editor.open(&helix_loader::log_file(), Action::Replace)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1170,8 +1428,12 @@ fn open_log(
|
|||
fn refresh_config(
|
||||
cx: &mut compositor::Context,
|
||||
_args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
cx.editor.config_events.0.send(ConfigEvent::Refresh)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1179,8 +1441,12 @@ fn refresh_config(
|
|||
fn append_output(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ensure!(!args.is_empty(), "Shell command required");
|
||||
shell(cx, &args.join(" "), &ShellBehavior::Append);
|
||||
Ok(())
|
||||
|
@ -1189,18 +1455,22 @@ fn append_output(
|
|||
fn insert_output(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ensure!(!args.is_empty(), "Shell command required");
|
||||
shell(cx, &args.join(" "), &ShellBehavior::Insert);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pipe(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
fn pipe(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ensure!(!args.is_empty(), "Shell command required");
|
||||
shell(cx, &args.join(" "), &ShellBehavior::Replace);
|
||||
Ok(())
|
||||
|
@ -1209,8 +1479,12 @@ fn pipe(
|
|||
fn run_shell_command(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
_event: PromptEvent,
|
||||
event: PromptEvent,
|
||||
) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let shell = &cx.editor.config().shell;
|
||||
let (output, success) = shell_impl(shell, &args.join(" "), None)?;
|
||||
if success {
|
||||
|
@ -1270,14 +1544,14 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
|
|||
aliases: &["bc", "bclose"],
|
||||
doc: "Close the current buffer.",
|
||||
fun: buffer_close,
|
||||
completer: Some(completers::buffer),
|
||||
completer: Some(completers::buffer),
|
||||
},
|
||||
TypableCommand {
|
||||
name: "buffer-close!",
|
||||
aliases: &["bc!", "bclose!"],
|
||||
doc: "Close the current buffer forcefully (ignoring unsaved changes).",
|
||||
fun: force_buffer_close,
|
||||
completer: Some(completers::buffer),
|
||||
completer: Some(completers::buffer),
|
||||
},
|
||||
TypableCommand {
|
||||
name: "buffer-close-others",
|
||||
|
@ -1561,7 +1835,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
|
|||
doc: "Display tree sitter scopes, primarily for theming and development.",
|
||||
fun: tree_sitter_scopes,
|
||||
completer: None,
|
||||
},
|
||||
},
|
||||
TypableCommand {
|
||||
name: "debug-start",
|
||||
aliases: &["dbg"],
|
||||
|
@ -1787,10 +2061,6 @@ pub fn command_mode(cx: &mut Context) {
|
|||
}
|
||||
}, // completion
|
||||
move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
|
||||
if event != PromptEvent::Validate {
|
||||
return;
|
||||
}
|
||||
|
||||
let parts = input.split_whitespace().collect::<Vec<&str>>();
|
||||
if parts.is_empty() {
|
||||
return;
|
||||
|
@ -1811,10 +2081,10 @@ pub fn command_mode(cx: &mut Context) {
|
|||
if let Err(e) = (cmd.fun)(cx, &args[1..], event) {
|
||||
cx.editor.set_error(format!("{}", e));
|
||||
}
|
||||
} else {
|
||||
} else if event == PromptEvent::Validate {
|
||||
cx.editor
|
||||
.set_error(format!("no such command: '{}'", parts[0]));
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
prompt.doc_fn = Box::new(|input: &str| {
|
||||
|
|
|
@ -237,6 +237,7 @@ pub mod completers {
|
|||
));
|
||||
names.push("default".into());
|
||||
names.push("base16_default".into());
|
||||
names.sort();
|
||||
|
||||
let mut names: Vec<_> = names
|
||||
.into_iter()
|
||||
|
|
|
@ -490,7 +490,7 @@ impl<T: Item + 'static> Component for Picker<T> {
|
|||
_ => return EventResult::Ignored(None),
|
||||
};
|
||||
|
||||
let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| {
|
||||
let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _cx| {
|
||||
// remove the layer
|
||||
compositor.last_picker = compositor.pop();
|
||||
})));
|
||||
|
|
|
@ -464,7 +464,6 @@ pub struct Editor {
|
|||
pub registers: Registers,
|
||||
pub macro_recording: Option<(char, Vec<KeyEvent>)>,
|
||||
pub macro_replaying: Vec<char>,
|
||||
pub theme: Theme,
|
||||
pub language_servers: helix_lsp::Registry,
|
||||
pub diagnostics: BTreeMap<lsp::Url, Vec<lsp::Diagnostic>>,
|
||||
|
||||
|
@ -476,6 +475,12 @@ pub struct Editor {
|
|||
|
||||
pub syn_loader: Arc<syntax::Loader>,
|
||||
pub theme_loader: Arc<theme::Loader>,
|
||||
/// last_theme is used for theme previews. We store the current theme here,
|
||||
/// and if previewing is cancelled, we can return to it.
|
||||
pub last_theme: Option<Theme>,
|
||||
/// The currently applied editor theme. While previewing a theme, the previewed theme
|
||||
/// is set here.
|
||||
pub theme: Theme,
|
||||
|
||||
pub status_msg: Option<(Cow<'static, str>, Severity)>,
|
||||
pub autoinfo: Option<Info>,
|
||||
|
@ -500,6 +505,11 @@ pub enum ConfigEvent {
|
|||
Update(Box<Config>),
|
||||
}
|
||||
|
||||
enum ThemeAction {
|
||||
Set,
|
||||
Preview,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CompleteAction {
|
||||
pub trigger_offset: usize,
|
||||
|
@ -544,6 +554,7 @@ impl Editor {
|
|||
breakpoints: HashMap::new(),
|
||||
syn_loader,
|
||||
theme_loader,
|
||||
last_theme: None,
|
||||
registers: Registers::default(),
|
||||
clipboard_provider: get_clipboard_provider(),
|
||||
status_msg: None,
|
||||
|
@ -613,7 +624,22 @@ impl Editor {
|
|||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn unset_theme_preview(&mut self) {
|
||||
if let Some(last_theme) = self.last_theme.take() {
|
||||
self.set_theme(last_theme);
|
||||
}
|
||||
// None likely occurs when the user types ":theme" and then exits before previewing
|
||||
}
|
||||
|
||||
pub fn set_theme_preview(&mut self, theme: Theme) {
|
||||
self.set_theme_impl(theme, ThemeAction::Preview);
|
||||
}
|
||||
|
||||
pub fn set_theme(&mut self, theme: Theme) {
|
||||
self.set_theme_impl(theme, ThemeAction::Set);
|
||||
}
|
||||
|
||||
fn set_theme_impl(&mut self, theme: Theme, preview: ThemeAction) {
|
||||
// `ui.selection` is the only scope required to be able to render a theme.
|
||||
if theme.find_scope_index("ui.selection").is_none() {
|
||||
self.set_error("Invalid theme: `ui.selection` required");
|
||||
|
@ -623,7 +649,18 @@ impl Editor {
|
|||
let scopes = theme.scopes();
|
||||
self.syn_loader.set_scopes(scopes.to_vec());
|
||||
|
||||
self.theme = theme;
|
||||
match preview {
|
||||
ThemeAction::Preview => {
|
||||
let last_theme = std::mem::replace(&mut self.theme, theme);
|
||||
// only insert on first preview: this will be the last theme the user has saved
|
||||
self.last_theme.get_or_insert(last_theme);
|
||||
}
|
||||
ThemeAction::Set => {
|
||||
self.last_theme = None;
|
||||
self.theme = theme;
|
||||
}
|
||||
}
|
||||
|
||||
self._refresh();
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,14 @@ impl Loader {
|
|||
names
|
||||
}
|
||||
|
||||
pub fn default_theme(&self, true_color: bool) -> Theme {
|
||||
if true_color {
|
||||
self.default()
|
||||
} else {
|
||||
self.base16_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the default theme
|
||||
pub fn default(&self) -> Theme {
|
||||
DEFAULT_THEME.clone()
|
||||
|
|
Loading…
Add table
Reference in a new issue