Merge branch 'master' into great_line_ending_and_cursor_range_cleanup
This commit is contained in:
commit
a77274e8bb
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@ -99,6 +99,7 @@ jobs:
|
||||
else
|
||||
cp "target/${{ matrix.target }}/release/hx" "dist/"
|
||||
fi
|
||||
cp -r runtime dist
|
||||
|
||||
- uses: actions/upload-artifact@v2.2.4
|
||||
with:
|
||||
@ -148,7 +149,7 @@ jobs:
|
||||
pkgname=helix-$TAG-$platform
|
||||
mkdir 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
|
||||
chmod +x tmp/$pkgname/hx$exe
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ target
|
||||
helix-term/rustfmt.toml
|
||||
helix-syntax/languages/
|
||||
result
|
||||
runtime/grammars
|
||||
|
57
Cargo.lock
generated
57
Cargo.lock
generated
@ -13,9 +13,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.41"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61"
|
||||
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@ -58,12 +58,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.68"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -354,8 +351,9 @@ dependencies = [
|
||||
name = "helix-syntax"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cc",
|
||||
"serde",
|
||||
"libloading",
|
||||
"threadpool",
|
||||
"tree-sitter",
|
||||
]
|
||||
@ -475,15 +473,6 @@ version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-core"
|
||||
version = "17.1.0"
|
||||
@ -509,6 +498,16 @@ version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "lock_api"
|
||||
version = "0.4.4"
|
||||
@ -914,9 +913,9 @@ checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
||||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
version = "1.0.3"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "585cd5dffe4e9e06f6dfdf66708b70aca3f781bed561f4f667b2d9c0d4559e36"
|
||||
checksum = "a952280edbecfb1d4bd3cf2dbc309dc6ab523e53487c438ae21a6df09fe84bc4"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
@ -957,18 +956,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.25"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
|
||||
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.25"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
|
||||
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1010,9 +1009,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.7.1"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fb2ed024293bb19f7a5dc54fe83bf86532a44c12a2bb8ba40d64a4509395ca2"
|
||||
checksum = "98c8b05dc14c75ea83d63dd391100353789f5f24b8b3866542a5e85c8be8e985"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@ -1041,9 +1040,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8864d706fdb3cc0843a49647ac892720dac98a6eeb818b77190592cf4994066"
|
||||
checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
@ -1104,9 +1103,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.7.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
|
11
README.md
11
README.md
@ -41,14 +41,17 @@ # Installation
|
||||
|
||||
This will install the `hx` binary to `$HOME/.cargo/bin`.
|
||||
|
||||
Now copy the `runtime/` directory somewhere. Helix will by default look for the runtime
|
||||
inside the config directory or the same directory as executable, but that can be overriden
|
||||
Helix also needs it's runtime files so make sure to copy/symlink the `runtime/` directory into the
|
||||
config directory (for example `~/.config/helix/runtime` on Linux/macOS). This location can be overriden
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
|
@ -41,26 +41,29 @@ ### Movement
|
||||
|
||||
### Changes
|
||||
|
||||
| Key | Description |
|
||||
| ----- | ----------- |
|
||||
| `r` | Replace with a character |
|
||||
| `R` | Replace with yanked text |
|
||||
| `i` | Insert before selection |
|
||||
| `a` | Insert after selection (append) |
|
||||
| `I` | Insert at the start of the line |
|
||||
| `A` | Insert at the end of the line |
|
||||
| `o` | Open new line below selection |
|
||||
| `o` | Open new line above selection |
|
||||
| `u` | Undo change |
|
||||
| `U` | Redo change |
|
||||
| `y` | Yank selection |
|
||||
| `p` | Paste after selection |
|
||||
| `P` | Paste before selection |
|
||||
| `>` | Indent selection |
|
||||
| `<` | Unindent selection |
|
||||
| `=` | Format selection |
|
||||
| `d` | Delete selection |
|
||||
| `c` | Change selection (delete and enter insert mode) |
|
||||
| Key | Description |
|
||||
| ----- | ----------- |
|
||||
| `r` | Replace with a character |
|
||||
| `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 |
|
||||
| `a` | Insert after selection (append) |
|
||||
| `I` | Insert at the start of the line |
|
||||
| `A` | Insert at the end of the line |
|
||||
| `o` | Open new line below selection |
|
||||
| `o` | Open new line above selection |
|
||||
| `u` | Undo change |
|
||||
| `U` | Redo change |
|
||||
| `y` | Yank selection |
|
||||
| `p` | Paste after selection |
|
||||
| `P` | Paste before selection |
|
||||
| `>` | Indent selection |
|
||||
| `<` | Unindent selection |
|
||||
| `=` | Format selection |
|
||||
| `d` | Delete selection |
|
||||
| `c` | Change selection (delete and enter insert mode) |
|
||||
|
||||
### Selection manipulation
|
||||
|
||||
|
@ -51,6 +51,7 @@ ## Creating a theme
|
||||
| `attribute` | |
|
||||
| `keyword` | |
|
||||
| `keyword.directive` | Preprocessor directives (\#if in C) |
|
||||
| `keyword.control` | Control flow |
|
||||
| `namespace` | |
|
||||
| `punctuation` | |
|
||||
| `punctuation.delimiter` | |
|
||||
@ -61,6 +62,7 @@ ## Creating a theme
|
||||
| `variable.parameter` | |
|
||||
| `type` | |
|
||||
| `type.builtin` | |
|
||||
| `type.enum.variant` | Enum variants |
|
||||
| `constructor` | |
|
||||
| `function` | |
|
||||
| `function.macro` | |
|
||||
|
@ -19,7 +19,7 @@ helix-syntax = { version = "0.3", path = "../helix-syntax" }
|
||||
ropey = "1.3"
|
||||
smallvec = "1.4"
|
||||
tendril = "0.4.2"
|
||||
unicode-segmentation = "1.7"
|
||||
unicode-segmentation = "1.8"
|
||||
unicode-width = "0.1"
|
||||
unicode-general-category = "0.4"
|
||||
# slab = "0.4.2"
|
||||
|
@ -253,14 +253,14 @@ pub fn change<I>(document: &Document, changes: I) -> Self
|
||||
|
||||
let doc = Rope::from(doc);
|
||||
use crate::syntax::{
|
||||
Configuration, IndentationConfiguration, Lang, LanguageConfiguration, Loader,
|
||||
Configuration, IndentationConfiguration, LanguageConfiguration, Loader,
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
let loader = Loader::new(Configuration {
|
||||
language: vec![LanguageConfiguration {
|
||||
scope: "source.rust".to_string(),
|
||||
file_types: vec!["rs".to_string()],
|
||||
language_id: Lang::Rust,
|
||||
language_id: "Rust".to_string(),
|
||||
highlight_config: OnceCell::new(),
|
||||
//
|
||||
roots: vec![],
|
||||
|
@ -5,7 +5,7 @@
|
||||
Rope, RopeSlice, Tendril,
|
||||
};
|
||||
|
||||
pub use helix_syntax::{get_language, get_language_name, Lang};
|
||||
pub use helix_syntax::get_language;
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
|
||||
@ -31,7 +31,7 @@ pub struct Configuration {
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct LanguageConfiguration {
|
||||
#[serde(rename = "name")]
|
||||
pub(crate) language_id: Lang,
|
||||
pub(crate) language_id: String,
|
||||
pub scope: String, // source.rust
|
||||
pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
|
||||
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 {
|
||||
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");
|
||||
// always highlight syntax errors
|
||||
@ -161,17 +161,17 @@ fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfigu
|
||||
|
||||
let injections_query = read_query(&language, "injections.scm");
|
||||
|
||||
let locals_query = "";
|
||||
let locals_query = read_query(&language, "locals.scm");
|
||||
|
||||
if highlights_query.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let language = get_language(self.language_id);
|
||||
let language = get_language(&crate::RUNTIME_DIR, &self.language_id).ok()?;
|
||||
let config = HighlightConfiguration::new(
|
||||
language,
|
||||
&highlights_query,
|
||||
&injections_query,
|
||||
locals_query,
|
||||
&locals_query,
|
||||
)
|
||||
.unwrap(); // TODO: no unwrap
|
||||
config.configure(scopes);
|
||||
@ -198,7 +198,7 @@ pub fn is_highlight_initialized(&self) -> bool {
|
||||
pub fn indent_query(&self) -> Option<&IndentQuery> {
|
||||
self.indent_query
|
||||
.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()?;
|
||||
toml::from_slice(toml.as_bytes()).ok()
|
||||
@ -1812,7 +1812,7 @@ fn test_parser() {
|
||||
.map(String::from)
|
||||
.collect();
|
||||
|
||||
let language = get_language(Lang::Rust);
|
||||
let language = get_language(&crate::RUNTIME_DIR, "Rust").unwrap();
|
||||
let config = HighlightConfiguration::new(
|
||||
language,
|
||||
&std::fs::read_to_string(
|
||||
|
@ -23,5 +23,5 @@ lsp-types = { version = "0.89", features = ["proposed"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.7", features = ["full"] }
|
||||
tokio-stream = "0.1.6"
|
||||
tokio = { version = "1.8", features = ["full"] }
|
||||
tokio-stream = "0.1.7"
|
||||
|
@ -12,8 +12,10 @@ include = ["src/**/*", "languages/**/*", "build.rs", "!**/docs/**/*", "!**/test/
|
||||
|
||||
[dependencies]
|
||||
tree-sitter = "0.19"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
libloading = "0.7"
|
||||
anyhow = "1"
|
||||
|
||||
[build-dependencies]
|
||||
cc = { version = "1", features = ["parallel"] }
|
||||
cc = { version = "1" }
|
||||
threadpool = { version = "1.0" }
|
||||
anyhow = "1"
|
||||
|
@ -1,79 +1,147 @@
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
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();
|
||||
for entry in fs::read_dir("languages").unwrap().flatten() {
|
||||
let path = entry.path();
|
||||
let dir = path.file_name().unwrap().to_str().unwrap().to_string();
|
||||
if !ignore.contains(&dir) {
|
||||
dirs.push(dir);
|
||||
}
|
||||
}
|
||||
dirs
|
||||
}
|
||||
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("languages");
|
||||
|
||||
fn collect_src_files(dir: &str) -> (Vec<String>, Vec<String>) {
|
||||
eprintln!("Collect files for {}", dir);
|
||||
|
||||
let mut c_files = Vec::new();
|
||||
let mut cpp_files = Vec::new();
|
||||
let path = PathBuf::from("languages").join(&dir).join("src");
|
||||
for entry in fs::read_dir(path).unwrap().flatten() {
|
||||
for entry in fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if path
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.starts_with("binding")
|
||||
{
|
||||
|
||||
if !entry.file_type()?.is_dir() {
|
||||
continue;
|
||||
}
|
||||
if let Some(ext) = path.extension() {
|
||||
if ext == "c" {
|
||||
c_files.push(path.to_str().unwrap().to_string());
|
||||
} else if ext == "cc" || ext == "cpp" || ext == "cxx" {
|
||||
cpp_files.push(path.to_str().unwrap().to_string());
|
||||
|
||||
let dir = path.file_name().unwrap().to_str().unwrap().to_string();
|
||||
|
||||
// filter ignores
|
||||
if ignore.contains(&dir) {
|
||||
continue;
|
||||
}
|
||||
dirs.push(dir)
|
||||
}
|
||||
|
||||
Ok(dirs)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
const DYLIB_EXTENSION: &str = "so";
|
||||
|
||||
#[cfg(windows)]
|
||||
const DYLIB_EXTENSION: &str = "dll";
|
||||
|
||||
fn build_library(src_path: &Path, language: &str) -> Result<()> {
|
||||
let header_path = src_path;
|
||||
// let grammar_path = src_path.join("grammar.json");
|
||||
let parser_path = src_path.join("parser.c");
|
||||
let mut scanner_path = src_path.join("scanner.c");
|
||||
|
||||
let scanner_path = if scanner_path.exists() {
|
||||
Some(scanner_path)
|
||||
} else {
|
||||
scanner_path.set_extension("cc");
|
||||
if scanner_path.exists() {
|
||||
Some(scanner_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
if cfg!(windows) {
|
||||
command
|
||||
.args(&["/nologo", "/LD", "/I"])
|
||||
.arg(header_path)
|
||||
.arg("/Od");
|
||||
if let Some(scanner_path) = scanner_path.as_ref() {
|
||||
command.arg(scanner_path);
|
||||
}
|
||||
|
||||
command
|
||||
.arg(parser_path)
|
||||
.arg("/link")
|
||||
.arg(format!("/out:{}", library_path.to_str().unwrap()));
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
(c_files, cpp_files)
|
||||
|
||||
let output = command
|
||||
.output()
|
||||
.with_context(|| "Failed to execute C compiler")?;
|
||||
if !output.status.success() {
|
||||
return Err(anyhow!(
|
||||
"Parser compilation failed.\nStdout: {}\nStderr: {}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
));
|
||||
}
|
||||
|
||||
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 build_c(files: Vec<String>, language: &str) {
|
||||
let mut build = cc::Build::new();
|
||||
for file in files {
|
||||
build
|
||||
.file(&file)
|
||||
.include(PathBuf::from(file).parent().unwrap())
|
||||
.pic(true)
|
||||
.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() {
|
||||
"/std:c++17"
|
||||
} else {
|
||||
"-std=c++14"
|
||||
};
|
||||
|
||||
for file in files {
|
||||
build
|
||||
.file(&file)
|
||||
.include(PathBuf::from(file).parent().unwrap())
|
||||
.pic(true)
|
||||
.warnings(false)
|
||||
.cpp(true)
|
||||
.flag_if_supported(flag);
|
||||
}
|
||||
build.compile(&format!("tree-sitter-{}-cpp", language));
|
||||
fn mtime(path: &Path) -> Result<SystemTime> {
|
||||
Ok(fs::metadata(path)?.modified()?)
|
||||
}
|
||||
|
||||
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'.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let (c, cpp) = collect_src_files(dir);
|
||||
if !c.is_empty() {
|
||||
build_c(c, language);
|
||||
}
|
||||
if !cpp.is_empty() {
|
||||
build_cpp(cpp, language);
|
||||
}
|
||||
|
||||
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("languages")
|
||||
.join(dir)
|
||||
.join("src");
|
||||
|
||||
build_library(&path, language).unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let ignore = vec![
|
||||
"tree-sitter-typescript".to_string(),
|
||||
"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 pool = threadpool::Builder::new().build(); // by going through the builder, it'll use num_cpus
|
||||
@ -118,7 +185,7 @@ fn main() {
|
||||
n_jobs += 1;
|
||||
|
||||
pool.execute(move || {
|
||||
let language = &dir[12..]; // skip tree-sitter- prefix
|
||||
let language = &dir.strip_prefix("tree-sitter-").unwrap();
|
||||
build_dir(&dir, language);
|
||||
|
||||
// report progress
|
||||
|
@ -1,94 +1,39 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use anyhow::{Context, Result};
|
||||
use libloading::{Library, Symbol};
|
||||
use tree_sitter::Language;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! mk_extern {
|
||||
( $( $name:ident ),* ) => {
|
||||
$(
|
||||
extern "C" { pub fn $name() -> Language; }
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! mk_enum {
|
||||
( $( $camel:ident ),* ) => {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Lang {
|
||||
$(
|
||||
$camel,
|
||||
)*
|
||||
fn replace_dashes_with_underscores(name: &str) -> String {
|
||||
let mut result = String::with_capacity(name.len());
|
||||
for c in name.chars() {
|
||||
if c == '-' {
|
||||
result.push('_');
|
||||
} else {
|
||||
result.push(c);
|
||||
}
|
||||
};
|
||||
}
|
||||
result
|
||||
}
|
||||
#[cfg(unix)]
|
||||
const DYLIB_EXTENSION: &str = "so";
|
||||
|
||||
#[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(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
const DYLIB_EXTENSION: &str = "dll";
|
||||
|
||||
pub fn get_language(runtime_path: &std::path::Path, name: &str) -> Result<Language> {
|
||||
let name = name.to_ascii_lowercase();
|
||||
let mut library_path = runtime_path.join("grammars").join(&name);
|
||||
// TODO: duplicated under build
|
||||
library_path.set_extension(DYLIB_EXTENSION);
|
||||
|
||||
let library = unsafe { Library::new(&library_path) }
|
||||
.with_context(|| format!("Error opening dynamic library {:?}", &library_path))?;
|
||||
let language_fn_name = format!("tree_sitter_{}", replace_dashes_with_underscores(&name));
|
||||
let language = unsafe {
|
||||
let language_fn: Symbol<unsafe extern "C" fn() -> Language> = library
|
||||
.get(language_fn_name.as_bytes())
|
||||
.with_context(|| format!("Failed to load symbol {}", language_fn_name))?;
|
||||
language_fn()
|
||||
};
|
||||
std::mem::forget(library);
|
||||
Ok(language)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! mk_get_language_name {
|
||||
( $( $camel:ident ),* ) => {
|
||||
#[must_use]
|
||||
pub const fn get_language_name(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
$(
|
||||
Lang::$camel => stringify!($camel),
|
||||
)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! mk_langs {
|
||||
( $( ($camel:ident, $name:ident) ),* ) => {
|
||||
mk_extern!($( $name ),*);
|
||||
mk_enum!($( $camel ),*);
|
||||
mk_get_language!($( ($camel, $name) ),*);
|
||||
mk_get_language_name!($( $camel ),*);
|
||||
};
|
||||
}
|
||||
|
||||
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 std::{
|
||||
io::{stdout, Write},
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::Error;
|
||||
@ -82,15 +83,18 @@ pub fn new(args: Args, mut config: Config) -> Result<Self, Error> {
|
||||
editor.new_file(Action::VerticalSplit);
|
||||
compositor.push(Box::new(ui::file_picker(first.clone())));
|
||||
} else {
|
||||
let nr_of_files = args.files.len();
|
||||
editor.open(first.to_path_buf(), Action::VerticalSplit)?;
|
||||
for file in args.files {
|
||||
if file.is_dir() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"expected a path to file, found a directory. (to open a directory pass it as first argument)"
|
||||
));
|
||||
} else {
|
||||
editor.open(file, Action::VerticalSplit)?;
|
||||
editor.open(file.to_path_buf(), Action::Load)?;
|
||||
}
|
||||
}
|
||||
editor.set_status(format!("Loaded {} files.", nr_of_files));
|
||||
}
|
||||
} else {
|
||||
editor.new_file(Action::VerticalSplit);
|
||||
@ -130,6 +134,8 @@ fn render(&mut self) {
|
||||
|
||||
pub async fn event_loop(&mut self) {
|
||||
let mut reader = EventStream::new();
|
||||
let mut last_render = Instant::now();
|
||||
let deadline = Duration::from_secs(1) / 60;
|
||||
|
||||
self.render();
|
||||
|
||||
@ -139,26 +145,22 @@ pub async fn event_loop(&mut self) {
|
||||
break;
|
||||
}
|
||||
|
||||
use futures_util::{FutureExt, StreamExt};
|
||||
use futures_util::StreamExt;
|
||||
|
||||
tokio::select! {
|
||||
biased;
|
||||
|
||||
event = reader.next() => {
|
||||
self.handle_terminal_events(event)
|
||||
}
|
||||
Some((id, call)) = self.editor.language_servers.incoming.next() => {
|
||||
self.handle_language_server_message(call, id).await;
|
||||
|
||||
// eagerly process any other available notifications/calls
|
||||
let now = std::time::Instant::now();
|
||||
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;
|
||||
}
|
||||
// limit render calls for fast language server messages
|
||||
let last = self.editor.language_servers.incoming.is_empty();
|
||||
if last || last_render.elapsed() > deadline {
|
||||
self.render();
|
||||
last_render = Instant::now();
|
||||
}
|
||||
self.render();
|
||||
}
|
||||
Some(callback) = self.jobs.futures.next() => {
|
||||
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 @@ fn default() -> Keymaps {
|
||||
key!('r') => Command::replace,
|
||||
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!(End) => Command::goto_line_end,
|
||||
|
||||
@ -120,7 +124,6 @@ fn default() -> Keymaps {
|
||||
alt!(';') => Command::flip_selections,
|
||||
key!('%') => Command::select_all,
|
||||
key!('x') => Command::extend_line,
|
||||
key!('x') => Command::extend_line,
|
||||
key!('X') => Command::extend_to_line_bounds,
|
||||
// crop_to_whole_line
|
||||
|
||||
|
@ -64,6 +64,7 @@ pub fn render_view(
|
||||
surface: &mut Surface,
|
||||
theme: &Theme,
|
||||
is_focused: bool,
|
||||
loader: &syntax::Loader,
|
||||
) {
|
||||
let area = Rect::new(
|
||||
view.area.x + OFFSET,
|
||||
@ -72,7 +73,7 @@ pub fn render_view(
|
||||
view.area.height.saturating_sub(1),
|
||||
); // - 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 viewport.right() != view.area.right() {
|
||||
@ -98,6 +99,7 @@ pub fn render_view(
|
||||
self.render_statusline(doc, view, area, surface, theme, is_focused);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn render_buffer(
|
||||
&self,
|
||||
doc: &Document,
|
||||
@ -106,6 +108,7 @@ pub fn render_buffer(
|
||||
surface: &mut Surface,
|
||||
theme: &Theme,
|
||||
is_focused: bool,
|
||||
loader: &syntax::Loader,
|
||||
) {
|
||||
let text = doc.text().slice(..);
|
||||
|
||||
@ -122,8 +125,26 @@ pub fn render_buffer(
|
||||
// TODO: range doesn't actually restrict source, just highlight range
|
||||
let highlights: Vec<_> = match doc.syntax() {
|
||||
Some(syntax) => {
|
||||
let scopes = theme.scopes();
|
||||
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
|
||||
}
|
||||
None => vec![Ok(HighlightEvent::Source {
|
||||
@ -735,7 +756,16 @@ fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
||||
|
||||
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);
|
||||
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) {
|
||||
|
@ -18,7 +18,7 @@ default = ["crossterm"]
|
||||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
cassowary = "0.3"
|
||||
unicode-segmentation = "1.2"
|
||||
unicode-segmentation = "1.8"
|
||||
crossterm = { version = "0.20", optional = true }
|
||||
serde = { version = "1", "optional" = true, features = ["derive"]}
|
||||
helix-view = { version = "0.3", path = "../helix-view", features = ["term"] }
|
||||
|
@ -456,14 +456,16 @@ pub fn open(
|
||||
theme: Option<&Theme>,
|
||||
config_loader: Option<&syntax::Loader>,
|
||||
) -> Result<Self, Error> {
|
||||
if !path.exists() {
|
||||
return Ok(Self::default());
|
||||
}
|
||||
let (mut rope, encoding) = if path.exists() {
|
||||
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 mut doc = Self::from(rope, Some(encoding));
|
||||
|
||||
// set the path and try detecting the language
|
||||
|
@ -39,6 +39,7 @@ pub struct Editor {
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Action {
|
||||
Load,
|
||||
Replace,
|
||||
HorizontalSplit,
|
||||
VerticalSplit,
|
||||
@ -97,16 +98,14 @@ pub fn set_theme(&mut self, theme: Theme) {
|
||||
self._refresh();
|
||||
}
|
||||
|
||||
pub fn set_theme_from_name(&mut self, theme: &str) {
|
||||
let theme = match self.theme_loader.load(theme.as_ref()) {
|
||||
Ok(theme) => theme,
|
||||
Err(e) => {
|
||||
log::warn!("failed setting theme `{}` - {}", theme, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn set_theme_from_name(&mut self, theme: &str) -> anyhow::Result<()> {
|
||||
use anyhow::Context;
|
||||
let theme = self
|
||||
.theme_loader
|
||||
.load(theme.as_ref())
|
||||
.with_context(|| format!("failed setting theme `{}`", theme))?;
|
||||
self.set_theme(theme);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn _refresh(&mut self) {
|
||||
@ -153,6 +152,9 @@ pub fn switch(&mut self, id: DocumentId, action: Action) {
|
||||
|
||||
return;
|
||||
}
|
||||
Action::Load => {
|
||||
return;
|
||||
}
|
||||
Action::HorizontalSplit => {
|
||||
let view = View::new(id);
|
||||
let view_id = self.tree.split(view, Layout::Horizontal);
|
||||
|
0
runtime/grammars/.gitkeep
Normal file
0
runtime/grammars/.gitkeep
Normal file
@ -54,6 +54,8 @@
|
||||
"." @punctuation.delimiter
|
||||
";" @punctuation.delimiter
|
||||
|
||||
(enumerator) @type.enum.variant
|
||||
|
||||
(string_literal) @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
|
||||
(#match? @constructor "^[A-Z]"))
|
||||
; -------
|
||||
; Types
|
||||
; -------
|
||||
|
||||
; Assume that uppercase names in paths are types
|
||||
(mod_item
|
||||
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
|
||||
path: (identifier) @type)
|
||||
(#match? @type "^[A-Z]"))
|
||||
((scoped_identifier
|
||||
path: (scoped_identifier
|
||||
name: (identifier) @type))
|
||||
(#match? @type "^[A-Z]"))
|
||||
|
||||
; Namespaces
|
||||
|
||||
(crate) @namespace
|
||||
(extern_crate_declaration
|
||||
(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
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function)
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function.method))
|
||||
(call_expression
|
||||
function: (scoped_identifier
|
||||
"::"
|
||||
name: (identifier) @function))
|
||||
|
||||
(generic_function
|
||||
function: (identifier) @function)
|
||||
(generic_function
|
||||
function: (scoped_identifier
|
||||
name: (identifier) @function))
|
||||
(generic_function
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function.method))
|
||||
|
||||
(macro_invocation
|
||||
macro: (identifier) @function.macro
|
||||
"!" @function.macro)
|
||||
(macro_invocation
|
||||
macro: (scoped_identifier
|
||||
(identifier) @function.macro .))
|
||||
|
||||
; (metavariable) @variable
|
||||
(metavariable) @function.macro
|
||||
|
||||
"$" @function.macro
|
||||
|
||||
; Function definitions
|
||||
|
||||
(function_item (identifier) @function)
|
||||
(function_signature_item (identifier) @function)
|
||||
|
||||
; Other identifiers
|
||||
|
||||
(type_identifier) @type
|
||||
(primitive_type) @type.builtin
|
||||
(field_identifier) @property
|
||||
|
||||
(line_comment) @comment
|
||||
(block_comment) @comment
|
||||
|
||||
"(" @punctuation.bracket
|
||||
")" @punctuation.bracket
|
||||
"[" @punctuation.bracket
|
||||
"]" @punctuation.bracket
|
||||
|
||||
(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
|
||||
(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
|
||||
(float_literal) @number
|
||||
; ---
|
||||
; Primitives
|
||||
; ---
|
||||
|
||||
(escape_sequence) @escape
|
||||
(primitive_type) @type.builtin
|
||||
(boolean_literal) @constant.builtin
|
||||
[
|
||||
(integer_literal)
|
||||
(float_literal)
|
||||
] @number
|
||||
[
|
||||
(char_literal)
|
||||
(string_literal)
|
||||
(raw_string_literal)
|
||||
] @string
|
||||
[
|
||||
(line_comment)
|
||||
(block_comment)
|
||||
] @comment
|
||||
|
||||
; ---
|
||||
; Extraneous
|
||||
; ---
|
||||
|
||||
(self) @variable.builtin
|
||||
(enum_variant (identifier) @type.enum.variant)
|
||||
|
||||
(field_initializer
|
||||
(field_identifier) @property)
|
||||
(shorthand_field_initializer) @variable
|
||||
(shorthand_field_identifier) @variable
|
||||
|
||||
(lifetime
|
||||
"'" @label
|
||||
(identifier) @label)
|
||||
(loop_label
|
||||
(identifier) @type)
|
||||
|
||||
; ---
|
||||
; Punctuation
|
||||
; ---
|
||||
|
||||
[
|
||||
"::"
|
||||
"."
|
||||
";"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
] @punctuation.bracket
|
||||
(type_arguments
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
(type_parameters
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
; ---
|
||||
; Parameters
|
||||
; ---
|
||||
|
||||
(parameter
|
||||
pattern: (identifier) @variable.parameter)
|
||||
(closure_parameters
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
|
||||
|
||||
; -------
|
||||
; Keywords
|
||||
; -------
|
||||
|
||||
(for_expression
|
||||
"for" @keyword.control)
|
||||
((identifier) @keyword.control
|
||||
(#match? @keyword.control "^yield$"))
|
||||
[
|
||||
"while"
|
||||
"loop"
|
||||
"in"
|
||||
"break"
|
||||
"continue"
|
||||
|
||||
"match"
|
||||
"if"
|
||||
"else"
|
||||
"return"
|
||||
|
||||
"await"
|
||||
] @keyword.control
|
||||
|
||||
[
|
||||
(crate)
|
||||
(super)
|
||||
"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
|
||||
|
||||
(mutable_specifier) @keyword.mut
|
||||
|
||||
|
||||
|
||||
; -------
|
||||
; Guess Other Types
|
||||
; -------
|
||||
|
||||
((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
|
||||
(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
|
||||
|
||||
|
||||
|
||||
; -------
|
||||
; 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
|
||||
|
||||
(type_identifier) @type
|
||||
(identifier) @variable
|
||||
(field_identifier) @variable
|
||||
|
80
runtime/themes/dark_plus.toml
Normal file
80
runtime/themes/dark_plus.toml
Normal file
@ -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"
|
115
theme.toml
115
theme.toml
@ -1,68 +1,81 @@
|
||||
"attribute" = "#dbbfef" # lilac
|
||||
"keyword" = "#eccdba" # almond
|
||||
"keyword.directive" = "#dbbfef" # lilac -- preprocessor comments (#if in C)
|
||||
"namespace" = "#dbbfef" # lilac
|
||||
"punctuation" = "#a4a0e8" # lavender
|
||||
"punctuation.delimiter" = "#a4a0e8" # lavender
|
||||
"operator" = "#dbbfef" # lilac
|
||||
"special" = "#efba5d" # honey
|
||||
# "property" = "#a4a0e8" # lavender
|
||||
"property" = "#ffffff" # white
|
||||
"variable" = "#a4a0e8" # lavender
|
||||
# "variable" = "#eccdba" # almond TODO: metavariables only
|
||||
"variable.parameter" = "#a4a0e8" # lavender
|
||||
# TODO distinguish type from type.builtin?
|
||||
"type" = "#ffffff" # white
|
||||
"type.builtin" = "#ffffff" # white
|
||||
"constructor" = "#dbbfef" # lilac
|
||||
"function" = "#ffffff" # white
|
||||
"function.macro" = "#dbbfef" # lilac
|
||||
"function.builtin" = "#ffffff" # white
|
||||
"comment" = "#697C81" # sirocco
|
||||
"variable.builtin" = "#9ff28f" # mint
|
||||
"constant" = "#ffffff" # white
|
||||
"constant.builtin" = "#ffffff" # white
|
||||
"string" = "#cccccc" # silver
|
||||
"number" = "#e8dca0" # chamois
|
||||
"escape" = "#efba5d" # honey
|
||||
attribute = "lilac"
|
||||
keyword = "almond"
|
||||
"keyword.directive" = "lilac" # -- preprocessor comments (#if in C)
|
||||
namespace = "lilac"
|
||||
punctuation = "lavender"
|
||||
"punctuation.delimiter" = "lavender"
|
||||
operator = "lilac"
|
||||
special = "honey"
|
||||
property = "white"
|
||||
variable = "lavender"
|
||||
# variable = "almond" # TODO: metavariables only
|
||||
"variable.parameter" = "lavender"
|
||||
"variable.builtin" = "mint"
|
||||
type = "white"
|
||||
"type.builtin" = "white" # TODO: distinguish?
|
||||
constructor = "lilac"
|
||||
function = "white"
|
||||
"function.macro" = "lilac"
|
||||
"function.builtin" = "white"
|
||||
comment = "sirocco"
|
||||
constant = "white"
|
||||
"constant.builtin" = "white"
|
||||
string = "silver"
|
||||
number = "chamois"
|
||||
escape = "honey"
|
||||
# used for lifetimes
|
||||
"label" = "#efba5d" # honey
|
||||
label = "honey"
|
||||
|
||||
# TODO: diferentiate number builtin
|
||||
# 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
|
||||
|
||||
"module" = "#ff0000"
|
||||
module = "#ff0000"
|
||||
|
||||
"ui.background" = { bg = "#3b224c" } # midnight
|
||||
"ui.linenr" = { fg = "#5a5977" } # comet
|
||||
"ui.linenr.selected" = { fg = "#dbbfef" } # lilac
|
||||
"ui.statusline" = { fg = "#dbbfef", bg = "#281733" } # revolver
|
||||
"ui.statusline.inactive" = { fg = "#a4a0e8", bg = "#281733" } # revolver
|
||||
"ui.popup" = { bg = "#281733" } # revolver
|
||||
"ui.window" = { fg = "#452859" } # bossa nova
|
||||
"ui.background" = { bg = "midnight" }
|
||||
"ui.linenr" = { fg = "comet" }
|
||||
"ui.linenr.selected" = { fg = "lilac" }
|
||||
"ui.statusline" = { fg = "lilac", bg = "revolver" }
|
||||
"ui.statusline.inactive" = { fg = "lavender", bg = "revolver" }
|
||||
"ui.popup" = { bg = "revolver" }
|
||||
"ui.window" = { fg = "bossanova" }
|
||||
"ui.help" = { bg = "#7958DC", fg = "#171452" }
|
||||
|
||||
"ui.text" = { fg = "#a4a0e8" } # lavender
|
||||
"ui.text.focus" = { fg = "#dbbfef" } # lilac
|
||||
"ui.text" = { fg = "lavender" }
|
||||
"ui.text.focus" = { fg = "lilac" }
|
||||
|
||||
"ui.selection" = { bg = "#540099" }
|
||||
"ui.selection.primary" = { bg = "#540099" }
|
||||
# TODO: namespace ui.cursor as ui.selection.cursor?
|
||||
"ui.cursor.select" = { bg = "#6F44F0" }
|
||||
"ui.cursor.insert" = { bg = "#ffffff" }
|
||||
"ui.cursor.select" = { bg = "delta" }
|
||||
"ui.cursor.insert" = { bg = "white" }
|
||||
"ui.cursor.match" = { fg = "#212121", bg = "#6C6999" }
|
||||
"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"
|
||||
"error" = "#f47868"
|
||||
"info" = "#6F44F0"
|
||||
"hint" = "#cccccc"
|
||||
warning = "lightning"
|
||||
error = "apricot"
|
||||
info = "delta"
|
||||
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…
Reference in New Issue
Block a user