Compare commits

...

4 Commits

Author SHA1 Message Date
Michael Davis
13b76ea797 Add a function for getting language config by injection layer 2024-01-11 14:34:12 -05:00
Michael Davis
3014a2ae9b Add language ID in HighlightConfiguration 2024-01-11 14:34:10 -05:00
Michael Davis
10b9c38ed9 Track languages by ID in the Loader 2024-01-11 14:25:32 -05:00
Michael Davis
4e1aeb1b99 Rename LanguageConfig language_id as language_name 2024-01-11 14:20:28 -05:00
6 changed files with 68 additions and 45 deletions

View File

@@ -11,7 +11,7 @@ use ahash::RandomState;
use arc_swap::{ArcSwap, Guard}; use arc_swap::{ArcSwap, Guard};
use bitflags::bitflags; use bitflags::bitflags;
use hashbrown::raw::RawTable; use hashbrown::raw::RawTable;
use slotmap::{DefaultKey as LayerId, HopSlotMap}; use slotmap::{DefaultKey as LayerId, DefaultKey as LanguageId, HopSlotMap};
use std::{ use std::{
borrow::Cow, borrow::Cow,
@@ -92,8 +92,10 @@ impl Default for Configuration {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)] #[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct LanguageConfiguration { pub struct LanguageConfiguration {
#[serde(skip)]
language_id: LanguageId,
#[serde(rename = "name")] #[serde(rename = "name")]
pub language_id: String, // c-sharp, rust, tsx pub language_name: String, // c-sharp, rust, tsx
#[serde(rename = "language-id")] #[serde(rename = "language-id")]
// see the table under https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem // see the table under https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem
pub language_server_language_id: Option<String>, // csharp, rust, typescriptreact, for the language-server pub language_server_language_id: Option<String>, // csharp, rust, typescriptreact, for the language-server
@@ -636,32 +638,33 @@ pub fn read_query(language: &str, filename: &str) -> String {
impl LanguageConfiguration { impl LanguageConfiguration {
fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> { fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
let highlights_query = read_query(&self.language_id, "highlights.scm"); let highlights_query = read_query(&self.language_name, "highlights.scm");
// always highlight syntax errors // always highlight syntax errors
// highlights_query += "\n(ERROR) @error"; // highlights_query += "\n(ERROR) @error";
let injections_query = read_query(&self.language_id, "injections.scm"); let injections_query = read_query(&self.language_name, "injections.scm");
let locals_query = read_query(&self.language_id, "locals.scm"); let locals_query = read_query(&self.language_name, "locals.scm");
if highlights_query.is_empty() { if highlights_query.is_empty() {
None None
} else { } else {
let language = get_language(self.grammar.as_deref().unwrap_or(&self.language_id)) let language = get_language(self.grammar.as_deref().unwrap_or(&self.language_name))
.map_err(|err| { .map_err(|err| {
log::error!( log::error!(
"Failed to load tree-sitter parser for language {:?}: {}", "Failed to load tree-sitter parser for language {:?}: {}",
self.language_id, self.language_name,
err err
) )
}) })
.ok()?; .ok()?;
let config = HighlightConfiguration::new( let config = HighlightConfiguration::new(
self.language_id,
language, language,
&highlights_query, &highlights_query,
&injections_query, &injections_query,
&locals_query, &locals_query,
) )
.map_err(|err| log::error!("Could not parse queries for language {:?}. Are your grammars out of sync? Try running 'hx --grammar fetch' and 'hx --grammar build'. This query could not be parsed: {:?}", self.language_id, err)) .map_err(|err| log::error!("Could not parse queries for language {:?}. Are your grammars out of sync? Try running 'hx --grammar fetch' and 'hx --grammar build'. This query could not be parsed: {:?}", self.language_name, err))
.ok()?; .ok()?;
config.configure(scopes); config.configure(scopes);
@@ -705,7 +708,7 @@ impl LanguageConfiguration {
} }
fn load_query(&self, kind: &str) -> Option<Query> { fn load_query(&self, kind: &str) -> Option<Query> {
let query_text = read_query(&self.language_id, kind); let query_text = read_query(&self.language_name, kind);
if query_text.is_empty() { if query_text.is_empty() {
return None; return None;
} }
@@ -715,7 +718,7 @@ impl LanguageConfiguration {
log::error!( log::error!(
"Failed to parse {} queries for {}: {}", "Failed to parse {} queries for {}: {}",
kind, kind,
self.language_id, self.language_name,
e e
) )
}) })
@@ -757,10 +760,10 @@ pub struct SoftWrap {
#[derive(Debug)] #[derive(Debug)]
pub struct Loader { pub struct Loader {
// highlight_names ? // highlight_names ?
language_configs: Vec<Arc<LanguageConfiguration>>, language_configs: HopSlotMap<LanguageId, Arc<LanguageConfiguration>>,
language_config_ids_by_extension: HashMap<String, usize>, // Vec<usize> language_config_ids_by_extension: HashMap<String, LanguageId>, // Vec<LanguageId>
language_config_ids_by_suffix: HashMap<String, usize>, language_config_ids_by_suffix: HashMap<String, LanguageId>,
language_config_ids_by_shebang: HashMap<String, usize>, language_config_ids_by_shebang: HashMap<String, LanguageId>,
language_server_configs: HashMap<String, LanguageServerConfiguration>, language_server_configs: HashMap<String, LanguageServerConfiguration>,
@@ -770,7 +773,7 @@ pub struct Loader {
impl Loader { impl Loader {
pub fn new(config: Configuration) -> Self { pub fn new(config: Configuration) -> Self {
let mut loader = Self { let mut loader = Self {
language_configs: Vec::new(), language_configs: HopSlotMap::new(),
language_server_configs: config.language_server, language_server_configs: config.language_server,
language_config_ids_by_extension: HashMap::new(), language_config_ids_by_extension: HashMap::new(),
language_config_ids_by_suffix: HashMap::new(), language_config_ids_by_suffix: HashMap::new(),
@@ -778,9 +781,12 @@ impl Loader {
scopes: ArcSwap::from_pointee(Vec::new()), scopes: ArcSwap::from_pointee(Vec::new()),
}; };
for config in config.language { for mut config in config.language {
// get the next id let language_id = loader.language_configs.insert_with_key(|key| {
let language_id = loader.language_configs.len(); config.language_id = key;
Arc::new(config)
});
let config = &loader.language_configs[language_id];
for file_type in &config.file_types { for file_type in &config.file_types {
// entry().or_insert(Vec::new).push(language_id); // entry().or_insert(Vec::new).push(language_id);
@@ -798,8 +804,6 @@ impl Loader {
.language_config_ids_by_shebang .language_config_ids_by_shebang
.insert(shebang.clone(), language_id); .insert(shebang.clone(), language_id);
} }
loader.language_configs.push(Arc::new(config));
} }
loader loader
@@ -850,15 +854,18 @@ impl Loader {
pub fn language_config_for_scope(&self, scope: &str) -> Option<Arc<LanguageConfiguration>> { pub fn language_config_for_scope(&self, scope: &str) -> Option<Arc<LanguageConfiguration>> {
self.language_configs self.language_configs
.iter() .values()
.find(|config| config.scope == scope) .find(|config| config.scope == scope)
.cloned() .cloned()
} }
pub fn language_config_for_language_id(&self, id: &str) -> Option<Arc<LanguageConfiguration>> { pub fn language_config_for_language_name(
&self,
name: &str,
) -> Option<Arc<LanguageConfiguration>> {
self.language_configs self.language_configs
.iter() .values()
.find(|config| config.language_id == id) .find(|config| config.language_name == name)
.cloned() .cloned()
} }
@@ -867,19 +874,19 @@ impl Loader {
pub fn language_config_for_name(&self, name: &str) -> Option<Arc<LanguageConfiguration>> { pub fn language_config_for_name(&self, name: &str) -> Option<Arc<LanguageConfiguration>> {
let mut best_match_length = 0; let mut best_match_length = 0;
let mut best_match_position = None; let mut best_match_position = None;
for (i, configuration) in self.language_configs.iter().enumerate() { for (id, configuration) in self.language_configs.iter() {
if let Some(injection_regex) = &configuration.injection_regex { if let Some(injection_regex) = &configuration.injection_regex {
if let Some(mat) = injection_regex.find(name) { if let Some(mat) = injection_regex.find(name) {
let length = mat.end() - mat.start(); let length = mat.end() - mat.start();
if length > best_match_length { if length > best_match_length {
best_match_position = Some(i); best_match_position = Some(id);
best_match_length = length; best_match_length = length;
} }
} }
} }
} }
best_match_position.map(|i| self.language_configs[i].clone()) best_match_position.map(|id| self.language_configs[id].clone())
} }
pub fn language_configuration_for_injection_string( pub fn language_configuration_for_injection_string(
@@ -890,13 +897,13 @@ impl Loader {
InjectionLanguageMarker::Name(string) => self.language_config_for_name(string), InjectionLanguageMarker::Name(string) => self.language_config_for_name(string),
InjectionLanguageMarker::Filename(file) => self.language_config_for_file_name(file), InjectionLanguageMarker::Filename(file) => self.language_config_for_file_name(file),
InjectionLanguageMarker::Shebang(shebang) => { InjectionLanguageMarker::Shebang(shebang) => {
self.language_config_for_language_id(shebang) self.language_config_for_language_name(shebang)
} }
} }
} }
pub fn language_configs(&self) -> impl Iterator<Item = &Arc<LanguageConfiguration>> { pub fn language_configs(&self) -> impl Iterator<Item = &Arc<LanguageConfiguration>> {
self.language_configs.iter() self.language_configs.values()
} }
pub fn language_server_configs(&self) -> &HashMap<String, LanguageServerConfiguration> { pub fn language_server_configs(&self) -> &HashMap<String, LanguageServerConfiguration> {
@@ -909,7 +916,7 @@ impl Loader {
// Reconfigure existing grammars // Reconfigure existing grammars
for config in self for config in self
.language_configs .language_configs
.iter() .values()
.filter(|cfg| cfg.is_highlight_initialized()) .filter(|cfg| cfg.is_highlight_initialized())
{ {
config.reconfigure(&self.scopes()); config.reconfigure(&self.scopes());
@@ -1338,6 +1345,13 @@ impl Syntax {
result result
} }
/// Gets the [LanguageConfiguration] for a given injection layer.
pub fn layer_config(&self, layer_id: LayerId) -> &Arc<LanguageConfiguration> {
let language_id = self.layers[layer_id].config.language_id;
&self.loader.language_configs[language_id]
}
// Commenting // Commenting
// comment_strings_for_pos // comment_strings_for_pos
// is_commented // is_commented
@@ -1576,6 +1590,7 @@ pub enum HighlightEvent {
/// This struct is immutable and can be shared between threads. /// This struct is immutable and can be shared between threads.
#[derive(Debug)] #[derive(Debug)]
pub struct HighlightConfiguration { pub struct HighlightConfiguration {
language_id: LanguageId,
pub language: Grammar, pub language: Grammar,
pub query: Query, pub query: Query,
injections_query: Query, injections_query: Query,
@@ -1673,6 +1688,7 @@ impl HighlightConfiguration {
/// ///
/// Returns a `HighlightConfiguration` that can then be used with the `highlight` method. /// Returns a `HighlightConfiguration` that can then be used with the `highlight` method.
pub fn new( pub fn new(
language_id: LanguageId,
language: Grammar, language: Grammar,
highlights_query: &str, highlights_query: &str,
injection_query: &str, injection_query: &str,
@@ -1749,6 +1765,7 @@ impl HighlightConfiguration {
let highlight_indices = ArcSwap::from_pointee(vec![None; query.capture_names().len()]); let highlight_indices = ArcSwap::from_pointee(vec![None; query.capture_names().len()]);
Ok(Self { Ok(Self {
language_id,
language, language,
query, query,
injections_query, injections_query,
@@ -2546,7 +2563,8 @@ mod test {
let textobject = TextObjectQuery { query }; let textobject = TextObjectQuery { query };
let mut cursor = QueryCursor::new(); let mut cursor = QueryCursor::new();
let config = HighlightConfiguration::new(language, "", "", "").unwrap(); let config =
HighlightConfiguration::new(LanguageId::default(), language, "", "", "").unwrap();
let syntax = Syntax::new(source.slice(..), Arc::new(config), Arc::new(loader)).unwrap(); let syntax = Syntax::new(source.slice(..), Arc::new(config), Arc::new(loader)).unwrap();
let root = syntax.tree().root_node(); let root = syntax.tree().root_node();
@@ -2605,6 +2623,7 @@ mod test {
let language = get_language("rust").unwrap(); let language = get_language("rust").unwrap();
let config = HighlightConfiguration::new( let config = HighlightConfiguration::new(
LanguageId::default(),
language, language,
&std::fs::read_to_string("../runtime/grammars/sources/rust/queries/highlights.scm") &std::fs::read_to_string("../runtime/grammars/sources/rust/queries/highlights.scm")
.unwrap(), .unwrap(),
@@ -2710,7 +2729,8 @@ mod test {
}); });
let language = get_language(language_name).unwrap(); let language = get_language(language_name).unwrap();
let config = HighlightConfiguration::new(language, "", "", "").unwrap(); let config =
HighlightConfiguration::new(LanguageId::default(), language, "", "", "").unwrap();
let syntax = Syntax::new(source.slice(..), Arc::new(config), Arc::new(loader)).unwrap(); let syntax = Syntax::new(source.slice(..), Arc::new(config), Arc::new(loader)).unwrap();
let root = syntax let root = syntax

View File

@@ -179,7 +179,7 @@ pub fn languages_all() -> std::io::Result<()> {
syn_loader_conf syn_loader_conf
.language .language
.sort_unstable_by_key(|l| l.language_id.clone()); .sort_unstable_by_key(|l| l.language_name.clone());
let check_binary = |cmd: Option<&str>| match cmd { let check_binary = |cmd: Option<&str>| match cmd {
Some(cmd) => match which::which(cmd) { Some(cmd) => match which::which(cmd) {
@@ -190,7 +190,7 @@ pub fn languages_all() -> std::io::Result<()> {
}; };
for lang in &syn_loader_conf.language { for lang in &syn_loader_conf.language {
column(&lang.language_id, Color::Reset); column(&lang.language_name, Color::Reset);
let mut cmds = lang.language_servers.iter().filter_map(|ls| { let mut cmds = lang.language_servers.iter().filter_map(|ls| {
syn_loader_conf syn_loader_conf
@@ -210,7 +210,7 @@ pub fn languages_all() -> std::io::Result<()> {
check_binary(formatter); check_binary(formatter);
for ts_feat in TsFeature::all() { for ts_feat in TsFeature::all() {
match load_runtime_file(&lang.language_id, ts_feat.runtime_filename()).is_ok() { match load_runtime_file(&lang.language_name, ts_feat.runtime_filename()).is_ok() {
true => column("", Color::Green), true => column("", Color::Green),
false => column("", Color::Red), false => column("", Color::Red),
} }
@@ -254,7 +254,7 @@ pub fn language(lang_str: String) -> std::io::Result<()> {
let lang = match syn_loader_conf let lang = match syn_loader_conf
.language .language
.iter() .iter()
.find(|l| l.language_id == lang_str) .find(|l| l.language_name == lang_str)
{ {
Some(l) => l, Some(l) => l,
None => { None => {
@@ -263,8 +263,11 @@ pub fn language(lang_str: String) -> std::io::Result<()> {
let suggestions: Vec<&str> = syn_loader_conf let suggestions: Vec<&str> = syn_loader_conf
.language .language
.iter() .iter()
.filter(|l| l.language_id.starts_with(lang_str.chars().next().unwrap())) .filter(|l| {
.map(|l| l.language_id.as_str()) l.language_name
.starts_with(lang_str.chars().next().unwrap())
})
.map(|l| l.language_name.as_str())
.collect(); .collect();
if !suggestions.is_empty() { if !suggestions.is_empty() {
let suggestions = suggestions.join(", "); let suggestions = suggestions.join(", ");

View File

@@ -339,7 +339,7 @@ pub mod completers {
let language_ids = editor let language_ids = editor
.syn_loader .syn_loader
.language_configs() .language_configs()
.map(|config| &config.language_id) .map(|config| &config.language_name)
.chain(std::iter::once(&text)); .chain(std::iter::once(&text));
fuzzy_match(input, language_ids, false) fuzzy_match(input, language_ids, false)

View File

@@ -1085,7 +1085,7 @@ impl Document {
config_loader: Arc<syntax::Loader>, config_loader: Arc<syntax::Loader>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let language_config = config_loader let language_config = config_loader
.language_config_for_language_id(language_id) .language_config_for_language_name(language_id)
.ok_or_else(|| anyhow!("invalid language id: {}", language_id))?; .ok_or_else(|| anyhow!("invalid language id: {}", language_id))?;
self.set_language(Some(language_config), Some(config_loader)); self.set_language(Some(language_config), Some(config_loader));
Ok(()) Ok(())
@@ -1530,7 +1530,7 @@ impl Document {
pub fn language_name(&self) -> Option<&str> { pub fn language_name(&self) -> Option<&str> {
self.language self.language
.as_ref() .as_ref()
.map(|language| language.language_id.as_str()) .map(|language| language.language_name.as_str())
} }
/// Language ID for the document. Either the `language-id`, /// Language ID for the document. Either the `language-id`,

View File

@@ -67,7 +67,7 @@ pub fn lang_features() -> Result<String, DynError> {
let mut langs = config let mut langs = config
.language .language
.iter() .iter()
.map(|l| l.language_id.clone()) .map(|l| l.language_name.clone())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
langs.sort_unstable(); langs.sort_unstable();
@@ -81,9 +81,9 @@ pub fn lang_features() -> Result<String, DynError> {
let lc = config let lc = config
.language .language
.iter() .iter()
.find(|l| l.language_id == lang) .find(|l| l.language_name == lang)
.unwrap(); // lang comes from config .unwrap(); // lang comes from config
row.push(lc.language_id.clone()); row.push(lc.language_name.clone());
for (_feat, support_list) in &ts_features_to_langs { for (_feat, support_list) in &ts_features_to_langs {
row.push( row.push(

View File

@@ -14,7 +14,7 @@ pub fn query_check() -> Result<(), DynError> {
]; ];
for language in lang_config().language { for language in lang_config().language {
let language_name = &language.language_id; let language_name = &language.language_name;
let grammar_name = language.grammar.as_ref().unwrap_or(language_name); let grammar_name = language.grammar.as_ref().unwrap_or(language_name);
for query_file in query_files { for query_file in query_files {
let language = get_language(grammar_name); let language = get_language(grammar_name);