syntastica_core/
language_set.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! Defines the [`LanguageSet`] trait and some related types.
//!
//! Also re-exports [`syntastica_highlight::HighlightConfiguration`], [`tree_sitter::Language`],
//! and [`tft::FileType`].

use std::{borrow::Cow, path::Path};

pub use syntastica_highlight::HighlightConfiguration;
pub use tft::FileType;

pub use crate::ts_runtime::Language;

/// A language included in a [`LanguageSet`].
///
/// Instances can be obtained with [`for_name`](SupportedLanguage::for_name),
/// [`for_file_type`](SupportedLanguage::for_file_type), and
/// [`for_injection`](SupportedLanguage::for_injection).
pub trait SupportedLanguage: Sized {
    /// Get the name for this language.
    ///
    /// Passing the output of this function to [`for_name`](SupportedLanguage::for_name) must
    /// always result in an [`Ok`] value.
    // TODO: should this function really be part of the trait?
    fn name(&self) -> Cow<'_, str>;

    /// Get the language with the given name.
    ///
    /// If no language for that name exists, implementations should return an
    /// [`UnsupportedLanguage`](crate::Error::UnsupportedLanguage) error.
    ///
    /// `syntastica` itself does not provide a list of valid language names, but the
    /// [official parser collections](https://rubixdev.github.io/syntastica/syntastica/#parser-collections)
    /// do. However, every string that may be returned by [`name`](SupportedLanguage::name) must
    /// result in an [`Ok`] value.
    fn for_name(name: impl AsRef<str>) -> Result<Self, crate::Error>;

    /// Find a language based on the given [`FileType`].
    ///
    /// Implementations should return the language that supports the given file type, if there is
    /// any.
    fn for_file_type(file_type: FileType) -> Option<Self>;

    /// Find a language for an injection.
    ///
    /// The passed `name` could be any string that somehow identifies a language. This very much
    /// depends on the source of the injection. For example, in fenced code blocks in markdown, the
    /// text after the opening `` ``` `` is passed to this function.
    ///
    /// Note that this function does not get called, if [`for_name`](SupportedLanguage::for_name)
    /// was able to return a language for `name`.
    ///
    /// The default implementation tries to detect a [`FileType`] using `name` as both a filename
    /// and a file extension, and passes that to
    /// [`for_file_type`](SupportedLanguage::for_file_type).
    fn for_injection(name: impl AsRef<str>) -> Option<Self> {
        let name = name.as_ref();
        // try to detect a file type, once using the name as a full path and once using it as the
        // extension, and if one is found, pass it to `self.for_file_type`
        tft::try_detect(Path::new(name), "")
            .or_else(|| tft::try_detect(Path::new(&format!("file.{name}")), ""))
            .and_then(|ft| Self::for_file_type(ft))
    }
}

/// Describes a type that is able to provide tree-sitter parsers and queries.
///
/// All official syntastica parser collections provide a type called `LanguageSetImpl` which
/// implements this trait. See
/// [the project overview](https://rubixdev.github.io/syntastica/syntastica/#parser-collections)
/// for more information on that.
pub trait LanguageSet {
    /// A type identifying a language that is included in this set.
    ///
    /// The given type should usually be an enum of all included languages.
    type Language: SupportedLanguage;

    /// Get the language with the given name.
    ///
    /// **The returned [`HighlightConfiguration`] _must_ be
    /// [configured](HighlightConfiguration::configure) with
    /// [`THEME_KEYS`](crate::theme::THEME_KEYS).**
    ///
    /// The function is given an instance of [`Self::Language`] and as such should not need to
    /// return an [`Error::UnsupportedLanguage`](crate::Error::UnsupportedLanguage) error.
    fn get_language(
        &self,
        language: Self::Language,
    ) -> Result<&HighlightConfiguration, crate::Error>;
}