1//! The `language` crate provides a large chunk of Zed's language-related
2//! features (the other big contributors being project and lsp crates that revolve around LSP features).
3//! Namely, this crate:
4//! - Provides [`Language`], [`Grammar`] and [`LanguageRegistry`] types that
5//! use Tree-sitter to provide syntax highlighting to the editor; note though that `language` doesn't perform the highlighting by itself. It only maps ranges in a buffer to colors. Treesitter is also used for buffer outlines (lists of symbols in a buffer)
6//! - Exposes [`LanguageConfig`] that describes how constructs (like brackets or line comments) should be handled by the editor for a source file of a particular language.
7//!
8//! Notably we do *not* assign a single language to a single file; in real world a single file can consist of multiple programming languages - HTML is a good example of that - and `language` crate tends to reflect that status quo in it's API.
9mod buffer;
10mod diagnostic_set;
11mod highlight_map;
12mod language_registry;
13pub mod language_settings;
14mod outline;
15pub mod proto;
16mod syntax_map;
17
18#[cfg(test)]
19mod buffer_tests;
20pub mod markdown;
21
22use anyhow::{anyhow, Context, Result};
23use async_trait::async_trait;
24use collections::{HashMap, HashSet};
25use gpui::{AppContext, AsyncAppContext, Task};
26pub use highlight_map::HighlightMap;
27use lazy_static::lazy_static;
28use lsp::{CodeActionKind, LanguageServerBinary};
29use parking_lot::Mutex;
30use regex::Regex;
31use schemars::{
32 gen::SchemaGenerator,
33 schema::{InstanceType, Schema, SchemaObject},
34 JsonSchema,
35};
36use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
37use serde_json::Value;
38use std::{
39 any::Any,
40 cell::RefCell,
41 fmt::Debug,
42 hash::Hash,
43 mem,
44 ops::Range,
45 path::{Path, PathBuf},
46 str,
47 sync::{
48 atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst},
49 Arc,
50 },
51};
52use syntax_map::SyntaxSnapshot;
53use theme::SyntaxTheme;
54use tree_sitter::{self, wasmtime, Query, WasmStore};
55use util::http::HttpClient;
56
57pub use buffer::Operation;
58pub use buffer::*;
59pub use diagnostic_set::DiagnosticEntry;
60pub use language_registry::{
61 LanguageQueries, LanguageRegistry, LanguageServerBinaryStatus, PendingLanguageServer,
62 QUERY_FILENAME_PREFIXES,
63};
64pub use lsp::LanguageServerId;
65pub use outline::{Outline, OutlineItem};
66pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};
67pub use text::LineEnding;
68pub use tree_sitter::{Parser, Tree};
69
70/// Initializes the `language` crate.
71///
72/// This should be called before making use of items from the create.
73pub fn init(cx: &mut AppContext) {
74 language_settings::init(cx);
75}
76
77thread_local! {
78 static PARSER: RefCell<Parser> = {
79 let mut parser = Parser::new();
80 parser.set_wasm_store(WasmStore::new(WASM_ENGINE.clone()).unwrap()).unwrap();
81 RefCell::new(parser)
82 };
83}
84
85lazy_static! {
86 static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();
87
88 static ref WASM_ENGINE: wasmtime::Engine = wasmtime::Engine::default();
89
90 /// A shared grammar for plain text, exposed for reuse by downstream crates.
91 pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(
92 LanguageConfig {
93 name: "Plain Text".into(),
94 ..Default::default()
95 },
96 None,
97 ));
98}
99
100/// Types that represent a position in a buffer, and can be converted into
101/// an LSP position, to send to a language server.
102pub trait ToLspPosition {
103 /// Converts the value into an LSP position.
104 fn to_lsp_position(self) -> lsp::Position;
105}
106
107/// A name of a language server.
108#[derive(Clone, Debug, PartialEq, Eq, Hash)]
109pub struct LanguageServerName(pub Arc<str>);
110
111/// Represents a Language Server, with certain cached sync properties.
112/// Uses [`LspAdapter`] under the hood, but calls all 'static' methods
113/// once at startup, and caches the results.
114pub struct CachedLspAdapter {
115 pub name: LanguageServerName,
116 pub short_name: &'static str,
117 pub disk_based_diagnostic_sources: Vec<String>,
118 pub disk_based_diagnostics_progress_token: Option<String>,
119 pub language_ids: HashMap<String, String>,
120 pub adapter: Arc<dyn LspAdapter>,
121 pub reinstall_attempt_count: AtomicU64,
122}
123
124impl CachedLspAdapter {
125 pub async fn new(adapter: Arc<dyn LspAdapter>) -> Arc<Self> {
126 let name = adapter.name();
127 let short_name = adapter.short_name();
128 let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources();
129 let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token();
130 let language_ids = adapter.language_ids();
131
132 Arc::new(CachedLspAdapter {
133 name,
134 short_name,
135 disk_based_diagnostic_sources,
136 disk_based_diagnostics_progress_token,
137 language_ids,
138 adapter,
139 reinstall_attempt_count: AtomicU64::new(0),
140 })
141 }
142
143 pub async fn fetch_latest_server_version(
144 &self,
145 delegate: &dyn LspAdapterDelegate,
146 ) -> Result<Box<dyn 'static + Send + Any>> {
147 self.adapter.fetch_latest_server_version(delegate).await
148 }
149
150 pub fn will_fetch_server(
151 &self,
152 delegate: &Arc<dyn LspAdapterDelegate>,
153 cx: &mut AsyncAppContext,
154 ) -> Option<Task<Result<()>>> {
155 self.adapter.will_fetch_server(delegate, cx)
156 }
157
158 pub fn will_start_server(
159 &self,
160 delegate: &Arc<dyn LspAdapterDelegate>,
161 cx: &mut AsyncAppContext,
162 ) -> Option<Task<Result<()>>> {
163 self.adapter.will_start_server(delegate, cx)
164 }
165
166 pub async fn fetch_server_binary(
167 &self,
168 version: Box<dyn 'static + Send + Any>,
169 container_dir: PathBuf,
170 delegate: &dyn LspAdapterDelegate,
171 ) -> Result<LanguageServerBinary> {
172 self.adapter
173 .fetch_server_binary(version, container_dir, delegate)
174 .await
175 }
176
177 pub async fn cached_server_binary(
178 &self,
179 container_dir: PathBuf,
180 delegate: &dyn LspAdapterDelegate,
181 ) -> Option<LanguageServerBinary> {
182 self.adapter
183 .cached_server_binary(container_dir, delegate)
184 .await
185 }
186
187 pub fn can_be_reinstalled(&self) -> bool {
188 self.adapter.can_be_reinstalled()
189 }
190
191 pub async fn installation_test_binary(
192 &self,
193 container_dir: PathBuf,
194 ) -> Option<LanguageServerBinary> {
195 self.adapter.installation_test_binary(container_dir).await
196 }
197
198 pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
199 self.adapter.code_action_kinds()
200 }
201
202 pub fn workspace_configuration(&self, workspace_root: &Path, cx: &mut AppContext) -> Value {
203 self.adapter.workspace_configuration(workspace_root, cx)
204 }
205
206 pub fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
207 self.adapter.process_diagnostics(params)
208 }
209
210 pub async fn process_completion(&self, completion_item: &mut lsp::CompletionItem) {
211 self.adapter.process_completion(completion_item).await
212 }
213
214 pub async fn label_for_completion(
215 &self,
216 completion_item: &lsp::CompletionItem,
217 language: &Arc<Language>,
218 ) -> Option<CodeLabel> {
219 self.adapter
220 .label_for_completion(completion_item, language)
221 .await
222 }
223
224 pub async fn label_for_symbol(
225 &self,
226 name: &str,
227 kind: lsp::SymbolKind,
228 language: &Arc<Language>,
229 ) -> Option<CodeLabel> {
230 self.adapter.label_for_symbol(name, kind, language).await
231 }
232
233 pub fn prettier_plugins(&self) -> &[&'static str] {
234 self.adapter.prettier_plugins()
235 }
236}
237
238/// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application
239// e.g. to display a notification or fetch data from the web.
240pub trait LspAdapterDelegate: Send + Sync {
241 fn show_notification(&self, message: &str, cx: &mut AppContext);
242 fn http_client(&self) -> Arc<dyn HttpClient>;
243}
244
245#[async_trait]
246pub trait LspAdapter: 'static + Send + Sync {
247 fn name(&self) -> LanguageServerName;
248
249 fn short_name(&self) -> &'static str;
250
251 async fn fetch_latest_server_version(
252 &self,
253 delegate: &dyn LspAdapterDelegate,
254 ) -> Result<Box<dyn 'static + Send + Any>>;
255
256 fn will_fetch_server(
257 &self,
258 _: &Arc<dyn LspAdapterDelegate>,
259 _: &mut AsyncAppContext,
260 ) -> Option<Task<Result<()>>> {
261 None
262 }
263
264 fn will_start_server(
265 &self,
266 _: &Arc<dyn LspAdapterDelegate>,
267 _: &mut AsyncAppContext,
268 ) -> Option<Task<Result<()>>> {
269 None
270 }
271
272 async fn fetch_server_binary(
273 &self,
274 version: Box<dyn 'static + Send + Any>,
275 container_dir: PathBuf,
276 delegate: &dyn LspAdapterDelegate,
277 ) -> Result<LanguageServerBinary>;
278
279 async fn cached_server_binary(
280 &self,
281 container_dir: PathBuf,
282 delegate: &dyn LspAdapterDelegate,
283 ) -> Option<LanguageServerBinary>;
284
285 /// Returns `true` if a language server can be reinstalled.
286 ///
287 /// If language server initialization fails, a reinstallation will be attempted unless the value returned from this method is `false`.
288 ///
289 /// Implementations that rely on software already installed on user's system
290 /// should have [`can_be_reinstalled`](Self::can_be_reinstalled) return `false`.
291 fn can_be_reinstalled(&self) -> bool {
292 true
293 }
294
295 async fn installation_test_binary(
296 &self,
297 container_dir: PathBuf,
298 ) -> Option<LanguageServerBinary>;
299
300 fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
301
302 /// A callback called for each [`lsp::CompletionItem`] obtained from LSP server.
303 /// Some LspAdapter implementations might want to modify the obtained item to
304 /// change how it's displayed.
305 async fn process_completion(&self, _: &mut lsp::CompletionItem) {}
306
307 async fn label_for_completion(
308 &self,
309 _: &lsp::CompletionItem,
310 _: &Arc<Language>,
311 ) -> Option<CodeLabel> {
312 None
313 }
314
315 async fn label_for_symbol(
316 &self,
317 _: &str,
318 _: lsp::SymbolKind,
319 _: &Arc<Language>,
320 ) -> Option<CodeLabel> {
321 None
322 }
323
324 /// Returns initialization options that are going to be sent to a LSP server as a part of [`lsp::InitializeParams`]
325 fn initialization_options(&self) -> Option<Value> {
326 None
327 }
328
329 fn workspace_configuration(&self, _workspace_root: &Path, _cx: &mut AppContext) -> Value {
330 serde_json::json!({})
331 }
332
333 /// Returns a list of code actions supported by a given LspAdapter
334 fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
335 Some(vec![
336 CodeActionKind::EMPTY,
337 CodeActionKind::QUICKFIX,
338 CodeActionKind::REFACTOR,
339 CodeActionKind::REFACTOR_EXTRACT,
340 CodeActionKind::SOURCE,
341 ])
342 }
343
344 fn disk_based_diagnostic_sources(&self) -> Vec<String> {
345 Default::default()
346 }
347
348 fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
349 None
350 }
351
352 fn language_ids(&self) -> HashMap<String, String> {
353 Default::default()
354 }
355
356 fn prettier_plugins(&self) -> &[&'static str] {
357 &[]
358 }
359}
360
361#[derive(Clone, Debug, PartialEq, Eq)]
362pub struct CodeLabel {
363 /// The text to display.
364 pub text: String,
365 /// Syntax highlighting runs.
366 pub runs: Vec<(Range<usize>, HighlightId)>,
367 /// The portion of the text that should be used in fuzzy filtering.
368 pub filter_range: Range<usize>,
369}
370
371#[derive(Clone, Deserialize, JsonSchema)]
372pub struct LanguageConfig {
373 /// Human-readable name of the language.
374 pub name: Arc<str>,
375 // The name of the grammar in a WASM bundle (experimental).
376 pub grammar: Option<Arc<str>>,
377 /// The criteria for matching this language to a given file.
378 #[serde(flatten)]
379 pub matcher: LanguageMatcher,
380 /// List of bracket types in a language.
381 #[serde(default)]
382 #[schemars(schema_with = "bracket_pair_config_json_schema")]
383 pub brackets: BracketPairConfig,
384 /// If set to true, auto indentation uses last non empty line to determine
385 /// the indentation level for a new line.
386 #[serde(default = "auto_indent_using_last_non_empty_line_default")]
387 pub auto_indent_using_last_non_empty_line: bool,
388 /// A regex that is used to determine whether the indentation level should be
389 /// increased in the following line.
390 #[serde(default, deserialize_with = "deserialize_regex")]
391 #[schemars(schema_with = "regex_json_schema")]
392 pub increase_indent_pattern: Option<Regex>,
393 /// A regex that is used to determine whether the indentation level should be
394 /// decreased in the following line.
395 #[serde(default, deserialize_with = "deserialize_regex")]
396 #[schemars(schema_with = "regex_json_schema")]
397 pub decrease_indent_pattern: Option<Regex>,
398 /// A list of characters that trigger the automatic insertion of a closing
399 /// bracket when they immediately precede the point where an opening
400 /// bracket is inserted.
401 #[serde(default)]
402 pub autoclose_before: String,
403 /// A placeholder used internally by Semantic Index.
404 #[serde(default)]
405 pub collapsed_placeholder: String,
406 /// A line comment string that is inserted in e.g. `toggle comments` action.
407 /// A language can have multiple flavours of line comments. All of the provided line comments are
408 /// used for comment continuations on the next line, but only the first one is used for Editor::ToggleComments.
409 #[serde(default)]
410 pub line_comments: Vec<Arc<str>>,
411 /// Starting and closing characters of a block comment.
412 #[serde(default)]
413 pub block_comment: Option<(Arc<str>, Arc<str>)>,
414 /// A list of language servers that are allowed to run on subranges of a given language.
415 #[serde(default)]
416 pub scope_opt_in_language_servers: Vec<String>,
417 #[serde(default)]
418 pub overrides: HashMap<String, LanguageConfigOverride>,
419 /// A list of characters that Zed should treat as word characters for the
420 /// purpose of features that operate on word boundaries, like 'move to next word end'
421 /// or a whole-word search in buffer search.
422 #[serde(default)]
423 pub word_characters: HashSet<char>,
424 /// The name of a Prettier parser that should be used for this language.
425 #[serde(default)]
426 pub prettier_parser_name: Option<String>,
427}
428
429#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
430pub struct LanguageMatcher {
431 /// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`.
432 #[serde(default)]
433 pub path_suffixes: Vec<String>,
434 /// A regex pattern that determines whether the language should be assigned to a file or not.
435 #[serde(
436 default,
437 serialize_with = "serialize_regex",
438 deserialize_with = "deserialize_regex"
439 )]
440 #[schemars(schema_with = "regex_json_schema")]
441 pub first_line_pattern: Option<Regex>,
442}
443
444/// Represents a language for the given range. Some languages (e.g. HTML)
445/// interleave several languages together, thus a single buffer might actually contain
446/// several nested scopes.
447#[derive(Clone, Debug)]
448pub struct LanguageScope {
449 language: Arc<Language>,
450 override_id: Option<u32>,
451}
452
453#[derive(Clone, Deserialize, Default, Debug, JsonSchema)]
454pub struct LanguageConfigOverride {
455 #[serde(default)]
456 pub line_comments: Override<Vec<Arc<str>>>,
457 #[serde(default)]
458 pub block_comment: Override<(Arc<str>, Arc<str>)>,
459 #[serde(skip_deserializing)]
460 #[schemars(skip)]
461 pub disabled_bracket_ixs: Vec<u16>,
462 #[serde(default)]
463 pub word_characters: Override<HashSet<char>>,
464 #[serde(default)]
465 pub opt_into_language_servers: Vec<String>,
466}
467
468#[derive(Clone, Deserialize, Debug, Serialize, JsonSchema)]
469#[serde(untagged)]
470pub enum Override<T> {
471 Remove { remove: bool },
472 Set(T),
473}
474
475impl<T> Default for Override<T> {
476 fn default() -> Self {
477 Override::Remove { remove: false }
478 }
479}
480
481impl<T> Override<T> {
482 fn as_option<'a>(this: Option<&'a Self>, original: Option<&'a T>) -> Option<&'a T> {
483 match this {
484 Some(Self::Set(value)) => Some(value),
485 Some(Self::Remove { remove: true }) => None,
486 Some(Self::Remove { remove: false }) | None => original,
487 }
488 }
489}
490
491impl Default for LanguageConfig {
492 fn default() -> Self {
493 Self {
494 name: "".into(),
495 grammar: None,
496 matcher: LanguageMatcher::default(),
497 brackets: Default::default(),
498 auto_indent_using_last_non_empty_line: auto_indent_using_last_non_empty_line_default(),
499 increase_indent_pattern: Default::default(),
500 decrease_indent_pattern: Default::default(),
501 autoclose_before: Default::default(),
502 line_comments: Default::default(),
503 block_comment: Default::default(),
504 scope_opt_in_language_servers: Default::default(),
505 overrides: Default::default(),
506 word_characters: Default::default(),
507 prettier_parser_name: None,
508 collapsed_placeholder: Default::default(),
509 }
510 }
511}
512
513fn auto_indent_using_last_non_empty_line_default() -> bool {
514 true
515}
516
517fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D::Error> {
518 let source = Option::<String>::deserialize(d)?;
519 if let Some(source) = source {
520 Ok(Some(regex::Regex::new(&source).map_err(de::Error::custom)?))
521 } else {
522 Ok(None)
523 }
524}
525
526fn regex_json_schema(_: &mut SchemaGenerator) -> Schema {
527 Schema::Object(SchemaObject {
528 instance_type: Some(InstanceType::String.into()),
529 ..Default::default()
530 })
531}
532
533fn serialize_regex<S>(regex: &Option<Regex>, serializer: S) -> Result<S::Ok, S::Error>
534where
535 S: Serializer,
536{
537 match regex {
538 Some(regex) => serializer.serialize_str(regex.as_str()),
539 None => serializer.serialize_none(),
540 }
541}
542
543#[doc(hidden)]
544#[cfg(any(test, feature = "test-support"))]
545pub struct FakeLspAdapter {
546 pub name: &'static str,
547 pub initialization_options: Option<Value>,
548 pub capabilities: lsp::ServerCapabilities,
549 pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
550 pub disk_based_diagnostics_progress_token: Option<String>,
551 pub disk_based_diagnostics_sources: Vec<String>,
552 pub prettier_plugins: Vec<&'static str>,
553}
554
555/// Configuration of handling bracket pairs for a given language.
556///
557/// This struct includes settings for defining which pairs of characters are considered brackets and
558/// also specifies any language-specific scopes where these pairs should be ignored for bracket matching purposes.
559#[derive(Clone, Debug, Default, JsonSchema)]
560pub struct BracketPairConfig {
561 /// A list of character pairs that should be treated as brackets in the context of a given language.
562 pub pairs: Vec<BracketPair>,
563 /// A list of tree-sitter scopes for which a given bracket should not be active.
564 /// N-th entry in `[Self::disabled_scopes_by_bracket_ix]` contains a list of disabled scopes for an n-th entry in `[Self::pairs]`
565 #[schemars(skip)]
566 pub disabled_scopes_by_bracket_ix: Vec<Vec<String>>,
567}
568
569fn bracket_pair_config_json_schema(gen: &mut SchemaGenerator) -> Schema {
570 Option::<Vec<BracketPairContent>>::json_schema(gen)
571}
572
573#[derive(Deserialize, JsonSchema)]
574pub struct BracketPairContent {
575 #[serde(flatten)]
576 pub bracket_pair: BracketPair,
577 #[serde(default)]
578 pub not_in: Vec<String>,
579}
580
581impl<'de> Deserialize<'de> for BracketPairConfig {
582 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
583 where
584 D: Deserializer<'de>,
585 {
586 let result = Vec::<BracketPairContent>::deserialize(deserializer)?;
587 let mut brackets = Vec::with_capacity(result.len());
588 let mut disabled_scopes_by_bracket_ix = Vec::with_capacity(result.len());
589 for entry in result {
590 brackets.push(entry.bracket_pair);
591 disabled_scopes_by_bracket_ix.push(entry.not_in);
592 }
593
594 Ok(BracketPairConfig {
595 pairs: brackets,
596 disabled_scopes_by_bracket_ix,
597 })
598 }
599}
600
601/// Describes a single bracket pair and how an editor should react to e.g. inserting
602/// an opening bracket or to a newline character insertion in between `start` and `end` characters.
603#[derive(Clone, Debug, Default, Deserialize, PartialEq, JsonSchema)]
604pub struct BracketPair {
605 /// Starting substring for a bracket.
606 pub start: String,
607 /// Ending substring for a bracket.
608 pub end: String,
609 /// True if `end` should be automatically inserted right after `start` characters.
610 pub close: bool,
611 /// True if an extra newline should be inserted while the cursor is in the middle
612 /// of that bracket pair.
613 pub newline: bool,
614}
615
616pub struct Language {
617 pub(crate) config: LanguageConfig,
618 pub(crate) grammar: Option<Arc<Grammar>>,
619 pub(crate) adapters: Vec<Arc<CachedLspAdapter>>,
620
621 #[cfg(any(test, feature = "test-support"))]
622 fake_adapter: Option<(
623 futures::channel::mpsc::UnboundedSender<lsp::FakeLanguageServer>,
624 Arc<FakeLspAdapter>,
625 )>,
626}
627
628pub struct Grammar {
629 id: usize,
630 pub ts_language: tree_sitter::Language,
631 pub(crate) error_query: Query,
632 pub(crate) highlights_query: Option<Query>,
633 pub(crate) brackets_config: Option<BracketConfig>,
634 pub(crate) redactions_config: Option<RedactionConfig>,
635 pub(crate) indents_config: Option<IndentConfig>,
636 pub outline_config: Option<OutlineConfig>,
637 pub embedding_config: Option<EmbeddingConfig>,
638 pub(crate) injection_config: Option<InjectionConfig>,
639 pub(crate) override_config: Option<OverrideConfig>,
640 pub(crate) highlight_map: Mutex<HighlightMap>,
641}
642
643struct IndentConfig {
644 query: Query,
645 indent_capture_ix: u32,
646 start_capture_ix: Option<u32>,
647 end_capture_ix: Option<u32>,
648 outdent_capture_ix: Option<u32>,
649}
650
651pub struct OutlineConfig {
652 pub query: Query,
653 pub item_capture_ix: u32,
654 pub name_capture_ix: u32,
655 pub context_capture_ix: Option<u32>,
656 pub extra_context_capture_ix: Option<u32>,
657}
658
659#[derive(Debug)]
660pub struct EmbeddingConfig {
661 pub query: Query,
662 pub item_capture_ix: u32,
663 pub name_capture_ix: Option<u32>,
664 pub context_capture_ix: Option<u32>,
665 pub collapse_capture_ix: Option<u32>,
666 pub keep_capture_ix: Option<u32>,
667}
668
669struct InjectionConfig {
670 query: Query,
671 content_capture_ix: u32,
672 language_capture_ix: Option<u32>,
673 patterns: Vec<InjectionPatternConfig>,
674}
675
676struct RedactionConfig {
677 pub query: Query,
678 pub redaction_capture_ix: u32,
679}
680
681struct OverrideConfig {
682 query: Query,
683 values: HashMap<u32, (String, LanguageConfigOverride)>,
684}
685
686#[derive(Default, Clone)]
687struct InjectionPatternConfig {
688 language: Option<Box<str>>,
689 combined: bool,
690}
691
692struct BracketConfig {
693 query: Query,
694 open_capture_ix: u32,
695 close_capture_ix: u32,
696}
697
698impl Language {
699 pub fn new(config: LanguageConfig, ts_language: Option<tree_sitter::Language>) -> Self {
700 Self {
701 config,
702 grammar: ts_language.map(|ts_language| {
703 Arc::new(Grammar {
704 id: NEXT_GRAMMAR_ID.fetch_add(1, SeqCst),
705 highlights_query: None,
706 brackets_config: None,
707 outline_config: None,
708 embedding_config: None,
709 indents_config: None,
710 injection_config: None,
711 override_config: None,
712 redactions_config: None,
713 error_query: Query::new(&ts_language, "(ERROR) @error").unwrap(),
714 ts_language,
715 highlight_map: Default::default(),
716 })
717 }),
718 adapters: Vec::new(),
719
720 #[cfg(any(test, feature = "test-support"))]
721 fake_adapter: None,
722 }
723 }
724
725 pub fn lsp_adapters(&self) -> &[Arc<CachedLspAdapter>] {
726 &self.adapters
727 }
728
729 pub fn id(&self) -> Option<usize> {
730 self.grammar.as_ref().map(|g| g.id)
731 }
732
733 pub fn with_queries(mut self, queries: LanguageQueries) -> Result<Self> {
734 if let Some(query) = queries.highlights {
735 self = self
736 .with_highlights_query(query.as_ref())
737 .context("Error loading highlights query")?;
738 }
739 if let Some(query) = queries.brackets {
740 self = self
741 .with_brackets_query(query.as_ref())
742 .context("Error loading brackets query")?;
743 }
744 if let Some(query) = queries.indents {
745 self = self
746 .with_indents_query(query.as_ref())
747 .context("Error loading indents query")?;
748 }
749 if let Some(query) = queries.outline {
750 self = self
751 .with_outline_query(query.as_ref())
752 .context("Error loading outline query")?;
753 }
754 if let Some(query) = queries.embedding {
755 self = self
756 .with_embedding_query(query.as_ref())
757 .context("Error loading embedding query")?;
758 }
759 if let Some(query) = queries.injections {
760 self = self
761 .with_injection_query(query.as_ref())
762 .context("Error loading injection query")?;
763 }
764 if let Some(query) = queries.overrides {
765 self = self
766 .with_override_query(query.as_ref())
767 .context("Error loading override query")?;
768 }
769 if let Some(query) = queries.redactions {
770 self = self
771 .with_redaction_query(query.as_ref())
772 .context("Error loading redaction query")?;
773 }
774 Ok(self)
775 }
776
777 pub fn with_highlights_query(mut self, source: &str) -> Result<Self> {
778 let grammar = self.grammar_mut();
779 grammar.highlights_query = Some(Query::new(&grammar.ts_language, source)?);
780 Ok(self)
781 }
782
783 pub fn with_outline_query(mut self, source: &str) -> Result<Self> {
784 let grammar = self.grammar_mut();
785 let query = Query::new(&grammar.ts_language, source)?;
786 let mut item_capture_ix = None;
787 let mut name_capture_ix = None;
788 let mut context_capture_ix = None;
789 let mut extra_context_capture_ix = None;
790 get_capture_indices(
791 &query,
792 &mut [
793 ("item", &mut item_capture_ix),
794 ("name", &mut name_capture_ix),
795 ("context", &mut context_capture_ix),
796 ("context.extra", &mut extra_context_capture_ix),
797 ],
798 );
799 if let Some((item_capture_ix, name_capture_ix)) = item_capture_ix.zip(name_capture_ix) {
800 grammar.outline_config = Some(OutlineConfig {
801 query,
802 item_capture_ix,
803 name_capture_ix,
804 context_capture_ix,
805 extra_context_capture_ix,
806 });
807 }
808 Ok(self)
809 }
810
811 pub fn with_embedding_query(mut self, source: &str) -> Result<Self> {
812 let grammar = self.grammar_mut();
813 let query = Query::new(&grammar.ts_language, source)?;
814 let mut item_capture_ix = None;
815 let mut name_capture_ix = None;
816 let mut context_capture_ix = None;
817 let mut collapse_capture_ix = None;
818 let mut keep_capture_ix = None;
819 get_capture_indices(
820 &query,
821 &mut [
822 ("item", &mut item_capture_ix),
823 ("name", &mut name_capture_ix),
824 ("context", &mut context_capture_ix),
825 ("keep", &mut keep_capture_ix),
826 ("collapse", &mut collapse_capture_ix),
827 ],
828 );
829 if let Some(item_capture_ix) = item_capture_ix {
830 grammar.embedding_config = Some(EmbeddingConfig {
831 query,
832 item_capture_ix,
833 name_capture_ix,
834 context_capture_ix,
835 collapse_capture_ix,
836 keep_capture_ix,
837 });
838 }
839 Ok(self)
840 }
841
842 pub fn with_brackets_query(mut self, source: &str) -> Result<Self> {
843 let grammar = self.grammar_mut();
844 let query = Query::new(&grammar.ts_language, source)?;
845 let mut open_capture_ix = None;
846 let mut close_capture_ix = None;
847 get_capture_indices(
848 &query,
849 &mut [
850 ("open", &mut open_capture_ix),
851 ("close", &mut close_capture_ix),
852 ],
853 );
854 if let Some((open_capture_ix, close_capture_ix)) = open_capture_ix.zip(close_capture_ix) {
855 grammar.brackets_config = Some(BracketConfig {
856 query,
857 open_capture_ix,
858 close_capture_ix,
859 });
860 }
861 Ok(self)
862 }
863
864 pub fn with_indents_query(mut self, source: &str) -> Result<Self> {
865 let grammar = self.grammar_mut();
866 let query = Query::new(&grammar.ts_language, source)?;
867 let mut indent_capture_ix = None;
868 let mut start_capture_ix = None;
869 let mut end_capture_ix = None;
870 let mut outdent_capture_ix = None;
871 get_capture_indices(
872 &query,
873 &mut [
874 ("indent", &mut indent_capture_ix),
875 ("start", &mut start_capture_ix),
876 ("end", &mut end_capture_ix),
877 ("outdent", &mut outdent_capture_ix),
878 ],
879 );
880 if let Some(indent_capture_ix) = indent_capture_ix {
881 grammar.indents_config = Some(IndentConfig {
882 query,
883 indent_capture_ix,
884 start_capture_ix,
885 end_capture_ix,
886 outdent_capture_ix,
887 });
888 }
889 Ok(self)
890 }
891
892 pub fn with_injection_query(mut self, source: &str) -> Result<Self> {
893 let grammar = self.grammar_mut();
894 let query = Query::new(&grammar.ts_language, source)?;
895 let mut language_capture_ix = None;
896 let mut content_capture_ix = None;
897 get_capture_indices(
898 &query,
899 &mut [
900 ("language", &mut language_capture_ix),
901 ("content", &mut content_capture_ix),
902 ],
903 );
904 let patterns = (0..query.pattern_count())
905 .map(|ix| {
906 let mut config = InjectionPatternConfig::default();
907 for setting in query.property_settings(ix) {
908 match setting.key.as_ref() {
909 "language" => {
910 config.language = setting.value.clone();
911 }
912 "combined" => {
913 config.combined = true;
914 }
915 _ => {}
916 }
917 }
918 config
919 })
920 .collect();
921 if let Some(content_capture_ix) = content_capture_ix {
922 grammar.injection_config = Some(InjectionConfig {
923 query,
924 language_capture_ix,
925 content_capture_ix,
926 patterns,
927 });
928 }
929 Ok(self)
930 }
931
932 pub fn with_override_query(mut self, source: &str) -> anyhow::Result<Self> {
933 let query = Query::new(&self.grammar_mut().ts_language, source)?;
934
935 let mut override_configs_by_id = HashMap::default();
936 for (ix, name) in query.capture_names().iter().enumerate() {
937 if !name.starts_with('_') {
938 let value = self.config.overrides.remove(*name).unwrap_or_default();
939 for server_name in &value.opt_into_language_servers {
940 if !self
941 .config
942 .scope_opt_in_language_servers
943 .contains(server_name)
944 {
945 util::debug_panic!("Server {server_name:?} has been opted-in by scope {name:?} but has not been marked as an opt-in server");
946 }
947 }
948
949 override_configs_by_id.insert(ix as u32, (name.to_string(), value));
950 }
951 }
952
953 if !self.config.overrides.is_empty() {
954 let keys = self.config.overrides.keys().collect::<Vec<_>>();
955 Err(anyhow!(
956 "language {:?} has overrides in config not in query: {keys:?}",
957 self.config.name
958 ))?;
959 }
960
961 for disabled_scope_name in self
962 .config
963 .brackets
964 .disabled_scopes_by_bracket_ix
965 .iter()
966 .flatten()
967 {
968 if !override_configs_by_id
969 .values()
970 .any(|(scope_name, _)| scope_name == disabled_scope_name)
971 {
972 Err(anyhow!(
973 "language {:?} has overrides in config not in query: {disabled_scope_name:?}",
974 self.config.name
975 ))?;
976 }
977 }
978
979 for (name, override_config) in override_configs_by_id.values_mut() {
980 override_config.disabled_bracket_ixs = self
981 .config
982 .brackets
983 .disabled_scopes_by_bracket_ix
984 .iter()
985 .enumerate()
986 .filter_map(|(ix, disabled_scope_names)| {
987 if disabled_scope_names.contains(name) {
988 Some(ix as u16)
989 } else {
990 None
991 }
992 })
993 .collect();
994 }
995
996 self.config.brackets.disabled_scopes_by_bracket_ix.clear();
997 self.grammar_mut().override_config = Some(OverrideConfig {
998 query,
999 values: override_configs_by_id,
1000 });
1001 Ok(self)
1002 }
1003
1004 pub fn with_redaction_query(mut self, source: &str) -> anyhow::Result<Self> {
1005 let grammar = self.grammar_mut();
1006 let query = Query::new(&grammar.ts_language, source)?;
1007 let mut redaction_capture_ix = None;
1008 get_capture_indices(&query, &mut [("redact", &mut redaction_capture_ix)]);
1009
1010 if let Some(redaction_capture_ix) = redaction_capture_ix {
1011 grammar.redactions_config = Some(RedactionConfig {
1012 query,
1013 redaction_capture_ix,
1014 });
1015 }
1016
1017 Ok(self)
1018 }
1019
1020 fn grammar_mut(&mut self) -> &mut Grammar {
1021 Arc::get_mut(self.grammar.as_mut().unwrap()).unwrap()
1022 }
1023
1024 pub async fn with_lsp_adapters(mut self, lsp_adapters: Vec<Arc<dyn LspAdapter>>) -> Self {
1025 for adapter in lsp_adapters {
1026 self.adapters.push(CachedLspAdapter::new(adapter).await);
1027 }
1028 self
1029 }
1030
1031 #[cfg(any(test, feature = "test-support"))]
1032 pub async fn set_fake_lsp_adapter(
1033 &mut self,
1034 fake_lsp_adapter: Arc<FakeLspAdapter>,
1035 ) -> futures::channel::mpsc::UnboundedReceiver<lsp::FakeLanguageServer> {
1036 let (servers_tx, servers_rx) = futures::channel::mpsc::unbounded();
1037 self.fake_adapter = Some((servers_tx, fake_lsp_adapter.clone()));
1038 let adapter = CachedLspAdapter::new(Arc::new(fake_lsp_adapter)).await;
1039 self.adapters = vec![adapter];
1040 servers_rx
1041 }
1042
1043 pub fn name(&self) -> Arc<str> {
1044 self.config.name.clone()
1045 }
1046
1047 pub async fn disk_based_diagnostic_sources(&self) -> &[String] {
1048 match self.adapters.first().as_ref() {
1049 Some(adapter) => &adapter.disk_based_diagnostic_sources,
1050 None => &[],
1051 }
1052 }
1053
1054 pub async fn disk_based_diagnostics_progress_token(&self) -> Option<&str> {
1055 for adapter in &self.adapters {
1056 let token = adapter.disk_based_diagnostics_progress_token.as_deref();
1057 if token.is_some() {
1058 return token;
1059 }
1060 }
1061
1062 None
1063 }
1064
1065 pub async fn process_completion(self: &Arc<Self>, completion: &mut lsp::CompletionItem) {
1066 for adapter in &self.adapters {
1067 adapter.process_completion(completion).await;
1068 }
1069 }
1070
1071 pub async fn label_for_completion(
1072 self: &Arc<Self>,
1073 completion: &lsp::CompletionItem,
1074 ) -> Option<CodeLabel> {
1075 self.adapters
1076 .first()
1077 .as_ref()?
1078 .label_for_completion(completion, self)
1079 .await
1080 }
1081
1082 pub async fn label_for_symbol(
1083 self: &Arc<Self>,
1084 name: &str,
1085 kind: lsp::SymbolKind,
1086 ) -> Option<CodeLabel> {
1087 self.adapters
1088 .first()
1089 .as_ref()?
1090 .label_for_symbol(name, kind, self)
1091 .await
1092 }
1093
1094 pub fn highlight_text<'a>(
1095 self: &'a Arc<Self>,
1096 text: &'a Rope,
1097 range: Range<usize>,
1098 ) -> Vec<(Range<usize>, HighlightId)> {
1099 let mut result = Vec::new();
1100 if let Some(grammar) = &self.grammar {
1101 let tree = grammar.parse_text(text, None);
1102 let captures =
1103 SyntaxSnapshot::single_tree_captures(range.clone(), text, &tree, self, |grammar| {
1104 grammar.highlights_query.as_ref()
1105 });
1106 let highlight_maps = vec![grammar.highlight_map()];
1107 let mut offset = 0;
1108 for chunk in BufferChunks::new(text, range, Some((captures, highlight_maps)), vec![]) {
1109 let end_offset = offset + chunk.text.len();
1110 if let Some(highlight_id) = chunk.syntax_highlight_id {
1111 if !highlight_id.is_default() {
1112 result.push((offset..end_offset, highlight_id));
1113 }
1114 }
1115 offset = end_offset;
1116 }
1117 }
1118 result
1119 }
1120
1121 pub fn path_suffixes(&self) -> &[String] {
1122 &self.config.matcher.path_suffixes
1123 }
1124
1125 pub fn should_autoclose_before(&self, c: char) -> bool {
1126 c.is_whitespace() || self.config.autoclose_before.contains(c)
1127 }
1128
1129 pub fn set_theme(&self, theme: &SyntaxTheme) {
1130 if let Some(grammar) = self.grammar.as_ref() {
1131 if let Some(highlights_query) = &grammar.highlights_query {
1132 *grammar.highlight_map.lock() =
1133 HighlightMap::new(highlights_query.capture_names(), theme);
1134 }
1135 }
1136 }
1137
1138 pub fn grammar(&self) -> Option<&Arc<Grammar>> {
1139 self.grammar.as_ref()
1140 }
1141
1142 pub fn default_scope(self: &Arc<Self>) -> LanguageScope {
1143 LanguageScope {
1144 language: self.clone(),
1145 override_id: None,
1146 }
1147 }
1148
1149 pub fn prettier_parser_name(&self) -> Option<&str> {
1150 self.config.prettier_parser_name.as_deref()
1151 }
1152}
1153
1154impl LanguageScope {
1155 pub fn collapsed_placeholder(&self) -> &str {
1156 self.language.config.collapsed_placeholder.as_ref()
1157 }
1158
1159 /// Returns line prefix that is inserted in e.g. line continuations or
1160 /// in `toggle comments` action.
1161 pub fn line_comment_prefixes(&self) -> Option<&Vec<Arc<str>>> {
1162 Override::as_option(
1163 self.config_override().map(|o| &o.line_comments),
1164 Some(&self.language.config.line_comments),
1165 )
1166 }
1167
1168 pub fn block_comment_delimiters(&self) -> Option<(&Arc<str>, &Arc<str>)> {
1169 Override::as_option(
1170 self.config_override().map(|o| &o.block_comment),
1171 self.language.config.block_comment.as_ref(),
1172 )
1173 .map(|e| (&e.0, &e.1))
1174 }
1175
1176 /// Returns a list of language-specific word characters.
1177 ///
1178 /// By default, Zed treats alphanumeric characters (and '_') as word characters for
1179 /// the purpose of actions like 'move to next word end` or whole-word search.
1180 /// It additionally accounts for language's additional word characters.
1181 pub fn word_characters(&self) -> Option<&HashSet<char>> {
1182 Override::as_option(
1183 self.config_override().map(|o| &o.word_characters),
1184 Some(&self.language.config.word_characters),
1185 )
1186 }
1187
1188 /// Returns a list of bracket pairs for a given language with an additional
1189 /// piece of information about whether the particular bracket pair is currently active for a given language.
1190 pub fn brackets(&self) -> impl Iterator<Item = (&BracketPair, bool)> {
1191 let mut disabled_ids = self
1192 .config_override()
1193 .map_or(&[] as _, |o| o.disabled_bracket_ixs.as_slice());
1194 self.language
1195 .config
1196 .brackets
1197 .pairs
1198 .iter()
1199 .enumerate()
1200 .map(move |(ix, bracket)| {
1201 let mut is_enabled = true;
1202 if let Some(next_disabled_ix) = disabled_ids.first() {
1203 if ix == *next_disabled_ix as usize {
1204 disabled_ids = &disabled_ids[1..];
1205 is_enabled = false;
1206 }
1207 }
1208 (bracket, is_enabled)
1209 })
1210 }
1211
1212 pub fn should_autoclose_before(&self, c: char) -> bool {
1213 c.is_whitespace() || self.language.config.autoclose_before.contains(c)
1214 }
1215
1216 pub fn language_allowed(&self, name: &LanguageServerName) -> bool {
1217 let config = &self.language.config;
1218 let opt_in_servers = &config.scope_opt_in_language_servers;
1219 if opt_in_servers.iter().any(|o| *o == *name.0) {
1220 if let Some(over) = self.config_override() {
1221 over.opt_into_language_servers.iter().any(|o| *o == *name.0)
1222 } else {
1223 false
1224 }
1225 } else {
1226 true
1227 }
1228 }
1229
1230 fn config_override(&self) -> Option<&LanguageConfigOverride> {
1231 let id = self.override_id?;
1232 let grammar = self.language.grammar.as_ref()?;
1233 let override_config = grammar.override_config.as_ref()?;
1234 override_config.values.get(&id).map(|e| &e.1)
1235 }
1236}
1237
1238impl Hash for Language {
1239 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1240 self.id().hash(state)
1241 }
1242}
1243
1244impl PartialEq for Language {
1245 fn eq(&self, other: &Self) -> bool {
1246 self.id().eq(&other.id())
1247 }
1248}
1249
1250impl Eq for Language {}
1251
1252impl Debug for Language {
1253 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1254 f.debug_struct("Language")
1255 .field("name", &self.config.name)
1256 .finish()
1257 }
1258}
1259
1260impl Grammar {
1261 pub fn id(&self) -> usize {
1262 self.id
1263 }
1264
1265 fn parse_text(&self, text: &Rope, old_tree: Option<Tree>) -> Tree {
1266 PARSER.with(|parser| {
1267 let mut parser = parser.borrow_mut();
1268 parser
1269 .set_language(&self.ts_language)
1270 .expect("incompatible grammar");
1271 let mut chunks = text.chunks_in_range(0..text.len());
1272 parser
1273 .parse_with(
1274 &mut move |offset, _| {
1275 chunks.seek(offset);
1276 chunks.next().unwrap_or("").as_bytes()
1277 },
1278 old_tree.as_ref(),
1279 )
1280 .unwrap()
1281 })
1282 }
1283
1284 pub fn highlight_map(&self) -> HighlightMap {
1285 self.highlight_map.lock().clone()
1286 }
1287
1288 pub fn highlight_id_for_name(&self, name: &str) -> Option<HighlightId> {
1289 let capture_id = self
1290 .highlights_query
1291 .as_ref()?
1292 .capture_index_for_name(name)?;
1293 Some(self.highlight_map.lock().get(capture_id))
1294 }
1295}
1296
1297impl CodeLabel {
1298 pub fn plain(text: String, filter_text: Option<&str>) -> Self {
1299 let mut result = Self {
1300 runs: Vec::new(),
1301 filter_range: 0..text.len(),
1302 text,
1303 };
1304 if let Some(filter_text) = filter_text {
1305 if let Some(ix) = result.text.find(filter_text) {
1306 result.filter_range = ix..ix + filter_text.len();
1307 }
1308 }
1309 result
1310 }
1311}
1312
1313impl Ord for LanguageMatcher {
1314 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1315 self.path_suffixes.cmp(&other.path_suffixes).then_with(|| {
1316 self.first_line_pattern
1317 .as_ref()
1318 .map(Regex::as_str)
1319 .cmp(&other.first_line_pattern.as_ref().map(Regex::as_str))
1320 })
1321 }
1322}
1323
1324impl PartialOrd for LanguageMatcher {
1325 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1326 Some(self.cmp(other))
1327 }
1328}
1329
1330impl Eq for LanguageMatcher {}
1331
1332impl PartialEq for LanguageMatcher {
1333 fn eq(&self, other: &Self) -> bool {
1334 self.path_suffixes == other.path_suffixes
1335 && self.first_line_pattern.as_ref().map(Regex::as_str)
1336 == other.first_line_pattern.as_ref().map(Regex::as_str)
1337 }
1338}
1339
1340#[cfg(any(test, feature = "test-support"))]
1341impl Default for FakeLspAdapter {
1342 fn default() -> Self {
1343 Self {
1344 name: "the-fake-language-server",
1345 capabilities: lsp::LanguageServer::full_capabilities(),
1346 initializer: None,
1347 disk_based_diagnostics_progress_token: None,
1348 initialization_options: None,
1349 disk_based_diagnostics_sources: Vec::new(),
1350 prettier_plugins: Vec::new(),
1351 }
1352 }
1353}
1354
1355#[cfg(any(test, feature = "test-support"))]
1356#[async_trait]
1357impl LspAdapter for Arc<FakeLspAdapter> {
1358 fn name(&self) -> LanguageServerName {
1359 LanguageServerName(self.name.into())
1360 }
1361
1362 fn short_name(&self) -> &'static str {
1363 "FakeLspAdapter"
1364 }
1365
1366 async fn fetch_latest_server_version(
1367 &self,
1368 _: &dyn LspAdapterDelegate,
1369 ) -> Result<Box<dyn 'static + Send + Any>> {
1370 unreachable!();
1371 }
1372
1373 async fn fetch_server_binary(
1374 &self,
1375 _: Box<dyn 'static + Send + Any>,
1376 _: PathBuf,
1377 _: &dyn LspAdapterDelegate,
1378 ) -> Result<LanguageServerBinary> {
1379 unreachable!();
1380 }
1381
1382 async fn cached_server_binary(
1383 &self,
1384 _: PathBuf,
1385 _: &dyn LspAdapterDelegate,
1386 ) -> Option<LanguageServerBinary> {
1387 unreachable!();
1388 }
1389
1390 async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
1391 unreachable!();
1392 }
1393
1394 fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
1395
1396 fn disk_based_diagnostic_sources(&self) -> Vec<String> {
1397 self.disk_based_diagnostics_sources.clone()
1398 }
1399
1400 fn disk_based_diagnostics_progress_token(&self) -> Option<String> {
1401 self.disk_based_diagnostics_progress_token.clone()
1402 }
1403
1404 fn initialization_options(&self) -> Option<Value> {
1405 self.initialization_options.clone()
1406 }
1407
1408 fn prettier_plugins(&self) -> &[&'static str] {
1409 &self.prettier_plugins
1410 }
1411}
1412
1413fn get_capture_indices(query: &Query, captures: &mut [(&str, &mut Option<u32>)]) {
1414 for (ix, name) in query.capture_names().iter().enumerate() {
1415 for (capture_name, index) in captures.iter_mut() {
1416 if capture_name == name {
1417 **index = Some(ix as u32);
1418 break;
1419 }
1420 }
1421 }
1422}
1423
1424pub fn point_to_lsp(point: PointUtf16) -> lsp::Position {
1425 lsp::Position::new(point.row, point.column)
1426}
1427
1428pub fn point_from_lsp(point: lsp::Position) -> Unclipped<PointUtf16> {
1429 Unclipped(PointUtf16::new(point.line, point.character))
1430}
1431
1432pub fn range_to_lsp(range: Range<PointUtf16>) -> lsp::Range {
1433 lsp::Range {
1434 start: point_to_lsp(range.start),
1435 end: point_to_lsp(range.end),
1436 }
1437}
1438
1439pub fn range_from_lsp(range: lsp::Range) -> Range<Unclipped<PointUtf16>> {
1440 let mut start = point_from_lsp(range.start);
1441 let mut end = point_from_lsp(range.end);
1442 if start > end {
1443 mem::swap(&mut start, &mut end);
1444 }
1445 start..end
1446}
1447
1448#[cfg(test)]
1449mod tests {
1450 use super::*;
1451 use gpui::TestAppContext;
1452
1453 #[gpui::test(iterations = 10)]
1454 async fn test_first_line_pattern(cx: &mut TestAppContext) {
1455 let mut languages = LanguageRegistry::test();
1456
1457 languages.set_executor(cx.executor());
1458 let languages = Arc::new(languages);
1459 languages.register_test_language(LanguageConfig {
1460 name: "JavaScript".into(),
1461 matcher: LanguageMatcher {
1462 path_suffixes: vec!["js".into()],
1463 first_line_pattern: Some(Regex::new(r"\bnode\b").unwrap()),
1464 },
1465 ..Default::default()
1466 });
1467
1468 languages
1469 .language_for_file("the/script", None)
1470 .await
1471 .unwrap_err();
1472 languages
1473 .language_for_file("the/script", Some(&"nothing".into()))
1474 .await
1475 .unwrap_err();
1476 assert_eq!(
1477 languages
1478 .language_for_file("the/script", Some(&"#!/bin/env node".into()))
1479 .await
1480 .unwrap()
1481 .name()
1482 .as_ref(),
1483 "JavaScript"
1484 );
1485 }
1486
1487 #[gpui::test(iterations = 10)]
1488 async fn test_language_loading(cx: &mut TestAppContext) {
1489 let mut languages = LanguageRegistry::test();
1490 languages.set_executor(cx.executor());
1491 let languages = Arc::new(languages);
1492 languages.register_native_grammars([
1493 ("json", tree_sitter_json::language()),
1494 ("rust", tree_sitter_rust::language()),
1495 ]);
1496 languages.register_test_language(LanguageConfig {
1497 name: "JSON".into(),
1498 grammar: Some("json".into()),
1499 matcher: LanguageMatcher {
1500 path_suffixes: vec!["json".into()],
1501 ..Default::default()
1502 },
1503 ..Default::default()
1504 });
1505 languages.register_test_language(LanguageConfig {
1506 name: "Rust".into(),
1507 grammar: Some("rust".into()),
1508 matcher: LanguageMatcher {
1509 path_suffixes: vec!["rs".into()],
1510 ..Default::default()
1511 },
1512 ..Default::default()
1513 });
1514 assert_eq!(
1515 languages.language_names(),
1516 &[
1517 "JSON".to_string(),
1518 "Plain Text".to_string(),
1519 "Rust".to_string(),
1520 ]
1521 );
1522
1523 let rust1 = languages.language_for_name("Rust");
1524 let rust2 = languages.language_for_name("Rust");
1525
1526 // Ensure language is still listed even if it's being loaded.
1527 assert_eq!(
1528 languages.language_names(),
1529 &[
1530 "JSON".to_string(),
1531 "Plain Text".to_string(),
1532 "Rust".to_string(),
1533 ]
1534 );
1535
1536 let (rust1, rust2) = futures::join!(rust1, rust2);
1537 assert!(Arc::ptr_eq(&rust1.unwrap(), &rust2.unwrap()));
1538
1539 // Ensure language is still listed even after loading it.
1540 assert_eq!(
1541 languages.language_names(),
1542 &[
1543 "JSON".to_string(),
1544 "Plain Text".to_string(),
1545 "Rust".to_string(),
1546 ]
1547 );
1548
1549 // Loading an unknown language returns an error.
1550 assert!(languages.language_for_name("Unknown").await.is_err());
1551 }
1552}