@@ -1,6 +1,8 @@
1use crate::{regex::Regex, Change, Rope, RopeSlice, Transaction};
1use crate::{regex::Regex, Change, Rope, RopeSlice, Transaction};
2pub use helix_syntax::{get_language, get_language_name, Lang};
2pub use helix_syntax::{get_language, get_language_name, Lang};
5 borrow::Cow,
7 borrow::Cow,
6 cell::RefCell,
8 cell::RefCell,
@@ -143,35 +145,48 @@ fn read_query(language: &str, filename: &str) -> String {
145impl LanguageConfiguration {
147impl LanguageConfiguration {
146− pub fn highlight_config(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
148+ fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
147− self.highlight_config
149− let language = get_language_name(self.language_id).to_ascii_lowercase();
149+ let language = get_language_name(self.language_id).to_ascii_lowercase();
151− let highlights_query = read_query(&language, "highlights.scm");
151+ let highlights_query = read_query(&language, "highlights.scm");
152− // always highlight syntax errors
152+ // always highlight syntax errors
153− // highlights_query += "\n(ERROR) @error";
153+ // highlights_query += "\n(ERROR) @error";
155− let injections_query = read_query(&language, "injections.scm");
155+ let injections_query = read_query(&language, "injections.scm");
157− let locals_query = "";
157+ let locals_query = "";
159− if highlights_query.is_empty() {
159+ if highlights_query.is_empty() {
161− } else {
161+ } else {
162− let language = get_language(self.language_id);
162+ let language = get_language(self.language_id);
163− let mut config = HighlightConfiguration::new(
163+ let mut config = HighlightConfiguration::new(
164− language,
164+ language,
165− &highlights_query,
165+ &highlights_query,
166− &injections_query,
166+ &injections_query,
167− locals_query,
167+ locals_query,
169− .unwrap(); // TODO: no unwrap
169+ .unwrap(); // TODO: no unwrap
170− config.configure(scopes);
170+ config.configure(scopes);
171− Some(Arc::new(config))
171+ Some(Arc::new(config))
175+ pub fn highlight_config(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
176+ if let Some(config) = self.highlight_config.get() {
177+ if let Some(config) = config {
178+ config.configure(scopes);
182+ self.highlight_config
183+ .get_or_init(|| self.initialize_highlight(scopes))
174− .clone()
184+ .clone()
188+ pub fn is_highlight_initialized(&self) -> bool {
189+ self.highlight_config.get().is_some()
177 pub fn indent_query(&self) -> Option<&IndentQuery> {
192 pub fn indent_query(&self) -> Option<&IndentQuery> {
@@ -190,22 +205,18 @@ impl LanguageConfiguration {
193−pub static LOADER: OnceCell<Loader> = OnceCell::new();
195#[derive(Debug)]
208#[derive(Debug)]
196pub struct Loader {
209pub struct Loader {
197 // highlight_names ?
210 // highlight_names ?
198 language_configs: Vec<Arc<LanguageConfiguration>>,
211 language_configs: Vec<Arc<LanguageConfiguration>>,
199 language_config_ids_by_file_type: HashMap<String, usize>, // Vec<usize>
212 language_config_ids_by_file_type: HashMap<String, usize>, // Vec<usize>
200− scopes: Vec<String>,
203impl Loader {
215impl Loader {
204− pub fn new(config: Configuration, scopes: Vec<String>) -> Self {
216+ pub fn new(config: Configuration) -> Self {
205 let mut loader = Self {
217 let mut loader = Self {
206 language_configs: Vec::new(),
218 language_configs: Vec::new(),
207 language_config_ids_by_file_type: HashMap::new(),
219 language_config_ids_by_file_type: HashMap::new(),
211 for config in config.language {
222 for config in config.language {
@@ -225,10 +236,6 @@ impl Loader {
228− pub fn scopes(&self) -> &[String] {
232 pub fn language_config_for_file_name(&self, path: &Path) -> Option<Arc<LanguageConfiguration>> {
239 pub fn language_config_for_file_name(&self, path: &Path) -> Option<Arc<LanguageConfiguration>> {
233 // Find all the language configurations that match this file name
240 // Find all the language configurations that match this file name
234 // or a suffix of the file name.
241 // or a suffix of the file name.
@@ -253,6 +260,10 @@ impl Loader {
253 .find(|config| config.scope == scope)
260 .find(|config| config.scope == scope)
254 .cloned()
261 .cloned()
264+ pub fn language_configs_iter(&self) -> impl Iterator<Item = &Arc<LanguageConfiguration>> {
265+ self.language_configs.iter()
258pub struct TsParser {
269pub struct TsParser {
@@ -771,7 +782,7 @@ pub struct HighlightConfiguration {
771 combined_injections_query: Option<Query>,
782 combined_injections_query: Option<Query>,
772 locals_pattern_index: usize,
783 locals_pattern_index: usize,
773 highlights_pattern_index: usize,
784 highlights_pattern_index: usize,
774− highlight_indices: Vec<Option<Highlight>>,
785+ highlight_indices: ArcSwap<Vec<Option<Highlight>>>,
775 non_local_variable_patterns: Vec<bool>,
786 non_local_variable_patterns: Vec<bool>,
776 injection_content_capture_index: Option<u32>,
787 injection_content_capture_index: Option<u32>,
777 injection_language_capture_index: Option<u32>,
788 injection_language_capture_index: Option<u32>,
@@ -923,7 +934,7 @@ impl HighlightConfiguration {
926− let highlight_indices = vec![None; query.capture_names().len()];
937+ let highlight_indices = ArcSwap::from_pointee(vec![None; query.capture_names().len()]);
927 Ok(Self {
938 Ok(Self {
928 language,
939 language,
@@ -956,17 +967,20 @@ impl HighlightConfiguration {
957 /// When highlighting, results are returned as `Highlight` values, which contain the index
968 /// When highlighting, results are returned as `Highlight` values, which contain the index
958 /// of the matched highlight this list of highlight names.
969 /// of the matched highlight this list of highlight names.
959− pub fn configure(&mut self, recognized_names: &[String]) {
970+ pub fn configure(&self, recognized_names: &[String]) {
960 let mut capture_parts = Vec::new();
971 let mut capture_parts = Vec::new();
961− self.highlight_indices.clear();
972+ let indices: Vec<_> = self
962− self.highlight_indices
973+ .query
963− .extend(self.query.capture_names().iter().map(move |capture_name| {
976+ .map(move |capture_name| {
964 capture_parts.clear();
977 capture_parts.clear();
965 capture_parts.extend(capture_name.split('.'));
978 capture_parts.extend(capture_name.split('.'));
967 let mut best_index = None;
980 let mut best_index = None;
968 let mut best_match_len = 0;
981 let mut best_match_len = 0;
969 for (i, recognized_name) in recognized_names.iter().enumerate() {
982 for (i, recognized_name) in recognized_names.iter().enumerate() {
983+ let recognized_name = recognized_name;
970 let mut len = 0;
984 let mut len = 0;
971 let mut matches = true;
985 let mut matches = true;
972 for part in recognized_name.split('.') {
986 for part in recognized_name.split('.') {
@@ -982,7 +996,10 @@ impl HighlightConfiguration {
984 best_index.map(Highlight)
998 best_index.map(Highlight)
1002+ self.highlight_indices.store(Arc::new(indices));
@@ -1561,7 +1578,7 @@ where
1564− let current_highlight = layer.config.highlight_indices[capture.index as usize];
1581+ let current_highlight = layer.config.highlight_indices.load()[capture.index as usize];
1566 // If this node represents a local definition, then store the current
1583 // If this node represents a local definition, then store the current
1567 // highlight value on the local scope entry representing this node.
1584 // highlight value on the local scope entry representing this node.