Merge branch 'master' into great_line_ending_and_cursor_range_cleanup

pull/376/head
Nathan Vegdahl 3 years ago
commit a77274e8bb

@ -99,6 +99,7 @@ jobs:
else else
cp "target/${{ matrix.target }}/release/hx" "dist/" cp "target/${{ matrix.target }}/release/hx" "dist/"
fi fi
cp -r runtime dist
- uses: actions/upload-artifact@v2.2.4 - uses: actions/upload-artifact@v2.2.4
with: with:
@ -148,7 +149,7 @@ jobs:
pkgname=helix-$TAG-$platform pkgname=helix-$TAG-$platform
mkdir tmp/$pkgname mkdir tmp/$pkgname
cp LICENSE README.md tmp/$pkgname cp LICENSE README.md tmp/$pkgname
cp -r runtime tmp/$pkgname/ mv bins-$platform/runtime tmp/$pkgname/
mv bins-$platform/hx$exe tmp/$pkgname mv bins-$platform/hx$exe tmp/$pkgname
chmod +x tmp/$pkgname/hx$exe chmod +x tmp/$pkgname/hx$exe

1
.gitignore vendored

@ -3,3 +3,4 @@ target
helix-term/rustfmt.toml helix-term/rustfmt.toml
helix-syntax/languages/ helix-syntax/languages/
result result
runtime/grammars

57
Cargo.lock generated

@ -13,9 +13,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.41" version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
[[package]] [[package]]
name = "arc-swap" name = "arc-swap"
@ -58,12 +58,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.68" version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
dependencies = [
"jobserver",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -354,8 +351,9 @@ dependencies = [
name = "helix-syntax" name = "helix-syntax"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"anyhow",
"cc", "cc",
"serde", "libloading",
"threadpool", "threadpool",
"tree-sitter", "tree-sitter",
] ]
@ -475,15 +473,6 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "jobserver"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "jsonrpc-core" name = "jsonrpc-core"
version = "17.1.0" version = "17.1.0"
@ -509,6 +498,16 @@ version = "0.2.97"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
[[package]]
name = "libloading"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
dependencies = [
"cfg-if 1.0.0",
"winapi",
]
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.4" version = "0.4.4"
@ -914,9 +913,9 @@ checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
[[package]] [[package]]
name = "slotmap" name = "slotmap"
version = "1.0.3" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585cd5dffe4e9e06f6dfdf66708b70aca3f781bed561f4f667b2d9c0d4559e36" checksum = "a952280edbecfb1d4bd3cf2dbc309dc6ab523e53487c438ae21a6df09fe84bc4"
dependencies = [ dependencies = [
"version_check", "version_check",
] ]
@ -957,18 +956,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.25" version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.25" version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1010,9 +1009,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.7.1" version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fb2ed024293bb19f7a5dc54fe83bf86532a44c12a2bb8ba40d64a4509395ca2" checksum = "98c8b05dc14c75ea83d63dd391100353789f5f24b8b3866542a5e85c8be8e985"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"bytes", "bytes",
@ -1041,9 +1040,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.6" version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8864d706fdb3cc0843a49647ac892720dac98a6eeb818b77190592cf4994066" checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"pin-project-lite", "pin-project-lite",
@ -1104,9 +1103,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.7.1" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"

@ -41,14 +41,17 @@ cargo install --path helix-term
This will install the `hx` binary to `$HOME/.cargo/bin`. This will install the `hx` binary to `$HOME/.cargo/bin`.
Now copy the `runtime/` directory somewhere. Helix will by default look for the runtime Helix also needs it's runtime files so make sure to copy/symlink the `runtime/` directory into the
inside the config directory or the same directory as executable, but that can be overriden config directory (for example `~/.config/helix/runtime` on Linux/macOS). This location can be overriden
via the `HELIX_RUNTIME` environment variable. via the `HELIX_RUNTIME` environment variable.
> NOTE: running via cargo doesn't require setting explicit `HELIX_RUNTIME` path, it will automatically Packages already solve this for you by wrapping the `hx` binary with a wrapper
that sets the variable to the install dir.
> NOTE: running via cargo also doesn't require setting explicit `HELIX_RUNTIME` path, it will automatically
> detect the `runtime` directory in the project root. > detect the `runtime` directory in the project root.
If you want to embed the `runtime/` directory into the Helix binary you can build Alternatively, if you want to embed the `runtime/` directory into the Helix binary you can build
it with: it with:
``` ```

@ -45,6 +45,9 @@
| ----- | ----------- | | ----- | ----------- |
| `r` | Replace with a character | | `r` | Replace with a character |
| `R` | Replace with yanked text | | `R` | Replace with yanked text |
| `~` | Switch case of the selected text |
| `\`` | Set the selected text to upper case |
| `Alt-\`` | Set the selected text to lower case |
| `i` | Insert before selection | | `i` | Insert before selection |
| `a` | Insert after selection (append) | | `a` | Insert after selection (append) |
| `I` | Insert at the start of the line | | `I` | Insert at the start of the line |

@ -51,6 +51,7 @@ Possible keys:
| `attribute` | | | `attribute` | |
| `keyword` | | | `keyword` | |
| `keyword.directive` | Preprocessor directives (\#if in C) | | `keyword.directive` | Preprocessor directives (\#if in C) |
| `keyword.control` | Control flow |
| `namespace` | | | `namespace` | |
| `punctuation` | | | `punctuation` | |
| `punctuation.delimiter` | | | `punctuation.delimiter` | |
@ -61,6 +62,7 @@ Possible keys:
| `variable.parameter` | | | `variable.parameter` | |
| `type` | | | `type` | |
| `type.builtin` | | | `type.builtin` | |
| `type.enum.variant` | Enum variants |
| `constructor` | | | `constructor` | |
| `function` | | | `function` | |
| `function.macro` | | | `function.macro` | |

@ -19,7 +19,7 @@ helix-syntax = { version = "0.3", path = "../helix-syntax" }
ropey = "1.3" ropey = "1.3"
smallvec = "1.4" smallvec = "1.4"
tendril = "0.4.2" tendril = "0.4.2"
unicode-segmentation = "1.7" unicode-segmentation = "1.8"
unicode-width = "0.1" unicode-width = "0.1"
unicode-general-category = "0.4" unicode-general-category = "0.4"
# slab = "0.4.2" # slab = "0.4.2"

@ -253,14 +253,14 @@ where
let doc = Rope::from(doc); let doc = Rope::from(doc);
use crate::syntax::{ use crate::syntax::{
Configuration, IndentationConfiguration, Lang, LanguageConfiguration, Loader, Configuration, IndentationConfiguration, LanguageConfiguration, Loader,
}; };
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
let loader = Loader::new(Configuration { let loader = Loader::new(Configuration {
language: vec![LanguageConfiguration { language: vec![LanguageConfiguration {
scope: "source.rust".to_string(), scope: "source.rust".to_string(),
file_types: vec!["rs".to_string()], file_types: vec!["rs".to_string()],
language_id: Lang::Rust, language_id: "Rust".to_string(),
highlight_config: OnceCell::new(), highlight_config: OnceCell::new(),
// //
roots: vec![], roots: vec![],

@ -5,7 +5,7 @@ use crate::{
Rope, RopeSlice, Tendril, Rope, RopeSlice, Tendril,
}; };
pub use helix_syntax::{get_language, get_language_name, Lang}; pub use helix_syntax::get_language;
use arc_swap::ArcSwap; use arc_swap::ArcSwap;
@ -31,7 +31,7 @@ pub struct Configuration {
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
pub struct LanguageConfiguration { pub struct LanguageConfiguration {
#[serde(rename = "name")] #[serde(rename = "name")]
pub(crate) language_id: Lang, pub(crate) language_id: String,
pub scope: String, // source.rust pub scope: String, // source.rust
pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc> pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml> pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
@ -153,7 +153,7 @@ fn read_query(language: &str, filename: &str) -> String {
impl LanguageConfiguration { impl LanguageConfiguration {
fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> { fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
let language = get_language_name(self.language_id).to_ascii_lowercase(); let language = self.language_id.to_ascii_lowercase();
let highlights_query = read_query(&language, "highlights.scm"); let highlights_query = read_query(&language, "highlights.scm");
// always highlight syntax errors // always highlight syntax errors
@ -161,17 +161,17 @@ impl LanguageConfiguration {
let injections_query = read_query(&language, "injections.scm"); let injections_query = read_query(&language, "injections.scm");
let locals_query = ""; let locals_query = read_query(&language, "locals.scm");
if highlights_query.is_empty() { if highlights_query.is_empty() {
None None
} else { } else {
let language = get_language(self.language_id); let language = get_language(&crate::RUNTIME_DIR, &self.language_id).ok()?;
let config = HighlightConfiguration::new( let config = HighlightConfiguration::new(
language, language,
&highlights_query, &highlights_query,
&injections_query, &injections_query,
locals_query, &locals_query,
) )
.unwrap(); // TODO: no unwrap .unwrap(); // TODO: no unwrap
config.configure(scopes); config.configure(scopes);
@ -198,7 +198,7 @@ impl LanguageConfiguration {
pub fn indent_query(&self) -> Option<&IndentQuery> { pub fn indent_query(&self) -> Option<&IndentQuery> {
self.indent_query self.indent_query
.get_or_init(|| { .get_or_init(|| {
let language = get_language_name(self.language_id).to_ascii_lowercase(); let language = self.language_id.to_ascii_lowercase();
let toml = load_runtime_file(&language, "indents.toml").ok()?; let toml = load_runtime_file(&language, "indents.toml").ok()?;
toml::from_slice(toml.as_bytes()).ok() toml::from_slice(toml.as_bytes()).ok()
@ -1812,7 +1812,7 @@ mod test {
.map(String::from) .map(String::from)
.collect(); .collect();
let language = get_language(Lang::Rust); let language = get_language(&crate::RUNTIME_DIR, "Rust").unwrap();
let config = HighlightConfiguration::new( let config = HighlightConfiguration::new(
language, language,
&std::fs::read_to_string( &std::fs::read_to_string(

@ -23,5 +23,5 @@ lsp-types = { version = "0.89", features = ["proposed"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
thiserror = "1.0" thiserror = "1.0"
tokio = { version = "1.7", features = ["full"] } tokio = { version = "1.8", features = ["full"] }
tokio-stream = "0.1.6" tokio-stream = "0.1.7"

@ -12,8 +12,10 @@ include = ["src/**/*", "languages/**/*", "build.rs", "!**/docs/**/*", "!**/test/
[dependencies] [dependencies]
tree-sitter = "0.19" tree-sitter = "0.19"
serde = { version = "1.0", features = ["derive"] } libloading = "0.7"
anyhow = "1"
[build-dependencies] [build-dependencies]
cc = { version = "1", features = ["parallel"] } cc = { version = "1" }
threadpool = { version = "1.0" } threadpool = { version = "1.0" }
anyhow = "1"

@ -1,79 +1,147 @@
use anyhow::{anyhow, Context, Result};
use std::fs; use std::fs;
use std::path::PathBuf; use std::time::SystemTime;
use std::{
path::{Path, PathBuf},
process::Command,
};
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
fn collect_tree_sitter_dirs(ignore: &[String]) -> Vec<String> { fn collect_tree_sitter_dirs(ignore: &[String]) -> Result<Vec<String>> {
let mut dirs = Vec::new(); let mut dirs = Vec::new();
for entry in fs::read_dir("languages").unwrap().flatten() { let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("languages");
for entry in fs::read_dir(path)? {
let entry = entry?;
let path = entry.path(); let path = entry.path();
if !entry.file_type()?.is_dir() {
continue;
}
let dir = path.file_name().unwrap().to_str().unwrap().to_string(); let dir = path.file_name().unwrap().to_str().unwrap().to_string();
if !ignore.contains(&dir) {
dirs.push(dir); // filter ignores
if ignore.contains(&dir) {
continue;
} }
dirs.push(dir)
} }
dirs
Ok(dirs)
} }
fn collect_src_files(dir: &str) -> (Vec<String>, Vec<String>) { #[cfg(unix)]
eprintln!("Collect files for {}", dir); const DYLIB_EXTENSION: &str = "so";
let mut c_files = Vec::new(); #[cfg(windows)]
let mut cpp_files = Vec::new(); const DYLIB_EXTENSION: &str = "dll";
let path = PathBuf::from("languages").join(&dir).join("src");
for entry in fs::read_dir(path).unwrap().flatten() { fn build_library(src_path: &Path, language: &str) -> Result<()> {
let path = entry.path(); let header_path = src_path;
if path // let grammar_path = src_path.join("grammar.json");
.file_stem() let parser_path = src_path.join("parser.c");
.unwrap() let mut scanner_path = src_path.join("scanner.c");
.to_str()
.unwrap() let scanner_path = if scanner_path.exists() {
.starts_with("binding") Some(scanner_path)
{ } else {
continue; scanner_path.set_extension("cc");
} if scanner_path.exists() {
if let Some(ext) = path.extension() { Some(scanner_path)
if ext == "c" { } else {
c_files.push(path.to_str().unwrap().to_string()); None
} else if ext == "cc" || ext == "cpp" || ext == "cxx" {
cpp_files.push(path.to_str().unwrap().to_string());
} }
};
let parser_lib_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../runtime/grammars");
let mut library_path = parser_lib_path.join(language);
library_path.set_extension(DYLIB_EXTENSION);
let recompile = needs_recompile(&library_path, &parser_path, &scanner_path)
.with_context(|| "Failed to compare source and binary timestamps")?;
if !recompile {
return Ok(());
} }
let mut config = cc::Build::new();
config.cpp(true).opt_level(2).cargo_metadata(false);
let compiler = config.get_compiler();
let mut command = Command::new(compiler.path());
command.current_dir(src_path);
for (key, value) in compiler.env() {
command.env(key, value);
} }
(c_files, cpp_files)
}
fn build_c(files: Vec<String>, language: &str) { if cfg!(windows) {
let mut build = cc::Build::new(); command
for file in files { .args(&["/nologo", "/LD", "/I"])
build .arg(header_path)
.file(&file) .arg("/Od");
.include(PathBuf::from(file).parent().unwrap()) if let Some(scanner_path) = scanner_path.as_ref() {
.pic(true) command.arg(scanner_path);
.warnings(false);
} }
build.compile(&format!("tree-sitter-{}-c", language));
}
fn build_cpp(files: Vec<String>, language: &str) {
let mut build = cc::Build::new();
let flag = if build.get_compiler().is_like_msvc() { command
"/std:c++17" .arg(parser_path)
.arg("/link")
.arg(format!("/out:{}", library_path.to_str().unwrap()));
} else { } else {
"-std=c++14" command
}; .arg("-shared")
.arg("-fPIC")
.arg("-fno-exceptions")
.arg("-g")
.arg("-I")
.arg(header_path)
.arg("-o")
.arg(&library_path)
.arg("-O2");
if let Some(scanner_path) = scanner_path.as_ref() {
if scanner_path.extension() == Some("c".as_ref()) {
command.arg("-xc").arg("-std=c99").arg(scanner_path);
} else {
command.arg(scanner_path);
}
}
command.arg("-xc").arg(parser_path);
}
for file in files { let output = command
build .output()
.file(&file) .with_context(|| "Failed to execute C compiler")?;
.include(PathBuf::from(file).parent().unwrap()) if !output.status.success() {
.pic(true) return Err(anyhow!(
.warnings(false) "Parser compilation failed.\nStdout: {}\nStderr: {}",
.cpp(true) String::from_utf8_lossy(&output.stdout),
.flag_if_supported(flag); String::from_utf8_lossy(&output.stderr)
));
} }
build.compile(&format!("tree-sitter-{}-cpp", language));
Ok(())
}
fn needs_recompile(
lib_path: &Path,
parser_c_path: &Path,
scanner_path: &Option<PathBuf>,
) -> Result<bool> {
if !lib_path.exists() {
return Ok(true);
}
let lib_mtime = mtime(lib_path)?;
if mtime(parser_c_path)? > lib_mtime {
return Ok(true);
}
if let Some(scanner_path) = scanner_path {
if mtime(scanner_path)? > lib_mtime {
return Ok(true);
}
}
Ok(false)
}
fn mtime(path: &Path) -> Result<SystemTime> {
Ok(fs::metadata(path)?.modified()?)
} }
fn build_dir(dir: &str, language: &str) { fn build_dir(dir: &str, language: &str) {
@ -92,22 +160,21 @@ fn build_dir(dir: &str, language: &str) {
eprintln!("You can fix in using 'git submodule init && git submodule update --recursive'."); eprintln!("You can fix in using 'git submodule init && git submodule update --recursive'.");
std::process::exit(1); std::process::exit(1);
} }
let (c, cpp) = collect_src_files(dir);
if !c.is_empty() { let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
build_c(c, language); .join("languages")
} .join(dir)
if !cpp.is_empty() { .join("src");
build_cpp(cpp, language);
} build_library(&path, language).unwrap();
} }
fn main() { fn main() {
let ignore = vec![ let ignore = vec![
"tree-sitter-typescript".to_string(), "tree-sitter-typescript".to_string(),
"tree-sitter-haskell".to_string(), // aarch64 failures: https://github.com/tree-sitter/tree-sitter-haskell/issues/34 "tree-sitter-haskell".to_string(), // aarch64 failures: https://github.com/tree-sitter/tree-sitter-haskell/issues/34
".DS_Store".to_string(),
]; ];
let dirs = collect_tree_sitter_dirs(&ignore); let dirs = collect_tree_sitter_dirs(&ignore).unwrap();
let mut n_jobs = 0; let mut n_jobs = 0;
let pool = threadpool::Builder::new().build(); // by going through the builder, it'll use num_cpus let pool = threadpool::Builder::new().build(); // by going through the builder, it'll use num_cpus
@ -118,7 +185,7 @@ fn main() {
n_jobs += 1; n_jobs += 1;
pool.execute(move || { pool.execute(move || {
let language = &dir[12..]; // skip tree-sitter- prefix let language = &dir.strip_prefix("tree-sitter-").unwrap();
build_dir(&dir, language); build_dir(&dir, language);
// report progress // report progress

@ -1,94 +1,39 @@
use serde::{Deserialize, Serialize}; use anyhow::{Context, Result};
use libloading::{Library, Symbol};
use tree_sitter::Language; use tree_sitter::Language;
#[macro_export] fn replace_dashes_with_underscores(name: &str) -> String {
macro_rules! mk_extern { let mut result = String::with_capacity(name.len());
( $( $name:ident ),* ) => { for c in name.chars() {
$( if c == '-' {
extern "C" { pub fn $name() -> Language; } result.push('_');
)* } else {
}; result.push(c);
}
#[macro_export]
macro_rules! mk_enum {
( $( $camel:ident ),* ) => {
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Lang {
$(
$camel,
)*
}
};
}
#[macro_export]
macro_rules! mk_get_language {
( $( ($camel:ident, $name:ident) ),* ) => {
#[must_use]
pub fn get_language(lang: Lang) -> Language {
unsafe {
match lang {
$(
Lang::$camel => $name(),
)*
}
} }
} }
}; result
} }
#[cfg(unix)]
const DYLIB_EXTENSION: &str = "so";
#[macro_export] #[cfg(windows)]
macro_rules! mk_get_language_name { const DYLIB_EXTENSION: &str = "dll";
( $( $camel:ident ),* ) => {
#[must_use] pub fn get_language(runtime_path: &std::path::Path, name: &str) -> Result<Language> {
pub const fn get_language_name(lang: Lang) -> &'static str { let name = name.to_ascii_lowercase();
match lang { let mut library_path = runtime_path.join("grammars").join(&name);
$( // TODO: duplicated under build
Lang::$camel => stringify!($camel), library_path.set_extension(DYLIB_EXTENSION);
)*
}
}
};
}
#[macro_export] let library = unsafe { Library::new(&library_path) }
macro_rules! mk_langs { .with_context(|| format!("Error opening dynamic library {:?}", &library_path))?;
( $( ($camel:ident, $name:ident) ),* ) => { let language_fn_name = format!("tree_sitter_{}", replace_dashes_with_underscores(&name));
mk_extern!($( $name ),*); let language = unsafe {
mk_enum!($( $camel ),*); let language_fn: Symbol<unsafe extern "C" fn() -> Language> = library
mk_get_language!($( ($camel, $name) ),*); .get(language_fn_name.as_bytes())
mk_get_language_name!($( $camel ),*); .with_context(|| format!("Failed to load symbol {}", language_fn_name))?;
language_fn()
}; };
std::mem::forget(library);
Ok(language)
} }
mk_langs!(
// 1) Name for enum
// 2) tree-sitter function to call to get a Language
(Agda, tree_sitter_agda),
(Bash, tree_sitter_bash),
(Cpp, tree_sitter_cpp),
(CSharp, tree_sitter_c_sharp),
(Css, tree_sitter_css),
(C, tree_sitter_c),
(Elixir, tree_sitter_elixir),
(Go, tree_sitter_go),
// (Haskell, tree_sitter_haskell),
(Html, tree_sitter_html),
(Javascript, tree_sitter_javascript),
(Java, tree_sitter_java),
(Json, tree_sitter_json),
(Julia, tree_sitter_julia),
(Latex, tree_sitter_latex),
(Nix, tree_sitter_nix),
(Php, tree_sitter_php),
(Python, tree_sitter_python),
(Ruby, tree_sitter_ruby),
(Rust, tree_sitter_rust),
(Scala, tree_sitter_scala),
(Swift, tree_sitter_swift),
(Toml, tree_sitter_toml),
(Tsx, tree_sitter_tsx),
(Typescript, tree_sitter_typescript)
);

@ -9,6 +9,7 @@ use log::error;
use std::{ use std::{
io::{stdout, Write}, io::{stdout, Write},
sync::Arc, sync::Arc,
time::{Duration, Instant},
}; };
use anyhow::Error; use anyhow::Error;
@ -82,15 +83,18 @@ impl Application {
editor.new_file(Action::VerticalSplit); editor.new_file(Action::VerticalSplit);
compositor.push(Box::new(ui::file_picker(first.clone()))); compositor.push(Box::new(ui::file_picker(first.clone())));
} else { } else {
let nr_of_files = args.files.len();
editor.open(first.to_path_buf(), Action::VerticalSplit)?;
for file in args.files { for file in args.files {
if file.is_dir() { if file.is_dir() {
return Err(anyhow::anyhow!( return Err(anyhow::anyhow!(
"expected a path to file, found a directory. (to open a directory pass it as first argument)" "expected a path to file, found a directory. (to open a directory pass it as first argument)"
)); ));
} else { } else {
editor.open(file, Action::VerticalSplit)?; editor.open(file.to_path_buf(), Action::Load)?;
} }
} }
editor.set_status(format!("Loaded {} files.", nr_of_files));
} }
} else { } else {
editor.new_file(Action::VerticalSplit); editor.new_file(Action::VerticalSplit);
@ -130,6 +134,8 @@ impl Application {
pub async fn event_loop(&mut self) { pub async fn event_loop(&mut self) {
let mut reader = EventStream::new(); let mut reader = EventStream::new();
let mut last_render = Instant::now();
let deadline = Duration::from_secs(1) / 60;
self.render(); self.render();
@ -139,26 +145,22 @@ impl Application {
break; break;
} }
use futures_util::{FutureExt, StreamExt}; use futures_util::StreamExt;
tokio::select! { tokio::select! {
biased;
event = reader.next() => { event = reader.next() => {
self.handle_terminal_events(event) self.handle_terminal_events(event)
} }
Some((id, call)) = self.editor.language_servers.incoming.next() => { Some((id, call)) = self.editor.language_servers.incoming.next() => {
self.handle_language_server_message(call, id).await; self.handle_language_server_message(call, id).await;
// limit render calls for fast language server messages
// eagerly process any other available notifications/calls let last = self.editor.language_servers.incoming.is_empty();
let now = std::time::Instant::now(); if last || last_render.elapsed() > deadline {
let deadline = std::time::Duration::from_millis(10);
while let Some(Some((id, call))) = self.editor.language_servers.incoming.next().now_or_never() {
self.handle_language_server_message(call, id).await;
if now.elapsed() > deadline { // use a deadline so we don't block too long
break;
}
}
self.render(); self.render();
last_render = Instant::now();
}
} }
Some(callback) = self.jobs.futures.next() => { Some(callback) = self.jobs.futures.next() => {
self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);

File diff suppressed because it is too large Load Diff

@ -82,6 +82,10 @@ impl Default for Keymaps {
key!('r') => Command::replace, key!('r') => Command::replace,
key!('R') => Command::replace_with_yanked, key!('R') => Command::replace_with_yanked,
key!('~') => Command::switch_case,
alt!('`') => Command::switch_to_uppercase,
key!('`') => Command::switch_to_lowercase,
key!(Home) => Command::goto_line_start, key!(Home) => Command::goto_line_start,
key!(End) => Command::goto_line_end, key!(End) => Command::goto_line_end,
@ -120,7 +124,6 @@ impl Default for Keymaps {
alt!(';') => Command::flip_selections, alt!(';') => Command::flip_selections,
key!('%') => Command::select_all, key!('%') => Command::select_all,
key!('x') => Command::extend_line, key!('x') => Command::extend_line,
key!('x') => Command::extend_line,
key!('X') => Command::extend_to_line_bounds, key!('X') => Command::extend_to_line_bounds,
// crop_to_whole_line // crop_to_whole_line

@ -64,6 +64,7 @@ impl EditorView {
surface: &mut Surface, surface: &mut Surface,
theme: &Theme, theme: &Theme,
is_focused: bool, is_focused: bool,
loader: &syntax::Loader,
) { ) {
let area = Rect::new( let area = Rect::new(
view.area.x + OFFSET, view.area.x + OFFSET,
@ -72,7 +73,7 @@ impl EditorView {
view.area.height.saturating_sub(1), view.area.height.saturating_sub(1),
); // - 1 for statusline ); // - 1 for statusline
self.render_buffer(doc, view, area, surface, theme, is_focused); self.render_buffer(doc, view, area, surface, theme, is_focused, loader);
// if we're not at the edge of the screen, draw a right border // if we're not at the edge of the screen, draw a right border
if viewport.right() != view.area.right() { if viewport.right() != view.area.right() {
@ -98,6 +99,7 @@ impl EditorView {
self.render_statusline(doc, view, area, surface, theme, is_focused); self.render_statusline(doc, view, area, surface, theme, is_focused);
} }
#[allow(clippy::too_many_arguments)]
pub fn render_buffer( pub fn render_buffer(
&self, &self,
doc: &Document, doc: &Document,
@ -106,6 +108,7 @@ impl EditorView {
surface: &mut Surface, surface: &mut Surface,
theme: &Theme, theme: &Theme,
is_focused: bool, is_focused: bool,
loader: &syntax::Loader,
) { ) {
let text = doc.text().slice(..); let text = doc.text().slice(..);
@ -122,8 +125,26 @@ impl EditorView {
// TODO: range doesn't actually restrict source, just highlight range // TODO: range doesn't actually restrict source, just highlight range
let highlights: Vec<_> = match doc.syntax() { let highlights: Vec<_> = match doc.syntax() {
Some(syntax) => { Some(syntax) => {
let scopes = theme.scopes();
syntax syntax
.highlight_iter(text.slice(..), Some(range), None, |_| None) .highlight_iter(text.slice(..), Some(range), None, |language| {
loader
.language_config_for_scope(&format!("source.{}", language))
.and_then(|language_config| {
let config = language_config.highlight_config(scopes)?;
let config_ref = config.as_ref();
// SAFETY: the referenced `HighlightConfiguration` behind
// the `Arc` is guaranteed to remain valid throughout the
// duration of the highlight.
let config_ref = unsafe {
std::mem::transmute::<
_,
&'static syntax::HighlightConfiguration,
>(config_ref)
};
Some(config_ref)
})
})
.collect() // TODO: we collect here to avoid holding the lock, fix later .collect() // TODO: we collect here to avoid holding the lock, fix later
} }
None => vec![Ok(HighlightEvent::Source { None => vec![Ok(HighlightEvent::Source {
@ -735,7 +756,16 @@ impl Component for EditorView {
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); let loader = &cx.editor.syn_loader;
self.render_view(
doc,
view,
area,
surface,
&cx.editor.theme,
is_focused,
loader,
);
} }
if let Some(info) = std::mem::take(&mut cx.editor.autoinfo) { if let Some(info) = std::mem::take(&mut cx.editor.autoinfo) {

@ -18,7 +18,7 @@ default = ["crossterm"]
[dependencies] [dependencies]
bitflags = "1.0" bitflags = "1.0"
cassowary = "0.3" cassowary = "0.3"
unicode-segmentation = "1.2" unicode-segmentation = "1.8"
crossterm = { version = "0.20", optional = true } crossterm = { version = "0.20", optional = true }
serde = { version = "1", "optional" = true, features = ["derive"]} serde = { version = "1", "optional" = true, features = ["derive"]}
helix-view = { version = "0.3", path = "../helix-view", features = ["term"] } helix-view = { version = "0.3", path = "../helix-view", features = ["term"] }

@ -456,14 +456,16 @@ impl Document {
theme: Option<&Theme>, theme: Option<&Theme>,
config_loader: Option<&syntax::Loader>, config_loader: Option<&syntax::Loader>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
if !path.exists() { let (mut rope, encoding) = if path.exists() {
return Ok(Self::default()); let mut file =
} std::fs::File::open(&path).context(format!("unable to open {:?}", path))?;
from_reader(&mut file, encoding)?
} else {
let encoding = encoding.unwrap_or(encoding_rs::UTF_8);
(Rope::from(DEFAULT_LINE_ENDING.as_str()), encoding)
};
let mut file = std::fs::File::open(&path).context(format!("unable to open {:?}", path))?;
let (mut rope, encoding) = from_reader(&mut file, encoding)?;
let line_ending = with_line_ending(&mut rope); let line_ending = with_line_ending(&mut rope);
let mut doc = Self::from(rope, Some(encoding)); let mut doc = Self::from(rope, Some(encoding));
// set the path and try detecting the language // set the path and try detecting the language

@ -39,6 +39,7 @@ pub struct Editor {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum Action { pub enum Action {
Load,
Replace, Replace,
HorizontalSplit, HorizontalSplit,
VerticalSplit, VerticalSplit,
@ -97,16 +98,14 @@ impl Editor {
self._refresh(); self._refresh();
} }
pub fn set_theme_from_name(&mut self, theme: &str) { pub fn set_theme_from_name(&mut self, theme: &str) -> anyhow::Result<()> {
let theme = match self.theme_loader.load(theme.as_ref()) { use anyhow::Context;
Ok(theme) => theme, let theme = self
Err(e) => { .theme_loader
log::warn!("failed setting theme `{}` - {}", theme, e); .load(theme.as_ref())
return; .with_context(|| format!("failed setting theme `{}`", theme))?;
}
};
self.set_theme(theme); self.set_theme(theme);
Ok(())
} }
fn _refresh(&mut self) { fn _refresh(&mut self) {
@ -153,6 +152,9 @@ impl Editor {
return; return;
} }
Action::Load => {
return;
}
Action::HorizontalSplit => { Action::HorizontalSplit => {
let view = View::new(id); let view = View::new(id);
let view_id = self.tree.split(view, Layout::Horizontal); let view_id = self.tree.split(view, Layout::Horizontal);

@ -54,6 +54,8 @@
"." @punctuation.delimiter "." @punctuation.delimiter
";" @punctuation.delimiter ";" @punctuation.delimiter
(enumerator) @type.enum.variant
(string_literal) @string (string_literal) @string
(system_lib_string) @string (system_lib_string) @string

@ -1,213 +1,336 @@
; Identifier conventions ; -------
; Tree-Sitter doesn't allow overrides in regards to captures,
; though it is possible to affect the child node of a captured
; node. Thus, the approach here is to flip the order so that
; overrides are unnecessary.
; -------
; Assume all-caps names are constants
((identifier) @constant
(#match? @constant "^[A-Z][A-Z\\d_]+$'"))
; Assume other uppercase names are enum constructors ; -------
((identifier) @constructor ; Types
(#match? @constructor "^[A-Z]")) ; -------
; Assume that uppercase names in paths are types ; ---
(mod_item ; Primitives
name: (identifier) @namespace) ; ---
(scoped_identifier
path: (identifier) @namespace)
(scoped_identifier
(scoped_identifier
name: (identifier) @namespace))
(scoped_type_identifier
path: (identifier) @namespace)
(scoped_type_identifier
(scoped_identifier
name: (identifier) @namespace))
((scoped_identifier (escape_sequence) @escape
path: (identifier) @type) (primitive_type) @type.builtin
(#match? @type "^[A-Z]")) (boolean_literal) @constant.builtin
((scoped_identifier [
path: (scoped_identifier (integer_literal)
name: (identifier) @type)) (float_literal)
(#match? @type "^[A-Z]")) ] @number
[
(char_literal)
(string_literal)
(raw_string_literal)
] @string
[
(line_comment)
(block_comment)
] @comment
; Namespaces ; ---
; Extraneous
; ---
(crate) @namespace (self) @variable.builtin
(extern_crate_declaration (enum_variant (identifier) @type.enum.variant)
(crate)
name: (identifier) @namespace)
(scoped_use_list
path: (identifier) @namespace)
(scoped_use_list
path: (scoped_identifier
(identifier) @namespace))
(use_list (scoped_identifier (identifier) @namespace . (_)))
; Function calls (field_initializer
(field_identifier) @property)
(shorthand_field_initializer) @variable
(shorthand_field_identifier) @variable
(call_expression (lifetime
function: (identifier) @function) "'" @label
(call_expression (identifier) @label)
function: (field_expression (loop_label
field: (field_identifier) @function.method)) (identifier) @type)
(call_expression
function: (scoped_identifier ; ---
; Punctuation
; ---
[
"::" "::"
name: (identifier) @function)) "."
";"
] @punctuation.delimiter
(generic_function [
function: (identifier) @function) "("
(generic_function ")"
function: (scoped_identifier "["
name: (identifier) @function)) "]"
(generic_function ] @punctuation.bracket
function: (field_expression (type_arguments
field: (field_identifier) @function.method)) [
"<"
">"
] @punctuation.bracket)
(type_parameters
[
"<"
">"
] @punctuation.bracket)
(macro_invocation ; ---
macro: (identifier) @function.macro ; Parameters
"!" @function.macro) ; ---
(macro_invocation
macro: (scoped_identifier
(identifier) @function.macro .))
; (metavariable) @variable (parameter
(metavariable) @function.macro pattern: (identifier) @variable.parameter)
(closure_parameters
(identifier) @variable.parameter)
"$" @function.macro
; Function definitions
(function_item (identifier) @function) ; -------
(function_signature_item (identifier) @function) ; Keywords
; -------
; Other identifiers (for_expression
"for" @keyword.control)
((identifier) @keyword.control
(#match? @keyword.control "^yield$"))
[
"while"
"loop"
"in"
"break"
"continue"
(type_identifier) @type "match"
(primitive_type) @type.builtin "if"
(field_identifier) @property "else"
"return"
(line_comment) @comment "await"
(block_comment) @comment ] @keyword.control
"(" @punctuation.bracket [
")" @punctuation.bracket (crate)
"[" @punctuation.bracket (super)
"]" @punctuation.bracket "as"
"use"
"pub"
"mod"
"extern"
"fn"
"struct"
"enum"
"impl"
"where"
"trait"
"for"
"type"
"union"
"unsafe"
"default"
"macro_rules!"
"let"
"ref"
"move"
"dyn"
"static"
"const"
"async"
] @keyword
(type_arguments
"<" @punctuation.bracket
">" @punctuation.bracket)
(type_parameters
"<" @punctuation.bracket
">" @punctuation.bracket)
"::" @punctuation.delimiter
"." @punctuation.delimiter
";" @punctuation.delimiter
(parameter (identifier) @variable.parameter)
(closure_parameters (_) @variable.parameter)
(lifetime (identifier) @label)
"async" @keyword
"break" @keyword
"const" @keyword
"continue" @keyword
(crate) @keyword
"default" @keyword
"dyn" @keyword
"else" @keyword
"enum" @keyword
"extern" @keyword
"fn" @keyword
"for" @keyword
"if" @keyword
"impl" @keyword
"in" @keyword
"let" @keyword
"let" @keyword
"loop" @keyword
"macro_rules!" @keyword
"match" @keyword
"mod" @keyword
"move" @keyword
"pub" @keyword
"ref" @keyword
"return" @keyword
"static" @keyword
"struct" @keyword
"trait" @keyword
"type" @keyword
"union" @keyword
"unsafe" @keyword
"use" @keyword
"where" @keyword
"while" @keyword
(mutable_specifier) @keyword.mut (mutable_specifier) @keyword.mut
(use_list (self) @keyword)
(scoped_use_list (self) @keyword)
(scoped_identifier (self) @keyword)
(super) @keyword
"as" @keyword
(self) @variable.builtin
[
(char_literal)
(string_literal)
(raw_string_literal)
] @string
(boolean_literal) @constant.builtin ; -------
(integer_literal) @number ; Guess Other Types
(float_literal) @number ; -------
(escape_sequence) @escape ((identifier) @constant
(#match? @constant "^[A-Z][A-Z\\d_]+$"))
; ---
; PascalCase identifiers in call_expressions (e.g. `Ok()`)
; are assumed to be enum constructors.
; ---
(call_expression
function: [
((identifier) @type.variant
(#match? @type.variant "^[A-Z]"))
(scoped_identifier
name: ((identifier) @type.variant
(#match? @type.variant "^[A-Z]")))
])
; ---
; Assume that types in match arms are enums and not
; tuple structs. Same for `if let` expressions.
; ---
(match_pattern
(scoped_identifier
name: (identifier) @constructor))
(tuple_struct_pattern
type: [
((identifier) @constructor)
(scoped_identifier
name: (identifier) @constructor)
])
(struct_pattern
type: [
((type_identifier) @constructor)
(scoped_type_identifier
name: (type_identifier) @constructor)
])
; ---
; Other PascalCase identifiers are assumed to be structs.
; ---
((identifier) @type
(#match? @type "^[A-Z]"))
; -------
; Functions
; -------
(call_expression
function: [
((identifier) @function)
(scoped_identifier
name: (identifier) @function)
(field_expression
field: (field_identifier) @function)
])
(generic_function
function: [
((identifier) @function)
(scoped_identifier
name: (identifier) @function)
(field_expression
field: (field_identifier) @function.method)
])
(function_item
name: (identifier) @function)
; ---
; Macros
; ---
(meta_item
(identifier) @attribute)
(attribute_item) @attribute (attribute_item) @attribute
(inner_attribute_item) @attribute (inner_attribute_item) @attribute
(macro_definition
name: (identifier) @function.macro)
(macro_invocation
macro: [
((identifier) @function.macro)
(scoped_identifier
name: (identifier) @function.macro)
]
"!" @function.macro)
(metavariable) @variable.parameter
(fragment_specifier) @variable.parameter
; -------
; Operators
; -------
[ [
"*" "*"
"'" "'"
"->" "->"
"=>" "=>"
"<=" "<="
"=" "="
"==" "=="
"!" "!"
"!=" "!="
"%" "%"
"%=" "%="
"&" "&"
"&=" "&="
"&&" "&&"
"|" "|"
"|=" "|="
"||" "||"
"^" "^"
"^=" "^="
"*" "*"
"*=" "*="
"-" "-"
"-=" "-="
"+" "+"
"+=" "+="
"/" "/"
"/=" "/="
">" ">"
"<" "<"
">=" ">="
">>" ">>"
"<<" "<<"
">>=" ">>="
"@" "@"
".." ".."
"..=" "..="
"'" "'"
] @operator ] @operator
; -------
; Paths
; -------
(use_declaration
argument: (identifier) @namespace)
(use_wildcard
(identifier) @namespace)
(extern_crate_declaration
name: (identifier) @namespace)
(mod_item
name: (identifier) @namespace)
(scoped_use_list
path: (identifier)? @namespace)
(use_list
(identifier) @namespace)
(use_as_clause
path: (identifier)? @namespace
alias: (identifier) @namespace)
; ---
; Remaining Paths
; ---
(scoped_identifier
path: (identifier)? @namespace
name: (identifier) @namespace)
(scoped_type_identifier
path: (identifier) @namespace)
; -------
; Remaining Identifiers
; -------
"?" @special "?" @special
(type_identifier) @type
(identifier) @variable
(field_identifier) @variable

@ -0,0 +1,80 @@
# Author: Shafkath Shuhan <shafkathshuhannyc@gmail.com>
"namespace" = { fg = "type" }
"module" = { fg = "type" }
"type" = { fg = "type" }
"type.builtin" = { fg = "type" }
"keyword" = { fg = "keyword" }
"keyword.directive" = { fg = "keyword" }
"function.macro" = { fg = "keyword" }
"variable.builtin" = { fg = "keyword" }
"label" = { fg = "keyword" }
"constant.builtin" = { fg = "keyword" }
"punctuation" = { fg = "text" }
"punctuation.delimiter" = { fg = "text" }
"keyword.control" = { fg = "special" }
"special" = { fg = "text" }
"operator" = { fg = "text" }
"variable" = { fg = "variable" }
"variable.parameter" = { fg = "variable" }
"property" = { fg = "variable" }
"attribute" = { fg = "fn_declaration" }
"function" = { fg = "fn_declaration" }
"function.builtin" = { fg = "fn_declaration" }
"comment" = { fg = "#6A9955" }
"constant" = { fg = "constant" }
"type.enum.variant" = { fg = "constant" }
"constructor" = { fg = "constant" }
"string" = { fg = "#ce9178" }
"number" = { fg = "#b5cea8" }
"escape" = { fg = "#d7ba7d" }
"ui.background" = { fg = "#d4d4d4", bg = "#1e1e1e" }
"ui.help" = { bg = "widget" }
"ui.popup" = { bg = "widget" }
"ui.window" = { bg = "widget" }
"ui.menu.selected" = { bg = "widget" }
"ui.cursor" = { fg = "cursor", modifiers = ["reversed"] }
"ui.cursor.primary" = { fg = "cursor", modifiers = ["reversed"] }
"ui.cursor.match" = { fg = "cursor", modifiers = ['underlined'] }
"ui.selection" = { bg = "#3a3d41" }
"ui.selection.primary" = { bg = "#add6ff26" }
"ui.linenr" = { fg = "#858585" }
"ui.linenr.selected" = { fg = "#c6c6c6" }
"ui.statusline" = { fg = "#ffffff", bg = "#007acc" }
"ui.statusline.inactive" = { fg = "#ffffff", bg = "#007acc" }
"ui.text" = { fg = "text", bg = "background" }
"ui.text.focus" = { fg = "#ffffff" }
"warning" = { fg = "#cca700" }
"error" = { fg = "#f48771" }
"info" = { fg = "#75beff" }
"hint" = { fg = "#eeeeeeb3" }
[palette]
type = "#4EC9B0"
keyword = "#569CD6"
regex = "#CE9178"
special = "#C586C0"
variable = "#9CDCFE"
fn_declaration = "#DCDCAA"
constant = "#4FC1FF"
background = "#1e1e1e"
text = "#d4d4d4"
cursor = "#a6a6a6"
widget = "#252526"

@ -1,68 +1,81 @@
"attribute" = "#dbbfef" # lilac attribute = "lilac"
"keyword" = "#eccdba" # almond keyword = "almond"
"keyword.directive" = "#dbbfef" # lilac -- preprocessor comments (#if in C) "keyword.directive" = "lilac" # -- preprocessor comments (#if in C)
"namespace" = "#dbbfef" # lilac namespace = "lilac"
"punctuation" = "#a4a0e8" # lavender punctuation = "lavender"
"punctuation.delimiter" = "#a4a0e8" # lavender "punctuation.delimiter" = "lavender"
"operator" = "#dbbfef" # lilac operator = "lilac"
"special" = "#efba5d" # honey special = "honey"
# "property" = "#a4a0e8" # lavender property = "white"
"property" = "#ffffff" # white variable = "lavender"
"variable" = "#a4a0e8" # lavender # variable = "almond" # TODO: metavariables only
# "variable" = "#eccdba" # almond TODO: metavariables only "variable.parameter" = "lavender"
"variable.parameter" = "#a4a0e8" # lavender "variable.builtin" = "mint"
# TODO distinguish type from type.builtin? type = "white"
"type" = "#ffffff" # white "type.builtin" = "white" # TODO: distinguish?
"type.builtin" = "#ffffff" # white constructor = "lilac"
"constructor" = "#dbbfef" # lilac function = "white"
"function" = "#ffffff" # white "function.macro" = "lilac"
"function.macro" = "#dbbfef" # lilac "function.builtin" = "white"
"function.builtin" = "#ffffff" # white comment = "sirocco"
"comment" = "#697C81" # sirocco constant = "white"
"variable.builtin" = "#9ff28f" # mint "constant.builtin" = "white"
"constant" = "#ffffff" # white string = "silver"
"constant.builtin" = "#ffffff" # white number = "chamois"
"string" = "#cccccc" # silver escape = "honey"
"number" = "#e8dca0" # chamois
"escape" = "#efba5d" # honey
# used for lifetimes # used for lifetimes
"label" = "#efba5d" # honey label = "honey"
# TODO: diferentiate number builtin
# TODO: diferentiate doc comment # TODO: diferentiate doc comment
# TODO: variable as lilac
# TODO: mod/use statements as white
# TODO: mod stuff as chamois
#
# concat (ERROR) @syntax-error and "MISSING ;" selectors for errors # concat (ERROR) @syntax-error and "MISSING ;" selectors for errors
"module" = "#ff0000" module = "#ff0000"
"ui.background" = { bg = "#3b224c" } # midnight "ui.background" = { bg = "midnight" }
"ui.linenr" = { fg = "#5a5977" } # comet "ui.linenr" = { fg = "comet" }
"ui.linenr.selected" = { fg = "#dbbfef" } # lilac "ui.linenr.selected" = { fg = "lilac" }
"ui.statusline" = { fg = "#dbbfef", bg = "#281733" } # revolver "ui.statusline" = { fg = "lilac", bg = "revolver" }
"ui.statusline.inactive" = { fg = "#a4a0e8", bg = "#281733" } # revolver "ui.statusline.inactive" = { fg = "lavender", bg = "revolver" }
"ui.popup" = { bg = "#281733" } # revolver "ui.popup" = { bg = "revolver" }
"ui.window" = { fg = "#452859" } # bossa nova "ui.window" = { fg = "bossanova" }
"ui.help" = { bg = "#7958DC", fg = "#171452" } "ui.help" = { bg = "#7958DC", fg = "#171452" }
"ui.text" = { fg = "#a4a0e8" } # lavender "ui.text" = { fg = "lavender" }
"ui.text.focus" = { fg = "#dbbfef" } # lilac "ui.text.focus" = { fg = "lilac" }
"ui.selection" = { bg = "#540099" } "ui.selection" = { bg = "#540099" }
"ui.selection.primary" = { bg = "#540099" } "ui.selection.primary" = { bg = "#540099" }
# TODO: namespace ui.cursor as ui.selection.cursor? # TODO: namespace ui.cursor as ui.selection.cursor?
"ui.cursor.select" = { bg = "#6F44F0" } "ui.cursor.select" = { bg = "delta" }
"ui.cursor.insert" = { bg = "#ffffff" } "ui.cursor.insert" = { bg = "white" }
"ui.cursor.match" = { fg = "#212121", bg = "#6C6999" } "ui.cursor.match" = { fg = "#212121", bg = "#6C6999" }
"ui.cursor" = { modifiers = ["reversed"] } "ui.cursor" = { modifiers = ["reversed"] }
"ui.menu.selected" = { fg = "#281733", bg = "#ffffff" } # revolver "ui.menu.selected" = { fg = "revolver", bg = "white" }
"diagnostic" = { modifiers = ["underlined"] } diagnostic = { modifiers = ["underlined"] }
"warning" = "#ffcd1c" warning = "lightning"
"error" = "#f47868" error = "apricot"
"info" = "#6F44F0" info = "delta"
"hint" = "#cccccc" hint = "silver"
[palette]
white = "#ffffff"
lilac = "#dbbfef"
lavender = "#a4a0e8"
comet = "#5a5977"
bossanova = "#452859"
midnight = "#3b224c"
revolver = "#281733"
silver = "#cccccc"
sirocco = "#697C81"
mint = "#9ff28f"
almond = "#eccdba"
chamois = "#E8DCA0"
honey = "#efba5d"
apricot = "#f47868"
lightning = "#ffcd1c"
delta = "#6F44F0"

Loading…
Cancel
Save