#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IndentStyle {
Spaces(u8),
Tabs { width: u8 },
}
impl IndentStyle {
pub fn unit(&self) -> String {
match self {
Self::Spaces(n) => " ".repeat(*n as usize),
Self::Tabs { .. } => "\t".to_string(),
}
}
pub fn width(&self) -> u8 {
match self {
Self::Spaces(n) => *n,
Self::Tabs { width } => *width,
}
}
}
impl Default for IndentStyle {
fn default() -> Self {
Self::Spaces(4)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BracketPair {
pub open: char,
pub close: char,
}
impl BracketPair {
pub const fn new(open: char, close: char) -> Self {
Self { open, close }
}
}
pub const COMMON_BRACKETS: &[BracketPair] = &[
BracketPair::new('(', ')'),
BracketPair::new('[', ']'),
BracketPair::new('{', '}'),
];
#[derive(Debug, Clone)]
pub struct CodeConfig {
pub indent: IndentStyle,
pub auto_indent: bool,
pub line_comment: Option<String>,
pub brackets: Vec<BracketPair>,
pub auto_close_brackets: bool,
pub match_brackets: bool,
}
impl Default for CodeConfig {
fn default() -> Self {
Self {
indent: IndentStyle::default(),
auto_indent: true,
line_comment: None,
brackets: Vec::new(),
auto_close_brackets: false,
match_brackets: false,
}
}
}
impl CodeConfig {
pub fn closing_for(&self, open: char) -> Option<char> {
self.brackets
.iter()
.find(|p| p.open == open)
.map(|p| p.close)
}
pub fn opening_for(&self, close: char) -> Option<char> {
self.brackets
.iter()
.find(|p| p.close == close)
.map(|p| p.open)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn spaces_indent_unit_is_its_width() {
assert_eq!(IndentStyle::Spaces(4).unit(), " ");
assert_eq!(IndentStyle::Spaces(2).unit(), " ");
}
#[test]
fn tab_indent_unit_is_one_character_regardless_of_width() {
assert_eq!(IndentStyle::Tabs { width: 8 }.unit(), "\t");
assert_eq!(IndentStyle::Tabs { width: 8 }.width(), 8);
}
#[test]
fn the_default_config_makes_no_language_assumptions() {
let c = CodeConfig::default();
assert!(c.line_comment.is_none(), "must not guess a comment token");
assert!(c.brackets.is_empty(), "must not guess bracket pairs");
assert!(!c.auto_close_brackets);
assert!(!c.match_brackets);
assert!(c.auto_indent);
assert_eq!(c.indent, IndentStyle::Spaces(4));
}
#[test]
fn bracket_lookup_resolves_both_directions() {
let c = CodeConfig {
brackets: COMMON_BRACKETS.to_vec(),
..CodeConfig::default()
};
assert_eq!(c.closing_for('('), Some(')'));
assert_eq!(c.opening_for('}'), Some('{'));
assert_eq!(c.closing_for('<'), None, "unconfigured pairs stay unknown");
}
}