diff --git a/book/src/keymap.md b/book/src/keymap.md index 05f71d3a6..65718655a 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -142,9 +142,8 @@ over text and not actively editing it). #### Goto mode -Jumps to various locations. - -> NOTE: Some of these features are only available with the LSP present. +Jumps to various locations. Mappings marked (**LSP**) require an +active language server for the file to work. | Key | Description | Command | | ----- | ----------- | ------- | @@ -156,10 +155,10 @@ Jumps to various locations. | `t` | Go to the top of the screen | `goto_window_top` | | `m` | Go to the middle of the screen | `goto_window_middle` | | `b` | Go to the bottom of the screen | `goto_window_bottom` | -| `d` | Go to definition | `goto_definition` | -| `y` | Go to type definition | `goto_type_definition` | -| `r` | Go to references | `goto_reference` | -| `i` | Go to implementation | `goto_implementation` | +| `d` | Go to definition (**LSP**) | `goto_definition` | +| `y` | Go to type definition (**LSP**) | `goto_type_definition` | +| `r` | Go to references (**LSP**) | `goto_reference` | +| `i` | Go to implementation (**LSP**) | `goto_implementation` | | `a` | Go to the last accessed/alternate file | `goto_last_accessed_file` | | `n` | Go to next buffer | `goto_next_buffer` | | `p` | Go to previous buffer | `goto_previous_buffer` | @@ -198,15 +197,18 @@ This layer is similar to vim keybindings as kakoune does not support window. #### Space mode -This layer is a kludge of mappings, mostly pickers. +This layer is a kludge of mappings, mostly pickers. Mappings marked +(**LSP**) require an active language server for the file to work. + | Key | Description | Command | | ----- | ----------- | ------- | -| `k` | Show documentation for the item under the cursor | `hover` | | `f` | Open file picker | `file_picker` | | `b` | Open buffer picker | `buffer_picker` | -| `s` | Open symbol picker (current document) | `symbol_picker` | -| `a` | Apply code action | `code_action` | +| `k` | Show documentation for item under cursor (**LSP**) | `hover` | +| `s` | Open document symbol picker (**LSP**) | `symbol_picker` | +| `r` | Rename symbol (**LSP**) | `rename_symbol` | +| `a` | Apply code action (**LSP**) | `code_action` | | `'` | Open last fuzzy picker | `last_picker` | | `w` | Enter [window mode](#window-mode) | N/A | | `p` | Paste system clipboard after selections | `paste_clipboard_after` | @@ -215,9 +217,8 @@ This layer is a kludge of mappings, mostly pickers. | `Y` | Yank main selection to clipboard | `yank_main_selection_to_clipboard` | | `R` | Replace selections by clipboard contents | `replace_selections_with_clipboard` | | `/` | Global search in workspace folder | `global_search` | -| `r` | Rename symbol using the language server | `rename_symbol` | -> NOTE: Global search display results in a fuzzy picker, use `space + '` to bring it back up after opening a file. +> TIP: Global search displays results in a fuzzy picker, use `space + '` to bring it back up after opening a file. #### Unimpaired @@ -234,25 +235,11 @@ Mappings in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaire ## Insert Mode -| Key | Description | Command | -| ----- | ----------- | ------- | -| `Escape` | Switch to normal mode | `normal_mode` | -| `Ctrl-x` | Autocomplete | `completion` | -| `Ctrl-w` | Delete previous word | `delete_word_backward` | -| `Alt-d` | Delete next word | `delete_word_forward` | -| `Alt-b`, `Alt-Left` | Backward a word | `move_prev_word_end` | -| `Ctrl-b`, `Left` | Backward a char | `move_char_left` | -| `Alt-f`, `Alt-Right` | Forward a word | `move_next_word_start` | -| `Ctrl-f`, `Right` | Forward a char | `move_char_right` | -| `Ctrl-e`, `End` | move to line end | `goto_line_end_newline` | -| `Ctrl-a`, `Home` | move to line start | `goto_line_start` | -| `Ctrl-w` | delete previous word | `delete_word_backwar` | -| `Ctrl-u` | delete to start of line | `kill_to_line_start` | -| `Ctrl-k` | delete to end of line | `kill_to_line_end` | -| `backspace`, `Ctrl-h` | delete previous char | `delete_char_backward` | -| `delete`, `Ctrl-d` | delete previous char | `delete_char_forward` | -| `Ctrl-p`, `Up` | move to previous line | `move_line_up` | -| `Ctrl-n`, `Down` | move to next line | `move_line_down` | +| Key | Description | Command | +| ----- | ----------- | ------- | +| `Escape` | Switch to normal mode | `normal_mode` | +| `Ctrl-x` | Autocomplete | `completion` | +| `Ctrl-w` | Delete previous word | `delete_word_backward` | ## Select / extend mode @@ -277,7 +264,9 @@ Keys to use within picker. Remapping currently not supported. | `Escape`, `Ctrl-c` | Close picker | # Prompt + Keys to use within prompt, Remapping currently not supported. + | Key | Description | | ----- | ------------- | | `Escape`, `Ctrl-c` | Close prompt | @@ -285,18 +274,18 @@ Keys to use within prompt, Remapping currently not supported. | `Ctrl-b`, `Left` | Backward a char | | `Alt-f`, `Alt-Right` | Forward a word | | `Ctrl-f`, `Right` | Forward a char | -| `Ctrl-e`, `End` | move prompt end | -| `Ctrl-a`, `Home` | move prompt start | -| `Ctrl-w` | delete previous word | -| `Alt-d` | delete next word | -| `Ctrl-u` | delete to start of line | -| `Ctrl-k` | delete to end of line | -| `backspace`, `Ctrl-h` | delete previous char | -| `delete`, `Ctrl-d` | delete previous char | -| `Ctrl-s` | insert a word under doc cursor, may be changed to Ctrl-r Ctrl-w later | -| `Ctrl-p`, `Up` | select previous history | -| `Ctrl-n`, `Down` | select next history | -| `Tab` | slect next completion item | -| `BackTab` | slect previous completion item | +| `Ctrl-e`, `End` | Move prompt end | +| `Ctrl-a`, `Home` | Move prompt start | +| `Ctrl-w` | Delete previous word | +| `Alt-d` | Delete next word | +| `Ctrl-u` | Delete to start of line | +| `Ctrl-k` | Delete to end of line | +| `backspace`, `Ctrl-h` | Delete previous char | +| `delete`, `Ctrl-d` | Delete previous char | +| `Ctrl-s` | Insert a word under doc cursor, may be changed to Ctrl-r Ctrl-w later | +| `Ctrl-p`, `Up` | Select previous history | +| `Ctrl-n`, `Down` | Select next history | +| `Tab` | Select next completion item | +| `BackTab` | Select previous completion item | | `Enter` | Open selected | diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index f18841992..b04eef0d8 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -7,7 +7,7 @@ use crate::{args::Args, compositor::Compositor, config::Config, job::Jobs, ui}; use log::{error, warn}; use std::{ - io::{stdout, Write}, + io::{stdin, stdout, Write}, sync::Arc, time::{Duration, Instant}, }; @@ -17,6 +17,7 @@ use anyhow::Error; use crossterm::{ event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream}, execute, terminal, + tty::IsTty, }; #[cfg(not(windows))] use { @@ -134,8 +135,17 @@ impl Application { } editor.set_status(format!("Loaded {} files.", nr_of_files)); } - } else { + } else if stdin().is_tty() { editor.new_file(Action::VerticalSplit); + } else if cfg!(target_os = "macos") { + // On Linux and Windows, we allow the output of a command to be piped into the new buffer. + // This doesn't currently work on macOS because of the following issue: + // https://github.com/crossterm-rs/crossterm/issues/500 + anyhow::bail!("Piping into helix-term is currently not supported on macOS"); + } else { + editor + .new_file_from_stdin(Action::VerticalSplit) + .unwrap_or_else(|_| editor.new_file(Action::VerticalSplit)); } editor.set_theme(theme); diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index bdb875ad8..7d2cd67e2 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2618,36 +2618,66 @@ fn file_picker(cx: &mut Context) { fn buffer_picker(cx: &mut Context) { let current = view!(cx.editor).doc; + struct BufferMeta { + id: DocumentId, + path: Option, + is_modified: bool, + is_current: bool, + } + + impl BufferMeta { + fn format(&self) -> Cow { + let path = self + .path + .as_deref() + .map(helix_core::path::get_relative_path); + let path = match path.as_deref().and_then(Path::to_str) { + Some(path) => path, + None => return Cow::Borrowed("[scratch buffer]"), + }; + + let mut flags = Vec::new(); + if self.is_modified { + flags.push("+"); + } + if self.is_current { + flags.push("*"); + } + + let flag = if flags.is_empty() { + "".into() + } else { + format!(" ({})", flags.join("")) + }; + Cow::Owned(format!("{}{}", path, flag)) + } + } + + let new_meta = |doc: &Document| BufferMeta { + id: doc.id(), + path: doc.path().cloned(), + is_modified: doc.is_modified(), + is_current: doc.id() == current, + }; + let picker = FilePicker::new( cx.editor .documents .iter() - .map(|(id, doc)| (*id, doc.path().cloned())) + .map(|(_, doc)| new_meta(doc)) .collect(), - move |(id, path): &(DocumentId, Option)| { - let path = path.as_deref().map(helix_core::path::get_relative_path); - match path.as_ref().and_then(|path| path.to_str()) { - Some(path) => { - if *id == current { - format!("{} (*)", &path).into() - } else { - path.to_owned().into() - } - } - None => "[scratch buffer]".into(), - } - }, - |editor: &mut Editor, (id, _path): &(DocumentId, Option), _action| { - editor.switch(*id, Action::Replace); + BufferMeta::format, + |editor: &mut Editor, meta, _action| { + editor.switch(meta.id, Action::Replace); }, - |editor, (id, path)| { - let doc = &editor.documents.get(id)?; + |editor, meta| { + let doc = &editor.documents.get(&meta.id)?; let &view_id = doc.selections().keys().next()?; let line = doc .selection(view_id) .primary() .cursor_line(doc.text().slice(..)); - Some((path.clone()?, Some((line, line)))) + Some((meta.path.clone()?, Some((line, line)))) }, ); cx.push_layer(Box::new(picker)); diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 24eb7acdf..62da0dce8 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -35,6 +35,7 @@ pub fn regex_prompt( let (view, doc) = current!(cx.editor); let view_id = view.id; let snapshot = doc.selection(view_id).clone(); + let offset_snapshot = view.offset; Prompt::new( prompt, @@ -45,6 +46,7 @@ pub fn regex_prompt( PromptEvent::Abort => { let (view, doc) = current!(cx.editor); doc.set_selection(view.id, snapshot.clone()); + view.offset = offset_snapshot; } PromptEvent::Validate => { // TODO: push_jump to store selection just before jump diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 631dcf0c6..7650d217f 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -9,6 +9,7 @@ use crate::{ use futures_util::future; use std::{ collections::BTreeMap, + io::stdin, path::{Path, PathBuf}, pin::Pin, sync::Arc, @@ -314,16 +315,24 @@ impl Editor { self._refresh(); } - pub fn new_file(&mut self, action: Action) -> DocumentId { + fn new_file_from_document(&mut self, action: Action, mut document: Document) -> DocumentId { let id = DocumentId(self.next_document_id); self.next_document_id += 1; - let mut doc = Document::default(); - doc.id = id; - self.documents.insert(id, doc); + document.id = id; + self.documents.insert(id, document); self.switch(id, action); id } + pub fn new_file(&mut self, action: Action) -> DocumentId { + self.new_file_from_document(action, Document::default()) + } + + pub fn new_file_from_stdin(&mut self, action: Action) -> Result { + let (rope, encoding) = crate::document::from_reader(&mut stdin(), None)?; + Ok(self.new_file_from_document(action, Document::from(rope, Some(encoding)))) + } + pub fn open(&mut self, path: PathBuf, action: Action) -> Result { let path = helix_core::path::get_canonicalized_path(&path)?;