mirror of https://github.com/helix-editor/helix
Compare commits
14 Commits
a686bf3586
...
18737753f8
Author | SHA1 | Date |
---|---|---|
|
18737753f8 | |
|
395a71bf53 | |
|
1e4bf6704a | |
|
7bb4ed201b | |
|
036e5c3195 | |
|
7c54c63d27 | |
|
5a830c7837 | |
|
1d79ba94ac | |
|
978c1a126d | |
|
5aaaf137cb | |
|
6891014241 | |
|
e4c90548e1 | |
|
d8fbff3856 | |
|
64311f77df |
|
@ -50,7 +50,7 @@ use helix_view::{
|
||||||
input::KeyEvent,
|
input::KeyEvent,
|
||||||
keyboard::KeyCode,
|
keyboard::KeyCode,
|
||||||
theme::Style,
|
theme::Style,
|
||||||
tree,
|
tree::{self, Dimension, Resize},
|
||||||
view::View,
|
view::View,
|
||||||
Document, DocumentId, Editor, ViewId,
|
Document, DocumentId, Editor, ViewId,
|
||||||
};
|
};
|
||||||
|
@ -455,6 +455,11 @@ impl MappableCommand {
|
||||||
goto_prev_change, "Goto previous change",
|
goto_prev_change, "Goto previous change",
|
||||||
goto_first_change, "Goto first change",
|
goto_first_change, "Goto first change",
|
||||||
goto_last_change, "Goto last change",
|
goto_last_change, "Goto last change",
|
||||||
|
grow_buffer_width, "Grow focused container width",
|
||||||
|
shrink_buffer_width, "Shrink focused container width",
|
||||||
|
grow_buffer_height, "Grow focused container height",
|
||||||
|
shrink_buffer_height, "Shrink focused container height",
|
||||||
|
toggle_focus_window, "Toggle focus mode on buffer",
|
||||||
goto_line_start, "Goto line start",
|
goto_line_start, "Goto line start",
|
||||||
goto_line_end, "Goto line end",
|
goto_line_end, "Goto line end",
|
||||||
goto_column, "Goto column",
|
goto_column, "Goto column",
|
||||||
|
@ -901,6 +906,25 @@ fn goto_line_start(cx: &mut Context) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn grow_buffer_width(cx: &mut Context) {
|
||||||
|
cx.editor.resize_buffer(Resize::Grow, Dimension::Width);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shrink_buffer_width(cx: &mut Context) {
|
||||||
|
cx.editor.resize_buffer(Resize::Shrink, Dimension::Width);
|
||||||
|
}
|
||||||
|
fn grow_buffer_height(cx: &mut Context) {
|
||||||
|
cx.editor.resize_buffer(Resize::Grow, Dimension::Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shrink_buffer_height(cx: &mut Context) {
|
||||||
|
cx.editor.resize_buffer(Resize::Shrink, Dimension::Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_focus_window(cx: &mut Context) {
|
||||||
|
cx.editor.toggle_focus_window();
|
||||||
|
}
|
||||||
|
|
||||||
fn goto_next_buffer(cx: &mut Context) {
|
fn goto_next_buffer(cx: &mut Context) {
|
||||||
goto_buffer(cx.editor, Direction::Forward, cx.count());
|
goto_buffer(cx.editor, Direction::Forward, cx.count());
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,22 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
|
||||||
"home" => goto_line_start,
|
"home" => goto_line_start,
|
||||||
"end" => goto_line_end,
|
"end" => goto_line_end,
|
||||||
|
|
||||||
|
"A-w" => { "Alter Window"
|
||||||
|
"A-h"|"A-left" |"h"|"left" => shrink_buffer_width,
|
||||||
|
"A-l"|"A-right"|"l"|"right" => grow_buffer_width,
|
||||||
|
"A-j"|"A-down" |"j"|"down" => shrink_buffer_height,
|
||||||
|
"A-k"|"A-up" |"k"|"up" => grow_buffer_height,
|
||||||
|
"A-f"|"f" => toggle_focus_window,
|
||||||
|
},
|
||||||
|
|
||||||
|
"A-W" => { "Alter Window" sticky=true
|
||||||
|
"h"|"left" => shrink_buffer_width,
|
||||||
|
"l"|"right" => grow_buffer_width,
|
||||||
|
"j"|"down" => shrink_buffer_height,
|
||||||
|
"k"|"up" => grow_buffer_height,
|
||||||
|
"f" => toggle_focus_window,
|
||||||
|
},
|
||||||
|
|
||||||
"w" => move_next_word_start,
|
"w" => move_next_word_start,
|
||||||
"b" => move_prev_word_start,
|
"b" => move_prev_word_start,
|
||||||
"e" => move_next_word_end,
|
"e" => move_next_word_end,
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
||||||
input::KeyEvent,
|
input::KeyEvent,
|
||||||
register::Registers,
|
register::Registers,
|
||||||
theme::{self, Theme},
|
theme::{self, Theme},
|
||||||
tree::{self, Tree},
|
tree::{self, Dimension, Resize, Tree},
|
||||||
Document, DocumentId, View, ViewId,
|
Document, DocumentId, View, ViewId,
|
||||||
};
|
};
|
||||||
use helix_event::dispatch;
|
use helix_event::dispatch;
|
||||||
|
@ -2028,6 +2028,14 @@ impl Editor {
|
||||||
self.tree.transpose();
|
self.tree.transpose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize_buffer(&mut self, resize_type: Resize, dimension: Dimension) {
|
||||||
|
self.tree.resize_buffer(resize_type, dimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_focus_window(&mut self) {
|
||||||
|
self.tree.toggle_focus_window();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn should_close(&self) -> bool {
|
pub fn should_close(&self) -> bool {
|
||||||
self.tree.is_empty()
|
self.tree.is_empty()
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,16 @@ pub enum Content {
|
||||||
Container(Box<Container>),
|
Container(Box<Container>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum Resize {
|
||||||
|
Shrink,
|
||||||
|
Grow,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Dimension {
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
pub fn container(layout: Layout) -> Self {
|
pub fn container(layout: Layout) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -65,6 +75,14 @@ pub struct Container {
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
children: Vec<ViewId>,
|
children: Vec<ViewId>,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
|
node_bounds: Vec<ContainerBounds>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ContainerBounds {
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
expand: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Container {
|
impl Container {
|
||||||
|
@ -73,8 +91,69 @@ impl Container {
|
||||||
layout,
|
layout,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
area: Rect::default(),
|
area: Rect::default(),
|
||||||
|
node_bounds: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_child_by_view_id(&mut self, node: ViewId) -> Option<&mut ContainerBounds> {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.position(|child| child == &node)
|
||||||
|
.and_then(|index| self.node_bounds.get_mut(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_child(&mut self, node: ViewId) -> &mut Self {
|
||||||
|
self.children.push(node);
|
||||||
|
self.add_child_bounds();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_child(&mut self, index: usize, node: ViewId) -> &mut Self {
|
||||||
|
self.children.insert(index, node);
|
||||||
|
self.insert_child_bounds(index);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_child_bounds(&mut self) -> &mut Self {
|
||||||
|
self.node_bounds.push(ContainerBounds {
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
expand: false,
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_child_bounds(&mut self, index: usize) -> &mut Self {
|
||||||
|
self.node_bounds.insert(
|
||||||
|
index,
|
||||||
|
ContainerBounds {
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
expand: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_slots_width(&self) -> usize {
|
||||||
|
self.node_bounds
|
||||||
|
.iter()
|
||||||
|
.map(|bounds| match bounds.expand {
|
||||||
|
true => 40,
|
||||||
|
false => bounds.width,
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_slots_height(&self) -> usize {
|
||||||
|
self.node_bounds
|
||||||
|
.iter()
|
||||||
|
.map(|bounds| match bounds.expand {
|
||||||
|
true => 40,
|
||||||
|
false => bounds.height,
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Container {
|
impl Default for Container {
|
||||||
|
@ -131,7 +210,7 @@ impl Tree {
|
||||||
pos + 1
|
pos + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
container.children.insert(pos, node);
|
container.insert_child(pos, node);
|
||||||
// focus the new node
|
// focus the new node
|
||||||
self.focus = node;
|
self.focus = node;
|
||||||
|
|
||||||
|
@ -168,7 +247,7 @@ impl Tree {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
pos + 1
|
pos + 1
|
||||||
};
|
};
|
||||||
container.children.insert(pos, node);
|
container.insert_child(pos, node);
|
||||||
self.nodes[node].parent = parent;
|
self.nodes[node].parent = parent;
|
||||||
} else {
|
} else {
|
||||||
let mut split = Node::container(layout);
|
let mut split = Node::container(layout);
|
||||||
|
@ -182,8 +261,8 @@ impl Tree {
|
||||||
} => container,
|
} => container,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
container.children.push(focus);
|
container.push_child(focus);
|
||||||
container.children.push(node);
|
container.push_child(node);
|
||||||
self.nodes[focus].parent = split;
|
self.nodes[focus].parent = split;
|
||||||
self.nodes[node].parent = split;
|
self.nodes[node].parent = split;
|
||||||
|
|
||||||
|
@ -382,12 +461,17 @@ impl Tree {
|
||||||
match container.layout {
|
match container.layout {
|
||||||
Layout::Horizontal => {
|
Layout::Horizontal => {
|
||||||
let len = container.children.len();
|
let len = container.children.len();
|
||||||
|
let slots = container.calculate_slots_height();
|
||||||
let height = area.height / len as u16;
|
let slot_height = area.height as f32 / slots as f32;
|
||||||
|
|
||||||
let mut child_y = area.y;
|
let mut child_y = area.y;
|
||||||
|
|
||||||
for (i, child) in container.children.iter().enumerate() {
|
for (i, child) in container.children.iter().enumerate() {
|
||||||
|
let bounds = container.node_bounds[i];
|
||||||
|
let height = match bounds.expand {
|
||||||
|
true => (40.0 * slot_height) as u16,
|
||||||
|
false => (slot_height * bounds.height as f32).floor() as u16,
|
||||||
|
};
|
||||||
|
|
||||||
let mut area = Rect::new(
|
let mut area = Rect::new(
|
||||||
container.area.x,
|
container.area.x,
|
||||||
child_y,
|
child_y,
|
||||||
|
@ -396,7 +480,7 @@ impl Tree {
|
||||||
);
|
);
|
||||||
child_y += height;
|
child_y += height;
|
||||||
|
|
||||||
// last child takes the remaining width because we can get uneven
|
// last child takes the remaining height because we can get uneven
|
||||||
// space from rounding
|
// space from rounding
|
||||||
if i == len - 1 {
|
if i == len - 1 {
|
||||||
area.height = container.area.y + container.area.height - area.y;
|
area.height = container.area.y + container.area.height - area.y;
|
||||||
|
@ -413,11 +497,19 @@ impl Tree {
|
||||||
let total_gap = inner_gap * len_u16.saturating_sub(2);
|
let total_gap = inner_gap * len_u16.saturating_sub(2);
|
||||||
|
|
||||||
let used_area = area.width.saturating_sub(total_gap);
|
let used_area = area.width.saturating_sub(total_gap);
|
||||||
let width = used_area / len_u16;
|
|
||||||
|
let slots = container.calculate_slots_width();
|
||||||
|
let slot_width: f32 = used_area as f32 / slots as f32;
|
||||||
|
|
||||||
let mut child_x = area.x;
|
let mut child_x = area.x;
|
||||||
|
|
||||||
for (i, child) in container.children.iter().enumerate() {
|
for (i, child) in container.children.iter().enumerate() {
|
||||||
|
let bounds = container.node_bounds[i];
|
||||||
|
let width = match bounds.expand {
|
||||||
|
true => (40.0 * slot_width) as u16,
|
||||||
|
false => (slot_width * bounds.width as f32).floor() as u16,
|
||||||
|
};
|
||||||
|
|
||||||
let mut area = Rect::new(
|
let mut area = Rect::new(
|
||||||
child_x,
|
child_x,
|
||||||
container.area.y,
|
container.area.y,
|
||||||
|
@ -596,6 +688,83 @@ impl Tree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_active_node_bounds_mut(
|
||||||
|
&mut self,
|
||||||
|
expect_layout: Layout,
|
||||||
|
) -> Option<&mut ContainerBounds> {
|
||||||
|
let mut focus = self.focus;
|
||||||
|
let mut parent = self.nodes[focus].parent;
|
||||||
|
|
||||||
|
// Parent expected to be container
|
||||||
|
if let Some(focused_layout) = match &self.nodes[parent].content {
|
||||||
|
Content::View(_) => unreachable!(),
|
||||||
|
Content::Container(node) => Some(node.layout),
|
||||||
|
} {
|
||||||
|
// if we want to make a width change and we have a `Horizontal` layout focused,
|
||||||
|
// alter the parent `Vertical` layout instead and vice versa
|
||||||
|
if focused_layout != expect_layout {
|
||||||
|
focus = parent;
|
||||||
|
parent = self.nodes[parent].parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Content::Container(node) = &mut self.nodes[parent].content {
|
||||||
|
return node.as_mut().get_child_by_view_id(focus);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize_buffer(&mut self, resize_type: Resize, dimension: Dimension) {
|
||||||
|
match dimension {
|
||||||
|
Dimension::Width => {
|
||||||
|
if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) {
|
||||||
|
match resize_type {
|
||||||
|
Resize::Shrink => {
|
||||||
|
if bounds.width > 1 {
|
||||||
|
bounds.width -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Resize::Grow => {
|
||||||
|
if bounds.width < 20 {
|
||||||
|
bounds.width += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.recalculate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Dimension::Height => {
|
||||||
|
if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) {
|
||||||
|
match resize_type {
|
||||||
|
Resize::Shrink => {
|
||||||
|
if bounds.height > 1 {
|
||||||
|
bounds.height -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Resize::Grow => {
|
||||||
|
if bounds.height < 20 {
|
||||||
|
bounds.height += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.recalculate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_focus_window(&mut self) {
|
||||||
|
if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Horizontal) {
|
||||||
|
bounds.expand = !bounds.expand;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(bounds) = self.get_active_node_bounds_mut(Layout::Vertical) {
|
||||||
|
bounds.expand = !bounds.expand;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.recalculate();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn swap_split_in_direction(&mut self, direction: Direction) -> Option<()> {
|
pub fn swap_split_in_direction(&mut self, direction: Direction) -> Option<()> {
|
||||||
let focus = self.focus;
|
let focus = self.focus;
|
||||||
let target = self.find_split_in_direction(focus, direction)?;
|
let target = self.find_split_in_direction(focus, direction)?;
|
||||||
|
|
|
@ -1022,6 +1022,7 @@ shebangs = []
|
||||||
comment-token = "#"
|
comment-token = "#"
|
||||||
language-servers = [ "nil", "nixd" ]
|
language-servers = [ "nil", "nixd" ]
|
||||||
indent = { tab-width = 2, unit = " " }
|
indent = { tab-width = 2, unit = " " }
|
||||||
|
formatter = { command = "nixfmt" }
|
||||||
|
|
||||||
[[grammar]]
|
[[grammar]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
|
@ -4243,10 +4244,11 @@ comment-token = "#"
|
||||||
block-comment-tokens = ["#-", "-#"]
|
block-comment-tokens = ["#-", "-#"]
|
||||||
indent = { tab-width = 2, unit = " " }
|
indent = { tab-width = 2, unit = " " }
|
||||||
language-servers = ["koto-ls"]
|
language-servers = ["koto-ls"]
|
||||||
|
formatter = {command = "koto", args = ["--format"]}
|
||||||
|
|
||||||
[[grammar]]
|
[[grammar]]
|
||||||
name = "koto"
|
name = "koto"
|
||||||
source = { git = "https://github.com/koto-lang/tree-sitter-koto", rev = "b420f7922d0d74905fd0d771e5b83be9ee8a8a9a" }
|
source = { git = "https://github.com/koto-lang/tree-sitter-koto", rev = "2ffc77c14f0ac1674384ff629bfc207b9c57ed89" }
|
||||||
|
|
||||||
[[language]]
|
[[language]]
|
||||||
name = "gpr"
|
name = "gpr"
|
||||||
|
|
|
@ -5,11 +5,13 @@
|
||||||
"*"
|
"*"
|
||||||
"/"
|
"/"
|
||||||
"%"
|
"%"
|
||||||
|
"^"
|
||||||
"+="
|
"+="
|
||||||
"-="
|
"-="
|
||||||
"*="
|
"*="
|
||||||
"/="
|
"/="
|
||||||
"%="
|
"%="
|
||||||
|
"^="
|
||||||
"=="
|
"=="
|
||||||
"!="
|
"!="
|
||||||
"<"
|
"<"
|
||||||
|
@ -99,12 +101,18 @@
|
||||||
(export
|
(export
|
||||||
(identifier) @namespace)
|
(identifier) @namespace)
|
||||||
|
|
||||||
(call
|
(chain
|
||||||
function: (identifier) @function.method)
|
start: (identifier) @function)
|
||||||
|
|
||||||
(chain
|
(chain
|
||||||
lookup: (identifier) @variable.other.member)
|
lookup: (identifier) @variable.other.member)
|
||||||
|
|
||||||
|
(call
|
||||||
|
function: (identifier)) @function
|
||||||
|
|
||||||
|
(call_arg
|
||||||
|
(identifier) @variable.other.member)
|
||||||
|
|
||||||
[
|
[
|
||||||
(true)
|
(true)
|
||||||
(false)
|
(false)
|
||||||
|
@ -139,13 +147,10 @@
|
||||||
|
|
||||||
(self) @variable.builtin
|
(self) @variable.builtin
|
||||||
|
|
||||||
(variable
|
(type
|
||||||
type: (identifier) @type)
|
_ @type)
|
||||||
|
|
||||||
(arg
|
(arg
|
||||||
(_ (identifier) @variable.parameter))
|
(_ (identifier) @variable.parameter))
|
||||||
|
|
||||||
(ellipsis) @variable.parameter
|
(ellipsis) @variable.parameter
|
||||||
|
|
||||||
(function
|
|
||||||
output_type: (identifier) @type)
|
|
||||||
|
|
|
@ -11,10 +11,6 @@
|
||||||
(call_args
|
(call_args
|
||||||
((call_arg) @parameter.inside . ","? @parameter.around) @parameter.around)
|
((call_arg) @parameter.inside . ","? @parameter.around) @parameter.around)
|
||||||
|
|
||||||
(chain
|
|
||||||
call: (tuple
|
|
||||||
((element) @parameter.inside . ","? @parameter.around) @parameter.around))
|
|
||||||
|
|
||||||
(map
|
(map
|
||||||
((entry_inline) @entry.inside . ","? @entry.around) @entry.around)
|
((entry_inline) @entry.inside . ","? @entry.around) @entry.around)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue