mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-22 09:26:19 +04:00
Move ui, keymap & commands to helix-view
This commit is contained in:
parent
11b8f068da
commit
1aa2b027d7
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -878,24 +878,17 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"chrono",
|
"chrono",
|
||||||
"content_inspector",
|
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"fern",
|
"fern",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"fuzzy-matcher",
|
|
||||||
"grep-regex",
|
|
||||||
"grep-searcher",
|
|
||||||
"helix-core",
|
"helix-core",
|
||||||
"helix-dap",
|
"helix-dap",
|
||||||
"helix-loader",
|
"helix-loader",
|
||||||
"helix-lsp",
|
"helix-lsp",
|
||||||
"helix-tui",
|
"helix-tui",
|
||||||
"helix-view",
|
"helix-view",
|
||||||
"ignore",
|
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pulldown-cmark",
|
|
||||||
"retain_mut",
|
|
||||||
"ropey",
|
"ropey",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -942,15 +935,23 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"chardetng",
|
"chardetng",
|
||||||
"clipboard-win",
|
"clipboard-win",
|
||||||
|
"content_inspector",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"fuzzy-matcher",
|
||||||
|
"grep-regex",
|
||||||
|
"grep-searcher",
|
||||||
"helix-core",
|
"helix-core",
|
||||||
"helix-dap",
|
"helix-dap",
|
||||||
"helix-graphics",
|
"helix-graphics",
|
||||||
|
"helix-loader",
|
||||||
"helix-lsp",
|
"helix-lsp",
|
||||||
"helix-tui",
|
"helix-tui",
|
||||||
|
"ignore",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"pulldown-cmark",
|
||||||
|
"retain_mut",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
|
@ -51,27 +51,12 @@ fern = "0.6"
|
|||||||
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
# File picker
|
|
||||||
fuzzy-matcher = "0.3"
|
|
||||||
ignore = "0.4"
|
|
||||||
# markdown doc rendering
|
|
||||||
pulldown-cmark = { version = "0.9", default-features = false }
|
|
||||||
# file type detection
|
|
||||||
content_inspector = "0.2.4"
|
|
||||||
|
|
||||||
# config
|
# config
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
|
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
# ripgrep for global search
|
|
||||||
grep-regex = "0.1.9"
|
|
||||||
grep-searcher = "0.1.8"
|
|
||||||
|
|
||||||
# Remove once retain_mut lands in stable rust
|
|
||||||
retain_mut = "0.1.7"
|
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
|
[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
|
||||||
signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
|
signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
|
||||||
|
|
||||||
|
@ -4,18 +4,20 @@
|
|||||||
pos_at_coords, syntax, Selection,
|
pos_at_coords, syntax, Selection,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "lsp")]
|
|
||||||
use crate::commands::apply_workspace_edit;
|
|
||||||
#[cfg(feature = "lsp")]
|
#[cfg(feature = "lsp")]
|
||||||
use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap};
|
use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap};
|
||||||
#[cfg(feature = "lsp")]
|
#[cfg(feature = "lsp")]
|
||||||
|
use helix_view::commands::apply_workspace_edit;
|
||||||
|
#[cfg(feature = "lsp")]
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use helix_view::{align_view, editor::ConfigEvent, graphics::Rect, theme, Align, Editor};
|
use helix_view::{
|
||||||
|
align_view, editor::ConfigEvent, graphics::Rect, theme, true_color, Align, Editor,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{args::Args, config::Config};
|
||||||
args::Args,
|
|
||||||
config::Config,
|
use helix_view::{
|
||||||
keymap::Keymaps,
|
keymap::Keymaps,
|
||||||
ui::{self, overlay::overlayed},
|
ui::{self, overlay::overlayed},
|
||||||
};
|
};
|
||||||
@ -96,7 +98,7 @@ pub fn new(args: Args) -> Result<Self, Error> {
|
|||||||
&helix_loader::runtime_dir(),
|
&helix_loader::runtime_dir(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let true_color = config.editor.true_color || crate::true_color();
|
let true_color = config.editor.true_color || true_color();
|
||||||
let theme = config
|
let theme = config
|
||||||
.theme
|
.theme
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -358,7 +360,7 @@ fn refresh_config(&mut self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn true_color(&self) -> bool {
|
fn true_color(&self) -> bool {
|
||||||
self.config.load().editor.true_color || crate::true_color()
|
self.config.load().editor.true_color || true_color()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use helix_core::Position;
|
use helix_core::Position;
|
||||||
use std::path::{Path, PathBuf};
|
use helix_view::args::parse_file;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
@ -65,37 +66,3 @@ pub fn parse_args() -> Result<Args> {
|
|||||||
Ok(args)
|
Ok(args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse arg into [`PathBuf`] and position.
|
|
||||||
pub(crate) fn parse_file(s: &str) -> (PathBuf, Position) {
|
|
||||||
let def = || (PathBuf::from(s), Position::default());
|
|
||||||
if Path::new(s).exists() {
|
|
||||||
return def();
|
|
||||||
}
|
|
||||||
split_path_row_col(s)
|
|
||||||
.or_else(|| split_path_row(s))
|
|
||||||
.unwrap_or_else(def)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Split file.rs:10:2 into [`PathBuf`], row and col.
|
|
||||||
///
|
|
||||||
/// Does not validate if file.rs is a file or directory.
|
|
||||||
fn split_path_row_col(s: &str) -> Option<(PathBuf, Position)> {
|
|
||||||
let mut s = s.rsplitn(3, ':');
|
|
||||||
let col: usize = s.next()?.parse().ok()?;
|
|
||||||
let row: usize = s.next()?.parse().ok()?;
|
|
||||||
let path = s.next()?.into();
|
|
||||||
let pos = Position::new(row.saturating_sub(1), col.saturating_sub(1));
|
|
||||||
Some((path, pos))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Split file.rs:10 into [`PathBuf`] and row.
|
|
||||||
///
|
|
||||||
/// Does not validate if file.rs is a file or directory.
|
|
||||||
fn split_path_row(s: &str) -> Option<(PathBuf, Position)> {
|
|
||||||
let (path, row) = s.rsplit_once(':')?;
|
|
||||||
let row: usize = row.parse().ok()?;
|
|
||||||
let path = path.into();
|
|
||||||
let pos = Position::new(row.saturating_sub(1), 0);
|
|
||||||
Some((path, pos))
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::keymap::{default::default, merge_keys, Keymap};
|
|
||||||
use helix_view::document::Mode;
|
use helix_view::document::Mode;
|
||||||
|
use helix_view::keymap::{default::default, Keymap};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@ -27,6 +27,15 @@ fn default() -> Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Merge default config keys with user overwritten keys for custom user config.
|
||||||
|
pub fn merge_keys(mut config: Config) -> Config {
|
||||||
|
let mut delta = std::mem::replace(&mut config.keys, default());
|
||||||
|
for (mode, keys) in &mut config.keys {
|
||||||
|
keys.merge(delta.remove(mode).unwrap_or_default())
|
||||||
|
}
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ConfigLoadError {
|
pub enum ConfigLoadError {
|
||||||
BadConfig(TomlError),
|
BadConfig(TomlError),
|
||||||
@ -63,10 +72,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parsing_keymaps_config_file() {
|
fn parsing_keymaps_config_file() {
|
||||||
use crate::keymap;
|
|
||||||
use crate::keymap::Keymap;
|
|
||||||
use helix_core::hashmap;
|
use helix_core::hashmap;
|
||||||
use helix_view::document::Mode;
|
use helix_view::document::Mode;
|
||||||
|
use helix_view::keymap::{self, Keymap};
|
||||||
|
|
||||||
let sample_keymaps = r#"
|
let sample_keymaps = r#"
|
||||||
[keys.insert]
|
[keys.insert]
|
||||||
@ -104,4 +112,104 @@ fn keys_resolve_to_correct_defaults() {
|
|||||||
let default_keys = Config::default().keys;
|
let default_keys = Config::default().keys;
|
||||||
assert_eq!(default_keys, default());
|
assert_eq!(default_keys, default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use arc_swap::access::Constant;
|
||||||
|
use helix_core::hashmap;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merge_partial_keys() {
|
||||||
|
let config = Config {
|
||||||
|
keys: hashmap! {
|
||||||
|
Mode::Normal => Keymap::new(
|
||||||
|
keymap!({ "Normal mode"
|
||||||
|
"i" => normal_mode,
|
||||||
|
"无" => insert_mode,
|
||||||
|
"z" => jump_backward,
|
||||||
|
"g" => { "Merge into goto mode"
|
||||||
|
"$" => goto_line_end,
|
||||||
|
"g" => delete_char_forward,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut merged_config = merge_keys(config.clone());
|
||||||
|
assert_ne!(config, merged_config);
|
||||||
|
|
||||||
|
let mut keymap = Keymaps::new(Box::new(Constant(merged_config.keys.clone())));
|
||||||
|
assert_eq!(
|
||||||
|
keymap.get(Mode::Normal, key!('i')),
|
||||||
|
KeymapResult::Matched(MappableCommand::normal_mode),
|
||||||
|
"Leaf should replace leaf"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
keymap.get(Mode::Normal, key!('无')),
|
||||||
|
KeymapResult::Matched(MappableCommand::insert_mode),
|
||||||
|
"New leaf should be present in merged keymap"
|
||||||
|
);
|
||||||
|
// Assumes that z is a node in the default keymap
|
||||||
|
assert_eq!(
|
||||||
|
keymap.get(Mode::Normal, key!('z')),
|
||||||
|
KeymapResult::Matched(MappableCommand::jump_backward),
|
||||||
|
"Leaf should replace node"
|
||||||
|
);
|
||||||
|
|
||||||
|
let keymap = merged_config.keys.get_mut(&Mode::Normal).unwrap();
|
||||||
|
// Assumes that `g` is a node in default keymap
|
||||||
|
assert_eq!(
|
||||||
|
keymap.root().search(&[key!('g'), key!('$')]).unwrap(),
|
||||||
|
&KeyTrie::Leaf(MappableCommand::goto_line_end),
|
||||||
|
"Leaf should be present in merged subnode"
|
||||||
|
);
|
||||||
|
// Assumes that `gg` is in default keymap
|
||||||
|
assert_eq!(
|
||||||
|
keymap.root().search(&[key!('g'), key!('g')]).unwrap(),
|
||||||
|
&KeyTrie::Leaf(MappableCommand::delete_char_forward),
|
||||||
|
"Leaf should replace old leaf in merged subnode"
|
||||||
|
);
|
||||||
|
// Assumes that `ge` is in default keymap
|
||||||
|
assert_eq!(
|
||||||
|
keymap.root().search(&[key!('g'), key!('e')]).unwrap(),
|
||||||
|
&KeyTrie::Leaf(MappableCommand::goto_last_line),
|
||||||
|
"Old leaves in subnode should be present in merged node"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(merged_config.keys.get(&Mode::Normal).unwrap().len() > 1);
|
||||||
|
assert!(merged_config.keys.get(&Mode::Insert).unwrap().len() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn order_should_be_set() {
|
||||||
|
let config = Config {
|
||||||
|
keys: hashmap! {
|
||||||
|
Mode::Normal => Keymap::new(
|
||||||
|
keymap!({ "Normal mode"
|
||||||
|
"space" => { ""
|
||||||
|
"s" => { ""
|
||||||
|
"v" => vsplit,
|
||||||
|
"c" => hsplit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut merged_config = merge_keys(config.clone());
|
||||||
|
assert_ne!(config, merged_config);
|
||||||
|
let keymap = merged_config.keys.get_mut(&Mode::Normal).unwrap();
|
||||||
|
// Make sure mapping works
|
||||||
|
assert_eq!(
|
||||||
|
keymap
|
||||||
|
.root()
|
||||||
|
.search(&[key!(' '), key!('s'), key!('v')])
|
||||||
|
.unwrap(),
|
||||||
|
&KeyTrie::Leaf(MappableCommand::vsplit),
|
||||||
|
"Leaf should be present in merged subnode"
|
||||||
|
);
|
||||||
|
// Make sure an order was set during merge
|
||||||
|
let node = keymap.root().search(&[helix_view::key!(' ')]).unwrap();
|
||||||
|
assert!(!node.node().unwrap().order().is_empty())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
#[macro_export]
|
|
||||||
macro_rules! key {
|
|
||||||
($key:ident) => {
|
|
||||||
::helix_view::input::KeyEvent {
|
|
||||||
code: ::helix_view::keyboard::KeyCode::$key,
|
|
||||||
modifiers: ::helix_view::keyboard::KeyModifiers::NONE,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($($ch:tt)*) => {
|
|
||||||
::helix_view::input::KeyEvent {
|
|
||||||
code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
|
|
||||||
modifiers: ::helix_view::keyboard::KeyModifiers::NONE,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! shift {
|
|
||||||
($key:ident) => {
|
|
||||||
::helix_view::input::KeyEvent {
|
|
||||||
code: ::helix_view::keyboard::KeyCode::$key,
|
|
||||||
modifiers: ::helix_view::keyboard::KeyModifiers::SHIFT,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($($ch:tt)*) => {
|
|
||||||
::helix_view::input::KeyEvent {
|
|
||||||
code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
|
|
||||||
modifiers: ::helix_view::keyboard::KeyModifiers::SHIFT,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! ctrl {
|
|
||||||
($key:ident) => {
|
|
||||||
::helix_view::input::KeyEvent {
|
|
||||||
code: ::helix_view::keyboard::KeyCode::$key,
|
|
||||||
modifiers: ::helix_view::keyboard::KeyModifiers::CONTROL,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($($ch:tt)*) => {
|
|
||||||
::helix_view::input::KeyEvent {
|
|
||||||
code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
|
|
||||||
modifiers: ::helix_view::keyboard::KeyModifiers::CONTROL,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! alt {
|
|
||||||
($key:ident) => {
|
|
||||||
::helix_view::input::KeyEvent {
|
|
||||||
code: ::helix_view::keyboard::KeyCode::$key,
|
|
||||||
modifiers: ::helix_view::keyboard::KeyModifiers::ALT,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($($ch:tt)*) => {
|
|
||||||
::helix_view::input::KeyEvent {
|
|
||||||
code: ::helix_view::keyboard::KeyCode::Char($($ch)*),
|
|
||||||
modifiers: ::helix_view::keyboard::KeyModifiers::ALT,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Macro for defining the root of a `Keymap` object. Example:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use helix_core::hashmap;
|
|
||||||
/// # use helix_term::keymap;
|
|
||||||
/// # use helix_term::keymap::Keymap;
|
|
||||||
/// let normal_mode = keymap!({ "Normal mode"
|
|
||||||
/// "i" => insert_mode,
|
|
||||||
/// "g" => { "Goto"
|
|
||||||
/// "g" => goto_file_start,
|
|
||||||
/// "e" => goto_file_end,
|
|
||||||
/// },
|
|
||||||
/// "j" | "down" => move_line_down,
|
|
||||||
/// });
|
|
||||||
/// let keymap = Keymap::new(normal_mode);
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! keymap {
|
|
||||||
(@trie $cmd:ident) => {
|
|
||||||
$crate::keymap::KeyTrie::Leaf($crate::commands::MappableCommand::$cmd)
|
|
||||||
};
|
|
||||||
|
|
||||||
(@trie
|
|
||||||
{ $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
|
|
||||||
) => {
|
|
||||||
keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ })
|
|
||||||
};
|
|
||||||
|
|
||||||
(@trie [$($cmd:ident),* $(,)?]) => {
|
|
||||||
$crate::keymap::KeyTrie::Sequence(vec![$($crate::commands::Command::$cmd),*])
|
|
||||||
};
|
|
||||||
|
|
||||||
(
|
|
||||||
{ $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
|
|
||||||
) => {
|
|
||||||
// modified from the hashmap! macro
|
|
||||||
{
|
|
||||||
let _cap = hashmap!(@count $($($key),+),*);
|
|
||||||
let mut _map = ::std::collections::HashMap::with_capacity(_cap);
|
|
||||||
let mut _order = ::std::vec::Vec::with_capacity(_cap);
|
|
||||||
$(
|
|
||||||
$(
|
|
||||||
let _key = $key.parse::<::helix_view::input::KeyEvent>().unwrap();
|
|
||||||
let _duplicate = _map.insert(
|
|
||||||
_key,
|
|
||||||
keymap!(@trie $value)
|
|
||||||
);
|
|
||||||
assert!(_duplicate.is_none(), "Duplicate key found: {:?}", _duplicate.unwrap());
|
|
||||||
_order.push(_key);
|
|
||||||
)+
|
|
||||||
)*
|
|
||||||
let mut _node = $crate::keymap::KeyTrieNode::new($label, _map, _order);
|
|
||||||
$( _node.is_sticky = $sticky; )?
|
|
||||||
$crate::keymap::KeyTrie::Node(_node)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use alt;
|
|
||||||
pub use ctrl;
|
|
||||||
pub use key;
|
|
||||||
pub use keymap;
|
|
||||||
pub use shift;
|
|
@ -3,20 +3,5 @@
|
|||||||
|
|
||||||
pub mod application;
|
pub mod application;
|
||||||
pub mod args;
|
pub mod args;
|
||||||
pub mod commands;
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod health;
|
pub mod health;
|
||||||
pub mod keymap;
|
|
||||||
pub mod ui;
|
|
||||||
pub use keymap::macros::*;
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
fn true_color() -> bool {
|
|
||||||
std::env::var("COLORTERM")
|
|
||||||
.map(|v| matches!(v.as_str(), "truecolor" | "24bit"))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn true_color() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
@ -23,6 +23,7 @@ helix-core = { version = "0.6", path = "../helix-core" }
|
|||||||
helix-graphics = { version = "0.6", path = "../helix-graphics" }
|
helix-graphics = { version = "0.6", path = "../helix-graphics" }
|
||||||
helix-lsp = { version = "0.6", path = "../helix-lsp", optional = true }
|
helix-lsp = { version = "0.6", path = "../helix-lsp", optional = true }
|
||||||
helix-dap = { version = "0.6", path = "../helix-dap", optional = true }
|
helix-dap = { version = "0.6", path = "../helix-dap", optional = true }
|
||||||
|
helix-loader = { version = "0.6", path = "../helix-loader" }
|
||||||
tokio-stream = { version = "0.1", optional = true }
|
tokio-stream = { version = "0.1", optional = true }
|
||||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"], optional = true }
|
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"], optional = true }
|
||||||
|
|
||||||
@ -46,6 +47,23 @@ serde_json = "1.0"
|
|||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
log = "~0.4"
|
log = "~0.4"
|
||||||
|
|
||||||
|
# Command dependencies
|
||||||
|
|
||||||
|
# File picker
|
||||||
|
fuzzy-matcher = "0.3"
|
||||||
|
ignore = "0.4"
|
||||||
|
# markdown doc rendering
|
||||||
|
pulldown-cmark = { version = "0.9", default-features = false }
|
||||||
|
# file type detection
|
||||||
|
content_inspector = "0.2.4"
|
||||||
|
|
||||||
|
# ripgrep for global search
|
||||||
|
grep-regex = "0.1.9"
|
||||||
|
grep-searcher = "0.1.8"
|
||||||
|
|
||||||
|
# Remove once retain_mut lands in stable rust
|
||||||
|
retain_mut = "0.1.7"
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
which = "4.2"
|
which = "4.2"
|
||||||
|
|
||||||
|
36
helix-view/src/args.rs
Normal file
36
helix-view/src/args.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use helix_core::Position;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
/// Parse arg into [`PathBuf`] and position.
|
||||||
|
pub fn parse_file(s: &str) -> (PathBuf, Position) {
|
||||||
|
let def = || (PathBuf::from(s), Position::default());
|
||||||
|
if Path::new(s).exists() {
|
||||||
|
return def();
|
||||||
|
}
|
||||||
|
split_path_row_col(s)
|
||||||
|
.or_else(|| split_path_row(s))
|
||||||
|
.unwrap_or_else(def)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split file.rs:10:2 into [`PathBuf`], row and col.
|
||||||
|
///
|
||||||
|
/// Does not validate if file.rs is a file or directory.
|
||||||
|
fn split_path_row_col(s: &str) -> Option<(PathBuf, Position)> {
|
||||||
|
let mut s = s.rsplitn(3, ':');
|
||||||
|
let col: usize = s.next()?.parse().ok()?;
|
||||||
|
let row: usize = s.next()?.parse().ok()?;
|
||||||
|
let path = s.next()?.into();
|
||||||
|
let pos = Position::new(row.saturating_sub(1), col.saturating_sub(1));
|
||||||
|
Some((path, pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split file.rs:10 into [`PathBuf`] and row.
|
||||||
|
///
|
||||||
|
/// Does not validate if file.rs is a file or directory.
|
||||||
|
fn split_path_row(s: &str) -> Option<(PathBuf, Position)> {
|
||||||
|
let (path, row) = s.rsplit_once(':')?;
|
||||||
|
let row: usize = row.parse().ok()?;
|
||||||
|
let path = path.into();
|
||||||
|
let pos = Position::new(row.saturating_sub(1), 0);
|
||||||
|
Some((path, pos))
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
use super::{Context, Editor};
|
use super::{Context, Editor};
|
||||||
|
use crate::editor::Breakpoint;
|
||||||
use crate::ui::{self, overlay::overlayed, FilePicker, Picker, Popup, Prompt, PromptEvent, Text};
|
use crate::ui::{self, overlay::overlayed, FilePicker, Picker, Popup, Prompt, PromptEvent, Text};
|
||||||
use helix_core::syntax::{DebugArgumentValue, DebugConfigCompletion};
|
use crate::{
|
||||||
use helix_dap::{self as dap, Client};
|
|
||||||
use helix_lsp::block_on;
|
|
||||||
use helix_view::editor::Breakpoint;
|
|
||||||
use helix_view::{
|
|
||||||
compositor::{self, Compositor},
|
compositor::{self, Compositor},
|
||||||
job::{Callback, Jobs},
|
job::{Callback, Jobs},
|
||||||
};
|
};
|
||||||
|
use helix_core::syntax::{DebugArgumentValue, DebugConfigCompletion};
|
||||||
|
use helix_dap::{self as dap, Client};
|
||||||
|
use helix_lsp::block_on;
|
||||||
|
|
||||||
use serde_json::{to_value, Value};
|
use serde_json::{to_value, Value};
|
||||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||||
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::{anyhow, bail};
|
||||||
|
|
||||||
use helix_view::handlers::dap::{breakpoints_changed, jump_to_stack_frame, select_thread_id};
|
use crate::debugger;
|
||||||
|
use crate::handlers::dap::{breakpoints_changed, jump_to_stack_frame, select_thread_id};
|
||||||
|
|
||||||
fn thread_picker(
|
fn thread_picker(
|
||||||
cx: &mut Context,
|
cx: &mut Context,
|
||||||
@ -474,7 +475,7 @@ pub fn dap_variables(cx: &mut Context) {
|
|||||||
let text_style = theme.get("ui.text.focus");
|
let text_style = theme.get("ui.text.focus");
|
||||||
|
|
||||||
for scope in scopes.iter() {
|
for scope in scopes.iter() {
|
||||||
// use helix_view::graphics::Style;
|
// use crate::graphics::Style;
|
||||||
use tui::text::{Span, Spans};
|
use tui::text::{Span, Spans};
|
||||||
let response = block_on(debugger.variables(scope.variables_reference));
|
let response = block_on(debugger.variables(scope.variables_reference));
|
||||||
|
|
@ -7,10 +7,10 @@
|
|||||||
use super::{align_view, push_jump, Align, Context, Editor};
|
use super::{align_view, push_jump, Align, Context, Editor};
|
||||||
|
|
||||||
use helix_core::Selection;
|
use helix_core::Selection;
|
||||||
use helix_view::editor::Action;
|
use crate::editor::Action;
|
||||||
|
|
||||||
use crate::ui::{self, overlay::overlayed, FileLocation, FilePicker, Popup, PromptEvent};
|
use crate::ui::{self, overlay::overlayed, FileLocation, FilePicker, Popup, PromptEvent};
|
||||||
use helix_view::compositor::{self, Compositor};
|
use crate::compositor::{self, Compositor};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
@ -255,7 +255,7 @@ pub fn code_action(cx: &mut Context) {
|
|||||||
});
|
});
|
||||||
picker.move_down(); // pre-select the first item
|
picker.move_down(); // pre-select the first item
|
||||||
|
|
||||||
let popup = Popup::new("code-action", picker).margin(helix_view::graphics::Margin {
|
let popup = Popup::new("code-action", picker).margin(crate::graphics::Margin {
|
||||||
vertical: 1,
|
vertical: 1,
|
||||||
horizontal: 1,
|
horizontal: 1,
|
||||||
});
|
});
|
@ -10,6 +10,16 @@
|
|||||||
pub use lsp::*;
|
pub use lsp::*;
|
||||||
pub use typed::*;
|
pub use typed::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
clipboard::ClipboardType,
|
||||||
|
document::{Mode, SCRATCH_BUFFER_NAME},
|
||||||
|
editor::{Action, Motion},
|
||||||
|
info::Info,
|
||||||
|
input::KeyEvent,
|
||||||
|
keyboard::KeyCode,
|
||||||
|
view::View,
|
||||||
|
Document, DocumentId, Editor, ViewId,
|
||||||
|
};
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
comment, coords_at_pos, find_first_non_whitespace_char, find_root, graphemes,
|
comment, coords_at_pos, find_first_non_whitespace_char, find_root, graphemes,
|
||||||
history::UndoKind,
|
history::UndoKind,
|
||||||
@ -29,18 +39,8 @@
|
|||||||
LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril,
|
LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril,
|
||||||
Transaction,
|
Transaction,
|
||||||
};
|
};
|
||||||
use helix_view::{
|
|
||||||
clipboard::ClipboardType,
|
|
||||||
document::{Mode, SCRATCH_BUFFER_NAME},
|
|
||||||
editor::{Action, Motion},
|
|
||||||
info::Info,
|
|
||||||
input::KeyEvent,
|
|
||||||
keyboard::KeyCode,
|
|
||||||
view::View,
|
|
||||||
Document, DocumentId, Editor, ViewId,
|
|
||||||
};
|
|
||||||
|
|
||||||
use helix_view::{
|
use crate::{
|
||||||
compositor::{self, Component, Compositor},
|
compositor::{self, Component, Compositor},
|
||||||
job::{self, Job, Jobs},
|
job::{self, Job, Jobs},
|
||||||
};
|
};
|
||||||
@ -56,8 +56,13 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
use futures_util::{FutureExt, StreamExt};
|
use futures_util::{FutureExt, StreamExt};
|
||||||
use std::{collections::HashMap, fmt, future::Future};
|
|
||||||
use std::{collections::HashSet, num::NonZeroUsize};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
fmt,
|
||||||
|
future::Future,
|
||||||
|
num::NonZeroUsize,
|
||||||
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
@ -77,7 +82,7 @@ pub struct Context<'a> {
|
|||||||
pub count: Option<NonZeroUsize>,
|
pub count: Option<NonZeroUsize>,
|
||||||
pub editor: &'a mut Editor,
|
pub editor: &'a mut Editor,
|
||||||
|
|
||||||
pub callback: Option<helix_view::compositor::Callback>,
|
pub callback: Option<crate::compositor::Callback>,
|
||||||
pub on_next_key_callback: Option<Box<dyn FnOnce(&mut Context, KeyEvent)>>,
|
pub on_next_key_callback: Option<Box<dyn FnOnce(&mut Context, KeyEvent)>>,
|
||||||
pub jobs: &'a mut Jobs,
|
pub jobs: &'a mut Jobs,
|
||||||
}
|
}
|
||||||
@ -126,7 +131,7 @@ pub fn count(&self) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use helix_view::{align_view, Align};
|
use crate::{align_view, Align};
|
||||||
|
|
||||||
/// A MappableCommand is either a static command like "jump_view_up" or a Typable command like
|
/// A MappableCommand is either a static command like "jump_view_up" or a Typable command like
|
||||||
/// :format. It causes a side-effect on the state (usually by creating and applying a transaction).
|
/// :format. It causes a side-effect on the state (usually by creating and applying a transaction).
|
||||||
@ -4397,8 +4402,8 @@ fn shell_prompt(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBeha
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn suspend(_cx: &mut Context) {
|
fn suspend(_cx: &mut Context) {
|
||||||
#[cfg(not(windows))]
|
// #[cfg(not(windows))]
|
||||||
signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
|
// signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_newline_above(cx: &mut Context) {
|
fn add_newline_above(cx: &mut Context) {
|
||||||
@ -4557,7 +4562,7 @@ fn record_macro(cx: &mut Context) {
|
|||||||
fn replay_macro(cx: &mut Context) {
|
fn replay_macro(cx: &mut Context) {
|
||||||
let reg = cx.register.unwrap_or('@');
|
let reg = cx.register.unwrap_or('@');
|
||||||
let keys: Vec<KeyEvent> = if let Some([keys_str]) = cx.editor.registers.read(reg) {
|
let keys: Vec<KeyEvent> = if let Some([keys_str]) = cx.editor.registers.read(reg) {
|
||||||
match helix_view::input::parse_macro(keys_str) {
|
match crate::input::parse_macro(keys_str) {
|
||||||
Ok(keys) => keys,
|
Ok(keys) => keys,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
cx.editor.set_error(format!("Invalid macro: {}", err));
|
cx.editor.set_error(format!("Invalid macro: {}", err));
|
@ -1,6 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use helix_view::editor::{Action, ConfigEvent};
|
use crate::editor::{Action, ConfigEvent};
|
||||||
use ui::completers::{self, Completer};
|
use ui::completers::{self, Completer};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
@ -2,12 +2,11 @@
|
|||||||
pub mod macros;
|
pub mod macros;
|
||||||
|
|
||||||
pub use crate::commands::MappableCommand;
|
pub use crate::commands::MappableCommand;
|
||||||
use crate::config::Config;
|
use crate::{document::Mode, info::Info, input::KeyEvent};
|
||||||
use arc_swap::{
|
use arc_swap::{
|
||||||
access::{DynAccess, DynGuard},
|
access::{DynAccess, DynGuard},
|
||||||
ArcSwap,
|
ArcSwap,
|
||||||
};
|
};
|
||||||
use helix_view::{document::Mode, info::Info, input::KeyEvent};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
@ -361,20 +360,10 @@ fn default() -> Self {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge default config keys with user overwritten keys for custom user config.
|
|
||||||
pub fn merge_keys(mut config: Config) -> Config {
|
|
||||||
let mut delta = std::mem::replace(&mut config.keys, default());
|
|
||||||
for (mode, keys) in &mut config.keys {
|
|
||||||
keys.merge(delta.remove(mode).unwrap_or_default())
|
|
||||||
}
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::macros::keymap;
|
use super::macros::keymap;
|
||||||
use super::*;
|
use super::*;
|
||||||
use arc_swap::access::Constant;
|
|
||||||
use helix_core::hashmap;
|
use helix_core::hashmap;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -392,103 +381,6 @@ fn check_duplicate_keys_in_default_keymap() {
|
|||||||
Keymaps::default();
|
Keymaps::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn merge_partial_keys() {
|
|
||||||
let config = Config {
|
|
||||||
keys: hashmap! {
|
|
||||||
Mode::Normal => Keymap::new(
|
|
||||||
keymap!({ "Normal mode"
|
|
||||||
"i" => normal_mode,
|
|
||||||
"无" => insert_mode,
|
|
||||||
"z" => jump_backward,
|
|
||||||
"g" => { "Merge into goto mode"
|
|
||||||
"$" => goto_line_end,
|
|
||||||
"g" => delete_char_forward,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let mut merged_config = merge_keys(config.clone());
|
|
||||||
assert_ne!(config, merged_config);
|
|
||||||
|
|
||||||
let mut keymap = Keymaps::new(Box::new(Constant(merged_config.keys.clone())));
|
|
||||||
assert_eq!(
|
|
||||||
keymap.get(Mode::Normal, key!('i')),
|
|
||||||
KeymapResult::Matched(MappableCommand::normal_mode),
|
|
||||||
"Leaf should replace leaf"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
keymap.get(Mode::Normal, key!('无')),
|
|
||||||
KeymapResult::Matched(MappableCommand::insert_mode),
|
|
||||||
"New leaf should be present in merged keymap"
|
|
||||||
);
|
|
||||||
// Assumes that z is a node in the default keymap
|
|
||||||
assert_eq!(
|
|
||||||
keymap.get(Mode::Normal, key!('z')),
|
|
||||||
KeymapResult::Matched(MappableCommand::jump_backward),
|
|
||||||
"Leaf should replace node"
|
|
||||||
);
|
|
||||||
|
|
||||||
let keymap = merged_config.keys.get_mut(&Mode::Normal).unwrap();
|
|
||||||
// Assumes that `g` is a node in default keymap
|
|
||||||
assert_eq!(
|
|
||||||
keymap.root().search(&[key!('g'), key!('$')]).unwrap(),
|
|
||||||
&KeyTrie::Leaf(MappableCommand::goto_line_end),
|
|
||||||
"Leaf should be present in merged subnode"
|
|
||||||
);
|
|
||||||
// Assumes that `gg` is in default keymap
|
|
||||||
assert_eq!(
|
|
||||||
keymap.root().search(&[key!('g'), key!('g')]).unwrap(),
|
|
||||||
&KeyTrie::Leaf(MappableCommand::delete_char_forward),
|
|
||||||
"Leaf should replace old leaf in merged subnode"
|
|
||||||
);
|
|
||||||
// Assumes that `ge` is in default keymap
|
|
||||||
assert_eq!(
|
|
||||||
keymap.root().search(&[key!('g'), key!('e')]).unwrap(),
|
|
||||||
&KeyTrie::Leaf(MappableCommand::goto_last_line),
|
|
||||||
"Old leaves in subnode should be present in merged node"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(merged_config.keys.get(&Mode::Normal).unwrap().len() > 1);
|
|
||||||
assert!(merged_config.keys.get(&Mode::Insert).unwrap().len() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn order_should_be_set() {
|
|
||||||
let config = Config {
|
|
||||||
keys: hashmap! {
|
|
||||||
Mode::Normal => Keymap::new(
|
|
||||||
keymap!({ "Normal mode"
|
|
||||||
"space" => { ""
|
|
||||||
"s" => { ""
|
|
||||||
"v" => vsplit,
|
|
||||||
"c" => hsplit,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let mut merged_config = merge_keys(config.clone());
|
|
||||||
assert_ne!(config, merged_config);
|
|
||||||
let keymap = merged_config.keys.get_mut(&Mode::Normal).unwrap();
|
|
||||||
// Make sure mapping works
|
|
||||||
assert_eq!(
|
|
||||||
keymap
|
|
||||||
.root()
|
|
||||||
.search(&[key!(' '), key!('s'), key!('v')])
|
|
||||||
.unwrap(),
|
|
||||||
&KeyTrie::Leaf(MappableCommand::vsplit),
|
|
||||||
"Leaf should be present in merged subnode"
|
|
||||||
);
|
|
||||||
// Make sure an order was set during merge
|
|
||||||
let node = keymap.root().search(&[crate::key!(' ')]).unwrap();
|
|
||||||
assert!(!node.node().unwrap().order().is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn aliased_modes_are_same_in_default_keymap() {
|
fn aliased_modes_are_same_in_default_keymap() {
|
||||||
let keymaps = Keymaps::default().map();
|
let keymaps = Keymaps::default().map();
|
60
helix-view/src/keymap/macros.rs
Normal file
60
helix-view/src/keymap/macros.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/// Macro for defining the root of a `Keymap` object. Example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use helix_core::hashmap;
|
||||||
|
/// # use helix_term::keymap;
|
||||||
|
/// # use helix_term::keymap::Keymap;
|
||||||
|
/// let normal_mode = keymap!({ "Normal mode"
|
||||||
|
/// "i" => insert_mode,
|
||||||
|
/// "g" => { "Goto"
|
||||||
|
/// "g" => goto_file_start,
|
||||||
|
/// "e" => goto_file_end,
|
||||||
|
/// },
|
||||||
|
/// "j" | "down" => move_line_down,
|
||||||
|
/// });
|
||||||
|
/// let keymap = Keymap::new(normal_mode);
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! keymap {
|
||||||
|
(@trie $cmd:ident) => {
|
||||||
|
$crate::keymap::KeyTrie::Leaf($crate::commands::MappableCommand::$cmd)
|
||||||
|
};
|
||||||
|
|
||||||
|
(@trie
|
||||||
|
{ $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
|
||||||
|
) => {
|
||||||
|
keymap!({ $label $(sticky=$sticky)? $($($key)|+ => $value,)+ })
|
||||||
|
};
|
||||||
|
|
||||||
|
(@trie [$($cmd:ident),* $(,)?]) => {
|
||||||
|
$crate::keymap::KeyTrie::Sequence(vec![$($crate::commands::Command::$cmd),*])
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
{ $label:literal $(sticky=$sticky:literal)? $($($key:literal)|+ => $value:tt,)+ }
|
||||||
|
) => {
|
||||||
|
// modified from the hashmap! macro
|
||||||
|
{
|
||||||
|
let _cap = hashmap!(@count $($($key),+),*);
|
||||||
|
let mut _map = ::std::collections::HashMap::with_capacity(_cap);
|
||||||
|
let mut _order = ::std::vec::Vec::with_capacity(_cap);
|
||||||
|
$(
|
||||||
|
$(
|
||||||
|
let _key = $key.parse::<$crate::input::KeyEvent>().unwrap();
|
||||||
|
let _duplicate = _map.insert(
|
||||||
|
_key,
|
||||||
|
keymap!(@trie $value)
|
||||||
|
);
|
||||||
|
assert!(_duplicate.is_none(), "Duplicate key found: {:?}", _duplicate.unwrap());
|
||||||
|
_order.push(_key);
|
||||||
|
)+
|
||||||
|
)*
|
||||||
|
let mut _node = $crate::keymap::KeyTrieNode::new($label, _map, _order);
|
||||||
|
$( _node.is_sticky = $sticky; )?
|
||||||
|
$crate::keymap::KeyTrie::Node(_node)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use crate::{alt, ctrl, key, shift};
|
||||||
|
pub use keymap;
|
@ -1,17 +1,22 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
|
|
||||||
|
pub mod args;
|
||||||
pub mod backend {
|
pub mod backend {
|
||||||
#[cfg(feature = "term")]
|
#[cfg(feature = "term")]
|
||||||
pub mod term;
|
pub mod term;
|
||||||
}
|
}
|
||||||
pub mod clipboard;
|
pub mod clipboard;
|
||||||
|
pub mod commands;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod document;
|
pub mod document;
|
||||||
pub mod editor;
|
pub mod editor;
|
||||||
|
pub mod ui;
|
||||||
pub use helix_graphics as graphics;
|
pub use helix_graphics as graphics;
|
||||||
pub mod gutter;
|
pub mod gutter;
|
||||||
pub mod job;
|
pub mod job;
|
||||||
|
pub mod keymap;
|
||||||
|
pub use keymap::macros::*;
|
||||||
pub mod handlers {
|
pub mod handlers {
|
||||||
#[cfg(feature = "dap")]
|
#[cfg(feature = "dap")]
|
||||||
pub mod dap;
|
pub mod dap;
|
||||||
@ -47,6 +52,17 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|||||||
pub struct ViewId;
|
pub struct ViewId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
pub fn true_color() -> bool {
|
||||||
|
std::env::var("COLORTERM")
|
||||||
|
.map(|v| matches!(v.as_str(), "truecolor" | "24bit"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn true_color() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Align {
|
pub enum Align {
|
||||||
Top,
|
Top,
|
||||||
Center,
|
Center,
|
||||||
|
@ -61,3 +61,69 @@ macro_rules! doc {
|
|||||||
$crate::current_ref!($editor).1
|
$crate::current_ref!($editor).1
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keymap macros
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! key {
|
||||||
|
($key:ident) => {
|
||||||
|
$crate::input::KeyEvent {
|
||||||
|
code: $crate::keyboard::KeyCode::$key,
|
||||||
|
modifiers: $crate::keyboard::KeyModifiers::NONE,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($($ch:tt)*) => {
|
||||||
|
$crate::input::KeyEvent {
|
||||||
|
code: $crate::keyboard::KeyCode::Char($($ch)*),
|
||||||
|
modifiers: $crate::keyboard::KeyModifiers::NONE,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! shift {
|
||||||
|
($key:ident) => {
|
||||||
|
$crate::input::KeyEvent {
|
||||||
|
code: $crate::keyboard::KeyCode::$key,
|
||||||
|
modifiers: $crate::keyboard::KeyModifiers::SHIFT,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($($ch:tt)*) => {
|
||||||
|
$crate::input::KeyEvent {
|
||||||
|
code: $crate::keyboard::KeyCode::Char($($ch)*),
|
||||||
|
modifiers: $crate::keyboard::KeyModifiers::SHIFT,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ctrl {
|
||||||
|
($key:ident) => {
|
||||||
|
$crate::input::KeyEvent {
|
||||||
|
code: $crate::keyboard::KeyCode::$key,
|
||||||
|
modifiers: $crate::keyboard::KeyModifiers::CONTROL,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($($ch:tt)*) => {
|
||||||
|
$crate::input::KeyEvent {
|
||||||
|
code: $crate::keyboard::KeyCode::Char($($ch)*),
|
||||||
|
modifiers: $crate::keyboard::KeyModifiers::CONTROL,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! alt {
|
||||||
|
($key:ident) => {
|
||||||
|
$crate::input::KeyEvent {
|
||||||
|
code: $crate::keyboard::KeyCode::$key,
|
||||||
|
modifiers: $crate::keyboard::KeyModifiers::ALT,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($($ch:tt)*) => {
|
||||||
|
$crate::input::KeyEvent {
|
||||||
|
code: $crate::keyboard::KeyCode::Char($($ch)*),
|
||||||
|
modifiers: $crate::keyboard::KeyModifiers::ALT,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use helix_view::compositor::{Component, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{Component, Context, Event, EventResult, RenderContext};
|
||||||
use helix_view::editor::CompleteAction;
|
use crate::editor::CompleteAction;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use helix_core::{Change, Transaction};
|
use helix_core::{Change, Transaction};
|
||||||
use helix_view::{
|
use crate::{
|
||||||
graphics::Rect,
|
graphics::Rect,
|
||||||
input::{KeyCode, KeyEvent},
|
input::{KeyCode, KeyEvent},
|
||||||
Document, Editor,
|
Document, Editor,
|
@ -4,7 +4,7 @@
|
|||||||
ui::{Completion, ProgressSpinners},
|
ui::{Completion, ProgressSpinners},
|
||||||
};
|
};
|
||||||
|
|
||||||
use helix_view::compositor::{Component, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{Component, Context, Event, EventResult, RenderContext};
|
||||||
|
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
coords_at_pos, encoding,
|
coords_at_pos, encoding,
|
||||||
@ -17,7 +17,7 @@
|
|||||||
unicode::width::UnicodeWidthStr,
|
unicode::width::UnicodeWidthStr,
|
||||||
LineEnding, Position, Range, Selection, Transaction,
|
LineEnding, Position, Range, Selection, Transaction,
|
||||||
};
|
};
|
||||||
use helix_view::{
|
use crate::{
|
||||||
document::{Mode, SCRATCH_BUFFER_NAME},
|
document::{Mode, SCRATCH_BUFFER_NAME},
|
||||||
editor::{CompleteAction, CursorShapeConfig},
|
editor::{CompleteAction, CursorShapeConfig},
|
||||||
graphics::{CursorKind, Modifier, Rect, Style},
|
graphics::{CursorKind, Modifier, Rect, Style},
|
||||||
@ -928,7 +928,7 @@ pub fn clear_completion(&mut self, editor: &mut Editor) {
|
|||||||
editor.clear_idle_timer(); // don't retrigger
|
editor.clear_idle_timer(); // don't retrigger
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_idle_timeout(&mut self, cx: &mut helix_view::compositor::Context) -> EventResult {
|
pub fn handle_idle_timeout(&mut self, cx: &mut crate::compositor::Context) -> EventResult {
|
||||||
if self.completion.is_some()
|
if self.completion.is_some()
|
||||||
|| !cx.editor.config().auto_completion
|
|| !cx.editor.config().auto_completion
|
||||||
|| doc!(cx.editor).mode != Mode::Insert
|
|| doc!(cx.editor).mode != Mode::Insert
|
||||||
@ -1163,7 +1163,7 @@ impl Component for EditorView {
|
|||||||
fn handle_event(
|
fn handle_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: Event,
|
event: Event,
|
||||||
context: &mut helix_view::compositor::Context,
|
context: &mut crate::compositor::Context,
|
||||||
) -> EventResult {
|
) -> EventResult {
|
||||||
let mut cx = commands::Context {
|
let mut cx = commands::Context {
|
||||||
editor: context.editor,
|
editor: context.editor,
|
||||||
@ -1314,7 +1314,7 @@ fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
|||||||
// render status msg
|
// render status msg
|
||||||
if let Some((status_msg, severity)) = &cx.editor.status_msg {
|
if let Some((status_msg, severity)) = &cx.editor.status_msg {
|
||||||
status_msg_width = status_msg.width();
|
status_msg_width = status_msg.width();
|
||||||
use helix_view::editor::Severity;
|
use crate::editor::Severity;
|
||||||
let style = if *severity == Severity::Error {
|
let style = if *severity == Severity::Error {
|
||||||
cx.editor.theme.get("error")
|
cx.editor.theme.get("error")
|
||||||
} else {
|
} else {
|
||||||
@ -1361,7 +1361,7 @@ fn render(&mut self, area: Rect, cx: &mut RenderContext<'_>) {
|
|||||||
if let Some((reg, _)) = cx.editor.macro_recording {
|
if let Some((reg, _)) = cx.editor.macro_recording {
|
||||||
let disp = format!("[{}]", reg);
|
let disp = format!("[{}]", reg);
|
||||||
let style = style
|
let style = style
|
||||||
.fg(helix_view::graphics::Color::Yellow)
|
.fg(crate::graphics::Color::Yellow)
|
||||||
.add_modifier(Modifier::BOLD);
|
.add_modifier(Modifier::BOLD);
|
||||||
cx.surface.set_string(
|
cx.surface.set_string(
|
||||||
area.x + area.width.saturating_sub(3),
|
area.x + area.width.saturating_sub(3),
|
@ -1,4 +1,4 @@
|
|||||||
use helix_view::compositor::{Component, RenderContext};
|
use crate::compositor::{Component, RenderContext};
|
||||||
use tui::text::{Span, Spans, Text};
|
use tui::text::{Span, Spans, Text};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -9,7 +9,7 @@
|
|||||||
syntax::{self, HighlightEvent, Syntax},
|
syntax::{self, HighlightEvent, Syntax},
|
||||||
Rope,
|
Rope,
|
||||||
};
|
};
|
||||||
use helix_view::{
|
use crate::{
|
||||||
graphics::{Margin, Rect, Style},
|
graphics::{Margin, Rect, Style},
|
||||||
Theme,
|
Theme,
|
||||||
};
|
};
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{ctrl, key, shift};
|
use crate::{ctrl, key, shift};
|
||||||
use helix_view::compositor::{
|
use crate::compositor::{
|
||||||
Callback, Component, Compositor, Context, Event, EventResult, RenderContext,
|
Callback, Component, Compositor, Context, Event, EventResult, RenderContext,
|
||||||
};
|
};
|
||||||
use tui::widgets::Table;
|
use tui::widgets::Table;
|
||||||
@ -9,7 +9,7 @@
|
|||||||
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
||||||
use fuzzy_matcher::FuzzyMatcher;
|
use fuzzy_matcher::FuzzyMatcher;
|
||||||
|
|
||||||
use helix_view::{graphics::Rect, Editor};
|
use crate::{graphics::Rect, Editor};
|
||||||
use tui::layout::Constraint;
|
use tui::layout::Constraint;
|
||||||
|
|
||||||
pub trait Item {
|
pub trait Item {
|
@ -19,9 +19,9 @@
|
|||||||
pub use spinner::{ProgressSpinners, Spinner};
|
pub use spinner::{ProgressSpinners, Spinner};
|
||||||
pub use text::Text;
|
pub use text::Text;
|
||||||
|
|
||||||
|
use crate::{Document, Editor, View};
|
||||||
use helix_core::regex::Regex;
|
use helix_core::regex::Regex;
|
||||||
use helix_core::regex::RegexBuilder;
|
use helix_core::regex::RegexBuilder;
|
||||||
use helix_view::{Document, Editor, View};
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ pub fn prompt(
|
|||||||
prompt: std::borrow::Cow<'static, str>,
|
prompt: std::borrow::Cow<'static, str>,
|
||||||
history_register: Option<char>,
|
history_register: Option<char>,
|
||||||
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
|
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
|
||||||
callback_fn: impl FnMut(&mut helix_view::compositor::Context, &str, PromptEvent) + 'static,
|
callback_fn: impl FnMut(&mut crate::compositor::Context, &str, PromptEvent) + 'static,
|
||||||
) {
|
) {
|
||||||
let mut prompt = Prompt::new(prompt, history_register, completion_fn, callback_fn);
|
let mut prompt = Prompt::new(prompt, history_register, completion_fn, callback_fn);
|
||||||
// Calculate initial completion
|
// Calculate initial completion
|
||||||
@ -55,7 +55,7 @@ pub fn regex_prompt(
|
|||||||
prompt,
|
prompt,
|
||||||
history_register,
|
history_register,
|
||||||
completion_fn,
|
completion_fn,
|
||||||
move |cx: &mut helix_view::compositor::Context, input: &str, event: PromptEvent| {
|
move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| {
|
||||||
match event {
|
match event {
|
||||||
PromptEvent::Abort => {
|
PromptEvent::Abort => {
|
||||||
let (view, doc) = current!(cx.editor);
|
let (view, doc) = current!(cx.editor);
|
||||||
@ -111,7 +111,7 @@ pub fn regex_prompt(
|
|||||||
cx.push_layer(Box::new(prompt));
|
cx.push_layer(Box::new(prompt));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePicker<PathBuf> {
|
pub fn file_picker(root: PathBuf, config: &crate::editor::Config) -> FilePicker<PathBuf> {
|
||||||
use ignore::{types::TypesBuilder, WalkBuilder};
|
use ignore::{types::TypesBuilder, WalkBuilder};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
@ -188,12 +188,12 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod completers {
|
pub mod completers {
|
||||||
|
use crate::document::SCRATCH_BUFFER_NAME;
|
||||||
|
use crate::theme;
|
||||||
use crate::ui::prompt::Completion;
|
use crate::ui::prompt::Completion;
|
||||||
|
use crate::{editor::Config, Editor};
|
||||||
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
||||||
use fuzzy_matcher::FuzzyMatcher;
|
use fuzzy_matcher::FuzzyMatcher;
|
||||||
use helix_view::document::SCRATCH_BUFFER_NAME;
|
|
||||||
use helix_view::theme;
|
|
||||||
use helix_view::{editor::Config, Editor};
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
@ -1,10 +1,10 @@
|
|||||||
use helix_core::Position;
|
use helix_core::Position;
|
||||||
use helix_view::{
|
use crate::{
|
||||||
graphics::{CursorKind, Rect},
|
graphics::{CursorKind, Rect},
|
||||||
Editor,
|
Editor,
|
||||||
};
|
};
|
||||||
|
|
||||||
use helix_view::compositor::{Component, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{Component, Context, Event, EventResult, RenderContext};
|
||||||
|
|
||||||
/// Contains a component placed in the center of the parent component
|
/// Contains a component placed in the center of the parent component
|
||||||
pub struct Overlay<T> {
|
pub struct Overlay<T> {
|
@ -2,7 +2,7 @@
|
|||||||
ctrl, key, shift,
|
ctrl, key, shift,
|
||||||
ui::{self, EditorView},
|
ui::{self, EditorView},
|
||||||
};
|
};
|
||||||
use helix_view::compositor::{Component, Compositor, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{Component, Compositor, Context, Event, EventResult, RenderContext};
|
||||||
use tui::widgets::{Block, BorderType, Borders};
|
use tui::widgets::{Block, BorderType, Borders};
|
||||||
|
|
||||||
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
||||||
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
use crate::ui::{Prompt, PromptEvent};
|
use crate::ui::{Prompt, PromptEvent};
|
||||||
use helix_core::{movement::Direction, Position};
|
use helix_core::{movement::Direction, Position};
|
||||||
use helix_view::{
|
use crate::{
|
||||||
editor::Action,
|
editor::Action,
|
||||||
graphics::{Color, CursorKind, Margin, Modifier, Rect, Style},
|
graphics::{Color, CursorKind, Margin, Modifier, Rect, Style},
|
||||||
Document, Editor,
|
Document, Editor,
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{ctrl, key};
|
use crate::{ctrl, key};
|
||||||
use helix_view::compositor::{Callback, Component, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{Callback, Component, Context, Event, EventResult, RenderContext};
|
||||||
|
|
||||||
use helix_core::Position;
|
use helix_core::Position;
|
||||||
use helix_view::{
|
use crate::{
|
||||||
graphics::{Margin, Rect},
|
graphics::{Margin, Rect},
|
||||||
Editor,
|
Editor,
|
||||||
};
|
};
|
@ -1,17 +1,17 @@
|
|||||||
use helix_view::compositor::{Component, Compositor, Context, Event, EventResult, RenderContext};
|
use crate::compositor::{Component, Compositor, Context, Event, EventResult, RenderContext};
|
||||||
|
use crate::input::KeyEvent;
|
||||||
|
use crate::keyboard::KeyCode;
|
||||||
use crate::{alt, ctrl, key, shift, ui};
|
use crate::{alt, ctrl, key, shift, ui};
|
||||||
use helix_view::input::KeyEvent;
|
|
||||||
use helix_view::keyboard::KeyCode;
|
|
||||||
use std::{borrow::Cow, ops::RangeFrom};
|
use std::{borrow::Cow, ops::RangeFrom};
|
||||||
use tui::widgets::{Block, Borders, Widget};
|
use tui::widgets::{Block, Borders, Widget};
|
||||||
|
|
||||||
use helix_core::{
|
use crate::{
|
||||||
unicode::segmentation::GraphemeCursor, unicode::width::UnicodeWidthStr, Position,
|
|
||||||
};
|
|
||||||
use helix_view::{
|
|
||||||
graphics::{CursorKind, Margin, Rect},
|
graphics::{CursorKind, Margin, Rect},
|
||||||
Editor,
|
Editor,
|
||||||
};
|
};
|
||||||
|
use helix_core::{
|
||||||
|
unicode::segmentation::GraphemeCursor, unicode::width::UnicodeWidthStr, Position,
|
||||||
|
};
|
||||||
|
|
||||||
pub type Completion = (RangeFrom<usize>, Cow<'static, str>);
|
pub type Completion = (RangeFrom<usize>, Cow<'static, str>);
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
use helix_view::compositor::{Component, RenderContext};
|
use crate::compositor::{Component, RenderContext};
|
||||||
|
use crate::graphics::Rect;
|
||||||
use helix_view::graphics::Rect;
|
|
||||||
|
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
pub(crate) contents: tui::text::Text<'static>,
|
pub(crate) contents: tui::text::Text<'static>,
|
Loading…
Reference in New Issue
Block a user