Compare commits

...

44 Commits

Author SHA1 Message Date
Blaž Hrastnik
7e8603247d Merge pull request #66 from IceDragon200/replaced-args-parser
Drop pico-args in favour of a hand rolled parser
2021-06-03 10:32:42 +09:00
Blaž Hrastnik
7140908f6e Nix: add lldb to shell 2021-06-03 10:31:33 +09:00
Blaž Hrastnik
6dba1e7ec7 Clippy lint 2021-06-03 10:31:14 +09:00
Blaž Hrastnik
c0332bd935 Fix split sizes getting out of sync with the terminal size, refs #69 2021-06-03 10:28:49 +09:00
Blaž Hrastnik
3c7729906c Merge pull request #70 from RLHerbert/master
Fix panic when buffer larger than terminal width
2021-06-03 10:28:14 +09:00
Rowan Herbert
1b67fae9f4 Fix panic when buffer larger than terminal width 2021-06-02 16:30:40 -07:00
Corey Powell
f0018280cb Refactored parse_args loop
Thanks @PabloMansanet
2021-06-02 14:26:20 -05:00
Corey Powell
7202953e69 Dropped pico-args in favour of a simpler hand roller parser
Not the greatest looking, but it gets the job done
2021-06-02 14:26:13 -05:00
Corey Powell
7761c88d61 Merge pull request #62 from pickfire/cell
Separate document history into Cell
2021-06-02 13:27:35 -05:00
Corey Powell
68f5031dcc Merge pull request #49 from eleijonmarck/patch-1
Update README.md to include shortcuts
2021-06-02 13:15:32 -05:00
Corey Powell
83031564db Merge pull request #57 from pickfire/fix-panic
Fix panic opening rust file
2021-06-02 13:14:19 -05:00
Ivan Tham
eab6e53511 Fix panic opening rust file
Application::new will use stuff that requires tokio runtime.
2021-06-02 23:49:26 +08:00
Ivan Tham
f5f46b1fed Separate document history into Cell
As history is used separately from the rest of the edits, separating it
can avoid needless borrowing and cloning. But one need to be aware later.
2021-06-02 23:47:50 +08:00
Eric Leijonmarck
5f49bafbe8 Update README.md 2021-06-02 17:05:15 +02:00
Blaž Hrastnik
2719a35123 Merge pull request #55 from helix-editor/autoresize
autoresize terminal in compositor render
2021-06-02 22:45:43 +09:00
Blaž Hrastnik
0a6672c626 Merge pull request #50 from wojciechkepka/config
Use config_dir for logging, create config_dir
2021-06-02 22:43:28 +09:00
Blaž Hrastnik
b51111a364 Merge pull request #21 from IceDragon200/elixir-syntax
Added elixir syntax
2021-06-02 22:41:51 +09:00
Jan Hrastnik
78980f575b autoresize terminal in compositor render 2021-06-02 15:40:08 +02:00
Corey Powell
0bb375bafa Added missing tree-sitter-elixir submodule 2021-06-02 06:43:22 -05:00
Eric Leijonmarck
c960bcfc24 Update README.md 2021-06-02 13:15:31 +02:00
Wojciech Kępka
e88383d990 Use config_dir for logging, create config_dir 2021-06-02 12:25:25 +02:00
Eric Leijonmarck
312b29f712 Update README.md 2021-06-02 12:05:39 +02:00
Blaž Hrastnik
f4560cb68a Better fix for w/e that also covers ia<esc>we/ia<esc>wb 2021-06-02 14:57:43 +09:00
Blaž Hrastnik
cbb3ebafdc Support ctrl-f and ctrl-b to page up/down, fixes #41 2021-06-02 13:20:36 +09:00
Blaž Hrastnik
0851110d10 f/t: Check if at bounds before searching, refs #43, closes #37 2021-06-02 13:20:27 +09:00
Blaž Hrastnik
3ace581191 Fix panics when triggering w or e on the last char of the line
Closes #32
2021-06-02 13:19:40 +09:00
Blaž Hrastnik
c0264b9f7f fix: Don't allow moving past last line, fixes #30, #24
Off by 1 error
2021-06-02 13:19:40 +09:00
Blaž Hrastnik
22dad592b8 Merge pull request #40 from data0x200/fix-empty-command
Fix empty command cause panic
2021-06-02 13:06:57 +09:00
Corey Powell
ca042a4bde Added elixir syntax
Using custom fork for now to get around generating the source files
2021-06-01 21:59:16 -05:00
Blaž Hrastnik
67b1cd32c7 Update install notes 2021-06-02 11:14:46 +09:00
Daichi Takamiya
4d12c7c3cf Fix empty command cause panic 2021-06-02 10:55:32 +09:00
Blaž Hrastnik
4f56a8e248 book: Always generate the CNAME file 2021-06-02 10:24:00 +09:00
Blaž Hrastnik
dbc392d92c Run fmt 2021-06-02 09:56:50 +09:00
Blaž Hrastnik
7967d312c0 Merge pull request #38 from nathom/master
Add .DS_Store to ignored directories
2021-06-02 09:37:09 +09:00
Blaž Hrastnik
db48d22384 Merge pull request #19 from wojciechkepka/archinstall
Add Arch Linux installation instructions to README
2021-06-02 09:30:41 +09:00
Blaž Hrastnik
533ff61d0e Merge pull request #34 from DanySpin97/improve-error
Improve errors handling in main by adding context
2021-06-02 09:30:16 +09:00
nathom
b1ce969d80 Add .DS_Store to ignored directories 2021-06-01 17:29:37 -07:00
Blaž Hrastnik
01bf363446 Merge pull request #31 from wullewutz/patch-1
Fixed c/p error in keymap doc
2021-06-02 09:26:29 +09:00
Blaž Hrastnik
cc323f7665 Merge pull request #36 from swdunlop/patch-1
Make HELIX_RUNTIME depend on pwd, not speed's HOME
2021-06-02 09:26:04 +09:00
Scott Dunlop
60caaf7fc4 Make HELIX_RUNTIME depend on pwd, not speed's HOME 2021-06-01 15:03:57 -07:00
Danilo Spinella
ea824ed05d Improve errors handling in main by adding context
Return a anyhow::Result in main function so that Context can be used
there too.
2021-06-01 23:27:16 +02:00
wullewutz
cfae07e7ba Fixed c/p error in keymap doc
Go to definition mapping is "gd" not "ge"
2021-06-01 22:36:42 +02:00
wojciechkepka
56dbc60840 Add Arch Linux installation instructions to README 2021-06-01 21:08:09 +02:00
Blaž Hrastnik
c2e6b9f506 Add typescript support & ts/js indentation queries 2021-06-01 17:55:11 +09:00
33 changed files with 459 additions and 90 deletions

4
.gitmodules vendored
View File

@@ -82,3 +82,7 @@
path = helix-syntax/languages/tree-sitter-toml path = helix-syntax/languages/tree-sitter-toml
url = https://github.com/ikatyang/tree-sitter-toml url = https://github.com/ikatyang/tree-sitter-toml
shallow = true 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
View File

@@ -326,7 +326,6 @@ dependencies = [
"log", "log",
"num_cpus", "num_cpus",
"once_cell", "once_cell",
"pico-args",
"pulldown-cmark", "pulldown-cmark",
"serde", "serde",
"serde_json", "serde_json",
@@ -607,12 +606,6 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pico-args"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d7afeb98c5a10e0bffcc7fc16e105b04d06729fac5fd6384aebf7ff5cb5a67d"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.6" version = "0.2.6"

View File

@@ -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 runtime inside the same folder as the executable, but that can be overriden via
the `HELIX_RUNTIME` environment variable. 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 # Contributing
Contributors are very welcome! **No contribution is too small and all contributions are valued.** 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 We provide an [architecture.md](./docs/architecture.md) that should give you
a good overview of the internals. 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 # Getting help
Discuss the project on the community [Matrix channel](https://matrix.to/#/#helix-community:matrix.org). Discuss the project on the community [Matrix channel](https://matrix.to/#/#helix-community:matrix.org).

View File

@@ -4,3 +4,6 @@ language = "en"
multilingual = false multilingual = false
src = "src" src = "src"
theme = "colibri" theme = "colibri"
[output.html]
cname = "docs.helix-editor.com"

View File

@@ -6,10 +6,7 @@ We provide pre-built binaries on the [GitHub Releases page](https://github.com/h
TODO: brew tap TODO: brew tap
``` Please use a pre-built binary release for the time being.
$ brew tap helix-editor/helix
$ brew install helix
```
## Linux ## Linux
@@ -21,7 +18,9 @@ shell for working on Helix.
### Arch Linux ### 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 ## Build from source

View File

@@ -118,7 +118,7 @@ Jumps to various locations.
|-----|-----------| |-----|-----------|
| g | Go to the start of the file | | g | Go to the start of the file |
| e | Go to the end of the file | | e | Go to the end of the file |
| e | Go to definition | | d | Go to definition |
| t | Go to type definition | | t | Go to type definition |
| r | Go to references | | r | Go to references |
| i | Go to implementation | | i | Go to implementation |

View File

@@ -65,9 +65,7 @@ impl History {
self.cursor == 0 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() { if self.at_root() {
// We're at the root of undo, nothing to do. // We're at the root of undo, nothing to do.
return None; return None;
@@ -77,17 +75,17 @@ impl History {
self.cursor = current_revision.parent; self.cursor = current_revision.parent;
Some(current_revision.revert.clone()) Some(&current_revision.revert)
} }
pub fn redo(&mut self) -> Option<Transaction> { pub fn redo(&mut self) -> Option<&Transaction> {
let current_revision = &self.revisions[self.cursor]; let current_revision = &self.revisions[self.cursor];
// for now, simply pick the latest child (linear undo / redo) // for now, simply pick the latest child (linear undo / redo)
if let Some((index, transaction)) = current_revision.children.last() { if let Some((index, transaction)) = current_revision.children.last() {
self.cursor = *index; self.cursor = *index;
return Some(transaction.clone()); return Some(&transaction);
} }
None None
} }

View File

@@ -45,7 +45,7 @@ pub fn move_vertically(
let new_line = match dir { let new_line = match dir {
Direction::Backward => row.saturating_sub(count), 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 // 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; begin += 1;
} }
// return if not skip while? if !skip_over_next(slice, &mut begin, |ch| ch == '\n') {
skip_over_next(slice, &mut begin, |ch| ch == '\n'); return None;
};
ch = slice.char(begin); ch = slice.char(begin);
end = begin + 1; 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; let mut end = begin;
for _ in 0..count { for _ in 0..count {
if begin + 1 == slice.len_chars() { if begin + 2 >= slice.len_chars() {
return None; return None;
} }
@@ -145,8 +146,9 @@ pub fn move_next_word_end(slice: RopeSlice, mut begin: usize, count: usize) -> O
begin += 1; begin += 1;
} }
// return if not skip while? if !skip_over_next(slice, &mut begin, |ch| ch == '\n') {
skip_over_next(slice, &mut begin, |ch| ch == '\n'); return None;
};
end = begin; end = begin;
@@ -199,18 +201,20 @@ fn categorize(ch: char) -> Category {
} }
#[inline] #[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 where
F: Fn(char) -> bool, F: Fn(char) -> bool,
{ {
let mut chars = slice.chars_at(*pos); let mut chars = slice.chars_at(*pos);
for ch in chars { while let Some(ch) = chars.next() {
if !fun(ch) { if !fun(ch) {
break; break;
} }
*pos += 1; *pos += 1;
} }
chars.next().is_some()
} }
#[inline] #[inline]

View File

@@ -7,6 +7,10 @@ pub fn find_nth_next(
n: usize, n: usize,
inclusive: bool, inclusive: bool,
) -> Option<usize> { ) -> Option<usize> {
if pos >= text.len_chars() {
return None;
}
// start searching right after pos // start searching right after pos
let mut chars = text.chars_at(pos + 1); let mut chars = text.chars_at(pos + 1);

View File

@@ -415,7 +415,7 @@ impl ChangeSet {
/// Transaction represents a single undoable unit of changes. Several changes can be grouped into /// Transaction represents a single undoable unit of changes. Several changes can be grouped into
/// a single transaction. /// a single transaction.
#[derive(Debug, Clone)] #[derive(Debug, Default, Clone)]
pub struct Transaction { pub struct Transaction {
changes: ChangeSet, changes: ChangeSet,
selection: Option<Selection>, selection: Option<Selection>,

View File

@@ -107,7 +107,10 @@ fn build_dir(dir: &str, language: &str) {
} }
fn main() { 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 dirs = collect_tree_sitter_dirs(&ignore);
let mut n_jobs = 0; let mut n_jobs = 0;

View File

@@ -72,6 +72,7 @@ mk_langs!(
(CSharp, tree_sitter_c_sharp), (CSharp, tree_sitter_c_sharp),
(Cpp, tree_sitter_cpp), (Cpp, tree_sitter_cpp),
(Css, tree_sitter_css), (Css, tree_sitter_css),
(Elixir, tree_sitter_elixir),
(Go, tree_sitter_go), (Go, tree_sitter_go),
// (Haskell, tree_sitter_haskell), // (Haskell, tree_sitter_haskell),
(Html, tree_sitter_html), (Html, tree_sitter_html),

View File

@@ -24,7 +24,6 @@ tokio = { version = "1", features = ["full"] }
num_cpus = "1" num_cpus = "1"
tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] } tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] }
crossterm = { version = "0.19", features = ["event-stream"] } crossterm = { version = "0.19", features = ["event-stream"] }
pico-args = "0.4"
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }

View File

@@ -473,10 +473,10 @@ fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
let last_line = view.last_line(doc); let last_line = view.last_line(doc);
// clamp into viewport // clamp into viewport
let line = cursor.row.clamp( let line = cursor
view.first_line + scrolloff, .row
last_line.saturating_sub(scrolloff), .min(view.first_line + scrolloff)
); .max(last_line.saturating_sub(scrolloff));
let text = doc.text().slice(..); let text = doc.text().slice(..);
let pos = pos_at_coords(text, Position::new(line, cursor.col)); // this func will properly truncate to line end 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>>(); let parts = input.split_ascii_whitespace().collect::<Vec<&str>>();
if parts.is_empty() {
return;
}
if let Some(cmd) = cmd::COMMANDS.get(parts[0]) { if let Some(cmd) = cmd::COMMANDS.get(parts[0]) {
(cmd.fun)(editor, &parts[1..], event); (cmd.fun)(editor, &parts[1..], event);

View File

@@ -122,9 +122,17 @@ impl Compositor {
} }
pub fn render(&mut self, cx: &mut Context) { 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 surface = self.terminal.current_buffer_mut();
let area = *surface.area();
for layer in &self.layers { for layer in &self.layers {
layer.render(area, surface, cx) layer.render(area, surface, cx)
} }

View File

@@ -240,10 +240,12 @@ pub fn default() -> Keymaps {
code: KeyCode::PageUp, code: KeyCode::PageUp,
modifiers: KeyModifiers::NONE modifiers: KeyModifiers::NONE
} => commands::page_up, } => commands::page_up,
ctrl!('b') => commands::page_up,
KeyEvent { KeyEvent {
code: KeyCode::PageDown, code: KeyCode::PageDown,
modifiers: KeyModifiers::NONE modifiers: KeyModifiers::NONE
} => commands::page_down, } => commands::page_down,
ctrl!('f') => commands::page_down,
ctrl!('u') => commands::half_page_up, ctrl!('u') => commands::half_page_up,
ctrl!('d') => commands::half_page_down, ctrl!('d') => commands::half_page_down,

View File

@@ -8,11 +8,13 @@ mod ui;
use application::Application; use application::Application;
use helix_core::config_dir;
use std::path::PathBuf; 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 mut base_config = fern::Dispatch::new();
// Let's say we depend on something which whose "info" level messages are too // 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), _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 // Separate file config so we can include year, month and day in file logs
let file_config = fern::Dispatch::new() let file_config = fern::Dispatch::new()
.format(|out, message, record| { .format(|out, message, record| {
@@ -40,7 +40,7 @@ fn setup_logging(verbosity: u64) -> Result<(), fern::InitError> {
message message
)) ))
}) })
.chain(fern::log_file(home.join("helix.log"))?); .chain(fern::log_file(config_dir().join("helix.log"))?);
base_config.chain(file_config).apply()?; base_config.chain(file_config).apply()?;
@@ -48,10 +48,54 @@ fn setup_logging(verbosity: u64) -> Result<(), fern::InitError> {
} }
pub struct Args { pub struct Args {
display_help: bool,
display_version: bool,
verbosity: u64,
files: Vec<PathBuf>, 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!( let help = format!(
"\ "\
{} {} {} {}
@@ -75,28 +119,35 @@ FLAGS:
env!("CARGO_PKG_DESCRIPTION"), 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. // Help has a higher priority and should be handled separately.
if pargs.contains(["-h", "--help"]) { if args.display_help {
print!("{}", help); print!("{}", help);
std::process::exit(0); std::process::exit(0);
} }
let mut verbosity: u64 = 0; if args.display_version {
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
if pargs.contains("-v") { std::process::exit(0);
verbosity = 1;
} }
setup_logging(verbosity).expect("failed to initialize logging."); let conf_dir = config_dir();
let args = Args { if !conf_dir.exists() {
files: pargs.finish().into_iter().map(|arg| arg.into()).collect(), std::fs::create_dir(&conf_dir);
}; }
setup_logging(args.verbosity).context("failed to initialize logging")?;
// initialize language registry // initialize language registry
use helix_core::config_dir;
use helix_core::syntax::{Loader, LOADER}; use helix_core::syntax::{Loader, LOADER};
// load $HOME/.config/helix/languages.toml, fallback to default config // load $HOME/.config/helix/languages.toml, fallback to default config
@@ -105,17 +156,12 @@ FLAGS:
.as_deref() .as_deref()
.unwrap_or(include_bytes!("../../languages.toml")); .unwrap_or(include_bytes!("../../languages.toml"));
LOADER.get_or_init(|| { let config = toml::from_slice(toml).context("Could not parse languages.toml")?;
let config = toml::from_slice(toml).expect("Could not parse languages.toml"); LOADER.get_or_init(|| Loader::new(config));
Loader::new(config)
});
let runtime = tokio::runtime::Runtime::new().unwrap();
// TODO: use the thread local executor to spawn the application task separately from the work pool // 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).context("unable to create new appliction")?;
let mut app = Application::new(args).unwrap(); app.run().await;
app.run().await; Ok(())
});
} }

View File

@@ -240,7 +240,7 @@ impl EditorView {
for selection in doc for selection in doc
.selection(view.id) .selection(view.id)
.iter() .iter()
.filter(|range| range.overlaps(&screen)) .filter(|range| screen.overlaps(&range))
{ {
// TODO: render also if only one of the ranges is in viewport // TODO: render also if only one of the ranges is in viewport
let mut start = view.screen_coords_at_pos(doc, text, selection.anchor); let mut start = view.screen_coords_at_pos(doc, text, selection.anchor);
@@ -261,7 +261,7 @@ impl EditorView {
Rect::new( Rect::new(
viewport.x + start.col as u16, viewport.x + start.col as u16,
viewport.y + start.row 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, 1,
), ),
selection_style, selection_style,
@@ -633,6 +633,10 @@ impl Component for EditorView {
// clear with background color // clear with background color
surface.set_style(area, cx.editor.theme.get("ui.background")); 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() { for (view, is_focused) in cx.editor.tree.views() {
let doc = cx.editor.document(view.doc).unwrap(); let doc = cx.editor.document(view.doc).unwrap();
self.render_view(doc, view, area, surface, &cx.editor.theme, is_focused); self.render_view(doc, view, area, surface, &cx.editor.theme, is_focused);

View File

@@ -160,7 +160,13 @@ impl Prompt {
if let Some(doc) = (self.doc_fn)(&self.line) { if let Some(doc) = (self.doc_fn)(&self.line) {
let text = ui::Text::new(doc.to_string()); 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"); let background = theme.get("ui.help");
surface.clear_with(area, background); surface.clear_with(area, background);

View File

@@ -137,14 +137,12 @@ where
} }
/// Queries the backend for size and resizes if it doesn't match the previous size. /// Queries the backend for size and resizes if it doesn't match the previous size.
pub fn autoresize(&mut self) -> io::Result<()> { pub fn autoresize(&mut self) -> io::Result<Rect> {
if self.viewport.resize_behavior == ResizeBehavior::Auto { let size = self.size()?;
let size = self.size()?; if size != self.viewport.area {
if size != self.viewport.area { self.resize(size)?;
self.resize(size)?;
}
}; };
Ok(()) Ok(size)
} }
/// Synchronizes terminal size, calls the rendering closure, flushes the current internal state /// Synchronizes terminal size, calls the rendering closure, flushes the current internal state

View File

@@ -1,4 +1,5 @@
use anyhow::{Context, Error}; use anyhow::{Context, Error};
use std::cell::Cell;
use std::future::Future; use std::future::Future;
use std::path::{Component, Path, PathBuf}; use std::path::{Component, Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
@@ -40,7 +41,10 @@ pub struct Document {
/// State at last commit. Used for calculating reverts. /// State at last commit. Used for calculating reverts.
old_state: Option<State>, old_state: Option<State>,
/// Undo tree. /// 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, last_saved_revision: usize,
version: i32, // should be usize? version: i32, // should be usize?
@@ -121,7 +125,7 @@ impl Document {
old_state, old_state,
diagnostics: Vec::new(), diagnostics: Vec::new(),
version: 0, version: 0,
history: History::default(), history: Cell::new(History::default()),
last_saved_revision: 0, last_saved_revision: 0,
language_server: None, language_server: None,
} }
@@ -190,7 +194,9 @@ impl Document {
let language_server = self.language_server.clone(); let language_server = self.language_server.clone();
// reset the modified flag // 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 { async move {
use tokio::{fs::File, io::AsyncWriteExt}; use tokio::{fs::File, io::AsyncWriteExt};
@@ -335,7 +341,8 @@ impl Document {
} }
pub fn undo(&mut self, view_id: ViewId) -> bool { 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); let success = self._apply(&transaction, view_id);
// reset changeset to fix len // reset changeset to fix len
@@ -343,11 +350,13 @@ impl Document {
return success; return success;
} }
self.history.set(history);
false false
} }
pub fn redo(&mut self, view_id: ViewId) -> bool { 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); let success = self._apply(&transaction, view_id);
// reset changeset to fix len // reset changeset to fix len
@@ -355,6 +364,7 @@ impl Document {
return success; return success;
} }
self.history.set(history);
false false
} }
@@ -373,7 +383,9 @@ impl Document {
// HAXX: we need to reconstruct the state as it was before the changes.. // 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"); 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] #[inline]
@@ -383,9 +395,11 @@ impl Document {
#[inline] #[inline]
pub fn is_modified(&self) -> bool { 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.path.is_some()
&& (self.history.current_revision() != self.last_saved_revision && (current_revision != self.last_saved_revision || !self.changes.is_empty())
|| !self.changes.is_empty())
} }
#[inline] #[inline]

View File

@@ -194,8 +194,9 @@ impl Editor {
} }
pub fn resize(&mut self, area: Rect) { pub fn resize(&mut self, area: Rect) {
self.tree.resize(area); if self.tree.resize(area) {
self._refresh(); self._refresh();
};
} }
pub fn focus_next(&mut self) { pub fn focus_next(&mut self) {

View File

@@ -293,9 +293,13 @@ impl Tree {
} }
} }
pub fn resize(&mut self, area: Rect) { pub fn resize(&mut self, area: Rect) -> bool {
self.area = area; if self.area != area {
self.recalculate(); self.area = area;
self.recalculate();
return true;
}
false
} }
pub fn recalculate(&mut self) { pub fn recalculate(&mut self) {

View File

@@ -143,8 +143,9 @@ impl View {
} }
} }
let row = line - self.first_line as usize; // It is possible for underflow to occur if the buffer length is larger than the terminal width.
let col = col - self.first_col as usize; let row = line.saturating_sub(self.first_line);
let col = col.saturating_sub(self.first_col);
Some(Position::new(row, col)) Some(Position::new(row, col))
} }

View File

@@ -17,6 +17,15 @@ roots = []
indent = { tab-width = 2, unit = " " } 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]] [[language]]
name = "json" name = "json"
scope = "source.json" scope = "source.json"
@@ -65,6 +74,17 @@ roots = []
indent = { tab-width = 2, unit = " " } 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]] [[language]]
name = "css" name = "css"
scope = "source.css" scope = "source.css"

View 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

View 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 = [
"}",
"]",
")"
]

View 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

View File

@@ -0,0 +1 @@
../javascript/indents.toml

View File

@@ -0,0 +1,2 @@
(required_parameter (identifier) @local.definition)
(optional_parameter (identifier) @local.definition)

View 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

View File

@@ -4,6 +4,8 @@ pkgs.mkShell {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
(rust-bin.stable.latest.default.override { extensions = ["rust-src"]; }) (rust-bin.stable.latest.default.override { extensions = ["rust-src"]; })
lld_10 lld_10
lldb
# pythonPackages.six
stdenv.cc.cc.lib stdenv.cc.cc.lib
# pkg-config # pkg-config
]; ];
@@ -12,7 +14,7 @@ pkgs.mkShell {
# https://github.com/rust-lang/rust/issues/55979 # https://github.com/rust-lang/rust/issues/55979
LD_LIBRARY_PATH="${stdenv.cc.cc.lib}/lib64:$LD_LIBRARY_PATH"; LD_LIBRARY_PATH="${stdenv.cc.cc.lib}/lib64:$LD_LIBRARY_PATH";
# HELIX_RUNTIME=./runtime; shellHook = ''
HELIX_RUNTIME="/home/speed/src/helix/runtime"; export HELIX_RUNTIME=$PWD/runtime
'';
} }