Skip to content

Commit f4d9f6d

Browse files
committed
Added zen-view mode
Added zen-view docs Adjusted default zen-view width Adjusted tree ctor Added `zen-view.auto-enter` option Added `zen-view.gutters` option
1 parent 2e4653e commit f4d9f6d

File tree

9 files changed

+186
-7
lines changed

9 files changed

+186
-7
lines changed

book/src/configuration.md

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

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

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

@@ -398,3 +399,20 @@ S-tab = "move_parent_node_start"
398399
tab = "extend_parent_node_end"
399400
S-tab = "extend_parent_node_start"
400401
```
402+
403+
### `[editor.zen-view]` Section
404+
405+
Options for the zen-view mode.
406+
407+
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
408+
a single file with either a wider monitor or if you have a lot of splits at once.
409+
410+
| Key | Description | Default |
411+
|--------------|-------------|---------|
412+
| `max-width` | The maximum width that the zen-view will take up. | `150` |
413+
| `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"` |
414+
415+
#### `[editor.zen-view.gutters]` Section
416+
417+
Same as the [normal gutters](#editorgutters-section) option, but specifically for zen-view.
418+
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
@@ -449,6 +449,8 @@ impl MappableCommand {
449449
vsplit_new, "Vertical right split scratch buffer",
450450
wclose, "Close window",
451451
wonly, "Close windows except current",
452+
toggle_zoom, "Toggle zoom for current window",
453+
toggle_zen_view, "Toggle zen view for current window",
452454
select_register, "Select register",
453455
insert_register, "Insert register",
454456
align_view_middle, "Align view middle",
@@ -5007,6 +5009,22 @@ fn wonly(cx: &mut Context) {
50075009
}
50085010
}
50095011

5012+
fn toggle_zoom(cx: &mut Context) {
5013+
cx.editor.tree.zoom = match cx.editor.tree.zoom {
5014+
Some(tree::ZoomMode::Normal) => None,
5015+
_ => Some(tree::ZoomMode::Normal),
5016+
};
5017+
cx.editor.tree.recalculate();
5018+
}
5019+
5020+
fn toggle_zen_view(cx: &mut Context) {
5021+
cx.editor.tree.zoom = match cx.editor.tree.zoom {
5022+
Some(tree::ZoomMode::Zen) => None,
5023+
_ => Some(tree::ZoomMode::Zen),
5024+
};
5025+
cx.editor.tree.recalculate();
5026+
}
5027+
50105028
fn select_register(cx: &mut Context) {
50115029
cx.editor.autoinfo = Some(Info::from_registers(&cx.editor.registers));
50125030
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
@@ -202,6 +202,8 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
202202
"C-s" | "s" => hsplit_new,
203203
"C-v" | "v" => vsplit_new,
204204
},
205+
"z" => toggle_zoom,
206+
"Z" => toggle_zen_view,
205207
},
206208

207209
// move under <space>c
@@ -266,6 +268,8 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
266268
"C-s" | "s" => hsplit_new,
267269
"C-v" | "v" => vsplit_new,
268270
},
271+
"z" => toggle_zoom,
272+
"Z" => toggle_zen_view,
269273
},
270274
"y" => yank_to_clipboard,
271275
"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
@@ -1446,8 +1453,10 @@ impl Component for EditorView {
14461453
}
14471454

14481455
for (view, is_focused) in cx.editor.tree.views() {
1449-
let doc = cx.editor.document(view.doc).unwrap();
1450-
self.render_view(cx.editor, doc, view, area, surface, is_focused);
1456+
if cx.editor.tree.zoom.is_none() || is_focused {
1457+
let doc = cx.editor.document(view.doc).unwrap();
1458+
self.render_view(cx.editor, doc, view, area, surface, is_focused);
1459+
}
14511460
}
14521461

14531462
if config.auto_info {

helix-term/src/ui/statusline.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ where
163163
helix_view::editor::StatusLineElement::Spacer => render_spacer,
164164
helix_view::editor::StatusLineElement::VersionControl => render_version_control,
165165
helix_view::editor::StatusLineElement::Register => render_register,
166+
helix_view::editor::StatusLineElement::Zoom => render_zoom,
166167
}
167168
}
168169

@@ -531,3 +532,16 @@ where
531532
write(context, format!(" reg={} ", reg), None)
532533
}
533534
}
535+
536+
fn render_zoom<F>(context: &mut RenderContext, write: F)
537+
where
538+
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
539+
{
540+
let Some(zoom) = context.editor.tree.zoom else { return };
541+
542+
use helix_view::tree::ZoomMode as E;
543+
match zoom {
544+
E::Normal => write(context, "[zoom]".to_owned(), None),
545+
E::Zen => write(context, "[zen]".to_owned(), None),
546+
}
547+
}

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
};
@@ -305,6 +305,9 @@ pub struct Config {
305305
/// Which indent heuristic to use when a new line is inserted
306306
#[serde(default)]
307307
pub indent_heuristic: IndentationHeuristic,
308+
/// Zen-view config.
309+
#[serde(default)]
310+
pub zen_view: ZenViewConfig,
308311
}
309312

310313
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)]
@@ -438,6 +441,7 @@ impl Default for StatusLineConfig {
438441
E::Spinner,
439442
E::FileName,
440443
E::ReadOnlyIndicator,
444+
E::Zoom,
441445
E::FileModificationIndicator,
442446
],
443447
center: vec![],
@@ -537,6 +541,9 @@ pub enum StatusLineElement {
537541

538542
/// Indicator for selected register
539543
Register,
544+
545+
/// Current zoom/zen state
546+
Zoom,
540547
}
541548

542549
// Cursor shape is read and used on every rendered frame and so needs
@@ -820,6 +827,69 @@ pub enum PopupBorderConfig {
820827
Menu,
821828
}
822829

830+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
831+
#[serde(rename_all = "kebab-case")]
832+
pub struct ZenViewConfig {
833+
#[serde(default = "ZenViewConfig::default_max_width")]
834+
pub max_width: u16,
835+
#[serde(default)]
836+
pub auto_enter: ZenViewAutoEnter,
837+
// Currently the line-numbers option isn't used, but it will
838+
// be once the updated config system is implemented in #9318
839+
// TODO(lizclipse): Once that PR is in implement the per-mode line_numbers config
840+
#[serde(default = "ZenViewConfig::default_gutters")]
841+
pub gutters: GutterConfig,
842+
}
843+
844+
impl ZenViewConfig {
845+
const fn default_max_width() -> u16 {
846+
120
847+
}
848+
849+
fn default_gutters() -> GutterConfig {
850+
GutterConfig {
851+
layout: vec![],
852+
..Default::default()
853+
}
854+
}
855+
}
856+
857+
impl Default for ZenViewConfig {
858+
fn default() -> Self {
859+
Self {
860+
max_width: Self::default_max_width(),
861+
auto_enter: ZenViewAutoEnter::default(),
862+
gutters: Self::default_gutters(),
863+
}
864+
}
865+
}
866+
867+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
868+
#[serde(rename_all = "kebab-case")]
869+
pub enum ZenViewAutoEnter {
870+
Off,
871+
SingleFile,
872+
MultiFile,
873+
Always,
874+
}
875+
876+
impl ZenViewAutoEnter {
877+
pub fn should_enter(&self, files: i32) -> Option<ZoomMode> {
878+
match self {
879+
Self::Always => Some(ZoomMode::Zen),
880+
Self::SingleFile if files == 1 => Some(ZoomMode::Zen),
881+
Self::MultiFile if files > 1 => Some(ZoomMode::Zen),
882+
_ => None,
883+
}
884+
}
885+
}
886+
887+
impl Default for ZenViewAutoEnter {
888+
fn default() -> Self {
889+
Self::Off
890+
}
891+
}
892+
823893
impl Default for Config {
824894
fn default() -> Self {
825895
Self {
@@ -870,6 +940,7 @@ impl Default for Config {
870940
smart_tab: Some(SmartTabConfig::default()),
871941
popup_border: PopupBorderConfig::None,
872942
indent_heuristic: IndentationHeuristic::default(),
943+
zen_view: ZenViewConfig::default(),
873944
}
874945
}
875946
}
@@ -1044,13 +1115,19 @@ impl Editor {
10441115
let language_servers = helix_lsp::Registry::new(syn_loader.clone());
10451116
let conf = config.load();
10461117
let auto_pairs = (&conf.auto_pairs).into();
1118+
let mut tree = Tree::new(area);
1119+
tree.zen_max_width = conf.zen_view.max_width;
1120+
tree.zoom = match conf.zen_view.auto_enter {
1121+
ZenViewAutoEnter::Always => Some(ZoomMode::Zen),
1122+
_ => None,
1123+
};
10471124

10481125
// HAXX: offset the render area height by 1 to account for prompt/commandline
10491126
area.height -= 1;
10501127

10511128
Self {
10521129
mode: Mode::Normal,
1053-
tree: Tree::new(area),
1130+
tree,
10541131
next_document_id: DocumentId::default(),
10551132
documents: BTreeMap::new(),
10561133
saves: HashMap::new(),
@@ -1126,6 +1203,8 @@ impl Editor {
11261203
pub fn refresh_config(&mut self) {
11271204
let config = self.config();
11281205
self.auto_pairs = (&config.auto_pairs).into();
1206+
self.tree.zen_max_width = config.zen_view.max_width;
1207+
self.tree.recalculate();
11291208
self.reset_idle_timer();
11301209
self._refresh();
11311210
}

helix-view/src/tree.rs

Lines changed: 31 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,25 @@ impl Tree {
337348
return;
338349
}
339350

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

342372
// take the area

0 commit comments

Comments
 (0)