From a0a5bd555b47cbc9fbbd73f680eef62ae3dcb511 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Sun, 4 Jul 2021 00:19:59 +0800 Subject: More responsive key input Use biased select!, don't eagerly process lsp message since we want to prioritize user input rather than lsp messages, but still limit rendering for lsp messages. --- helix-term/src/application.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'helix-term') diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 17ba2652..79dd7c3b 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -9,6 +9,7 @@ use log::error; use std::{ io::{stdout, Write}, sync::Arc, + time::{Duration, Instant}, }; use anyhow::Error; @@ -130,6 +131,8 @@ impl Application { pub async fn event_loop(&mut self) { let mut reader = EventStream::new(); + let mut last_render = Instant::now(); + let deadline = Duration::from_secs(1) / 60; self.render(); @@ -139,26 +142,22 @@ impl Application { break; } - use futures_util::{FutureExt, StreamExt}; + use futures_util::StreamExt; tokio::select! { + biased; + event = reader.next() => { self.handle_terminal_events(event) } Some((id, call)) = self.editor.language_servers.incoming.next() => { self.handle_language_server_message(call, id).await; - - // eagerly process any other available notifications/calls - let now = std::time::Instant::now(); - let deadline = std::time::Duration::from_millis(10); - while let Some(Some((id, call))) = self.editor.language_servers.incoming.next().now_or_never() { - self.handle_language_server_message(call, id).await; - - if now.elapsed() > deadline { // use a deadline so we don't block too long - break; - } + // limit render calls for fast language server messages + let last = self.editor.language_servers.incoming.is_empty(); + if last || last_render.elapsed() > deadline { + self.render(); + last_render = Instant::now(); } - self.render(); } Some(callback) = self.jobs.futures.next() => { self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); -- cgit v1.2.3-70-g09d2 From 1c71fced0e38a4f126155cb7161d4f4fa4f82673 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Tue, 6 Jul 2021 22:18:30 +0800 Subject: Add more modes to infobox --- helix-term/src/commands.rs | 132 ++++++++++++++++++++------------------------- 1 file changed, 57 insertions(+), 75 deletions(-) (limited to 'helix-term') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 63b91942..2810d54a 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -9,8 +9,8 @@ use helix_core::{ object, pos_at_coords, regex::{self, Regex}, register::Register, - search, selection, LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, - SmallVec, Tendril, Transaction, + search, selection, surround, textobject, LineEnding, Position, Range, Rope, RopeGraphemes, + RopeSlice, Selection, SmallVec, Tendril, Transaction, }; use helix_view::{ @@ -78,7 +78,9 @@ impl<'a> Context<'a> { #[inline] pub fn on_next_key_mode(&mut self, map: HashMap) { + let count = self.count; self.on_next_key(move |cx, event| { + cx.count = count; cx.editor.autoinfo = None; if let Some(func) = map.get(&event) { func(cx); @@ -3337,24 +3339,6 @@ fn jump_backward(cx: &mut Context) { }; } -fn window_mode(cx: &mut Context) { - cx.on_next_key(move |cx, event| { - if let KeyEvent { - code: KeyCode::Char(ch), - .. - } = event - { - match ch { - 'w' => rotate_view(cx), - 'h' => hsplit(cx), - 'v' => vsplit(cx), - 'q' => wclose(cx), - _ => {} - } - } - }) -} - fn rotate_view(cx: &mut Context) { cx.editor.focus_next() } @@ -3443,62 +3427,12 @@ fn view_mode(cx: &mut Context) { }) } -fn left_bracket_mode(cx: &mut Context) { - cx.on_next_key(move |cx, event| { - if let KeyEvent { - code: KeyCode::Char(ch), - .. - } = event - { - match ch { - 'd' => goto_prev_diag(cx), - 'D' => goto_first_diag(cx), - _ => (), - } - } - }) -} - -fn right_bracket_mode(cx: &mut Context) { - cx.on_next_key(move |cx, event| { - if let KeyEvent { - code: KeyCode::Char(ch), - .. - } = event - { - match ch { - 'd' => goto_next_diag(cx), - 'D' => goto_last_diag(cx), - _ => (), - } - } - }) +fn select_textobject_around(cx: &mut Context) { + select_textobject(cx, textobject::TextObject::Around); } -use helix_core::surround; -use helix_core::textobject; - -fn match_mode(cx: &mut Context) { - let count = cx.count; - cx.on_next_key(move |cx, event| { - if let KeyEvent { - code: KeyCode::Char(ch), - .. - } = event - { - // FIXME: count gets reset because of cx.on_next_key() - cx.count = count; - match ch { - 'm' => match_brackets(cx), - 's' => surround_add(cx), - 'r' => surround_replace(cx), - 'd' => surround_delete(cx), - 'a' => select_textobject(cx, textobject::TextObject::Around), - 'i' => select_textobject(cx, textobject::TextObject::Inside), - _ => (), - } - } - }) +fn select_textobject_inner(cx: &mut Context) { + select_textobject(cx, textobject::TextObject::Inside); } fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) { @@ -3719,7 +3653,7 @@ mode_info! { } mode_info! { - /// goto mode + /// goto /// /// When specified with a count, it will go to that line without entering the mode. goto_mode, GOTO_MODE, goto_prehook, @@ -3750,3 +3684,51 @@ mode_info! { /// last accessed file "a" => goto_last_accessed_file, } + +mode_info! { + /// window + window_mode, WINDOW_MODE, + /// rotate + "w" => rotate_view, + /// horizontal split + "h" => hsplit, + /// vertical split + "v" => vsplit, + /// close + "q" => wclose, +} + +mode_info! { + /// match + match_mode, MATCH_MODE, + /// matching character + "m" => match_brackets, + /// surround add + "s" => surround_add, + /// surround replace + "r" => surround_replace, + /// surround delete + "d" => surround_delete, + /// around object + "a" => select_textobject_around, + /// inside object + "i" => select_textobject_inner, +} + +mode_info! { + /// select to previous + left_bracket_mode, LEFT_BRACKET_MODE, + /// previous diagnostic + "d" => goto_prev_diag, + /// diagnostic (first) + "D" => goto_first_diag, +} + +mode_info! { + /// select to next + right_bracket_mode, RIGHT_BRACKET_MODE, + /// diagnostic + "d" => goto_next_diag, + /// diagnostic (last) + "D" => goto_last_diag, +} -- cgit v1.2.3-70-g09d2 From bb121a3e4b97efef1380414f33361404900d6f72 Mon Sep 17 00:00:00 2001 From: Kirawi Date: Sat, 10 Jul 2021 21:40:18 -0400 Subject: Injection Query Support (#430) * wip * wip * fixed unsafe * fix clippy * move out reference variable * fmt * remove arc * change safety comment--- helix-term/src/ui/editor.rs | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'helix-term') diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index ef13004c..9a2fbf57 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -64,6 +64,7 @@ impl EditorView { surface: &mut Surface, theme: &Theme, is_focused: bool, + loader: &syntax::Loader, ) { let area = Rect::new( view.area.x + OFFSET, @@ -72,7 +73,7 @@ impl EditorView { view.area.height.saturating_sub(1), ); // - 1 for statusline - self.render_buffer(doc, view, area, surface, theme, is_focused); + self.render_buffer(doc, view, area, surface, theme, is_focused, loader); // if we're not at the edge of the screen, draw a right border if viewport.right() != view.area.right() { @@ -98,6 +99,7 @@ impl EditorView { self.render_statusline(doc, view, area, surface, theme, is_focused); } + #[allow(clippy::too_many_arguments)] pub fn render_buffer( &self, doc: &Document, @@ -106,6 +108,7 @@ impl EditorView { surface: &mut Surface, theme: &Theme, is_focused: bool, + loader: &syntax::Loader, ) { let text = doc.text().slice(..); @@ -122,8 +125,26 @@ impl EditorView { // TODO: range doesn't actually restrict source, just highlight range let highlights: Vec<_> = match doc.syntax() { Some(syntax) => { + let scopes = theme.scopes(); syntax - .highlight_iter(text.slice(..), Some(range), None, |_| None) + .highlight_iter(text.slice(..), Some(range), None, |language| { + loader + .language_config_for_scope(&format!("source.{}", language)) + .and_then(|language_config| { + let config = language_config.highlight_config(scopes)?; + let config_ref = config.as_ref(); + // SAFETY: the referenced `HighlightConfiguration` behind + // the `Arc` is guaranteed to remain valid throughout the + // duration of the highlight. + let config_ref = unsafe { + std::mem::transmute::< + _, + &'static syntax::HighlightConfiguration, + >(config_ref) + }; + Some(config_ref) + }) + }) .collect() // TODO: we collect here to avoid holding the lock, fix later } None => vec![Ok(HighlightEvent::Source { @@ -719,7 +740,16 @@ impl Component for EditorView { for (view, is_focused) in cx.editor.tree.views() { let doc = cx.editor.document(view.doc).unwrap(); - self.render_view(doc, view, area, surface, &cx.editor.theme, is_focused); + let loader = &cx.editor.syn_loader; + self.render_view( + doc, + view, + area, + surface, + &cx.editor.theme, + is_focused, + loader, + ); } if let Some(info) = std::mem::take(&mut cx.editor.autoinfo) { -- cgit v1.2.3-70-g09d2 From 3e4cd8f8e6456fdea4f0e82908d395eb516e1be6 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Thu, 8 Jul 2021 09:58:11 +0800 Subject: Add infobox for view --- helix-term/src/commands.rs | 89 +++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 40 deletions(-) (limited to 'helix-term') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 2810d54a..e35b36f9 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -40,6 +40,7 @@ use crate::{ use crate::job::{self, Job, Jobs}; use futures_util::{FutureExt, TryFutureExt}; use std::collections::HashMap; +use std::num::NonZeroUsize; use std::{fmt, future::Future}; use std::{ @@ -52,7 +53,7 @@ use serde::de::{self, Deserialize, Deserializer}; pub struct Context<'a> { pub selected_register: helix_view::RegisterSelection, - pub count: Option, + pub count: Option, pub editor: &'a mut Editor, pub callback: Option, @@ -3384,47 +3385,38 @@ fn select_register(cx: &mut Context) { }) } -fn view_mode(cx: &mut Context) { - cx.on_next_key(move |cx, event| { - if let KeyEvent { - code: KeyCode::Char(ch), - .. - } = event - { - // if lock, call cx again - // TODO: temporarily show VIE in the mode list - match ch { - // center - 'z' | 'c' - // top - | 't' - // bottom - | 'b' => { - let (view, doc) = current!(cx.editor); +fn align_view_top(cx: &mut Context) { + let (view, doc) = current!(cx.editor); + align_view(doc, view, Align::Top); +} - align_view(doc, view, match ch { - 'z' | 'c' => Align::Center, - 't' => Align::Top, - 'b' => Align::Bottom, - _ => unreachable!() - }); - } - 'm' => { - let (view, doc) = current!(cx.editor); - let pos = doc.selection(view.id).cursor(); - let pos = coords_at_pos(doc.text().slice(..), pos); +fn align_view_center(cx: &mut Context) { + let (view, doc) = current!(cx.editor); + align_view(doc, view, Align::Center); +} - const OFFSET: usize = 7; // gutters - view.first_col = pos.col.saturating_sub(((view.area.width as usize).saturating_sub(OFFSET)) / 2); - }, - 'h' => (), - 'j' => scroll(cx, 1, Direction::Forward), - 'k' => scroll(cx, 1, Direction::Backward), - 'l' => (), - _ => (), - } - } - }) +fn align_view_bottom(cx: &mut Context) { + let (view, doc) = current!(cx.editor); + align_view(doc, view, Align::Bottom); +} + +fn align_view_middle(cx: &mut Context) { + let (view, doc) = current!(cx.editor); + let pos = doc.selection(view.id).cursor(); + let pos = coords_at_pos(doc.text().slice(..), pos); + + const OFFSET: usize = 7; // gutters + view.first_col = pos + .col + .saturating_sub(((view.area.width as usize).saturating_sub(OFFSET)) / 2); +} + +fn scroll_up(cx: &mut Context) { + scroll(cx, cx.count(), Direction::Backward); +} + +fn scroll_down(cx: &mut Context) { + scroll(cx, cx.count(), Direction::Forward); } fn select_textobject_around(cx: &mut Context) { @@ -3732,3 +3724,20 @@ mode_info! { /// diagnostic (last) "D" => goto_last_diag, } + +mode_info! { + /// view + view_mode, VIEW_MODE, + /// align view top + "t" => align_view_top, + /// align view center + "z" | "c" => align_view_center, + /// align view bottom + "b" => align_view_bottom, + /// align view middle + "m" => align_view_middle, + /// scroll up + "k" => scroll_up, + /// scroll down + "j" => scroll_down, +} -- cgit v1.2.3-70-g09d2 From 9c02a1b070b90668c97968b848421ad2de9d459b Mon Sep 17 00:00:00 2001 From: Lionel Flandrin Date: Sat, 26 Jun 2021 18:50:44 +0100 Subject: Make command implementation return a Result<()> The error message is displayed with cx.editor.set_error. --- helix-term/src/commands.rs | 415 ++++++++++++++++++++++++++++----------------- helix-view/src/editor.rs | 5 +- 2 files changed, 263 insertions(+), 157 deletions(-) (limited to 'helix-term') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index e35b36f9..8a9ffb91 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -23,7 +23,7 @@ use helix_view::{ Document, DocumentId, Editor, ViewId, }; -use anyhow::anyhow; +use anyhow::{anyhow, bail}; use helix_lsp::{ lsp, util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range}, @@ -1161,34 +1161,52 @@ mod cmd { pub alias: Option<&'static str>, pub doc: &'static str, // params, flags, helper, completer - pub fun: fn(&mut compositor::Context, &[&str], PromptEvent), + pub fun: fn(&mut compositor::Context, &[&str], PromptEvent) -> anyhow::Result<()>, pub completer: Option, } - fn quit(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) { + fn quit( + cx: &mut compositor::Context, + _args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { // last view and we have unsaved changes - if cx.editor.tree.views().count() == 1 && buffers_remaining_impl(cx.editor) { - return; + if cx.editor.tree.views().count() == 1 { + buffers_remaining_impl(cx.editor)? } + cx.editor .close(view!(cx.editor).id, /* close_buffer */ false); + + Ok(()) } - fn force_quit(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) { + fn force_quit( + cx: &mut compositor::Context, + _args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { cx.editor .close(view!(cx.editor).id, /* close_buffer */ false); + + Ok(()) } - fn open(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) { + fn open( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { match args.get(0) { Some(path) => { - // TODO: handle error - let _ = cx.editor.open(path.into(), Action::Replace); + let _ = cx.editor.open(path.into(), Action::Replace)?; + + Ok(()) } None => { - cx.editor.set_error("wrong argument count".to_string()); + bail!("wrong argument count"); } - }; + } } fn write_impl>( @@ -1200,11 +1218,11 @@ mod cmd { if let Some(path) = path { if let Err(err) = doc.set_path(path.as_ref()) { - return Err(anyhow!("invalid filepath: {}", err)); + bail!("invalid filepath: {}", err); }; } if doc.path().is_none() { - return Err(anyhow!("cannot write a buffer without a filename")); + bail!("cannot write a buffer without a filename"); } let fmt = doc.auto_format().map(|fmt| { let shared = fmt.shared(); @@ -1220,21 +1238,33 @@ mod cmd { Ok(tokio::spawn(doc.format_and_save(fmt))) } - fn write(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) { - match write_impl(cx, args.first()) { - Err(e) => cx.editor.set_error(e.to_string()), - Ok(handle) => { - cx.jobs - .add(Job::new(handle.unwrap_or_else(|e| Err(e.into()))).wait_before_exiting()); - } - }; + fn write( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + let handle = write_impl(cx, args.first())?; + cx.jobs + .add(Job::new(handle.unwrap_or_else(|e| Err(e.into()))).wait_before_exiting()); + + Ok(()) } - fn new_file(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) { + fn new_file( + cx: &mut compositor::Context, + _args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { cx.editor.new_file(Action::Replace); + + Ok(()) } - fn format(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) { + fn format( + cx: &mut compositor::Context, + _args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { let (_, doc) = current!(cx.editor); if let Some(format) = doc.format() { @@ -1242,9 +1272,14 @@ mod cmd { make_format_callback(doc.id(), doc.version(), Modified::LeaveModified, format); cx.jobs.callback(callback); } - } - fn set_indent_style(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) { + Ok(()) + } + fn set_indent_style( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { use IndentStyle::*; // If no argument, report current indent style. @@ -1256,7 +1291,7 @@ mod cmd { Spaces(n) if (2..=8).contains(&n) => format!("{} spaces", n), _ => "error".into(), // Shouldn't happen. }); - return; + return Ok(()); } // Attempt to parse argument as an indent style. @@ -1274,15 +1309,19 @@ mod cmd { if let Some(s) = style { let doc = doc_mut!(cx.editor); doc.indent_style = s; + + Ok(()) } else { - // Invalid argument. - cx.editor - .set_error(format!("invalid indent style '{}'", args[0],)); + bail!("invalid indent style '{}'", args[0]); } } /// Sets or reports the current document's line ending setting. - fn set_line_ending(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) { + fn set_line_ending( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { use LineEnding::*; // If no argument, report current line ending setting. @@ -1298,7 +1337,8 @@ mod cmd { // These should never be a document's default line ending. VT | LS | PS => "error".into(), }); - return; + + return Ok(()); } // Attempt to parse argument as a line ending. @@ -1314,70 +1354,66 @@ mod cmd { if let Some(le) = line_ending { doc_mut!(cx.editor).line_ending = le; + Ok(()) } else { - // Invalid argument. - cx.editor - .set_error(format!("invalid line ending '{}'", args[0],)); + bail!("invalid line ending '{}'", args[0]); } } - fn earlier(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) { - let uk = match args.join(" ").parse::() { - Ok(uk) => uk, - Err(msg) => { - cx.editor.set_error(msg); - return; - } - }; + fn earlier( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + let uk = args + .join(" ") + .parse::() + .map_err(|s| anyhow!(s))?; + let (view, doc) = current!(cx.editor); - doc.earlier(view.id, uk) + doc.earlier(view.id, uk); + + Ok(()) } - fn later(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) { - let uk = match args.join(" ").parse::() { - Ok(uk) => uk, - Err(msg) => { - cx.editor.set_error(msg); - return; - } - }; + fn later( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + let uk = args + .join(" ") + .parse::() + .map_err(|s| anyhow!(s))?; let (view, doc) = current!(cx.editor); - doc.later(view.id, uk) + doc.later(view.id, uk); + + Ok(()) } - fn write_quit(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) { - match write_impl(cx, args.first()) { - Ok(handle) => { - if let Err(e) = helix_lsp::block_on(handle) { - cx.editor.set_error(e.to_string()); - } else { - quit(cx, &[], event); - } - } - Err(e) => { - cx.editor.set_error(e.to_string()); - } - } + fn write_quit( + cx: &mut compositor::Context, + args: &[&str], + event: PromptEvent, + ) -> anyhow::Result<()> { + let handle = write_impl(cx, args.first())?; + helix_lsp::block_on(handle)?; + quit(cx, &[], event) } - fn force_write_quit(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) { - match write_impl(cx, args.first()) { - Ok(handle) => { - if let Err(e) = helix_lsp::block_on(handle) { - cx.editor.set_error(e.to_string()); - } else { - force_quit(cx, &[], event); - } - } - Err(e) => { - cx.editor.set_error(e.to_string()); - } - } + fn force_write_quit( + cx: &mut compositor::Context, + args: &[&str], + event: PromptEvent, + ) -> anyhow::Result<()> { + let handle = write_impl(cx, args.first())?; + helix_lsp::block_on(handle)?; + force_quit(cx, &[], event) } - /// Returns `true` if there are modified buffers remaining and sets editor error, - /// otherwise returns `false` - fn buffers_remaining_impl(editor: &mut Editor) -> bool { + /// Results an error if there are modified buffers remaining and sets editor error, + /// otherwise returns `Ok(())` + fn buffers_remaining_impl(editor: &mut Editor) -> anyhow::Result<()> { let modified: Vec<_> = editor .documents() .filter(|doc| doc.is_modified()) @@ -1388,16 +1424,13 @@ mod cmd { }) .collect(); if !modified.is_empty() { - let err = format!( + bail!( "{} unsaved buffer(s) remaining: {:?}", modified.len(), modified ); - editor.set_error(err); - true - } else { - false } + Ok(()) } fn write_all_impl( @@ -1406,7 +1439,7 @@ mod cmd { _event: PromptEvent, quit: bool, force: bool, - ) { + ) -> anyhow::Result<()> { let mut errors = String::new(); // save all documents @@ -1419,11 +1452,10 @@ mod cmd { // TODO: handle error. let _ = helix_lsp::block_on(tokio::spawn(doc.save())); } - editor.set_error(errors); if quit { - if !force && buffers_remaining_impl(editor) { - return; + if !force { + buffers_remaining_impl(editor)?; } // close all views @@ -1432,23 +1464,42 @@ mod cmd { editor.close(view_id, false); } } + + bail!(errors) } - fn write_all(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) { + fn write_all( + cx: &mut compositor::Context, + args: &[&str], + event: PromptEvent, + ) -> anyhow::Result<()> { write_all_impl(&mut cx.editor, args, event, false, false) } - fn write_all_quit(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) { + fn write_all_quit( + cx: &mut compositor::Context, + args: &[&str], + event: PromptEvent, + ) -> anyhow::Result<()> { write_all_impl(&mut cx.editor, args, event, true, false) } - fn force_write_all_quit(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) { + fn force_write_all_quit( + cx: &mut compositor::Context, + args: &[&str], + event: PromptEvent, + ) -> anyhow::Result<()> { write_all_impl(&mut cx.editor, args, event, true, true) } - fn quit_all_impl(editor: &mut Editor, _args: &[&str], _event: PromptEvent, force: bool) { - if !force && buffers_remaining_impl(editor) { - return; + fn quit_all_impl( + editor: &mut Editor, + _args: &[&str], + _event: PromptEvent, + force: bool, + ) -> anyhow::Result<()> { + if !force { + buffers_remaining_impl(editor)?; } // close all views @@ -1456,57 +1507,82 @@ mod cmd { for view_id in views { editor.close(view_id, false); } + + Ok(()) } - fn quit_all(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) { + fn quit_all( + cx: &mut compositor::Context, + args: &[&str], + event: PromptEvent, + ) -> anyhow::Result<()> { quit_all_impl(&mut cx.editor, args, event, false) } - fn force_quit_all(cx: &mut compositor::Context, args: &[&str], event: PromptEvent) { + fn force_quit_all( + cx: &mut compositor::Context, + args: &[&str], + event: PromptEvent, + ) -> anyhow::Result<()> { quit_all_impl(&mut cx.editor, args, event, true) } - fn theme(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) { + fn theme( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { let theme = if let Some(theme) = args.first() { theme } else { - cx.editor.set_error("theme name not provided".into()); - return; + bail!("theme name not provided"); }; - cx.editor.set_theme_from_name(theme); + cx.editor.set_theme_from_name(theme) } fn yank_main_selection_to_clipboard( cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent, - ) { - yank_main_selection_to_clipboard_impl(&mut cx.editor); + ) -> anyhow::Result<()> { + yank_main_selection_to_clipboard_impl(&mut cx.editor) } - fn yank_joined_to_clipboard(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) { + fn yank_joined_to_clipboard( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { let (_, doc) = current!(cx.editor); let separator = args .first() .copied() .unwrap_or_else(|| doc.line_ending.as_str()); - yank_joined_to_clipboard_impl(&mut cx.editor, separator); + yank_joined_to_clipboard_impl(&mut cx.editor, separator) } - fn paste_clipboard_after(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) { - paste_clipboard_impl(&mut cx.editor, Paste::After); + fn paste_clipboard_after( + cx: &mut compositor::Context, + _args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + paste_clipboard_impl(&mut cx.editor, Paste::After) } - fn paste_clipboard_before(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) { - paste_clipboard_impl(&mut cx.editor, Paste::After); + fn paste_clipboard_before( + cx: &mut compositor::Context, + _args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + paste_clipboard_impl(&mut cx.editor, Paste::After) } fn replace_selections_with_clipboard( cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent, - ) { + ) -> anyhow::Result<()> { let (view, doc) = current!(cx.editor); match cx.editor.clipboard_provider.get_contents() { @@ -1520,71 +1596,89 @@ mod cmd { doc.apply(&transaction, view.id); doc.append_changes_to_history(view.id); + Ok(()) + } + Err(e) => { + log::error!("Couldn't get system clipboard contents: {:?}", e); + bail!("Couldn't get system clipboard contents: {:?}", e); } - Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e), } } - fn show_clipboard_provider(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) { + fn show_clipboard_provider( + cx: &mut compositor::Context, + _args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { cx.editor .set_status(cx.editor.clipboard_provider.name().into()); + Ok(()) } - fn change_current_directory(cx: &mut compositor::Context, args: &[&str], _event: PromptEvent) { - let dir = match args.first() { - Some(dir) => dir, - None => { - cx.editor.set_error("target directory not provided".into()); - return; - } - }; + fn change_current_directory( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + let dir = args + .first() + .ok_or_else(|| anyhow!("target directory not provided"))?; if let Err(e) = std::env::set_current_dir(dir) { - cx.editor.set_error(format!( - "Couldn't change the current working directory: {:?}", - e - )); - return; + bail!("Couldn't change the current working directory: {:?}", e); } match std::env::current_dir() { - Ok(cwd) => cx.editor.set_status(format!( - "Current working directory is now {}", - cwd.display() - )), - Err(e) => cx - .editor - .set_error(format!("Couldn't get the new working directory: {}", e)), + Ok(cwd) => { + cx.editor.set_status(format!( + "Current working directory is now {}", + cwd.display() + )); + Ok(()) + } + Err(e) => bail!("Couldn't get the new working directory: {}", e), } } - fn show_current_directory(cx: &mut compositor::Context, _args: &[&str], _event: PromptEvent) { + fn show_current_directory( + cx: &mut compositor::Context, + _args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { match std::env::current_dir() { - Ok(cwd) => cx - .editor - .set_status(format!("Current working directory is {}", cwd.display())), - Err(e) => cx - .editor - .set_error(format!("Couldn't get the current working directory: {}", e)), + Ok(cwd) => { + cx.editor + .set_status(format!("Current working directory is {}", cwd.display())); + Ok(()) + } + Err(e) => bail!("Couldn't get the current working directory: {}", e), } } /// Sets the [`Document`]'s encoding.. - fn set_encoding(cx: &mut compositor::Context, args: &[&str], _: PromptEvent) { + fn set_encoding( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { let (_, doc) = current!(cx.editor); if let Some(label) = args.first() { doc.set_encoding(label) - .unwrap_or_else(|e| cx.editor.set_error(e.to_string())); } else { let encoding = doc.encoding().name().to_string(); - cx.editor.set_status(encoding) + cx.editor.set_status(encoding); + Ok(()) } } /// Reload the [`Document`] from its source file. - fn reload(cx: &mut compositor::Context, _args: &[&str], _: PromptEvent) { + fn reload( + cx: &mut compositor::Context, + _args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { let (view, doc) = current!(cx.editor); - doc.reload(view.id).unwrap(); + doc.reload(view.id) } pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ @@ -1849,7 +1943,9 @@ fn command_mode(cx: &mut Context) { } if let Some(cmd) = cmd::COMMANDS.get(parts[0]) { - (cmd.fun)(cx, &parts[1..], event); + if let Err(e) = (cmd.fun)(cx, &parts[1..], event) { + cx.editor.set_error(format!("{}", e)); + } } else { cx.editor .set_error(format!("no such command: '{}'", parts[0])); @@ -2758,7 +2854,7 @@ fn yank(cx: &mut Context) { cx.editor.set_status(msg) } -fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) { +fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) -> anyhow::Result<()> { let (view, doc) = current!(editor); let values: Vec = doc @@ -2776,17 +2872,20 @@ fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) { if let Err(e) = editor.clipboard_provider.set_contents(joined) { log::error!("Couldn't set system clipboard content: {:?}", e); + bail!("Couldn't set system clipboard content: {:?}", e); } editor.set_status(msg); + + Ok(()) } fn yank_joined_to_clipboard(cx: &mut Context) { let line_ending = current!(cx.editor).1.line_ending; - yank_joined_to_clipboard_impl(&mut cx.editor, line_ending.as_str()); + let _ = yank_joined_to_clipboard_impl(&mut cx.editor, line_ending.as_str()); } -fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) { +fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) -> anyhow::Result<()> { let (view, doc) = current!(editor); let value = doc @@ -2796,13 +2895,15 @@ fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) { if let Err(e) = editor.clipboard_provider.set_contents(value.into_owned()) { log::error!("Couldn't set system clipboard content: {:?}", e); + bail!("Couldn't set system clipboard content: {:?}", e); } editor.set_status("yanked main selection to system clipboard".to_owned()); + Ok(()) } fn yank_main_selection_to_clipboard(cx: &mut Context) { - yank_main_selection_to_clipboard_impl(&mut cx.editor); + let _ = yank_main_selection_to_clipboard_impl(&mut cx.editor); } #[derive(Copy, Clone)] @@ -2850,7 +2951,7 @@ fn paste_impl( Some(transaction) } -fn paste_clipboard_impl(editor: &mut Editor, action: Paste) { +fn paste_clipboard_impl(editor: &mut Editor, action: Paste) -> anyhow::Result<()> { let (view, doc) = current!(editor); match editor @@ -2861,18 +2962,22 @@ fn paste_clipboard_impl(editor: &mut Editor, action: Paste) { Ok(Some(transaction)) => { doc.apply(&transaction, view.id); doc.append_changes_to_history(view.id); + Ok(()) + } + Ok(None) => Ok(()), + Err(e) => { + log::error!("Couldn't get system clipboard contents: {:?}", e); + bail!("Couldn't get system clipboard contents: {:?}", e); } - Ok(None) => {} - Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e), } } fn paste_clipboard_after(cx: &mut Context) { - paste_clipboard_impl(&mut cx.editor, Paste::After); + let _ = paste_clipboard_impl(&mut cx.editor, Paste::After); } fn paste_clipboard_before(cx: &mut Context) { - paste_clipboard_impl(&mut cx.editor, Paste::Before); + let _ = paste_clipboard_impl(&mut cx.editor, Paste::Before); } fn replace_with_yanked(cx: &mut Context) { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 4f01cce4..c80535ed 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -97,16 +97,17 @@ impl Editor { self._refresh(); } - pub fn set_theme_from_name(&mut self, theme: &str) { + pub fn set_theme_from_name(&mut self, theme: &str) -> anyhow::Result<()> { let theme = match self.theme_loader.load(theme.as_ref()) { Ok(theme) => theme, Err(e) => { log::warn!("failed setting theme `{}` - {}", theme, e); - return; + anyhow::bail!("failed setting theme `{}` - {}", theme, e); } }; self.set_theme(theme); + Ok(()) } fn _refresh(&mut self) { -- cgit v1.2.3-70-g09d2 From d530d6e39d58f5759d2db0a9bda1ea5e21154f83 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Sun, 11 Jul 2021 16:35:57 +0900 Subject: Further simplify error handling in :commands --- helix-term/src/commands.rs | 108 +++++++++++++++------------------------------ helix-view/src/editor.rs | 13 +++--- 2 files changed, 41 insertions(+), 80 deletions(-) (limited to 'helix-term') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 8a9ffb91..8bbdfdfa 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -23,7 +23,7 @@ use helix_view::{ Document, DocumentId, Editor, ViewId, }; -use anyhow::{anyhow, bail}; +use anyhow::{anyhow, bail, Context as _}; use helix_lsp::{ lsp, util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range}, @@ -1197,16 +1197,9 @@ mod cmd { args: &[&str], _event: PromptEvent, ) -> anyhow::Result<()> { - match args.get(0) { - Some(path) => { - let _ = cx.editor.open(path.into(), Action::Replace)?; - - Ok(()) - } - None => { - bail!("wrong argument count"); - } - } + let path = args.get(0).context("wrong argument count")?; + let _ = cx.editor.open(path.into(), Action::Replace)?; + Ok(()) } fn write_impl>( @@ -1217,9 +1210,7 @@ mod cmd { let (_, doc) = current!(cx.editor); if let Some(path) = path { - if let Err(err) = doc.set_path(path.as_ref()) { - bail!("invalid filepath: {}", err); - }; + doc.set_path(path.as_ref()).context("invalid filepath")?; } if doc.path().is_none() { bail!("cannot write a buffer without a filename"); @@ -1306,14 +1297,11 @@ mod cmd { _ => None, }; - if let Some(s) = style { - let doc = doc_mut!(cx.editor); - doc.indent_style = s; + let style = style.context("invalid indent style")?; + let doc = doc_mut!(cx.editor); + doc.indent_style = style; - Ok(()) - } else { - bail!("invalid indent style '{}'", args[0]); - } + Ok(()) } /// Sets or reports the current document's line ending setting. @@ -1352,12 +1340,9 @@ mod cmd { _ => None, }; - if let Some(le) = line_ending { - doc_mut!(cx.editor).line_ending = le; - Ok(()) - } else { - bail!("invalid line ending '{}'", args[0]); - } + let line_ending = line_ending.context("invalid line ending")?; + doc_mut!(cx.editor).line_ending = line_ending; + Ok(()) } fn earlier( @@ -1397,7 +1382,7 @@ mod cmd { event: PromptEvent, ) -> anyhow::Result<()> { let handle = write_impl(cx, args.first())?; - helix_lsp::block_on(handle)?; + let _ = helix_lsp::block_on(handle)?; quit(cx, &[], event) } @@ -1407,7 +1392,7 @@ mod cmd { event: PromptEvent, ) -> anyhow::Result<()> { let handle = write_impl(cx, args.first())?; - helix_lsp::block_on(handle)?; + let _ = helix_lsp::block_on(handle)?; force_quit(cx, &[], event) } @@ -1532,12 +1517,7 @@ mod cmd { args: &[&str], _event: PromptEvent, ) -> anyhow::Result<()> { - let theme = if let Some(theme) = args.first() { - theme - } else { - bail!("theme name not provided"); - }; - + let theme = args.first().context("theme not provided")?; cx.editor.set_theme_from_name(theme) } @@ -1598,10 +1578,7 @@ mod cmd { doc.append_changes_to_history(view.id); Ok(()) } - Err(e) => { - log::error!("Couldn't get system clipboard contents: {:?}", e); - bail!("Couldn't get system clipboard contents: {:?}", e); - } + Err(e) => Err(e.context("Couldn't get system clipboard contents")), } } @@ -1620,24 +1597,18 @@ mod cmd { args: &[&str], _event: PromptEvent, ) -> anyhow::Result<()> { - let dir = args - .first() - .ok_or_else(|| anyhow!("target directory not provided"))?; + let dir = args.first().context("target directory not provided")?; if let Err(e) = std::env::set_current_dir(dir) { bail!("Couldn't change the current working directory: {:?}", e); } - match std::env::current_dir() { - Ok(cwd) => { - cx.editor.set_status(format!( - "Current working directory is now {}", - cwd.display() - )); - Ok(()) - } - Err(e) => bail!("Couldn't get the new working directory: {}", e), - } + let cwd = std::env::current_dir().context("Couldn't get the new working directory")?; + cx.editor.set_status(format!( + "Current working directory is now {}", + cwd.display() + )); + Ok(()) } fn show_current_directory( @@ -1645,14 +1616,10 @@ mod cmd { _args: &[&str], _event: PromptEvent, ) -> anyhow::Result<()> { - match std::env::current_dir() { - Ok(cwd) => { - cx.editor - .set_status(format!("Current working directory is {}", cwd.display())); - Ok(()) - } - Err(e) => bail!("Couldn't get the current working directory: {}", e), - } + 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())); + Ok(()) } /// Sets the [`Document`]'s encoding.. @@ -2870,10 +2837,10 @@ fn yank_joined_to_clipboard_impl(editor: &mut Editor, separator: &str) -> anyhow let joined = values.join(separator); - if let Err(e) = editor.clipboard_provider.set_contents(joined) { - log::error!("Couldn't set system clipboard content: {:?}", e); - bail!("Couldn't set system clipboard content: {:?}", e); - } + editor + .clipboard_provider + .set_contents(joined) + .context("Couldn't set system clipboard content")?; editor.set_status(msg); @@ -2894,7 +2861,6 @@ fn yank_main_selection_to_clipboard_impl(editor: &mut Editor) -> anyhow::Result< .fragment(doc.text().slice(..)); if let Err(e) = editor.clipboard_provider.set_contents(value.into_owned()) { - log::error!("Couldn't set system clipboard content: {:?}", e); bail!("Couldn't set system clipboard content: {:?}", e); } @@ -2965,10 +2931,7 @@ fn paste_clipboard_impl(editor: &mut Editor, action: Paste) -> anyhow::Result<() Ok(()) } Ok(None) => Ok(()), - Err(e) => { - log::error!("Couldn't get system clipboard contents: {:?}", e); - bail!("Couldn't get system clipboard contents: {:?}", e); - } + Err(e) => Err(e.context("Couldn't get system clipboard contents")), } } @@ -3000,7 +2963,7 @@ fn replace_with_yanked(cx: &mut Context) { } } -fn replace_selections_with_clipboard_impl(editor: &mut Editor) { +fn replace_selections_with_clipboard_impl(editor: &mut Editor) -> anyhow::Result<()> { let (view, doc) = current!(editor); match editor.clipboard_provider.get_contents() { @@ -3014,13 +2977,14 @@ fn replace_selections_with_clipboard_impl(editor: &mut Editor) { doc.apply(&transaction, view.id); doc.append_changes_to_history(view.id); + Ok(()) } - Err(e) => log::error!("Couldn't get system clipboard contents: {:?}", e), + Err(e) => Err(e.context("Couldn't get system clipboard contents")), } } fn replace_selections_with_clipboard(cx: &mut Context) { - replace_selections_with_clipboard_impl(&mut cx.editor); + let _ = replace_selections_with_clipboard_impl(&mut cx.editor); } fn paste_after(cx: &mut Context) { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index c80535ed..a468b87c 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -98,14 +98,11 @@ impl Editor { } pub fn set_theme_from_name(&mut self, theme: &str) -> anyhow::Result<()> { - let theme = match self.theme_loader.load(theme.as_ref()) { - Ok(theme) => theme, - Err(e) => { - log::warn!("failed setting theme `{}` - {}", theme, e); - anyhow::bail!("failed setting theme `{}` - {}", theme, e); - } - }; - + use anyhow::Context; + let theme = self + .theme_loader + .load(theme.as_ref()) + .with_context(|| format!("failed setting theme `{}`", theme))?; self.set_theme(theme); Ok(()) } -- cgit v1.2.3-70-g09d2 From 4a5cb0e04bf4861fe58ead4a83dc90a9f9940f1c Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Sun, 11 Jul 2021 16:42:23 +0900 Subject: Restore C-w shortcut --- helix-term/src/commands.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'helix-term') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 8bbdfdfa..3b208ca8 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3750,7 +3750,7 @@ mode_info! { /// window window_mode, WINDOW_MODE, /// rotate - "w" => rotate_view, + "w" | "C-w" => rotate_view, /// horizontal split "h" => hsplit, /// vertical split -- cgit v1.2.3-70-g09d2 From e2bcef718abfe9df8b3169e6017053506de5e1d1 Mon Sep 17 00:00:00 2001 From: Cor Date: Thu, 15 Jul 2021 10:27:27 +0200 Subject: Removed double entry of extend_line --- helix-term/src/keymap.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'helix-term') diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index d815e006..fe32e49f 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -120,7 +120,6 @@ impl Default for Keymaps { alt!(';') => Command::flip_selections, key!('%') => Command::select_all, key!('x') => Command::extend_line, - key!('x') => Command::extend_line, key!('X') => Command::extend_to_line_bounds, // crop_to_whole_line -- cgit v1.2.3-70-g09d2 From 722cfedb382abf2a4e48ac45bdfd9b3c5de50a58 Mon Sep 17 00:00:00 2001 From: Cor Peters Date: Fri, 16 Jul 2021 18:12:59 +0200 Subject: Added change_case command (#441) * Added change_case command * Added switch_to_uppercase and switch_to_lowercase Renamed change_case to switch_case. * Updated the Keymap section of the Book * Use flat_map instead of map + flatten * Fix switch_to_uppercase using to_lowercase * Switched 'Alt-`' to uppercase and '`' to lowercase Co-authored-by: Cor --- book/src/keymap.md | 43 +++++++++++++++++++----------------- helix-term/src/commands.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++ helix-term/src/keymap.rs | 4 ++++ 3 files changed, 81 insertions(+), 20 deletions(-) (limited to 'helix-term') diff --git a/book/src/keymap.md b/book/src/keymap.md index c0c455d3..5d6e5795 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -41,26 +41,29 @@ ### Changes -| Key | Description | -| ----- | ----------- | -| `r` | Replace with a character | -| `R` | Replace with yanked text | -| `i` | Insert before selection | -| `a` | Insert after selection (append) | -| `I` | Insert at the start of the line | -| `A` | Insert at the end of the line | -| `o` | Open new line below selection | -| `o` | Open new line above selection | -| `u` | Undo change | -| `U` | Redo change | -| `y` | Yank selection | -| `p` | Paste after selection | -| `P` | Paste before selection | -| `>` | Indent selection | -| `<` | Unindent selection | -| `=` | Format selection | -| `d` | Delete selection | -| `c` | Change selection (delete and enter insert mode) | +| Key | Description | +| ----- | ----------- | +| `r` | Replace with a character | +| `R` | Replace with yanked text | +| `~` | Switch case of the selected text | +| `\`` | Set the selected text to upper case | +| `Alt-\`` | Set the selected text to lower case | +| `i` | Insert before selection | +| `a` | Insert after selection (append) | +| `I` | Insert at the start of the line | +| `A` | Insert at the end of the line | +| `o` | Open new line below selection | +| `o` | Open new line above selection | +| `u` | Undo change | +| `U` | Redo change | +| `y` | Yank selection | +| `p` | Paste after selection | +| `P` | Paste before selection | +| `>` | Indent selection | +| `<` | Unindent selection | +| `=` | Format selection | +| `d` | Delete selection | +| `c` | Change selection (delete and enter insert mode) | ### Selection manipulation diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 3b208ca8..7e769f4e 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -186,6 +186,9 @@ impl Command { extend_till_prev_char, extend_prev_char, replace, + switch_case, + switch_to_uppercase, + switch_to_lowercase, page_up, page_down, half_page_up, @@ -780,6 +783,57 @@ fn replace(cx: &mut Context) { }) } +fn switch_case(cx: &mut Context) { + let (view, doc) = current!(cx.editor); + let transaction = + Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { + let text: Tendril = range + .fragment(doc.text().slice(..)) + .chars() + .flat_map(|ch| { + if ch.is_lowercase() { + ch.to_uppercase().collect() + } else if ch.is_uppercase() { + ch.to_lowercase().collect() + } else { + vec![ch] + } + }) + .collect(); + + (range.from(), range.to() + 1, Some(text)) + }); + + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); +} + +fn switch_to_uppercase(cx: &mut Context) { + let (view, doc) = current!(cx.editor); + let transaction = + Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { + let text: Tendril = range.fragment(doc.text().slice(..)).to_uppercase().into(); + + (range.from(), range.to() + 1, Some(text)) + }); + + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); +} + +fn switch_to_lowercase(cx: &mut Context) { + let (view, doc) = current!(cx.editor); + let transaction = + Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { + let text: Tendril = range.fragment(doc.text().slice(..)).to_lowercase().into(); + + (range.from(), range.to() + 1, Some(text)) + }); + + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view.id); +} + fn scroll(cx: &mut Context, offset: usize, direction: Direction) { use Direction::*; let (view, doc) = current!(cx.editor); diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index fe32e49f..32994c37 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -82,6 +82,10 @@ impl Default for Keymaps { key!('r') => Command::replace, key!('R') => Command::replace_with_yanked, + key!('~') => Command::switch_case, + alt!('`') => Command::switch_to_uppercase, + key!('`') => Command::switch_to_lowercase, + key!(Home) => Command::goto_line_start, key!(End) => Command::goto_line_end, -- cgit v1.2.3-70-g09d2 From 9fcbbfa46762484fc132383d9c2855ce37f60d6f Mon Sep 17 00:00:00 2001 From: Cor Date: Wed, 14 Jul 2021 21:29:39 +0200 Subject: Changed startup behaviour to only open a single view when multiple files are specified on the commandline. Changed the behaviour; the first argument on the commandline is the file on display --- helix-term/src/application.rs | 5 ++++- helix-view/src/editor.rs | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'helix-term') diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 79dd7c3b..c55d4c98 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -83,15 +83,18 @@ impl Application { editor.new_file(Action::VerticalSplit); compositor.push(Box::new(ui::file_picker(first.clone()))); } else { + let nr_of_files = args.files.len(); + editor.open(first.to_path_buf(), Action::VerticalSplit)?; for file in args.files { if file.is_dir() { return Err(anyhow::anyhow!( "expected a path to file, found a directory. (to open a directory pass it as first argument)" )); } else { - editor.open(file, Action::VerticalSplit)?; + editor.open(file.to_path_buf(), Action::Load)?; } } + editor.set_status(format!("Loaded {} files.", nr_of_files)); } } else { editor.new_file(Action::VerticalSplit); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index a468b87c..cd9d0a92 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -39,6 +39,7 @@ pub struct Editor { #[derive(Debug, Copy, Clone)] pub enum Action { + Load, Replace, HorizontalSplit, VerticalSplit, @@ -151,6 +152,9 @@ impl Editor { return; } + Action::Load => { + return; + } Action::HorizontalSplit => { let view = View::new(id); let view_id = self.tree.split(view, Layout::Horizontal); -- cgit v1.2.3-70-g09d2