Skip to content

Commit 1067ef4

Browse files
committed
Added zen-view mode
1 parent c69ad7b commit 1067ef4

File tree

9 files changed

+184
-7
lines changed

9 files changed

+184
-7
lines changed

book/src/configuration.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ The `[editor.statusline]` key takes the following sub-keys:
9494

9595
| Key | Description | Default |
9696
| --- | --- | --- |
97-
| `left` | A list of elements aligned to the left of the statusline | `["mode", "spinner", "file-name", "read-only-indicator", "file-modification-indicator"]` |
97+
| `left` | A list of elements aligned to the left of the statusline | `["mode", "spinner", "file-name", "read-only-indicator", "zoom", "file-modification-indicator"]` |
9898
| `center` | A list of elements aligned to the middle of the statusline | `[]` |
9999
| `right` | A list of elements aligned to the right of the statusline | `["diagnostics", "selections", "register", "position", "file-encoding"]` |
100100
| `separator` | The character used to separate elements in the statusline | `"│"` |
@@ -127,6 +127,7 @@ The following statusline elements can be configured:
127127
| `spacer` | Inserts a space between elements (multiple/contiguous spacers may be specified) |
128128
| `version-control` | The current branch name or detached commit hash of the opened workspace |
129129
| `register` | The current selected register |
130+
| `zoom` | The current window zoom/zen state |
130131

131132
### `[editor.lsp]` Section
132133

@@ -402,3 +403,20 @@ S-tab = "move_parent_node_start"
402403
tab = "extend_parent_node_end"
403404
S-tab = "extend_parent_node_start"
404405
```
406+
407+
### `[editor.zen-view]` Section
408+
409+
Options for the zen-view mode.
410+
411+
Zen-view will center the currently-focused editor on the screen and hide all the others, useful for when you want to focus on editing
412+
a single file with either a wider monitor or if you have a lot of splits at once.
413+
414+
| Key | Description | Default |
415+
|--------------|-------------|---------|
416+
| `max-width` | The maximum width that the zen-view will take up. | `120` |
417+
| `auto-enter` | Whether zen-view should be automatically entered on startup. Can be one of `"off"` (never auto-enter), `"single-file"` (only auto-enter if Helix is opened with a single file), `"multi-file"` (only auto-enter if opened with >1 files), `"always"` (always start in zen-view). | `"off"` |
418+
419+
#### `[editor.zen-view.gutters]` Section
420+
421+
Same as the [normal gutters](#editorgutters-section) option, but specifically for zen-view.
422+
It only supports the `layout` option currently (including setting `gutters = [...]` directly).

book/src/keymap.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ useful when you're simply looking over text and not actively editing it.
195195
| `Ctrl-b`, `PageUp` | Move page up | `page_up` |
196196
| `Ctrl-u` | Move cursor and page half page up | `page_cursor_half_up` |
197197
| `Ctrl-d` | Move cursor and page half page down | `page_cursor_half_down` |
198+
| `Z` | Toggle 'zen-mode' for the focused view | `toggle_zen_view` |
198199

199200
#### Goto mode
200201

helix-term/src/application.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ impl Application {
204204
nr_of_files,
205205
if nr_of_files == 1 { "" } else { "s" } // avoid "Loaded 1 files." grammo
206206
));
207+
editor.tree.zoom = editor
208+
.config()
209+
.zen_view
210+
.auto_enter
211+
.should_enter(nr_of_files);
212+
editor.tree.recalculate();
207213
// align the view to center after all files are loaded,
208214
// does not affect views without pos since it is at the top
209215
let (view, doc) = current!(editor);

helix-term/src/commands.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ impl MappableCommand {
451451
vsplit_new, "Vertical right split scratch buffer",
452452
wclose, "Close window",
453453
wonly, "Close windows except current",
454+
toggle_zoom, "Toggle zoom for current window",
455+
toggle_zen_view, "Toggle zen view for current window",
454456
select_register, "Select register",
455457
insert_register, "Insert register",
456458
align_view_middle, "Align view middle",
@@ -5019,6 +5021,22 @@ fn wonly(cx: &mut Context) {
50195021
}
50205022
}
50215023

5024+
fn toggle_zoom(cx: &mut Context) {
5025+
cx.editor.tree.zoom = match cx.editor.tree.zoom {
5026+
Some(tree::ZoomMode::Normal) => None,
5027+
_ => Some(tree::ZoomMode::Normal),
5028+
};
5029+
cx.editor.tree.recalculate();
5030+
}
5031+
5032+
fn toggle_zen_view(cx: &mut Context) {
5033+
cx.editor.tree.zoom = match cx.editor.tree.zoom {
5034+
Some(tree::ZoomMode::Zen) => None,
5035+
_ => Some(tree::ZoomMode::Zen),
5036+
};
5037+
cx.editor.tree.recalculate();
5038+
}
5039+
50225040
fn select_register(cx: &mut Context) {
50235041
cx.editor.autoinfo = Some(Info::from_registers(&cx.editor.registers));
50245042
cx.on_next_key(move |cx, event| {

helix-term/src/keymap/default.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
203203
"C-s" | "s" => hsplit_new,
204204
"C-v" | "v" => vsplit_new,
205205
},
206+
"z" => toggle_zoom,
207+
"Z" => toggle_zen_view,
206208
},
207209

208210
// move under <space>c
@@ -267,6 +269,8 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
267269
"C-s" | "s" => hsplit_new,
268270
"C-v" | "v" => vsplit_new,
269271
},
272+
"z" => toggle_zoom,
273+
"Z" => toggle_zen_view,
270274
},
271275
"y" => yank_to_clipboard,
272276
"Y" => yank_main_selection_to_clipboard,

helix-term/src/ui/editor.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,14 @@ impl EditorView {
634634
let gutter_style_virtual = theme.get("ui.gutter.virtual");
635635
let gutter_selected_style_virtual = theme.get("ui.gutter.selected.virtual");
636636

637-
for gutter_type in view.gutters() {
637+
let config = editor.config();
638+
let gutters = if matches!(editor.tree.zoom, Some(helix_view::tree::ZoomMode::Zen)) {
639+
&config.zen_view.gutters.layout
640+
} else {
641+
view.gutters()
642+
};
643+
644+
for gutter_type in gutters {
638645
let mut gutter = gutter_type.style(editor, doc, view, theme, is_focused);
639646
let width = gutter_type.width(view, doc);
640647
// avoid lots of small allocations by reusing a text buffer for each line
@@ -1467,8 +1474,10 @@ impl Component for EditorView {
14671474
}
14681475

14691476
for (view, is_focused) in cx.editor.tree.views() {
1470-
let doc = cx.editor.document(view.doc).unwrap();
1471-
self.render_view(cx.editor, doc, view, area, surface, is_focused);
1477+
if cx.editor.tree.zoom.is_none() || is_focused {
1478+
let doc = cx.editor.document(view.doc).unwrap();
1479+
self.render_view(cx.editor, doc, view, area, surface, is_focused);
1480+
}
14721481
}
14731482

14741483
if config.auto_info {

helix-term/src/ui/statusline.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ fn get_render_function<'a>(
155155
helix_view::editor::StatusLineElement::Spacer => render_spacer,
156156
helix_view::editor::StatusLineElement::VersionControl => render_version_control,
157157
helix_view::editor::StatusLineElement::Register => render_register,
158+
helix_view::editor::StatusLineElement::Zoom => render_zoom,
158159
}
159160
}
160161

@@ -459,3 +460,13 @@ fn render_register<'a>(context: &RenderContext) -> Spans<'a> {
459460
Spans::default()
460461
}
461462
}
463+
464+
fn render_zoom<'a>(context: &RenderContext) -> Spans<'a> {
465+
let Some(zoom) = context.editor.tree.zoom else { return Spans::default() };
466+
467+
use helix_view::tree::ZoomMode as E;
468+
match zoom {
469+
E::Normal => "[zoom]".into(),
470+
E::Zen => "[zen]".into(),
471+
}
472+
}

helix-view/src/editor.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
input::KeyEvent,
88
register::Registers,
99
theme::{self, Theme},
10-
tree::{self, Tree},
10+
tree::{self, Tree, ZoomMode},
1111
view::ViewPosition,
1212
Align, Document, DocumentId, View, ViewId,
1313
};
@@ -325,6 +325,9 @@ pub struct Config {
325325
/// labels characters used in jumpmode
326326
#[serde(skip_serializing, deserialize_with = "deserialize_alphabet")]
327327
pub jump_label_alphabet: Vec<char>,
328+
/// Zen-view config.
329+
#[serde(default)]
330+
pub zen_view: ZenViewConfig,
328331
}
329332

330333
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)]
@@ -458,6 +461,7 @@ impl Default for StatusLineConfig {
458461
E::Spinner,
459462
E::FileName,
460463
E::ReadOnlyIndicator,
464+
E::Zoom,
461465
E::FileModificationIndicator,
462466
],
463467
center: vec![],
@@ -557,6 +561,9 @@ pub enum StatusLineElement {
557561

558562
/// Indicator for selected register
559563
Register,
564+
565+
/// Current zoom/zen state
566+
Zoom,
560567
}
561568

562569
// Cursor shape is read and used on every rendered frame and so needs
@@ -851,6 +858,69 @@ pub enum PopupBorderConfig {
851858
Menu,
852859
}
853860

861+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
862+
#[serde(rename_all = "kebab-case")]
863+
pub struct ZenViewConfig {
864+
#[serde(default = "ZenViewConfig::default_max_width")]
865+
pub max_width: u16,
866+
#[serde(default)]
867+
pub auto_enter: ZenViewAutoEnter,
868+
// Currently the line-numbers option isn't used, but it will
869+
// be once the updated config system is implemented in #9318
870+
// TODO(lizclipse): Once that PR is in implement the per-mode line_numbers config
871+
#[serde(default = "ZenViewConfig::default_gutters")]
872+
pub gutters: GutterConfig,
873+
}
874+
875+
impl ZenViewConfig {
876+
const fn default_max_width() -> u16 {
877+
120
878+
}
879+
880+
fn default_gutters() -> GutterConfig {
881+
GutterConfig {
882+
layout: vec![],
883+
..Default::default()
884+
}
885+
}
886+
}
887+
888+
impl Default for ZenViewConfig {
889+
fn default() -> Self {
890+
Self {
891+
max_width: Self::default_max_width(),
892+
auto_enter: ZenViewAutoEnter::default(),
893+
gutters: Self::default_gutters(),
894+
}
895+
}
896+
}
897+
898+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
899+
#[serde(rename_all = "kebab-case")]
900+
pub enum ZenViewAutoEnter {
901+
Off,
902+
SingleFile,
903+
MultiFile,
904+
Always,
905+
}
906+
907+
impl ZenViewAutoEnter {
908+
pub fn should_enter(&self, files: i32) -> Option<ZoomMode> {
909+
match self {
910+
Self::Always => Some(ZoomMode::Zen),
911+
Self::SingleFile if files == 1 => Some(ZoomMode::Zen),
912+
Self::MultiFile if files > 1 => Some(ZoomMode::Zen),
913+
_ => None,
914+
}
915+
}
916+
}
917+
918+
impl Default for ZenViewAutoEnter {
919+
fn default() -> Self {
920+
Self::Off
921+
}
922+
}
923+
854924
impl Default for Config {
855925
fn default() -> Self {
856926
Self {
@@ -902,6 +972,7 @@ impl Default for Config {
902972
popup_border: PopupBorderConfig::None,
903973
indent_heuristic: IndentationHeuristic::default(),
904974
jump_label_alphabet: ('a'..='z').collect(),
975+
zen_view: ZenViewConfig::default(),
905976
}
906977
}
907978
}
@@ -1076,13 +1147,19 @@ impl Editor {
10761147
let language_servers = helix_lsp::Registry::new(syn_loader.clone());
10771148
let conf = config.load();
10781149
let auto_pairs = (&conf.auto_pairs).into();
1150+
let mut tree = Tree::new(area);
1151+
tree.zen_max_width = conf.zen_view.max_width;
1152+
tree.zoom = match conf.zen_view.auto_enter {
1153+
ZenViewAutoEnter::Always => Some(ZoomMode::Zen),
1154+
_ => None,
1155+
};
10791156

10801157
// HAXX: offset the render area height by 1 to account for prompt/commandline
10811158
area.height -= 1;
10821159

10831160
Self {
10841161
mode: Mode::Normal,
1085-
tree: Tree::new(area),
1162+
tree,
10861163
next_document_id: DocumentId::default(),
10871164
documents: BTreeMap::new(),
10881165
saves: HashMap::new(),
@@ -1158,6 +1235,8 @@ impl Editor {
11581235
pub fn refresh_config(&mut self) {
11591236
let config = self.config();
11601237
self.auto_pairs = (&config.auto_pairs).into();
1238+
self.tree.zen_max_width = config.zen_view.max_width;
1239+
self.tree.recalculate();
11611240
self.reset_idle_timer();
11621241
self._refresh();
11631242
}

helix-view/src/tree.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ pub struct Tree {
88
root: ViewId,
99
// (container, index inside the container)
1010
pub focus: ViewId,
11-
// fullscreen: bool,
1211
area: Rect,
12+
// Maximum width to aim for when in zen-view.
13+
pub zen_max_width: u16,
14+
// The current zoom state.
15+
pub zoom: Option<ZoomMode>,
1316

1417
nodes: HopSlotMap<ViewId, Node>,
1518

@@ -29,6 +32,12 @@ pub enum Content {
2932
Container(Box<Container>),
3033
}
3134

35+
#[derive(Debug, Clone, Copy)]
36+
pub enum ZoomMode {
37+
Normal,
38+
Zen,
39+
}
40+
3241
impl Node {
3342
pub fn container(layout: Layout) -> Self {
3443
Self {
@@ -98,6 +107,8 @@ impl Tree {
98107
focus: root,
99108
// fullscreen: false,
100109
area,
110+
zen_max_width: 150,
111+
zoom: None,
101112
nodes,
102113
stack: Vec::new(),
103114
}
@@ -337,6 +348,26 @@ impl Tree {
337348
return;
338349
}
339350

351+
if let Some(zoom) = self.zoom {
352+
let width = match (zoom, self.zen_max_width) {
353+
(ZoomMode::Normal, _) => self.area.width,
354+
(ZoomMode::Zen, max_width) if max_width == 0 => self.area.width,
355+
(ZoomMode::Zen, max_width) => std::cmp::min(max_width, self.area.width),
356+
};
357+
let area = Rect::new(
358+
self.area.width / 2 - width / 2,
359+
self.area.y,
360+
width,
361+
self.area.height,
362+
);
363+
364+
for (view, _focused) in self.views_mut() {
365+
view.area = area;
366+
}
367+
368+
return;
369+
}
370+
340371
self.stack.push((self.root, self.area));
341372

342373
// take the area

0 commit comments

Comments
 (0)