syntastica_core/language_set.rs
1//! Defines the [`LanguageSet`] trait and some related types.
2//!
3//! Also re-exports [`syntastica_highlight::HighlightConfiguration`], [`tree_sitter::Language`],
4//! and [`tft::FileType`].
5
6use std::{borrow::Cow, path::Path};
7
8pub use syntastica_highlight::HighlightConfiguration;
9pub use tft::FileType;
10
11pub use crate::ts_runtime::Language;
12
13mod union;
14
15pub use union::*;
16
17/// A language included in a [`LanguageSet`].
18///
19/// Instances can be obtained with [`for_name`](SupportedLanguage::for_name),
20/// [`for_file_type`](SupportedLanguage::for_file_type), and
21/// [`for_injection`](SupportedLanguage::for_injection).
22pub trait SupportedLanguage<'set, S>: Sized {
23 /// Get the name for this language.
24 ///
25 /// Passing the output of this function to [`for_name`](SupportedLanguage::for_name) must
26 /// always result in an [`Ok`] value.
27 // TODO: should this function really be part of the trait?
28 fn name(&self) -> Cow<'_, str>;
29
30 /// Get the language with the given name.
31 ///
32 /// If no language for that name exists, implementations should return an
33 /// [`UnsupportedLanguage`](crate::Error::UnsupportedLanguage) error.
34 ///
35 /// `syntastica` itself does not provide a list of valid language names, but the
36 /// [official parser collections](https://rubixdev.github.io/syntastica/syntastica/#parser-collections)
37 /// do. However, every string that may be returned by [`name`](SupportedLanguage::name) must
38 /// result in an [`Ok`] value.
39 fn for_name(name: impl AsRef<str>, set: &'set S) -> Result<Self, crate::Error>;
40
41 /// Find a language based on the given [`FileType`].
42 ///
43 /// Implementations should return the language that supports the given file type, if there is
44 /// any.
45 fn for_file_type(file_type: FileType, set: &'set S) -> Option<Self>;
46
47 /// Find a language for an injection.
48 ///
49 /// The passed `name` could be any string that somehow identifies a language. This very much
50 /// depends on the source of the injection. For example, in fenced code blocks in markdown, the
51 /// text after the opening `` ``` `` is passed to this function.
52 ///
53 /// Note that this function does not get called, if [`for_name`](SupportedLanguage::for_name)
54 /// was able to return a language for `name`.
55 ///
56 /// The default implementation tries to detect a [`FileType`] using `name` as both a filename
57 /// and a file extension, and passes that to
58 /// [`for_file_type`](SupportedLanguage::for_file_type).
59 fn for_injection(name: impl AsRef<str>, set: &'set S) -> Option<Self> {
60 let name = name.as_ref();
61 // try to detect a file type, once using the name as a full path and once using it as the
62 // extension, and if one is found, pass it to `self.for_file_type`
63 tft::try_detect(Path::new(name), "")
64 .or_else(|| tft::try_detect(Path::new(&format!("file.{name}")), ""))
65 .and_then(|ft| Self::for_file_type(ft, set))
66 }
67}
68
69/// Describes a type that is able to provide tree-sitter parsers and queries.
70///
71/// All official syntastica parser collections provide a type called `LanguageSetImpl` which
72/// implements this trait. See
73/// [the project overview](https://rubixdev.github.io/syntastica/syntastica/#parser-collections)
74/// for more information on that.
75pub trait LanguageSet<'s>: Sized {
76 /// A type identifying a language that is included in this set.
77 ///
78 /// The given type should usually be an enum of all included languages.
79 type Language: SupportedLanguage<'s, Self>;
80
81 /// Get the [`HighlightConfiguration`] for a given [language](LanguageSet::Language).
82 ///
83 /// **The returned [`HighlightConfiguration`] _must_ be
84 /// [configured](HighlightConfiguration::configure) with
85 /// [`THEME_KEYS`](crate::theme::THEME_KEYS).**
86 ///
87 /// The function is given an instance of [`Self::Language`] and as such should not need to
88 /// return an [`Error::UnsupportedLanguage`](crate::Error::UnsupportedLanguage) error.
89 fn get_language(
90 &self,
91 language: Self::Language,
92 ) -> Result<&HighlightConfiguration, crate::Error>;
93}