use monostyle_core::Language;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BlockStyle {
Brace,
Indentation,
EndKeyword,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Interpolation {
Dollar {
braced: bool,
},
Hash,
DollarBrace,
Brace,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HeredocSyntax {
pub marker: &'static str,
pub allows_dash: bool,
pub allows_tilde: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StringRule {
pub open: &'static str,
pub close: &'static str,
pub escapes: bool,
pub multiline: bool,
pub interpolates: bool,
pub extra_escapes: &'static [&'static str],
}
impl StringRule {
#[must_use]
pub const fn escaped(open: &'static str, close: &'static str) -> Self {
Self {
open,
close,
escapes: true,
multiline: false,
interpolates: false,
extra_escapes: &[],
}
}
#[must_use]
pub const fn raw(open: &'static str, close: &'static str) -> Self {
Self {
open,
close,
escapes: false,
multiline: false,
interpolates: false,
extra_escapes: &[],
}
}
#[must_use]
pub const fn multiline(open: &'static str, close: &'static str) -> Self {
Self {
open,
close,
escapes: true,
multiline: true,
interpolates: false,
extra_escapes: &[],
}
}
#[must_use]
pub const fn multiline_raw(open: &'static str, close: &'static str) -> Self {
Self {
open,
close,
escapes: false,
multiline: true,
interpolates: false,
extra_escapes: &[],
}
}
#[must_use]
pub const fn interpolated(open: &'static str, close: &'static str) -> Self {
Self {
open,
close,
escapes: true,
multiline: false,
interpolates: true,
extra_escapes: &[],
}
}
#[must_use]
pub const fn multiline_interpolated(open: &'static str, close: &'static str) -> Self {
Self {
open,
close,
escapes: true,
multiline: true,
interpolates: true,
extra_escapes: &[],
}
}
#[must_use]
pub const fn with_extra_escapes(mut self, escapes: &'static [&'static str]) -> Self {
self.extra_escapes = escapes;
self
}
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, Copy)]
pub struct LanguageProfile {
pub language: Language,
pub line_comments: &'static [&'static str],
pub block_comments: &'static [(&'static str, &'static str)],
pub nestable_comments: bool,
pub strings: &'static [StringRule],
pub string_prefixes: &'static [char],
pub hashed_raw_strings: bool,
pub raw_string_prefix: Option<char>,
pub interpolation: Option<Interpolation>,
pub heredoc: Option<HeredocSyntax>,
pub regex_literals: bool,
pub decision_keywords: &'static [&'static str],
pub nesting_keywords: &'static [&'static str],
pub logical_operators: &'static [&'static str],
pub jump_keywords: &'static [&'static str],
pub ternary_operator: Option<char>,
pub null_coalescing: &'static [&'static str],
pub block_style: BlockStyle,
pub end_keywords: &'static [&'static str],
pub parameter_list_start: Option<char>,
pub variables_use_sigil: bool,
}
impl LanguageProfile {
#[must_use]
pub fn block_comment_at(&self, text: &str) -> Option<(&'static str, &'static str)> {
let mut matches: Vec<(&'static str, &'static str)> = self
.block_comments
.iter()
.copied()
.filter(|(open, _)| text.starts_with(open))
.collect();
matches.sort_by_key(|(open, _)| std::cmp::Reverse(open.len()));
matches.into_iter().next()
}
#[must_use]
pub fn line_comment_at(&self, text: &str) -> Option<&'static str> {
let mut matches: Vec<&'static str> = self
.line_comments
.iter()
.copied()
.filter(|token| text.starts_with(token))
.collect();
matches.sort_by_key(|token| std::cmp::Reverse(token.len()));
matches.into_iter().next()
}
#[must_use]
pub const fn uses_doc_strings(self) -> bool {
matches!(self.language, Language::Python | Language::Elixir)
}
#[must_use]
pub fn is_documentation_comment(&self, text: &str) -> bool {
let trimmed = text.trim_start();
if trimmed.starts_with("///") || trimmed.starts_with("//!") {
return true;
}
if trimmed.starts_with("/**") {
return true;
}
if trimmed.starts_with("##") {
return true;
}
trimmed.starts_with("//<") || trimmed.starts_with("///<")
}
#[must_use]
pub fn string_at(&self, text: &str) -> Option<&'static StringRule> {
let mut matches: Vec<&'static StringRule> = self
.strings
.iter()
.filter(|rule| text.starts_with(rule.open))
.collect();
matches.sort_by_key(|rule| std::cmp::Reverse(rule.open.len()));
matches.into_iter().next()
}
#[must_use]
pub fn regex_allowed_after(&self, previous: char) -> bool {
if !self.regex_literals {
return false;
}
matches!(
previous,
'(' | ','
| '=' | ':' | '['
| '!' | '&' | '|'
| '?' | '{' | '}'
| ';' | '+' | '-'
| '*' | '%' | '<'
| '>' | '^' | '~'
)
}
}