diff --git a/book/src/editor.md b/book/src/editor.md index b091eea87b4a..b6e589d7132b 100644 --- a/book/src/editor.md +++ b/book/src/editor.md @@ -106,7 +106,7 @@ The `[editor.statusline]` key takes the following sub-keys: | Key | Description | Default | | --- | --- | --- | -| `left` | A list of elements aligned to the left of the statusline | `["mode", "spinner", "file-name", "read-only-indicator", "file-modification-indicator"]` | +| `left` | A list of elements aligned to the left of the statusline | `["mode", "spinner", "file-name", "read-only-indicator", "zoom", "file-modification-indicator"]` | | `center` | A list of elements aligned to the middle of the statusline | `[]` | | `right` | A list of elements aligned to the right of the statusline | `["diagnostics", "selections", "register", "position", "file-encoding"]` | | `separator` | The character used to separate elements in the statusline | `"│"` | @@ -139,6 +139,7 @@ The following statusline elements can be configured: | `spacer` | Inserts a space between elements (multiple/contiguous spacers may be specified) | | `version-control` | The current branch name or detached commit hash of the opened workspace | | `register` | The current selected register | +| `zoom` | The current window zoom/zen state | ### `[editor.lsp]` Section diff --git a/book/src/generated/static-cmd.md b/book/src/generated/static-cmd.md index 08c8e7023a7f..c7fc05d74e11 100644 --- a/book/src/generated/static-cmd.md +++ b/book/src/generated/static-cmd.md @@ -234,6 +234,7 @@ | `vsplit_new` | Vertical right split scratch buffer | normal: `` nv ``, `` wnv ``, `` n ``, `` wn ``, select: `` nv ``, `` wnv ``, `` n ``, `` wn `` | | `wclose` | Close window | normal: `` q ``, `` wq ``, `` ``, `` w ``, select: `` q ``, `` wq ``, `` ``, `` w `` | | `wonly` | Close windows except current | normal: `` o ``, `` wo ``, `` ``, `` w ``, select: `` o ``, `` wo ``, `` ``, `` w `` | +| `toggle_zoom` | Toggle zoom for current window | normal: `` z ``, `` wz ``, select: `` z ``, `` wz `` | | `select_register` | Select register | normal: `` " ``, select: `` " `` | | `insert_register` | Insert register | insert: `` `` | | `align_view_middle` | Align view middle | normal: `` Zm ``, `` zm ``, select: `` Zm ``, `` zm `` | diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index f0d9a0f492a5..e9dbc9c6fee4 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -88,3 +88,4 @@ | `:move`, `:mv` | Move the current buffer and its corresponding file to a different path | | `:yank-diagnostic` | Yank diagnostic(s) under primary cursor to register, or clipboard by default | | `:read`, `:r` | Load a file into buffer | +| `:set-max-width` | Set the maximum width of the editor, or swap between 2 widths. If set to 0 it will take up the entire width. | diff --git a/book/src/keymap.md b/book/src/keymap.md index 2797eaee2908..f7f854ed34a5 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -274,6 +274,7 @@ This layer is similar to Vim keybindings as Kakoune does not support windows. | `J` | Swap window downwards | `swap_view_down` | | `K` | Swap window upwards | `swap_view_up` | | `L` | Swap window to the right | `swap_view_right` | +| `z` | Toggle zoom for the focused view | `toggle_zoom` | #### Space mode diff --git a/book/src/remapping.md b/book/src/remapping.md index 4eb14c558e29..85f8281bf7eb 100644 --- a/book/src/remapping.md +++ b/book/src/remapping.md @@ -68,6 +68,9 @@ k = "scroll_down" m = ":run-shell-command make" c = ":run-shell-command cargo build" t = ":run-shell-command cargo test" + +# Creates a basic 'zen-mode' similar to VSCode's +z = ["toggle_zoom", ":set-max-width 120 0", ":set gutters.layout []"] ``` ## Special keys and modifiers diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 85842523ca81..126875fbc4a4 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -527,6 +527,7 @@ impl MappableCommand { vsplit_new, "Vertical right split scratch buffer", wclose, "Close window", wonly, "Close windows except current", + toggle_zoom, "Toggle zoom for current window", select_register, "Select register", insert_register, "Insert register", align_view_middle, "Align view middle", @@ -5475,6 +5476,11 @@ fn wonly(cx: &mut Context) { } } +fn toggle_zoom(cx: &mut Context) { + cx.editor.tree.zoom = !cx.editor.tree.zoom; + cx.editor.tree.recalculate(); +} + fn select_register(cx: &mut Context) { cx.editor.autoinfo = Some(Info::from_registers(&cx.editor.registers)); cx.on_next_key(move |cx, event| { diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 4a2546d7c8d8..7fc6440f89bd 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2539,6 +2539,44 @@ fn read(cx: &mut compositor::Context, args: &[Cow], event: PromptEvent) -> Ok(()) } +fn set_max_width( + cx: &mut compositor::Context, + args: &[Cow], + event: PromptEvent, +) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + + let mut args = args.iter(); + let Some(width) = args.next() else { + bail!(":set-max-width takes 1 or 2 arguments") + }; + let width: u16 = width.parse()?; + let alt_width: Option = args.next().map(|w| w.parse()).transpose()?; + + let set_width = match alt_width { + Some(alt_width) if cx.editor.tree.max_width == width => { + cx.editor.tree.max_width = alt_width; + alt_width + } + _ => { + cx.editor.tree.max_width = width; + width + } + }; + cx.editor.tree.recalculate(); + + if set_width == 0 { + cx.editor.set_status("Unset maximum width"); + } else { + cx.editor + .set_status(format!("Set maximum width to {}", set_width)); + } + + Ok(()) +} + pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "quit", @@ -3160,6 +3198,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ fun: read, signature: CommandSignature::positional(&[completers::filename]), }, + TypableCommand { + name: "set-max-width", + aliases: &[], + doc: "Set the maximum width of the editor, or swap between 2 widths. If set to 0 it will take up the entire width.", + fun: set_max_width, + signature: CommandSignature::positional(&[completers::none]), + }, ]; pub static TYPABLE_COMMAND_MAP: Lazy> = diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index c6cefd927574..660b25b46974 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -208,6 +208,7 @@ pub fn default() -> HashMap { "C-s" | "s" => hsplit_new, "C-v" | "v" => vsplit_new, }, + "z" => toggle_zoom, }, // move under c @@ -273,6 +274,7 @@ pub fn default() -> HashMap { "C-s" | "s" => hsplit_new, "C-v" | "v" => vsplit_new, }, + "z" => toggle_zoom, }, "y" => yank_to_clipboard, "Y" => yank_main_selection_to_clipboard, diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 5d028415e85f..e42e0c338b2b 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -212,8 +212,8 @@ impl EditorView { ); Self::render_rulers(editor, doc, view, inner, surface, theme); - // if we're not at the edge of the screen, draw a right border - if viewport.right() != view.area.right() { + // if we're not at the edge of the screen or zoomed, draw a right border + if viewport.right() != view.area.right() && !editor.tree.zoom { let x = area.right(); let border_style = theme.get("ui.window"); for y in area.top()..area.bottom() { @@ -1150,8 +1150,13 @@ impl EditorView { .. } = *event; + // In zoom, only the focused view is interactable. + let zoom = cxt.editor.tree.zoom; let pos_and_view = |editor: &Editor, row, column, ignore_virtual_text| { - editor.tree.views().find_map(|(view, _focus)| { + editor.tree.views().find_map(|(view, focus)| { + if zoom && !focus { + return None; + } view.pos_at_screen_coords( &editor.documents[&view.doc], row, @@ -1163,7 +1168,10 @@ impl EditorView { }; let gutter_coords_and_view = |editor: &Editor, row, column| { - editor.tree.views().find_map(|(view, _focus)| { + editor.tree.views().find_map(|(view, focus)| { + if zoom && !focus { + return None; + } view.gutter_coords_at_screen_coords(row, column) .map(|coords| (coords, view.id)) }) @@ -1558,8 +1566,12 @@ impl Component for EditorView { } for (view, is_focused) in cx.editor.tree.views() { - let doc = cx.editor.document(view.doc).unwrap(); - self.render_view(cx.editor, doc, view, area, surface, is_focused); + // If in zoom, only the focused document is rendered, with the unfocused ones being + // positioned off-screen. + if !cx.editor.tree.zoom || is_focused { + let doc = cx.editor.document(view.doc).unwrap(); + self.render_view(cx.editor, doc, view, area, surface, is_focused); + } } if config.auto_info { diff --git a/helix-term/src/ui/statusline.rs b/helix-term/src/ui/statusline.rs index 7437cbd074e5..6eeb27392107 100644 --- a/helix-term/src/ui/statusline.rs +++ b/helix-term/src/ui/statusline.rs @@ -163,6 +163,7 @@ where helix_view::editor::StatusLineElement::Spacer => render_spacer, helix_view::editor::StatusLineElement::VersionControl => render_version_control, helix_view::editor::StatusLineElement::Register => render_register, + helix_view::editor::StatusLineElement::Zoom => render_zoom, } } @@ -531,3 +532,12 @@ where write(context, format!(" reg={} ", reg), None) } } + +fn render_zoom(context: &mut RenderContext, write: F) +where + F: Fn(&mut RenderContext, String, Option