mirror of
https://github.com/helix-editor/helix.git
synced 2025-10-06 00:13:28 +02:00
Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7e8603247d | ||
|
7140908f6e | ||
|
6dba1e7ec7 | ||
|
c0332bd935 | ||
|
3c7729906c | ||
|
1b67fae9f4 | ||
|
f0018280cb | ||
|
7202953e69 | ||
|
7761c88d61 | ||
|
68f5031dcc | ||
|
83031564db | ||
|
eab6e53511 | ||
|
f5f46b1fed | ||
|
5f49bafbe8 | ||
|
2719a35123 | ||
|
0a6672c626 | ||
|
b51111a364 | ||
|
78980f575b | ||
|
0bb375bafa | ||
|
c960bcfc24 | ||
|
e88383d990 | ||
|
312b29f712 | ||
|
f4560cb68a | ||
|
cbb3ebafdc | ||
|
0851110d10 | ||
|
3ace581191 | ||
|
c0264b9f7f | ||
|
22dad592b8 | ||
|
ca042a4bde | ||
|
67b1cd32c7 | ||
|
4d12c7c3cf | ||
|
4f56a8e248 | ||
|
dbc392d92c | ||
|
7967d312c0 | ||
|
db48d22384 | ||
|
533ff61d0e | ||
|
b1ce969d80 | ||
|
01bf363446 | ||
|
cc323f7665 | ||
|
60caaf7fc4 | ||
|
ea824ed05d | ||
|
cfae07e7ba | ||
|
56dbc60840 | ||
|
c2e6b9f506 |
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -82,3 +82,7 @@
|
||||
path = helix-syntax/languages/tree-sitter-toml
|
||||
url = https://github.com/ikatyang/tree-sitter-toml
|
||||
shallow = true
|
||||
[submodule "helix-syntax/languages/tree-sitter-elixir"]
|
||||
path = helix-syntax/languages/tree-sitter-elixir
|
||||
url = https://github.com/IceDragon200/tree-sitter-elixir
|
||||
shallow = true
|
||||
|
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -326,7 +326,6 @@ dependencies = [
|
||||
"log",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"pico-args",
|
||||
"pulldown-cmark",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -607,12 +606,6 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d7afeb98c5a10e0bffcc7fc16e105b04d06729fac5fd6384aebf7ff5cb5a67d"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.6"
|
||||
|
15
README.md
15
README.md
@@ -42,6 +42,14 @@ Now copy the `runtime/` directory somewhere. Helix will by default look for the
|
||||
runtime inside the same folder as the executable, but that can be overriden via
|
||||
the `HELIX_RUNTIME` environment variable.
|
||||
|
||||
> NOTE: You should set this to <path to repository>/runtime in development (if
|
||||
> running via cargo).
|
||||
|
||||
## Arch Linux
|
||||
There are two packages available from AUR:
|
||||
- `helix-bin`: contains prebuilt binary from GitHub releases
|
||||
- `helix-git`: builds the master branch of this repository
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributors are very welcome! **No contribution is too small and all contributions are valued.**
|
||||
@@ -57,6 +65,13 @@ Some suggestions to get started:
|
||||
We provide an [architecture.md](./docs/architecture.md) that should give you
|
||||
a good overview of the internals.
|
||||
|
||||
## Usage
|
||||
|
||||
### Keyboard shortcuts / Keymap
|
||||
|
||||
All shortcuts/keymaps can be found in the documentation on the website:
|
||||
- https://docs.helix-editor.com/keymap.html
|
||||
|
||||
# Getting help
|
||||
|
||||
Discuss the project on the community [Matrix channel](https://matrix.to/#/#helix-community:matrix.org).
|
||||
|
@@ -4,3 +4,6 @@ language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
theme = "colibri"
|
||||
|
||||
[output.html]
|
||||
cname = "docs.helix-editor.com"
|
||||
|
@@ -6,10 +6,7 @@ We provide pre-built binaries on the [GitHub Releases page](https://github.com/h
|
||||
|
||||
TODO: brew tap
|
||||
|
||||
```
|
||||
$ brew tap helix-editor/helix
|
||||
$ brew install helix
|
||||
```
|
||||
Please use a pre-built binary release for the time being.
|
||||
|
||||
## Linux
|
||||
|
||||
@@ -21,7 +18,9 @@ shell for working on Helix.
|
||||
|
||||
### Arch Linux
|
||||
|
||||
A binary package is available on AUR as [helix-bin](https://aur.archlinux.org/packages/helix-bin/).
|
||||
Binary packages are available on AUR:
|
||||
- [helix-bin](https://aur.archlinux.org/packages/helix-bin/) contains the pre-built release
|
||||
- [helix-git](https://aur.archlinux.org/packages/helix-git/) builds the master branch
|
||||
|
||||
## Build from source
|
||||
|
||||
|
@@ -118,7 +118,7 @@ Jumps to various locations.
|
||||
|-----|-----------|
|
||||
| g | Go to the start of the file |
|
||||
| e | Go to the end of the file |
|
||||
| e | Go to definition |
|
||||
| d | Go to definition |
|
||||
| t | Go to type definition |
|
||||
| r | Go to references |
|
||||
| i | Go to implementation |
|
||||
|
@@ -65,9 +65,7 @@ impl History {
|
||||
self.cursor == 0
|
||||
}
|
||||
|
||||
// TODO: I'd like to pass Transaction by reference but it fights with the borrowck
|
||||
|
||||
pub fn undo(&mut self) -> Option<Transaction> {
|
||||
pub fn undo(&mut self) -> Option<&Transaction> {
|
||||
if self.at_root() {
|
||||
// We're at the root of undo, nothing to do.
|
||||
return None;
|
||||
@@ -77,17 +75,17 @@ impl History {
|
||||
|
||||
self.cursor = current_revision.parent;
|
||||
|
||||
Some(current_revision.revert.clone())
|
||||
Some(¤t_revision.revert)
|
||||
}
|
||||
|
||||
pub fn redo(&mut self) -> Option<Transaction> {
|
||||
pub fn redo(&mut self) -> Option<&Transaction> {
|
||||
let current_revision = &self.revisions[self.cursor];
|
||||
|
||||
// for now, simply pick the latest child (linear undo / redo)
|
||||
if let Some((index, transaction)) = current_revision.children.last() {
|
||||
self.cursor = *index;
|
||||
|
||||
return Some(transaction.clone());
|
||||
return Some(&transaction);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ pub fn move_vertically(
|
||||
|
||||
let new_line = match dir {
|
||||
Direction::Backward => row.saturating_sub(count),
|
||||
Direction::Forward => std::cmp::min(row.saturating_add(count), text.len_lines() - 1),
|
||||
Direction::Forward => std::cmp::min(row.saturating_add(count), text.len_lines() - 2),
|
||||
};
|
||||
|
||||
// convert to 0-indexed, subtract another 1 because len_chars() counts \n
|
||||
@@ -76,8 +76,9 @@ pub fn move_next_word_start(slice: RopeSlice, mut begin: usize, count: usize) ->
|
||||
begin += 1;
|
||||
}
|
||||
|
||||
// return if not skip while?
|
||||
skip_over_next(slice, &mut begin, |ch| ch == '\n');
|
||||
if !skip_over_next(slice, &mut begin, |ch| ch == '\n') {
|
||||
return None;
|
||||
};
|
||||
ch = slice.char(begin);
|
||||
|
||||
end = begin + 1;
|
||||
@@ -134,7 +135,7 @@ pub fn move_next_word_end(slice: RopeSlice, mut begin: usize, count: usize) -> O
|
||||
let mut end = begin;
|
||||
|
||||
for _ in 0..count {
|
||||
if begin + 1 == slice.len_chars() {
|
||||
if begin + 2 >= slice.len_chars() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -145,8 +146,9 @@ pub fn move_next_word_end(slice: RopeSlice, mut begin: usize, count: usize) -> O
|
||||
begin += 1;
|
||||
}
|
||||
|
||||
// return if not skip while?
|
||||
skip_over_next(slice, &mut begin, |ch| ch == '\n');
|
||||
if !skip_over_next(slice, &mut begin, |ch| ch == '\n') {
|
||||
return None;
|
||||
};
|
||||
|
||||
end = begin;
|
||||
|
||||
@@ -199,18 +201,20 @@ fn categorize(ch: char) -> Category {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn skip_over_next<F>(slice: RopeSlice, pos: &mut usize, fun: F)
|
||||
/// Returns true if there are more characters left after the new position.
|
||||
pub fn skip_over_next<F>(slice: RopeSlice, pos: &mut usize, fun: F) -> bool
|
||||
where
|
||||
F: Fn(char) -> bool,
|
||||
{
|
||||
let mut chars = slice.chars_at(*pos);
|
||||
|
||||
for ch in chars {
|
||||
while let Some(ch) = chars.next() {
|
||||
if !fun(ch) {
|
||||
break;
|
||||
}
|
||||
*pos += 1;
|
||||
}
|
||||
chars.next().is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@@ -7,6 +7,10 @@ pub fn find_nth_next(
|
||||
n: usize,
|
||||
inclusive: bool,
|
||||
) -> Option<usize> {
|
||||
if pos >= text.len_chars() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// start searching right after pos
|
||||
let mut chars = text.chars_at(pos + 1);
|
||||
|
||||
|
@@ -415,7 +415,7 @@ impl ChangeSet {
|
||||
|
||||
/// Transaction represents a single undoable unit of changes. Several changes can be grouped into
|
||||
/// a single transaction.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Transaction {
|
||||
changes: ChangeSet,
|
||||
selection: Option<Selection>,
|
||||
|
@@ -107,7 +107,10 @@ fn build_dir(dir: &str, language: &str) {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let ignore = vec!["tree-sitter-typescript".to_string()];
|
||||
let ignore = vec![
|
||||
"tree-sitter-typescript".to_string(),
|
||||
".DS_Store".to_string(),
|
||||
];
|
||||
let dirs = collect_tree_sitter_dirs(&ignore);
|
||||
|
||||
let mut n_jobs = 0;
|
||||
|
1
helix-syntax/languages/tree-sitter-elixir
Submodule
1
helix-syntax/languages/tree-sitter-elixir
Submodule
Submodule helix-syntax/languages/tree-sitter-elixir added at 295e62a43b
@@ -72,6 +72,7 @@ mk_langs!(
|
||||
(CSharp, tree_sitter_c_sharp),
|
||||
(Cpp, tree_sitter_cpp),
|
||||
(Css, tree_sitter_css),
|
||||
(Elixir, tree_sitter_elixir),
|
||||
(Go, tree_sitter_go),
|
||||
// (Haskell, tree_sitter_haskell),
|
||||
(Html, tree_sitter_html),
|
||||
|
@@ -24,7 +24,6 @@ tokio = { version = "1", features = ["full"] }
|
||||
num_cpus = "1"
|
||||
tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] }
|
||||
crossterm = { version = "0.19", features = ["event-stream"] }
|
||||
pico-args = "0.4"
|
||||
|
||||
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
|
||||
|
||||
|
@@ -473,10 +473,10 @@ fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
|
||||
let last_line = view.last_line(doc);
|
||||
|
||||
// clamp into viewport
|
||||
let line = cursor.row.clamp(
|
||||
view.first_line + scrolloff,
|
||||
last_line.saturating_sub(scrolloff),
|
||||
);
|
||||
let line = cursor
|
||||
.row
|
||||
.min(view.first_line + scrolloff)
|
||||
.max(last_line.saturating_sub(scrolloff));
|
||||
|
||||
let text = doc.text().slice(..);
|
||||
let pos = pos_at_coords(text, Position::new(line, cursor.col)); // this func will properly truncate to line end
|
||||
@@ -1031,6 +1031,9 @@ pub fn command_mode(cx: &mut Context) {
|
||||
}
|
||||
|
||||
let parts = input.split_ascii_whitespace().collect::<Vec<&str>>();
|
||||
if parts.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(cmd) = cmd::COMMANDS.get(parts[0]) {
|
||||
(cmd.fun)(editor, &parts[1..], event);
|
||||
|
@@ -122,9 +122,17 @@ impl Compositor {
|
||||
}
|
||||
|
||||
pub fn render(&mut self, cx: &mut Context) {
|
||||
let area = self.size();
|
||||
let area = self
|
||||
.terminal
|
||||
.autoresize()
|
||||
.expect("Unable to determine terminal size");
|
||||
|
||||
// TODO: need to recalculate view tree if necessary
|
||||
|
||||
let surface = self.terminal.current_buffer_mut();
|
||||
|
||||
let area = *surface.area();
|
||||
|
||||
for layer in &self.layers {
|
||||
layer.render(area, surface, cx)
|
||||
}
|
||||
|
@@ -240,10 +240,12 @@ pub fn default() -> Keymaps {
|
||||
code: KeyCode::PageUp,
|
||||
modifiers: KeyModifiers::NONE
|
||||
} => commands::page_up,
|
||||
ctrl!('b') => commands::page_up,
|
||||
KeyEvent {
|
||||
code: KeyCode::PageDown,
|
||||
modifiers: KeyModifiers::NONE
|
||||
} => commands::page_down,
|
||||
ctrl!('f') => commands::page_down,
|
||||
ctrl!('u') => commands::half_page_up,
|
||||
ctrl!('d') => commands::half_page_down,
|
||||
|
||||
|
@@ -8,11 +8,13 @@ mod ui;
|
||||
|
||||
use application::Application;
|
||||
|
||||
use helix_core::config_dir;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Error;
|
||||
use anyhow::{Context, Error, Result};
|
||||
|
||||
fn setup_logging(verbosity: u64) -> Result<(), fern::InitError> {
|
||||
fn setup_logging(verbosity: u64) -> Result<()> {
|
||||
let mut base_config = fern::Dispatch::new();
|
||||
|
||||
// Let's say we depend on something which whose "info" level messages are too
|
||||
@@ -27,8 +29,6 @@ fn setup_logging(verbosity: u64) -> Result<(), fern::InitError> {
|
||||
_3_or_more => base_config.level(log::LevelFilter::Trace),
|
||||
};
|
||||
|
||||
let home = dirs_next::home_dir().expect("can't find the home directory");
|
||||
|
||||
// Separate file config so we can include year, month and day in file logs
|
||||
let file_config = fern::Dispatch::new()
|
||||
.format(|out, message, record| {
|
||||
@@ -40,7 +40,7 @@ fn setup_logging(verbosity: u64) -> Result<(), fern::InitError> {
|
||||
message
|
||||
))
|
||||
})
|
||||
.chain(fern::log_file(home.join("helix.log"))?);
|
||||
.chain(fern::log_file(config_dir().join("helix.log"))?);
|
||||
|
||||
base_config.chain(file_config).apply()?;
|
||||
|
||||
@@ -48,10 +48,54 @@ fn setup_logging(verbosity: u64) -> Result<(), fern::InitError> {
|
||||
}
|
||||
|
||||
pub struct Args {
|
||||
display_help: bool,
|
||||
display_version: bool,
|
||||
verbosity: u64,
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn parse_args(mut args: Args) -> Result<Args> {
|
||||
let argv: Vec<String> = std::env::args().collect();
|
||||
let mut iter = argv.iter();
|
||||
|
||||
iter.next(); // skip the program, we don't care about that
|
||||
|
||||
while let Some(arg) = iter.next() {
|
||||
match arg.as_str() {
|
||||
"--" => break, // stop parsing at this point treat the remaining as files
|
||||
"--version" => args.display_version = true,
|
||||
"--help" => args.display_help = true,
|
||||
arg if arg.starts_with("--") => {
|
||||
return Err(Error::msg(format!(
|
||||
"unexpected double dash argument: {}",
|
||||
arg
|
||||
)))
|
||||
}
|
||||
arg if arg.starts_with('-') => {
|
||||
let arg = arg.get(1..).unwrap().chars();
|
||||
for chr in arg {
|
||||
match chr {
|
||||
'v' => args.verbosity += 1,
|
||||
'V' => args.display_version = true,
|
||||
'h' => args.display_help = true,
|
||||
_ => return Err(Error::msg(format!("unexpected short arg {}", chr))),
|
||||
}
|
||||
}
|
||||
}
|
||||
arg => args.files.push(PathBuf::from(arg)),
|
||||
}
|
||||
}
|
||||
|
||||
// push the remaining args, if any to the files
|
||||
for filename in iter {
|
||||
args.files.push(PathBuf::from(filename));
|
||||
}
|
||||
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let help = format!(
|
||||
"\
|
||||
{} {}
|
||||
@@ -75,28 +119,35 @@ FLAGS:
|
||||
env!("CARGO_PKG_DESCRIPTION"),
|
||||
);
|
||||
|
||||
let mut pargs = pico_args::Arguments::from_env();
|
||||
let mut args: Args = Args {
|
||||
display_help: false,
|
||||
display_version: false,
|
||||
verbosity: 0,
|
||||
files: [].to_vec(),
|
||||
};
|
||||
|
||||
args = parse_args(args).context("could not parse arguments")?;
|
||||
|
||||
// Help has a higher priority and should be handled separately.
|
||||
if pargs.contains(["-h", "--help"]) {
|
||||
if args.display_help {
|
||||
print!("{}", help);
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
let mut verbosity: u64 = 0;
|
||||
|
||||
if pargs.contains("-v") {
|
||||
verbosity = 1;
|
||||
if args.display_version {
|
||||
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
setup_logging(verbosity).expect("failed to initialize logging.");
|
||||
let conf_dir = config_dir();
|
||||
|
||||
let args = Args {
|
||||
files: pargs.finish().into_iter().map(|arg| arg.into()).collect(),
|
||||
};
|
||||
if !conf_dir.exists() {
|
||||
std::fs::create_dir(&conf_dir);
|
||||
}
|
||||
|
||||
setup_logging(args.verbosity).context("failed to initialize logging")?;
|
||||
|
||||
// initialize language registry
|
||||
use helix_core::config_dir;
|
||||
use helix_core::syntax::{Loader, LOADER};
|
||||
|
||||
// load $HOME/.config/helix/languages.toml, fallback to default config
|
||||
@@ -105,17 +156,12 @@ FLAGS:
|
||||
.as_deref()
|
||||
.unwrap_or(include_bytes!("../../languages.toml"));
|
||||
|
||||
LOADER.get_or_init(|| {
|
||||
let config = toml::from_slice(toml).expect("Could not parse languages.toml");
|
||||
Loader::new(config)
|
||||
});
|
||||
|
||||
let runtime = tokio::runtime::Runtime::new().unwrap();
|
||||
let config = toml::from_slice(toml).context("Could not parse languages.toml")?;
|
||||
LOADER.get_or_init(|| Loader::new(config));
|
||||
|
||||
// TODO: use the thread local executor to spawn the application task separately from the work pool
|
||||
runtime.block_on(async move {
|
||||
let mut app = Application::new(args).unwrap();
|
||||
let mut app = Application::new(args).context("unable to create new appliction")?;
|
||||
app.run().await;
|
||||
|
||||
app.run().await;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -240,7 +240,7 @@ impl EditorView {
|
||||
for selection in doc
|
||||
.selection(view.id)
|
||||
.iter()
|
||||
.filter(|range| range.overlaps(&screen))
|
||||
.filter(|range| screen.overlaps(&range))
|
||||
{
|
||||
// TODO: render also if only one of the ranges is in viewport
|
||||
let mut start = view.screen_coords_at_pos(doc, text, selection.anchor);
|
||||
@@ -261,7 +261,7 @@ impl EditorView {
|
||||
Rect::new(
|
||||
viewport.x + start.col as u16,
|
||||
viewport.y + start.row as u16,
|
||||
(end.col - start.col) as u16 + 1,
|
||||
((end.col - start.col) as u16 + 1).min(viewport.width),
|
||||
1,
|
||||
),
|
||||
selection_style,
|
||||
@@ -633,6 +633,10 @@ impl Component for EditorView {
|
||||
// clear with background color
|
||||
surface.set_style(area, cx.editor.theme.get("ui.background"));
|
||||
|
||||
// if the terminal size suddenly changed, we need to trigger a resize
|
||||
cx.editor
|
||||
.resize(Rect::new(area.x, area.y, area.width, area.height - 1)); // - 1 to account for commandline
|
||||
|
||||
for (view, is_focused) in cx.editor.tree.views() {
|
||||
let doc = cx.editor.document(view.doc).unwrap();
|
||||
self.render_view(doc, view, area, surface, &cx.editor.theme, is_focused);
|
||||
|
@@ -160,7 +160,13 @@ impl Prompt {
|
||||
if let Some(doc) = (self.doc_fn)(&self.line) {
|
||||
let text = ui::Text::new(doc.to_string());
|
||||
|
||||
let area = Rect::new(completion_area.x, completion_area.y - 3, BASE_WIDTH * 3, 3);
|
||||
let viewport = area;
|
||||
let area = viewport.intersection(Rect::new(
|
||||
completion_area.x,
|
||||
completion_area.y - 3,
|
||||
BASE_WIDTH * 3,
|
||||
3,
|
||||
));
|
||||
|
||||
let background = theme.get("ui.help");
|
||||
surface.clear_with(area, background);
|
||||
|
@@ -137,14 +137,12 @@ where
|
||||
}
|
||||
|
||||
/// Queries the backend for size and resizes if it doesn't match the previous size.
|
||||
pub fn autoresize(&mut self) -> io::Result<()> {
|
||||
if self.viewport.resize_behavior == ResizeBehavior::Auto {
|
||||
let size = self.size()?;
|
||||
if size != self.viewport.area {
|
||||
self.resize(size)?;
|
||||
}
|
||||
pub fn autoresize(&mut self) -> io::Result<Rect> {
|
||||
let size = self.size()?;
|
||||
if size != self.viewport.area {
|
||||
self.resize(size)?;
|
||||
};
|
||||
Ok(())
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
/// Synchronizes terminal size, calls the rendering closure, flushes the current internal state
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use anyhow::{Context, Error};
|
||||
use std::cell::Cell;
|
||||
use std::future::Future;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
@@ -40,7 +41,10 @@ pub struct Document {
|
||||
/// State at last commit. Used for calculating reverts.
|
||||
old_state: Option<State>,
|
||||
/// Undo tree.
|
||||
history: History,
|
||||
// It can be used as a cell where we will take it out to get some parts of the history and put
|
||||
// it back as it separated from the edits. We could split out the parts manually but that will
|
||||
// be more troublesome.
|
||||
history: Cell<History>,
|
||||
last_saved_revision: usize,
|
||||
version: i32, // should be usize?
|
||||
|
||||
@@ -121,7 +125,7 @@ impl Document {
|
||||
old_state,
|
||||
diagnostics: Vec::new(),
|
||||
version: 0,
|
||||
history: History::default(),
|
||||
history: Cell::new(History::default()),
|
||||
last_saved_revision: 0,
|
||||
language_server: None,
|
||||
}
|
||||
@@ -190,7 +194,9 @@ impl Document {
|
||||
let language_server = self.language_server.clone();
|
||||
|
||||
// reset the modified flag
|
||||
self.last_saved_revision = self.history.current_revision();
|
||||
let history = self.history.take();
|
||||
self.last_saved_revision = history.current_revision();
|
||||
self.history.set(history);
|
||||
|
||||
async move {
|
||||
use tokio::{fs::File, io::AsyncWriteExt};
|
||||
@@ -335,7 +341,8 @@ impl Document {
|
||||
}
|
||||
|
||||
pub fn undo(&mut self, view_id: ViewId) -> bool {
|
||||
if let Some(transaction) = self.history.undo() {
|
||||
let mut history = self.history.take();
|
||||
if let Some(transaction) = history.undo() {
|
||||
let success = self._apply(&transaction, view_id);
|
||||
|
||||
// reset changeset to fix len
|
||||
@@ -343,11 +350,13 @@ impl Document {
|
||||
|
||||
return success;
|
||||
}
|
||||
self.history.set(history);
|
||||
false
|
||||
}
|
||||
|
||||
pub fn redo(&mut self, view_id: ViewId) -> bool {
|
||||
if let Some(transaction) = self.history.redo() {
|
||||
let mut history = self.history.take();
|
||||
if let Some(transaction) = history.redo() {
|
||||
let success = self._apply(&transaction, view_id);
|
||||
|
||||
// reset changeset to fix len
|
||||
@@ -355,6 +364,7 @@ impl Document {
|
||||
|
||||
return success;
|
||||
}
|
||||
self.history.set(history);
|
||||
false
|
||||
}
|
||||
|
||||
@@ -373,7 +383,9 @@ impl Document {
|
||||
// HAXX: we need to reconstruct the state as it was before the changes..
|
||||
let old_state = self.old_state.take().expect("no old_state available");
|
||||
|
||||
self.history.commit_revision(&transaction, &old_state);
|
||||
let mut history = self.history.take();
|
||||
history.commit_revision(&transaction, &old_state);
|
||||
self.history.set(history);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -383,9 +395,11 @@ impl Document {
|
||||
|
||||
#[inline]
|
||||
pub fn is_modified(&self) -> bool {
|
||||
let history = self.history.take();
|
||||
let current_revision = history.current_revision();
|
||||
self.history.set(history);
|
||||
self.path.is_some()
|
||||
&& (self.history.current_revision() != self.last_saved_revision
|
||||
|| !self.changes.is_empty())
|
||||
&& (current_revision != self.last_saved_revision || !self.changes.is_empty())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@@ -194,8 +194,9 @@ impl Editor {
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, area: Rect) {
|
||||
self.tree.resize(area);
|
||||
self._refresh();
|
||||
if self.tree.resize(area) {
|
||||
self._refresh();
|
||||
};
|
||||
}
|
||||
|
||||
pub fn focus_next(&mut self) {
|
||||
|
@@ -293,9 +293,13 @@ impl Tree {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, area: Rect) {
|
||||
self.area = area;
|
||||
self.recalculate();
|
||||
pub fn resize(&mut self, area: Rect) -> bool {
|
||||
if self.area != area {
|
||||
self.area = area;
|
||||
self.recalculate();
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn recalculate(&mut self) {
|
||||
|
@@ -143,8 +143,9 @@ impl View {
|
||||
}
|
||||
}
|
||||
|
||||
let row = line - self.first_line as usize;
|
||||
let col = col - self.first_col as usize;
|
||||
// It is possible for underflow to occur if the buffer length is larger than the terminal width.
|
||||
let row = line.saturating_sub(self.first_line);
|
||||
let col = col.saturating_sub(self.first_col);
|
||||
|
||||
Some(Position::new(row, col))
|
||||
}
|
||||
|
@@ -17,6 +17,15 @@ roots = []
|
||||
|
||||
indent = { tab-width = 2, unit = " " }
|
||||
|
||||
[[language]]
|
||||
name = "elixir"
|
||||
scope = "source.elixir"
|
||||
injection-regex = "elixir"
|
||||
file-types = ["ex", "exs"]
|
||||
roots = []
|
||||
|
||||
indent = { tab-width = 2, unit = " " }
|
||||
|
||||
[[language]]
|
||||
name = "json"
|
||||
scope = "source.json"
|
||||
@@ -65,6 +74,17 @@ roots = []
|
||||
|
||||
indent = { tab-width = 2, unit = " " }
|
||||
|
||||
[[language]]
|
||||
name = "typescript"
|
||||
scope = "source.ts"
|
||||
injection-regex = "^(ts|typescript)$"
|
||||
file-types = ["ts"]
|
||||
roots = []
|
||||
# TODO: highlights-jsx, highlights-params
|
||||
|
||||
language-server = { command = "typescript-language-server", args = ["--stdio"] }
|
||||
indent = { tab-width = 2, unit = " " }
|
||||
|
||||
[[language]]
|
||||
name = "css"
|
||||
scope = "source.css"
|
||||
|
146
runtime/queries/elixir/highlights.scm
Normal file
146
runtime/queries/elixir/highlights.scm
Normal file
@@ -0,0 +1,146 @@
|
||||
["when" "and" "or" "not in" "not" "in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword
|
||||
|
||||
[(true) (false) (nil)] @constant.builtin
|
||||
|
||||
(keyword
|
||||
[(keyword_literal)
|
||||
":"] @tag)
|
||||
|
||||
(keyword
|
||||
(keyword_string
|
||||
[(string_start)
|
||||
(string_content)
|
||||
(string_end)] @tag))
|
||||
|
||||
[(atom_literal)
|
||||
(atom_start)
|
||||
(atom_content)
|
||||
(atom_end)] @tag
|
||||
|
||||
(comment) @comment
|
||||
|
||||
(escape_sequence) @escape
|
||||
|
||||
(call function: (function_identifier) @keyword
|
||||
(#match? @keyword "^(defmodule|defexception|defp|def|with|case|cond|raise|import|require|use|defmacrop|defmacro|defguardp|defguard|defdelegate|defstruct|alias|defimpl|defprotocol|defoverridable|receive|if|for|try|throw|unless|reraise|super|quote|unquote|unquote_splicing)$"))
|
||||
|
||||
(call function: (function_identifier) @keyword
|
||||
[(call
|
||||
function: (function_identifier) @function
|
||||
(arguments
|
||||
[(identifier) @variable.parameter
|
||||
(_ (identifier) @variable.parameter)
|
||||
(_ (_ (identifier) @variable.parameter))
|
||||
(_ (_ (_ (identifier) @variable.parameter)))
|
||||
(_ (_ (_ (_ (identifier) @variable.parameter))))
|
||||
(_ (_ (_ (_ (_ (identifier) @variable.parameter)))))]))
|
||||
(binary_op
|
||||
left:
|
||||
(call
|
||||
function: (function_identifier) @function
|
||||
(arguments
|
||||
[(identifier) @variable.parameter
|
||||
(_ (identifier) @variable.parameter)
|
||||
(_ (_ (identifier) @variable.parameter))
|
||||
(_ (_ (_ (identifier) @variable.parameter)))
|
||||
(_ (_ (_ (_ (identifier) @variable.parameter))))
|
||||
(_ (_ (_ (_ (_ (identifier) @variable.parameter)))))]))
|
||||
operator: "when")
|
||||
(binary_op
|
||||
left: (identifier) @variable.parameter
|
||||
operator: _ @function
|
||||
right: (identifier) @variable.parameter)]
|
||||
(#match? @keyword "^(defp|def|defmacrop|defmacro|defguardp|defguard|defdelegate)$")
|
||||
(#match? @variable.parameter "^[^_]"))
|
||||
|
||||
(call (function_identifier) @keyword
|
||||
[(call
|
||||
function: (function_identifier) @function)
|
||||
(identifier) @function
|
||||
(binary_op
|
||||
left:
|
||||
[(call
|
||||
function: (function_identifier) @function)
|
||||
(identifier) @function]
|
||||
operator: "when")]
|
||||
(#match? @keyword "^(defp|def|defmacrop|defmacro|defguardp|defguard|defdelegate)$"))
|
||||
|
||||
(anonymous_function
|
||||
(stab_expression
|
||||
left: (bare_arguments
|
||||
[(identifier) @variable.parameter
|
||||
(_ (identifier) @variable.parameter)
|
||||
(_ (_ (identifier) @variable.parameter))
|
||||
(_ (_ (_ (identifier) @variable.parameter)))
|
||||
(_ (_ (_ (_ (identifier) @variable.parameter))))
|
||||
(_ (_ (_ (_ (_ (identifier) @variable.parameter)))))]))
|
||||
(#match? @variable.parameter "^[^_]"))
|
||||
|
||||
(unary_op
|
||||
operator: "@"
|
||||
(call (identifier) @attribute
|
||||
(heredoc
|
||||
[(heredoc_start)
|
||||
(heredoc_content)
|
||||
(heredoc_end)] @doc))
|
||||
(#match? @attribute "^(doc|moduledoc)$"))
|
||||
|
||||
(module) @type
|
||||
|
||||
(unary_op
|
||||
operator: "@" @attribute
|
||||
[(call
|
||||
function: (function_identifier) @attribute)
|
||||
(identifier) @attribute])
|
||||
|
||||
(unary_op
|
||||
operator: _ @operator)
|
||||
|
||||
(binary_op
|
||||
operator: _ @operator)
|
||||
|
||||
(heredoc
|
||||
[(heredoc_start)
|
||||
(heredoc_content)
|
||||
(heredoc_end)] @string)
|
||||
|
||||
(string
|
||||
[(string_start)
|
||||
(string_content)
|
||||
(string_end)] @string)
|
||||
|
||||
(sigil_start) @string.special
|
||||
(sigil_content) @string
|
||||
(sigil_end) @string.special
|
||||
|
||||
(interpolation
|
||||
"#{" @punctuation.special
|
||||
"}" @punctuation.special)
|
||||
|
||||
[
|
||||
","
|
||||
"->"
|
||||
"."
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
"<<"
|
||||
">>"
|
||||
] @punctuation.bracket
|
||||
|
||||
[(identifier) @function.special
|
||||
(#match? @function.special "^__.+__$")]
|
||||
|
||||
[(remote_identifier) @function.special
|
||||
(#match? @function.special "^__.+__$")]
|
||||
|
||||
[(identifier) @comment
|
||||
(#match? @comment "^_")]
|
||||
|
||||
(ERROR) @warning
|
28
runtime/queries/javascript/indents.toml
Normal file
28
runtime/queries/javascript/indents.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
indent = [
|
||||
"array",
|
||||
"object",
|
||||
"arguments",
|
||||
"formal_parameters",
|
||||
|
||||
"statement_block",
|
||||
"object_pattern",
|
||||
"class_body",
|
||||
"named_imports",
|
||||
|
||||
"binary_expression",
|
||||
"return_statement",
|
||||
"template_substitution",
|
||||
# (expression_statement (call_expression))
|
||||
"export_clause",
|
||||
|
||||
# typescript
|
||||
"enum_declaration",
|
||||
"interface_declaration",
|
||||
"object_type",
|
||||
]
|
||||
|
||||
outdent = [
|
||||
"}",
|
||||
"]",
|
||||
")"
|
||||
]
|
36
runtime/queries/typescript/highlights.scm
Normal file
36
runtime/queries/typescript/highlights.scm
Normal file
@@ -0,0 +1,36 @@
|
||||
; inherits: javascript
|
||||
|
||||
; Types
|
||||
|
||||
(type_identifier) @type
|
||||
(predefined_type) @type.builtin
|
||||
|
||||
((identifier) @type
|
||||
(#match? @type "^[A-Z]"))
|
||||
|
||||
(type_arguments
|
||||
"<" @punctuation.bracket
|
||||
">" @punctuation.bracket)
|
||||
|
||||
; Variables
|
||||
|
||||
(required_parameter (identifier) @variable.parameter)
|
||||
(optional_parameter (identifier) @variable.parameter)
|
||||
|
||||
; Keywords
|
||||
|
||||
[
|
||||
"abstract"
|
||||
"declare"
|
||||
"enum"
|
||||
"export"
|
||||
"implements"
|
||||
"interface"
|
||||
"keyof"
|
||||
"namespace"
|
||||
"private"
|
||||
"protected"
|
||||
"public"
|
||||
"type"
|
||||
"readonly"
|
||||
] @keyword
|
1
runtime/queries/typescript/indents.toml
Symbolic link
1
runtime/queries/typescript/indents.toml
Symbolic link
@@ -0,0 +1 @@
|
||||
../javascript/indents.toml
|
2
runtime/queries/typescript/locals.scm
Normal file
2
runtime/queries/typescript/locals.scm
Normal file
@@ -0,0 +1,2 @@
|
||||
(required_parameter (identifier) @local.definition)
|
||||
(optional_parameter (identifier) @local.definition)
|
23
runtime/queries/typescript/tags.scm
Normal file
23
runtime/queries/typescript/tags.scm
Normal file
@@ -0,0 +1,23 @@
|
||||
(function_signature
|
||||
name: (identifier) @name) @definition.function
|
||||
|
||||
(method_signature
|
||||
name: (property_identifier) @name) @definition.method
|
||||
|
||||
(abstract_method_signature
|
||||
name: (property_identifier) @name) @definition.method
|
||||
|
||||
(abstract_class_declaration
|
||||
name: (type_identifier) @name) @definition.class
|
||||
|
||||
(module
|
||||
name: (identifier) @name) @definition.module
|
||||
|
||||
(interface_declaration
|
||||
name: (type_identifier) @name) @definition.interface
|
||||
|
||||
(type_annotation
|
||||
(type_identifier) @name) @reference.type
|
||||
|
||||
(new_expression
|
||||
constructor: (identifier) @name) @reference.class
|
@@ -4,6 +4,8 @@ pkgs.mkShell {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
(rust-bin.stable.latest.default.override { extensions = ["rust-src"]; })
|
||||
lld_10
|
||||
lldb
|
||||
# pythonPackages.six
|
||||
stdenv.cc.cc.lib
|
||||
# pkg-config
|
||||
];
|
||||
@@ -12,7 +14,7 @@ pkgs.mkShell {
|
||||
# https://github.com/rust-lang/rust/issues/55979
|
||||
LD_LIBRARY_PATH="${stdenv.cc.cc.lib}/lib64:$LD_LIBRARY_PATH";
|
||||
|
||||
# HELIX_RUNTIME=./runtime;
|
||||
HELIX_RUNTIME="/home/speed/src/helix/runtime";
|
||||
shellHook = ''
|
||||
export HELIX_RUNTIME=$PWD/runtime
|
||||
'';
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user