Merge branch 'master' into ilyakooo0:iko/init-hoon

This commit is contained in:
Michael Davis 2024-02-12 09:01:56 -05:00
commit bef7c2ac6c
No known key found for this signature in database
27 changed files with 534 additions and 254 deletions

19
Cargo.lock generated
View File

@ -424,9 +424,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fern"
@ -1240,6 +1240,7 @@ dependencies = [
"dunce",
"encoding_rs",
"etcetera",
"globset",
"hashbrown 0.14.3",
"helix-loader",
"helix-stdx",
@ -1386,6 +1387,7 @@ dependencies = [
"signal-hook-tokio",
"smallvec",
"tempfile",
"termini",
"tokio",
"tokio-stream",
"toml",
@ -1971,9 +1973,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustix"
version = "0.38.30"
version = "0.38.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
dependencies = [
"bitflags 2.4.2",
"errno",
@ -2201,13 +2203,12 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.9.0"
version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall 0.4.1",
"rustix",
"windows-sys 0.52.0",
]
@ -2316,9 +2317,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.35.1"
version = "1.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
dependencies = [
"backtrace",
"bytes",

View File

@ -14,6 +14,7 @@
| cabal | | | | `haskell-language-server-wrapper` |
| cairo | ✓ | ✓ | ✓ | `cairo-language-server` |
| capnp | ✓ | | ✓ | |
| cel | ✓ | | | |
| clojure | ✓ | | | `clojure-lsp` |
| cmake | ✓ | ✓ | ✓ | `cmake-language-server` |
| comment | ✓ | | | |
@ -68,8 +69,8 @@
| haskell-persistent | ✓ | | | |
| hcl | ✓ | | ✓ | `terraform-ls` |
| heex | ✓ | ✓ | | `elixir-ls` |
| hoon | ✓ | | | |
| hocon | ✓ | | ✓ | |
| hoon | ✓ | | | |
| hosts | ✓ | | | |
| html | ✓ | | | `vscode-html-language-server` |
| hurl | ✓ | | ✓ | |
@ -155,6 +156,7 @@
| smithy | ✓ | | | `cs` |
| sml | ✓ | | | |
| solidity | ✓ | | | `solc` |
| spicedb | ✓ | | | |
| sql | ✓ | | | |
| sshclientconfig | ✓ | | | |
| starlark | ✓ | ✓ | | |

View File

@ -78,24 +78,26 @@ ### File-type detection and the `file-types` key
example:
```toml
file-types = ["Makefile", "toml", { suffix = ".git/config" }]
file-types = ["toml", { glob = "Makefile" }, { glob = ".git/config" }, { glob = ".github/workflows/*.yaml" } ]
```
When determining a language configuration to use, Helix searches the file-types
with the following priorities:
1. Exact match: if the filename of a file is an exact match of a string in a
`file-types` list, that language wins. In the example above, `"Makefile"`
will match against `Makefile` files.
2. Extension: if there are no exact matches, any `file-types` string that
matches the file extension of a given file wins. In the example above, the
`"toml"` matches files like `Cargo.toml` or `languages.toml`.
3. Suffix: if there are still no matches, any values in `suffix` tables
are checked against the full path of the given file. In the example above,
the `{ suffix = ".git/config" }` would match against any `config` files
in `.git` directories. Note: `/` is used as the directory separator but is
replaced at runtime with the appropriate path separator for the operating
system, so this rule would match against `.git\config` files on Windows.
1. Glob: values in `glob` tables are checked against the full path of the given
file. Globs are standard Unix-style path globs (e.g. the kind you use in Shell)
and can be used to match paths for a specific prefix, suffix, directory, etc.
In the above example, the `{ glob = "Makefile" }` config would match files
with the name `Makefile`, the `{ glob = ".git/config" }` config would match
`config` files in `.git` directories, and the `{ glob = ".github/workflows/*.yaml" }`
config would match any `yaml` files in `.github/workflow` directories. Note
that globs should always use the Unix path separator `/` even on Windows systems;
the matcher will automatically take the machine-specific separators into account.
If the glob isn't an absolute path or doesn't already start with a glob prefix,
`*/` will automatically be added to ensure it matches for any subdirectory.
2. Extension: if there are no glob matches, any `file-types` string that matches
the file extension of a given file wins. In the example above, the `"toml"`
config matches files like `Cargo.toml` or `languages.toml`.
## Language Server configuration
@ -120,13 +122,14 @@ ## Language Server configuration
These are the available options for a language server.
| Key | Description |
| ---- | ----------- |
| `command` | The name or path of the language server binary to execute. Binaries must be in `$PATH` |
| `args` | A list of arguments to pass to the language server binary |
| `config` | LSP initialization options |
| `timeout` | The maximum time a request to the language server may take, in seconds. Defaults to `20` |
| `environment` | Any environment variables that will be used when starting the language server `{ "KEY1" = "Value1", "KEY2" = "Value2" }` |
| Key | Description |
| ---- | ----------- |
| `command` | The name or path of the language server binary to execute. Binaries must be in `$PATH` |
| `args` | A list of arguments to pass to the language server binary |
| `config` | LSP initialization options |
| `timeout` | The maximum time a request to the language server may take, in seconds. Defaults to `20` |
| `environment` | Any environment variables that will be used when starting the language server `{ "KEY1" = "Value1", "KEY2" = "Value2" }` |
| `required-root-patterns` | A list of `glob` patterns to look for in the working directory. The language server is started if at least one of them is found. |
A `format` sub-table within `config` can be used to pass extra formatting options to
[Document Formatting Requests](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_formatting).

View File

@ -52,6 +52,7 @@ textwrap = "0.16.0"
nucleo.workspace = true
parking_lot = "0.12"
globset = "0.4.14"
[dev-dependencies]
quickcheck = { version = "1", default-features = false }

View File

@ -1,10 +1,45 @@
/// Syntax configuration loader based on built-in languages.toml.
pub fn default_syntax_loader() -> crate::syntax::Configuration {
use crate::syntax::{Configuration, Loader, LoaderError};
/// Language configuration based on built-in languages.toml.
pub fn default_lang_config() -> Configuration {
helix_loader::config::default_lang_config()
.try_into()
.expect("Could not serialize built-in languages.toml")
.expect("Could not deserialize built-in languages.toml")
}
/// Syntax configuration loader based on user configured languages.toml.
pub fn user_syntax_loader() -> Result<crate::syntax::Configuration, toml::de::Error> {
/// Language configuration loader based on built-in languages.toml.
pub fn default_lang_loader() -> Loader {
Loader::new(default_lang_config()).expect("Could not compile loader for default config")
}
#[derive(Debug)]
pub enum LanguageLoaderError {
DeserializeError(toml::de::Error),
LoaderError(LoaderError),
}
impl std::fmt::Display for LanguageLoaderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DeserializeError(err) => write!(f, "Failed to parse language config: {err}"),
Self::LoaderError(err) => write!(f, "Failed to compile language config: {err}"),
}
}
}
impl std::error::Error for LanguageLoaderError {}
/// Language configuration based on user configured languages.toml.
pub fn user_lang_config() -> Result<Configuration, toml::de::Error> {
helix_loader::config::user_lang_config()?.try_into()
}
/// Language configuration loader based on user configured languages.toml.
pub fn user_lang_loader() -> Result<Loader, LanguageLoaderError> {
let config: Configuration = helix_loader::config::user_lang_config()
.map_err(LanguageLoaderError::DeserializeError)?
.try_into()
.map_err(LanguageLoaderError::DeserializeError)?;
Loader::new(config).map_err(LanguageLoaderError::LoaderError)
}

View File

@ -10,6 +10,7 @@
use ahash::RandomState;
use arc_swap::{ArcSwap, Guard};
use bitflags::bitflags;
use globset::GlobSet;
use hashbrown::raw::RawTable;
use slotmap::{DefaultKey as LayerId, HopSlotMap};
@ -82,12 +83,6 @@ pub struct Configuration {
pub language_server: HashMap<String, LanguageServerConfiguration>,
}
impl Default for Configuration {
fn default() -> Self {
crate::config::default_syntax_loader()
}
}
// largely based on tree-sitter/cli/src/loader.rs
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
@ -164,9 +159,11 @@ pub enum FileType {
/// The extension of the file, either the `Path::extension` or the full
/// filename if the file does not have an extension.
Extension(String),
/// The suffix of a file. This is compared to a given file's absolute
/// path, so it can be used to detect files based on their directories.
Suffix(String),
/// A Unix-style path glob. This is compared to the file's absolute path, so
/// it can be used to detect files based on their directories. If the glob
/// is not an absolute path and does not already start with a glob pattern,
/// a glob pattern will be prepended to it.
Glob(globset::Glob),
}
impl Serialize for FileType {
@ -178,9 +175,9 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
match self {
FileType::Extension(extension) => serializer.serialize_str(extension),
FileType::Suffix(suffix) => {
FileType::Glob(glob) => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("suffix", &suffix.replace(std::path::MAIN_SEPARATOR, "/"))?;
map.serialize_entry("glob", glob.glob())?;
map.end()
}
}
@ -213,9 +210,20 @@ fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
M: serde::de::MapAccess<'de>,
{
match map.next_entry::<String, String>()? {
Some((key, suffix)) if key == "suffix" => Ok(FileType::Suffix({
suffix.replace('/', std::path::MAIN_SEPARATOR_STR)
})),
Some((key, mut glob)) if key == "glob" => {
// If the glob isn't an absolute path or already starts
// with a glob pattern, add a leading glob so we
// properly match relative paths.
if !glob.starts_with('/') && !glob.starts_with("*/") {
glob.insert_str(0, "*/");
}
globset::Glob::new(glob.as_str())
.map(FileType::Glob)
.map_err(|err| {
serde::de::Error::custom(format!("invalid `glob` pattern: {}", err))
})
}
Some((key, _value)) => Err(serde::de::Error::custom(format!(
"unknown key in `file-types` list: {}",
key
@ -358,6 +366,22 @@ fn serialize_lang_features<S>(
serializer.end()
}
fn deserialize_required_root_patterns<'de, D>(deserializer: D) -> Result<Option<GlobSet>, D::Error>
where
D: serde::Deserializer<'de>,
{
let patterns = Vec::<String>::deserialize(deserializer)?;
if patterns.is_empty() {
return Ok(None);
}
let mut builder = globset::GlobSetBuilder::new();
for pattern in patterns {
let glob = globset::Glob::new(&pattern).map_err(serde::de::Error::custom)?;
builder.add(glob);
}
builder.build().map(Some).map_err(serde::de::Error::custom)
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct LanguageServerConfiguration {
@ -371,6 +395,12 @@ pub struct LanguageServerConfiguration {
pub config: Option<serde_json::Value>,
#[serde(default = "default_timeout")]
pub timeout: u64,
#[serde(
default,
skip_serializing,
deserialize_with = "deserialize_required_root_patterns"
)]
pub required_root_patterns: Option<GlobSet>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -752,6 +782,47 @@ pub struct SoftWrap {
pub wrap_at_text_width: Option<bool>,
}
#[derive(Debug)]
struct FileTypeGlob {
glob: globset::Glob,
language_id: usize,
}
impl FileTypeGlob {
fn new(glob: globset::Glob, language_id: usize) -> Self {
Self { glob, language_id }
}
}
#[derive(Debug)]
struct FileTypeGlobMatcher {
matcher: globset::GlobSet,
file_types: Vec<FileTypeGlob>,
}
impl FileTypeGlobMatcher {
fn new(file_types: Vec<FileTypeGlob>) -> Result<Self, globset::Error> {
let mut builder = globset::GlobSetBuilder::new();
for file_type in &file_types {
builder.add(file_type.glob.clone());
}
Ok(Self {
matcher: builder.build()?,
file_types,
})
}
fn language_id_for_path(&self, path: &Path) -> Option<&usize> {
self.matcher
.matches(path)
.iter()
.filter_map(|idx| self.file_types.get(*idx))
.max_by_key(|file_type| file_type.glob.glob().len())
.map(|file_type| &file_type.language_id)
}
}
// Expose loader as Lazy<> global since it's always static?
#[derive(Debug)]
@ -759,7 +830,7 @@ pub struct Loader {
// highlight_names ?
language_configs: Vec<Arc<LanguageConfiguration>>,
language_config_ids_by_extension: HashMap<String, usize>, // Vec<usize>
language_config_ids_by_suffix: HashMap<String, usize>,
language_config_ids_glob_matcher: FileTypeGlobMatcher,
language_config_ids_by_shebang: HashMap<String, usize>,
language_server_configs: HashMap<String, LanguageServerConfiguration>,
@ -767,66 +838,57 @@ pub struct Loader {
scopes: ArcSwap<Vec<String>>,
}
pub type LoaderError = globset::Error;
impl Loader {
pub fn new(config: Configuration) -> Self {
let mut loader = Self {
language_configs: Vec::new(),
language_server_configs: config.language_server,
language_config_ids_by_extension: HashMap::new(),
language_config_ids_by_suffix: HashMap::new(),
language_config_ids_by_shebang: HashMap::new(),
scopes: ArcSwap::from_pointee(Vec::new()),
};
pub fn new(config: Configuration) -> Result<Self, LoaderError> {
let mut language_configs = Vec::new();
let mut language_config_ids_by_extension = HashMap::new();
let mut language_config_ids_by_shebang = HashMap::new();
let mut file_type_globs = Vec::new();
for config in config.language {
// get the next id
let language_id = loader.language_configs.len();
let language_id = language_configs.len();
for file_type in &config.file_types {
// entry().or_insert(Vec::new).push(language_id);
match file_type {
FileType::Extension(extension) => loader
.language_config_ids_by_extension
.insert(extension.clone(), language_id),
FileType::Suffix(suffix) => loader
.language_config_ids_by_suffix
.insert(suffix.clone(), language_id),
FileType::Extension(extension) => {
language_config_ids_by_extension.insert(extension.clone(), language_id);
}
FileType::Glob(glob) => {
file_type_globs.push(FileTypeGlob::new(glob.to_owned(), language_id));
}
};
}
for shebang in &config.shebangs {
loader
.language_config_ids_by_shebang
.insert(shebang.clone(), language_id);
language_config_ids_by_shebang.insert(shebang.clone(), language_id);
}
loader.language_configs.push(Arc::new(config));
language_configs.push(Arc::new(config));
}
loader
Ok(Self {
language_configs,
language_config_ids_by_extension,
language_config_ids_glob_matcher: FileTypeGlobMatcher::new(file_type_globs)?,
language_config_ids_by_shebang,
language_server_configs: config.language_server,
scopes: ArcSwap::from_pointee(Vec::new()),
})
}
pub fn language_config_for_file_name(&self, path: &Path) -> Option<Arc<LanguageConfiguration>> {
// Find all the language configurations that match this file name
// or a suffix of the file name.
let configuration_id = path
.file_name()
.and_then(|n| n.to_str())
.and_then(|file_name| self.language_config_ids_by_extension.get(file_name))
let configuration_id = self
.language_config_ids_glob_matcher
.language_id_for_path(path)
.or_else(|| {
path.extension()
.and_then(|extension| extension.to_str())
.and_then(|extension| self.language_config_ids_by_extension.get(extension))
})
.or_else(|| {
self.language_config_ids_by_suffix
.iter()
.find_map(|(file_type, id)| {
if path.to_str()?.ends_with(file_type) {
Some(id)
} else {
None
}
})
});
configuration_id.and_then(|&id| self.language_configs.get(id).cloned())
@ -2592,7 +2654,8 @@ fn test_textobject_queries() {
let loader = Loader::new(Configuration {
language: vec![],
language_server: HashMap::new(),
});
})
.unwrap();
let language = get_language("rust").unwrap();
let query = Query::new(language, query_str).unwrap();
@ -2654,7 +2717,8 @@ fn test_parser() {
let loader = Loader::new(Configuration {
language: vec![],
language_server: HashMap::new(),
});
})
.unwrap();
let language = get_language("rust").unwrap();
let config = HighlightConfiguration::new(
@ -2760,7 +2824,8 @@ fn assert_pretty_print(
let loader = Loader::new(Configuration {
language: vec![],
language_server: HashMap::new(),
});
})
.unwrap();
let language = get_language(language_name).unwrap();
let config = HighlightConfiguration::new(language, "", "", "").unwrap();

View File

@ -186,7 +186,7 @@ fn test_treesitter_indent(
lang_scope: &str,
ignored_lines: Vec<std::ops::Range<usize>>,
) {
let loader = Loader::new(indent_tests_config());
let loader = Loader::new(indent_tests_config()).unwrap();
// set runtime path so we can find the queries
let mut runtime = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));

View File

@ -30,7 +30,7 @@ log = "0.4"
# cloning/compiling tree-sitter grammars
cc = { version = "1" }
threadpool = { version = "1.0" }
tempfile = "3.9.0"
tempfile = "3.10.0"
dunce = "1.0.4"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]

View File

@ -27,6 +27,6 @@ lsp-types = { version = "0.95" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
tokio = { version = "1.35", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
tokio = { version = "1.36", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
tokio-stream = "0.1.14"
parking_lot = "0.12.1"

View File

@ -177,12 +177,11 @@ pub fn start(
args: &[String],
config: Option<Value>,
server_environment: HashMap<String, String>,
root_markers: &[String],
manual_roots: &[PathBuf],
root_path: PathBuf,
root_uri: Option<lsp::Url>,
id: usize,
name: String,
req_timeout: u64,
doc_path: Option<&std::path::PathBuf>,
) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> {
// Resolve path to the binary
let cmd = helix_stdx::env::which(cmd)?;
@ -206,22 +205,6 @@ pub fn start(
let (server_rx, server_tx, initialize_notify) =
Transport::start(reader, writer, stderr, id, name.clone());
let (workspace, workspace_is_cwd) = find_workspace();
let workspace = path::normalize(workspace);
let root = find_lsp_workspace(
doc_path
.and_then(|x| x.parent().and_then(|x| x.to_str()))
.unwrap_or("."),
root_markers,
manual_roots,
&workspace,
workspace_is_cwd,
);
// `root_uri` and `workspace_folder` can be empty in case there is no workspace
// `root_url` can not, use `workspace` as a fallback
let root_path = root.clone().unwrap_or_else(|| workspace.clone());
let root_uri = root.and_then(|root| lsp::Url::from_file_path(root).ok());
let workspace_folders = root_uri
.clone()

View File

@ -680,7 +680,7 @@ fn start_client(
doc_path: Option<&std::path::PathBuf>,
root_dirs: &[PathBuf],
enable_snippets: bool,
) -> Result<Arc<Client>> {
) -> Result<Option<Arc<Client>>> {
let config = self
.syn_loader
.language_server_configs()
@ -688,7 +688,7 @@ fn start_client(
.ok_or_else(|| anyhow::anyhow!("Language server '{name}' not defined"))?;
let id = self.counter;
self.counter += 1;
let NewClient(client, incoming) = start_client(
if let Some(NewClient(client, incoming)) = start_client(
id,
name,
ls_config,
@ -696,9 +696,12 @@ fn start_client(
doc_path,
root_dirs,
enable_snippets,
)?;
self.incoming.push(UnboundedReceiverStream::new(incoming));
Ok(client)
)? {
self.incoming.push(UnboundedReceiverStream::new(incoming));
Ok(Some(client))
} else {
Ok(None)
}
}
/// If this method is called, all documents that have a reference to language servers used by the language config have to refresh their language servers,
@ -723,8 +726,8 @@ pub fn restart(
root_dirs,
enable_snippets,
) {
Ok(client) => client,
error => return Some(error),
Ok(client) => client?,
Err(error) => return Some(Err(error)),
};
let old_clients = self
.inner
@ -764,13 +767,13 @@ pub fn get<'a>(
root_dirs: &'a [PathBuf],
enable_snippets: bool,
) -> impl Iterator<Item = (LanguageServerName, Result<Arc<Client>>)> + 'a {
language_config.language_servers.iter().map(
language_config.language_servers.iter().filter_map(
move |LanguageServerFeatures { name, .. }| {
if let Some(clients) = self.inner.get(name) {
if let Some((_, client)) = clients.iter().enumerate().find(|(i, client)| {
client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0)
}) {
return (name.to_owned(), Ok(client.clone()));
return Some((name.to_owned(), Ok(client.clone())));
}
}
match self.start_client(
@ -781,13 +784,14 @@ pub fn get<'a>(
enable_snippets,
) {
Ok(client) => {
let client = client?;
self.inner
.entry(name.to_owned())
.or_default()
.push(client.clone());
(name.clone(), Ok(client))
Some((name.clone(), Ok(client)))
}
Err(err) => (name.to_owned(), Err(err)),
Err(err) => Some((name.to_owned(), Err(err))),
}
},
)
@ -888,18 +892,45 @@ fn start_client(
doc_path: Option<&std::path::PathBuf>,
root_dirs: &[PathBuf],
enable_snippets: bool,
) -> Result<NewClient> {
) -> Result<Option<NewClient>> {
let (workspace, workspace_is_cwd) = helix_loader::find_workspace();
let workspace = path::normalize(workspace);
let root = find_lsp_workspace(
doc_path
.and_then(|x| x.parent().and_then(|x| x.to_str()))
.unwrap_or("."),
&config.roots,
config.workspace_lsp_roots.as_deref().unwrap_or(root_dirs),
&workspace,
workspace_is_cwd,
);
// `root_uri` and `workspace_folder` can be empty in case there is no workspace
// `root_url` can not, use `workspace` as a fallback
let root_path = root.clone().unwrap_or_else(|| workspace.clone());
let root_uri = root.and_then(|root| lsp::Url::from_file_path(root).ok());
if let Some(globset) = &ls_config.required_root_patterns {
if !root_path
.read_dir()?
.flatten()
.map(|entry| entry.file_name())
.any(|entry| globset.is_match(entry))
{
return Ok(None);
}
}
let (client, incoming, initialize_notify) = Client::start(
&ls_config.command,
&ls_config.args,
ls_config.config.clone(),
ls_config.environment.clone(),
&config.roots,
config.workspace_lsp_roots.as_deref().unwrap_or(root_dirs),
root_path,
root_uri,
id,
name,
ls_config.timeout,
doc_path,
)?;
let client = Arc::new(client);
@ -938,7 +969,7 @@ fn start_client(
initialize_notify.notify_one();
});
Ok(NewClient(client, incoming))
Ok(Some(NewClient(client, incoming)))
}
/// Find an LSP workspace of a file using the following mechanism:

View File

@ -18,4 +18,4 @@ ropey = { version = "1.6.1", default-features = false }
which = "6.0"
[dev-dependencies]
tempfile = "3.9"
tempfile = "3.10"

View File

@ -42,6 +42,7 @@ signal-hook = "0.3"
tokio-stream = "0.1"
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
arc-swap = { version = "1.6.0" }
termini = "1"
# Logging
fern = "0.6"
@ -83,4 +84,4 @@ helix-loader = { path = "../helix-loader" }
[dev-dependencies]
smallvec = "1.13"
indoc = "2.0.4"
tempfile = "3.9.0"
tempfile = "3.10.0"

View File

@ -96,11 +96,7 @@ fn setup_integration_logging() {
}
impl Application {
pub fn new(
args: Args,
config: Config,
syn_loader_conf: syntax::Configuration,
) -> Result<Self, Error> {
pub fn new(args: Args, config: Config, lang_loader: syntax::Loader) -> Result<Self, Error> {
#[cfg(feature = "integration")]
setup_integration_logging();
@ -126,7 +122,7 @@ pub fn new(
})
.unwrap_or_else(|| theme_loader.default_theme(true_color));
let syn_loader = std::sync::Arc::new(syntax::Loader::new(syn_loader_conf));
let syn_loader = std::sync::Arc::new(lang_loader);
#[cfg(not(feature = "integration"))]
let backend = CrosstermBackend::new(stdout(), &config.editor);
@ -394,10 +390,8 @@ pub fn handle_config_events(&mut self, config_event: ConfigEvent) {
/// refresh language config after config change
fn refresh_language_config(&mut self) -> Result<(), Error> {
let syntax_config = helix_core::config::user_syntax_loader()
.map_err(|err| anyhow::anyhow!("Failed to load language config: {}", err))?;
self.syn_loader = std::sync::Arc::new(syntax::Loader::new(syntax_config));
let lang_loader = helix_core::config::user_lang_loader()?;
self.syn_loader = std::sync::Arc::new(lang_loader);
self.editor.syn_loader = self.syn_loader.clone();
for document in self.editor.documents.values_mut() {
document.detect_language(self.syn_loader.clone());

View File

@ -2,7 +2,7 @@
style::{Color, Print, Stylize},
tty::IsTty,
};
use helix_core::config::{default_syntax_loader, user_syntax_loader};
use helix_core::config::{default_lang_config, user_lang_config};
use helix_loader::grammar::load_runtime_file;
use helix_view::clipboard::get_clipboard_provider;
use std::io::Write;
@ -128,7 +128,7 @@ pub fn languages_all() -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
let mut syn_loader_conf = match user_syntax_loader() {
let mut syn_loader_conf = match user_lang_config() {
Ok(conf) => conf,
Err(err) => {
let stderr = std::io::stderr();
@ -141,7 +141,7 @@ pub fn languages_all() -> std::io::Result<()> {
err
)?;
writeln!(stderr, "{}", "Using default language config".yellow())?;
default_syntax_loader()
default_lang_config()
}
};
@ -234,7 +234,7 @@ pub fn language(lang_str: String) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
let syn_loader_conf = match user_syntax_loader() {
let syn_loader_conf = match user_lang_config() {
Ok(conf) => conf,
Err(err) => {
let stderr = std::io::stderr();
@ -247,7 +247,7 @@ pub fn language(lang_str: String) -> std::io::Result<()> {
err
)?;
writeln!(stderr, "{}", "Using default language config".yellow())?;
default_syntax_loader()
default_lang_config()
}
};

View File

@ -22,17 +22,30 @@
pub use keymap::macros::*;
#[cfg(not(windows))]
fn true_color() -> bool {
std::env::var("COLORTERM")
.map(|v| matches!(v.as_str(), "truecolor" | "24bit"))
.unwrap_or(false)
}
#[cfg(windows)]
fn true_color() -> bool {
true
}
#[cfg(not(windows))]
fn true_color() -> bool {
if matches!(
std::env::var("COLORTERM").map(|v| matches!(v.as_str(), "truecolor" | "24bit")),
Ok(true)
) {
return true;
}
match termini::TermInfo::from_env() {
Ok(t) => {
t.extended_cap("RGB").is_some()
|| t.extended_cap("Tc").is_some()
|| (t.extended_cap("setrgbf").is_some() && t.extended_cap("setrgbb").is_some())
}
Err(_) => false,
}
}
/// Function used for filtering dir entries in the various file pickers.
fn filter_picker_entry(entry: &DirEntry, root: &Path, dedup_symlinks: bool) -> bool {
// We always want to ignore the .git directory, otherwise if

View File

@ -145,18 +145,18 @@ async fn main_impl() -> Result<i32> {
}
};
let syn_loader_conf = helix_core::config::user_syntax_loader().unwrap_or_else(|err| {
eprintln!("Bad language config: {}", err);
let lang_loader = helix_core::config::user_lang_loader().unwrap_or_else(|err| {
eprintln!("{}", err);
eprintln!("Press <ENTER> to continue with default language config");
use std::io::Read;
// This waits for an enter press.
let _ = std::io::stdin().read(&mut []);
helix_core::config::default_syntax_loader()
helix_core::config::default_lang_loader()
});
// TODO: use the thread local executor to spawn the application task separately from the work pool
let mut app = Application::new(args, config, syn_loader_conf)
.context("unable to create new application")?;
let mut app =
Application::new(args, config, lang_loader).context("unable to create new application")?;
let exit_code = app.run(&mut EventStream::new()).await?;

View File

@ -315,7 +315,7 @@ async fn test_write_auto_format_fails_still_writes() -> anyhow::Result<()> {
let mut app = helpers::AppBuilder::new()
.with_file(file.path(), None)
.with_input_text("#[l|]#et foo = 0;\n")
.with_lang_config(helpers::test_syntax_conf(Some(lang_conf.into())))
.with_lang_loader(helpers::test_syntax_loader(Some(lang_conf.into())))
.build()?;
test_key_sequences(&mut app, vec![(Some(":w<ret>"), None)], false).await?;

View File

@ -139,7 +139,7 @@ pub async fn test_key_sequence_with_input_text<T: Into<TestCase>>(
let test_case = test_case.into();
let mut app = match app {
Some(app) => app,
None => Application::new(Args::default(), test_config(), test_syntax_conf(None))?,
None => Application::new(Args::default(), test_config(), test_syntax_loader(None))?,
};
let (view, doc) = helix_view::current!(app.editor);
@ -162,9 +162,9 @@ pub async fn test_key_sequence_with_input_text<T: Into<TestCase>>(
.await
}
/// Generates language configs that merge in overrides, like a user language
/// Generates language config loader that merge in overrides, like a user language
/// config. The argument string must be a raw TOML document.
pub fn test_syntax_conf(overrides: Option<String>) -> helix_core::syntax::Configuration {
pub fn test_syntax_loader(overrides: Option<String>) -> helix_core::syntax::Loader {
let mut lang = helix_loader::config::default_lang_config();
if let Some(overrides) = overrides {
@ -172,7 +172,7 @@ pub fn test_syntax_conf(overrides: Option<String>) -> helix_core::syntax::Config
lang = helix_loader::merge_toml_values(lang, override_toml, 3);
}
lang.try_into().unwrap()
helix_core::syntax::Loader::new(lang.try_into().unwrap()).unwrap()
}
/// Use this for very simple test cases where there is one input
@ -271,7 +271,7 @@ pub fn new_readonly_tempfile() -> anyhow::Result<NamedTempFile> {
pub struct AppBuilder {
args: Args,
config: Config,
syn_conf: helix_core::syntax::Configuration,
syn_loader: helix_core::syntax::Loader,
input: Option<(String, Selection)>,
}
@ -280,7 +280,7 @@ fn default() -> Self {
Self {
args: Args::default(),
config: test_config(),
syn_conf: test_syntax_conf(None),
syn_loader: test_syntax_loader(None),
input: None,
}
}
@ -314,8 +314,8 @@ pub fn with_input_text<S: Into<String>>(mut self, input_text: S) -> Self {
self
}
pub fn with_lang_config(mut self, syn_conf: helix_core::syntax::Configuration) -> Self {
self.syn_conf = syn_conf;
pub fn with_lang_loader(mut self, syn_loader: helix_core::syntax::Loader) -> Self {
self.syn_loader = syn_loader;
self
}
@ -328,7 +328,7 @@ pub fn build(self) -> anyhow::Result<Application> {
bail!("Having the directory {path:?} in args.files[0] is not yet supported for integration tests");
}
let mut app = Application::new(self.args, self.config, self.syn_conf)?;
let mut app = Application::new(self.args, self.config, self.syn_loader)?;
if let Some((text, selection)) = self.input {
let (view, doc) = helix_view::current!(app.editor);

View File

@ -29,4 +29,4 @@ log = "0.4"
git = ["gix"]
[dev-dependencies]
tempfile = "3.9"
tempfile = "3.10"

View File

@ -253,7 +253,7 @@ source = { git = "https://github.com/FuelLabs/tree-sitter-sway", rev = "e491a005
name = "toml"
scope = "source.toml"
injection-regex = "toml"
file-types = ["toml", "poetry.lock", "Cargo.lock"]
file-types = ["toml", { glob = "poetry.lock" }, { glob = "Cargo.lock" }]
comment-token = "#"
language-servers = [ "taplo" ]
indent = { tab-width = 2, unit = " " }
@ -292,7 +292,7 @@ source = { git = "https://github.com/yusdacra/tree-sitter-protobuf", rev = "19c2
name = "elixir"
scope = "source.elixir"
injection-regex = "(elixir|ex)"
file-types = ["ex", "exs", "mix.lock"]
file-types = ["ex", "exs", { glob = "mix.lock" }]
shebangs = ["elixir"]
roots = ["mix.exs", "mix.lock"]
comment-token = "#"
@ -361,20 +361,20 @@ file-types = [
"geojson",
"gltf",
"webmanifest",
"flake.lock",
".babelrc",
".bowerrc",
".jscrc",
{ glob = "flake.lock" },
{ glob = ".babelrc" },
{ glob = ".bowerrc" },
{ glob = ".jscrc" },
"js.map",
"ts.map",
"css.map",
".jslintrc",
{ glob = ".jslintrc" },
"jsonld",
".vuerc",
"composer.lock",
".watchmanconfig",
{ glob = ".vuerc" },
{ glob = "composer.lock" },
{ glob = ".watchmanconfig" },
"avsc",
".prettierrc"
{ glob = ".prettierrc" },
]
language-servers = [ "vscode-json-language-server" ]
auto-format = true
@ -439,7 +439,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-c", rev = "7175a6dd
name = "cpp"
scope = "source.cpp"
injection-regex = "cpp"
file-types = ["cc", "hh", "c++", "cpp", "hpp", "h", "ipp", "tpp", "cxx", "hxx", "ixx", "txx", "ino", "C", "H", "cu", "cuh", "cppm", "h++", "ii", "inl", { suffix = ".hpp.in" }, { suffix = ".h.in" }]
file-types = ["cc", "hh", "c++", "cpp", "hpp", "h", "ipp", "tpp", "cxx", "hxx", "ixx", "txx", "ino", "C", "H", "cu", "cuh", "cppm", "h++", "ii", "inl", { glob = ".hpp.in" }, { glob = ".h.in" }]
comment-token = "//"
language-servers = [ "clangd" ]
indent = { tab-width = 2, unit = " " }
@ -514,6 +514,30 @@ args = { processId = "{0}" }
name = "c-sharp"
source = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", rev = "5b60f99545fea00a33bbfae5be956f684c4c69e2" }
[[language]]
name = "cel"
scope = "source.cel"
injection-regex = "cel"
file-types = ["cel"]
comment-token = "//"
indent = { tab-width = 2, unit = " " }
[[grammar]]
name = "cel"
source = { git = "https://github.com/bufbuild/tree-sitter-cel", rev = "9f2b65da14c216df53933748e489db0f11121464" }
[[language]]
name = "spicedb"
scope = "source.zed"
injection-regex = "spicedb"
file-types = ["zed"]
comment-token = "//"
indent = { tab-width = 2, unit = " " }
[[grammar]]
name = "spicedb"
source = { git = "https://github.com/jzelinskie/tree-sitter-spicedb", rev = "a4e4645651f86d6684c15dfa9931b7841dc52a66" }
[[language]]
name = "go"
scope = "source.go"
@ -571,7 +595,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "64457ea
name = "gomod"
scope = "source.gomod"
injection-regex = "gomod"
file-types = ["go.mod"]
file-types = [{ glob = "go.mod" }]
auto-format = true
comment-token = "//"
language-servers = [ "gopls" ]
@ -598,7 +622,7 @@ source = { git = "https://github.com/dannylongeuay/tree-sitter-go-template", rev
name = "gowork"
scope = "source.gowork"
injection-regex = "gowork"
file-types = ["go.work"]
file-types = [{ glob = "go.work" }]
auto-format = true
comment-token = "//"
language-servers = [ "gopls" ]
@ -613,7 +637,7 @@ name = "javascript"
scope = "source.js"
injection-regex = "(js|javascript)"
language-id = "javascript"
file-types = ["js", "mjs", "cjs", "rules", "es6", "pac", "jakefile"]
file-types = ["js", "mjs", "cjs", "rules", "es6", "pac", { glob = "jakefile" }]
shebangs = ["node"]
comment-token = "//"
language-servers = [ "typescript-language-server" ]
@ -716,7 +740,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-html", rev = "29f53
name = "python"
scope = "source.python"
injection-regex = "python"
file-types = ["py","pyi","py3","pyw","ptl",".pythonstartup",".pythonrc","SConstruct", "rpy", "cpy", "ipy", "pyt", "SConscript"]
file-types = ["py", "pyi", "py3", "pyw", "ptl", "rpy", "cpy", "ipy", "pyt", { glob = ".pythonstartup" }, { glob = ".pythonrc" }, { glob = "SConstruct" }, { glob = "SConscript" }]
shebangs = ["python"]
roots = ["pyproject.toml", "setup.py", "poetry.lock", "pyrightconfig.json"]
comment-token = "#"
@ -769,38 +793,38 @@ injection-regex = "ruby"
file-types = [
"rb",
"rake",
"rakefile",
"irb",
"gemfile",
"gemspec",
"Rakefile",
"Gemfile",
"rabl",
"jbuilder",
"jb",
"Podfile",
"podspec",
"Vagrantfile",
"Brewfile",
"rjs",
"rbi",
"Guardfile",
"Capfile",
"Cheffile",
"Hobofile",
"Appraisals",
"Rantfile",
"Berksfile",
"Berksfile.lock",
"Thorfile",
"Puppetfile",
"Fastfile",
"Appfile",
"Deliverfile",
"Matchfile",
"Scanfile",
"Snapfile",
"Gymfile"
{ glob = "rakefile" },
{ glob = "gemfile" },
{ glob = "Rakefile" },
{ glob = "Gemfile" },
{ glob = "Podfile" },
{ glob = "Vagrantfile" },
{ glob = "Brewfile" },
{ glob = "Guardfile" },
{ glob = "Capfile" },
{ glob = "Cheffile" },
{ glob = "Hobofile" },
{ glob = "Appraisals" },
{ glob = "Rantfile" },
{ glob = "Berksfile" },
{ glob = "Berksfile.lock" },
{ glob = "Thorfile" },
{ glob = "Puppetfile" },
{ glob = "Fastfile" },
{ glob = "Appfile" },
{ glob = "Deliverfile" },
{ glob = "Matchfile" },
{ glob = "Scanfile" },
{ glob = "Snapfile" },
{ glob = "Gymfile" },
]
shebangs = ["ruby"]
comment-token = "#"
@ -819,43 +843,43 @@ file-types = [
"sh",
"bash",
"zsh",
".bash_history",
".bash_login",
".bash_logout",
".bash_profile",
".bashrc",
".profile",
".zshenv",
"zshenv",
".zlogin",
"zlogin",
".zlogout",
"zlogout",
".zprofile",
"zprofile",
".zshrc",
"zshrc",
".zimrc",
"APKBUILD",
"PKGBUILD",
"eclass",
"ebuild",
"bazelrc",
".bash_aliases",
"Renviron",
".Renviron",
".xprofile",
".xsession",
".xsessionrc",
"zsh-theme",
"ksh",
"cshrc",
"tcshrc",
".yashrc",
".yash_profile",
".hushlogin",
"bashrc_Apple_Terminal",
"zshrc_Apple_Terminal"
"zshrc_Apple_Terminal",
{ glob = ".bash_history" },
{ glob = ".bash_login" },
{ glob = ".bash_logout" },
{ glob = ".bash_profile" },
{ glob = ".bashrc" },
{ glob = ".profile" },
{ glob = ".zshenv" },
{ glob = ".zlogin" },
{ glob = ".zlogout" },
{ glob = ".zprofile" },
{ glob = ".zshrc" },
{ glob = ".zimrc" },
{ glob = "APKBUILD" },
{ glob = "PKGBUILD" },
{ glob = ".bash_aliases" },
{ glob = ".Renviron" },
{ glob = ".xprofile" },
{ glob = ".xsession" },
{ glob = ".xsessionrc" },
{ glob = ".yashrc" },
{ glob = ".yash_profile" },
{ glob = ".hushlogin" },
]
shebangs = ["sh", "bash", "dash", "zsh"]
comment-token = "#"
@ -1216,7 +1240,7 @@ source = { git = "https://github.com/the-mikedavis/tree-sitter-tsq", rev = "48b5
[[language]]
name = "cmake"
scope = "source.cmake"
file-types = ["cmake", "CMakeLists.txt"]
file-types = ["cmake", { glob = "CMakeLists.txt" }]
comment-token = "#"
indent = { tab-width = 2, unit = " " }
language-servers = [ "cmake-language-server" ]
@ -1229,7 +1253,7 @@ source = { git = "https://github.com/uyha/tree-sitter-cmake", rev = "6e51463ef30
[[language]]
name = "make"
scope = "source.make"
file-types = ["Makefile", "makefile", "make", "mk", "mak", "GNUmakefile", "OCamlMakefile"]
file-types = [{ glob = "Makefile" }, { glob = "makefile" }, "make", "mk", "mak", {glob = "GNUmakefile" }, { glob = "OCamlMakefile" }]
shebangs = ["make", "gmake"]
injection-regex = "(make|makefile|Makefile|mk)"
comment-token = "#"
@ -1372,7 +1396,7 @@ source = { git = "https://github.com/Flakebi/tree-sitter-tablegen", rev = "568dd
name = "markdown"
scope = "source.md"
injection-regex = "md|markdown"
file-types = ["md", "markdown", "PULLREQ_EDITMSG", "mkd", "mdwn", "mdown", "markdn", "mdtxt", "mdtext", "workbook"]
file-types = ["md", "markdown", "mkd", "mdwn", "mdown", "markdn", "mdtxt", "mdtext", "workbook", { glob = "PULLREQ_EDITMSG" }]
roots = [".marksman.toml"]
language-servers = [ "marksman" ]
indent = { tab-width = 2, unit = " " }
@ -1424,7 +1448,7 @@ name = "dockerfile"
scope = "source.dockerfile"
injection-regex = "docker|dockerfile"
roots = ["Dockerfile", "Containerfile"]
file-types = ["Dockerfile", "dockerfile", "Containerfile", "containerfile"]
file-types = [{ glob = "Dockerfile*" }, { glob = "dockerfile*" }, { glob = "Containerfile*" }, { glob = "containerfile*" }]
comment-token = "#"
indent = { tab-width = 2, unit = " " }
language-servers = [ "docker-langserver" ]
@ -1436,7 +1460,7 @@ source = { git = "https://github.com/camdencheek/tree-sitter-dockerfile", rev =
[[language]]
name = "git-commit"
scope = "git.commitmsg"
file-types = ["COMMIT_EDITMSG"]
file-types = [{ glob = "COMMIT_EDITMSG" }]
comment-token = "#"
indent = { tab-width = 2, unit = " " }
rulers = [51, 73]
@ -1461,7 +1485,7 @@ source = { git = "https://github.com/the-mikedavis/tree-sitter-diff", rev = "fd7
[[language]]
name = "git-rebase"
scope = "source.gitrebase"
file-types = ["git-rebase-todo"]
file-types = [{ glob = "git-rebase-todo" }]
injection-regex = "git-rebase"
comment-token = "#"
indent = { tab-width = 2, unit = "y" }
@ -1474,7 +1498,7 @@ source = { git = "https://github.com/the-mikedavis/tree-sitter-git-rebase", rev
name = "regex"
scope = "source.regex"
injection-regex = "regex"
file-types = ["regex", ".Rbuildignore"]
file-types = ["regex", { glob = ".Rbuildignore" }]
[[grammar]]
name = "regex"
@ -1483,7 +1507,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-regex", rev = "e1cf
[[language]]
name = "git-config"
scope = "source.gitconfig"
file-types = [".gitmodules", ".gitconfig", { suffix = ".git/config" }, { suffix = ".config/git/config" }]
file-types = [{ glob = ".gitmodules" }, { glob = ".gitconfig" }, { glob = ".git/config" }, { glob = ".config/git/config" }]
injection-regex = "git-config"
comment-token = "#"
indent = { tab-width = 4, unit = "\t" }
@ -1495,7 +1519,7 @@ source = { git = "https://github.com/the-mikedavis/tree-sitter-git-config", rev
[[language]]
name = "git-attributes"
scope = "source.gitattributes"
file-types = [".gitattributes"]
file-types = [{ glob = ".gitattributes" }]
injection-regex = "git-attributes"
comment-token = "#"
grammar = "gitattributes"
@ -1507,7 +1531,7 @@ source = { git = "https://github.com/mtoohey31/tree-sitter-gitattributes", rev =
[[language]]
name = "git-ignore"
scope = "source.gitignore"
file-types = [".gitignore", ".gitignore_global", ".ignore", ".prettierignore", ".eslintignore", ".npmignore", "CODEOWNERS", { suffix = ".config/helix/ignore" }, { suffix = ".helix/ignore" }]
file-types = [{ glob = ".gitignore" }, { glob = ".gitignore_global" }, { glob = ".ignore" }, { glob = ".prettierignore" }, { glob = ".eslintignore" }, { glob = ".npmignore"}, { glob = "CODEOWNERS" }, { glob = ".config/helix/ignore" }, { glob = ".helix/ignore" }]
injection-regex = "git-ignore"
comment-token = "#"
grammar = "gitignore"
@ -1572,7 +1596,7 @@ source = { git = "https://github.com/jaredramirez/tree-sitter-rescript", rev = "
name = "erlang"
scope = "source.erlang"
injection-regex = "erl(ang)?"
file-types = ["erl", "hrl", "app", "rebar.config", "rebar.lock"]
file-types = ["erl", "hrl", "app", { glob = "rebar.config" }, { glob = "rebar.lock" }]
roots = ["rebar.config"]
shebangs = ["escript"]
comment-token = "%%"
@ -1698,7 +1722,7 @@ source = { git = "https://github.com/Hubro/tree-sitter-robot", rev = "322e4cc657
name = "r"
scope = "source.r"
injection-regex = "(r|R)"
file-types = ["r", "R", ".Rprofile", "Rprofile.site", ".RHistory"]
file-types = ["r", "R", { glob = ".Rprofile" }, { glob = "Rprofile.site" }, { glob = ".RHistory" }]
shebangs = ["r", "R"]
comment-token = "#"
indent = { tab-width = 2, unit = " " }
@ -1730,7 +1754,7 @@ language-servers = [ "sourcekit-lsp" ]
[[grammar]]
name = "swift"
source = { git = "https://github.com/alex-pinkus/tree-sitter-swift", rev = "77c6312c8438f4dbaa0350cec92b3d6dd3d74a66" }
source = { git = "https://github.com/alex-pinkus/tree-sitter-swift", rev = "b1b66955d420d5cf5ff268ae552f0d6e43ff66e1" }
[[language]]
name = "erb"
@ -1913,7 +1937,7 @@ source = { git = "https://github.com/ap29600/tree-sitter-odin", rev = "b219207e4
name = "meson"
scope = "source.meson"
injection-regex = "meson"
file-types = ["meson.build", "meson_options.txt"]
file-types = [{ glob = "meson.build" }, { glob = "meson_options.txt" }]
comment-token = "#"
indent = { tab-width = 2, unit = " " }
@ -1924,7 +1948,7 @@ source = { git = "https://github.com/staysail/tree-sitter-meson", rev = "32a83e8
[[language]]
name = "sshclientconfig"
scope = "source.sshclientconfig"
file-types = [{ suffix = ".ssh/config" }, { suffix = "/etc/ssh/ssh_config" }]
file-types = [{ glob = ".ssh/config" }, { glob = "/etc/ssh/ssh_config" }]
comment-token = "#"
[[grammar]]
@ -2045,7 +2069,7 @@ source = { git = "https://github.com/sogaiu/tree-sitter-clojure", rev = "e57c569
name = "starlark"
scope = "source.starlark"
injection-regex = "(starlark|bzl|bazel)"
file-types = ["bzl", "bazel", "BUILD", "star"]
file-types = ["bzl", "bazel", "star", { glob = "BUILD" }, { glob = "BUILD.*" }]
comment-token = "#"
indent = { tab-width = 4, unit = " " }
grammar = "python"
@ -2413,7 +2437,7 @@ source = { git = "https://github.com/hh9527/tree-sitter-wit", rev = "c917790ab9a
[[language]]
name = "env"
scope = "source.env"
file-types = [".env", ".env.local", ".env.development", ".env.production", ".env.dist", ".envrc", ".envrc.local", ".envrc.private"]
file-types = [{ glob = ".env" }, { glob = ".env.*" }, { glob = ".envrc" }, { glob = ".envrc.*" }]
injection-regex = "env"
comment-token = "#"
indent = { tab-width = 4, unit = "\t" }
@ -2441,7 +2465,7 @@ file-types = [
"volume",
"kube",
"network",
".editorconfig",
{ glob = ".editorconfig" },
"properties",
"cfg",
"directory"
@ -2569,7 +2593,7 @@ source = { git = "https://github.com/mtoohey31/tree-sitter-pem", rev = "be67a433
[[language]]
name = "passwd"
scope = "source.passwd"
file-types = ["passwd"]
file-types = [{ glob = "passwd" }]
[[grammar]]
name = "passwd"
@ -2578,7 +2602,7 @@ source = { git = "https://github.com/ath3/tree-sitter-passwd", rev = "20239395ea
[[language]]
name = "hosts"
scope = "source.hosts"
file-types = ["hosts"]
file-types = [{ glob = "hosts" }]
comment-token = "#"
[[grammar]]
@ -2786,7 +2810,7 @@ source = { git = "https://github.com/lefp/tree-sitter-opencl", rev = "8e1d24a570
[[language]]
name = "just"
scope = "source.just"
file-types = ["justfile", "Justfile", ".justfile", ".Justfile"]
file-types = [{ glob = "justfile" }, { glob = "Justfile" }, { glob = ".justfile" }, { glob = ".Justfile" }]
injection-regex = "just"
comment-token = "#"
indent = { tab-width = 4, unit = "\t" }
@ -2945,7 +2969,7 @@ source = { git = "https://github.com/kylegoetz/tree-sitter-unison", rev = "1f505
[[language]]
name = "todotxt"
scope = "text.todotxt"
file-types = [{ suffix = ".todo.txt" }, "todotxt"]
file-types = [{ glob = "todo.txt" }, { glob = "*.todo.txt" }, "todotxt"]
formatter = { command = "sort" }
auto-format = true

View File

@ -0,0 +1,66 @@
; Operators
[
"-"
"!"
"*"
"/"
"&&"
"%"
"+"
"<"
"<="
"=="
">"
">="
"||"
] @operator
; Keywords
[
"in"
] @keyword
; Function calls
(call_expression
function: (identifier) @function)
(member_call_expression
function: (identifier) @function)
; Identifiers
(select_expression
operand: (identifier) @type)
(select_expression
operand: (select_expression
member: (identifier) @type))
(identifier) @variable.other.member
; Literals
[
(double_quote_string_literal)
(single_quoted_string_literal)
(triple_double_quote_string_literal)
(triple_single_quoted_string_literal)
] @string
[
(int_literal)
(uint_literal)
] @constant.numeric.integer
(float_literal) @constant.numeric.float
[
(true)
(false)
] @constant.builtin.boolean
(null) @constant.builtin
(comment) @comment

View File

@ -0,0 +1,47 @@
; highlights.scm
[
"definition"
"caveat"
"permission"
"relation"
"nil"
] @keyword
[
","
":"
] @punctuation.delimiter
[
"("
")"
"{"
"}"
] @punctuation.bracket
[
"|"
"+"
"-"
"&"
"#"
"->"
"="
] @operator
("with") @keyword.operator
[
"nil"
"*"
] @constant.builtin
(comment) @comment
(type_identifier) @type
(cel_type_identifier) @type
(cel_variable_identifier) @variable.parameter
(field_identifier) @variable.other.member
[
(func_identifier)
(method_identifier)
] @function.method

View File

@ -0,0 +1,5 @@
((comment) @injection.content
(#set! injection.language "comment"))
((caveat_expr) @injection.content
(#set! injection.language "cel"))

View File

@ -0,0 +1,4 @@
(object_definition
name: (type_identifier) @name) @definition.type
(type_identifier) @name @reference.type

View File

@ -1,10 +1,10 @@
; Upstream: https://github.com/alex-pinkus/tree-sitter-swift/blob/8d2fd80e3322df51e3f70952e60d57f5d4077eb8/queries/highlights.scm
; Upstream: https://github.com/alex-pinkus/tree-sitter-swift/blob/1c586339fb00014b23d6933f2cc32b588a226f3b/queries/highlights.scm
(line_string_literal
["\\(" ")"] @punctuation.special)
["." ";" ":" "," ] @punctuation.delimiter
["(" ")" "[" "]" "{" "}"] @punctuation.bracket ; TODO: "\\(" ")" in interpolations should be @punctuation.special
["(" ")" "[" "]" "{" "}"] @punctuation.bracket
; Identifiers
(attribute) @variable
@ -26,6 +26,7 @@
(function_declaration "init" @constructor)
(throws) @keyword
"async" @keyword
"await" @keyword
(where_keyword) @keyword
(parameter external_name: (simple_identifier) @variable.parameter)
(parameter name: (simple_identifier) @variable.parameter)
@ -48,6 +49,7 @@
"convenience"
"required"
"some"
"any"
] @keyword
[

View File

@ -63,6 +63,9 @@
"ui.cursorline.primary" = { bg = "bg1" }
"ui.statusline" = { fg = "fg", bg = "bg3" }
"ui.statusline.inactive" = { fg = "grey", bg = "bg1" }
"ui.statusline.normal" = { fg = "bg0", bg = "blue", modifiers = ["bold"] }
"ui.statusline.insert" = { fg = "bg0", bg = "green", modifiers = ["bold"] }
"ui.statusline.select" = { fg = "bg0", bg = "purple", modifiers = ["bold"] }
"ui.popup" = { fg = "grey", bg = "bg2" }
"ui.window" = { fg = "grey", bg = "bg0" }
"ui.help" = { fg = "fg", bg = "bg1" }
@ -71,7 +74,7 @@
"ui.menu" = { fg = "fg", bg = "bg2" }
"ui.menu.selected" = { fg = "bg0", bg = "green" }
"ui.virtual.whitespace" = { fg = "grey_dim" }
"ui.virtual.ruler" = { bg = "grey_dim" }
"ui.virtual.ruler" = { bg = "bg3" }
"ui.virtual.inlay-hint" = { fg = "grey_dim" }
info = { fg = 'green', bg = 'bg2' }