use crate::{shape::Shape, CallParenType, Config, IndentType, LineEndings, Range as FormatRange};
use full_moon::{
node::Node,
tokenizer::{Token, TokenType},
};
#[derive(Debug, Clone, Copy)]
pub struct Context {
config: Config,
range: Option<FormatRange>,
formatting_disabled: bool,
}
impl Context {
pub fn new(config: Config, range: Option<FormatRange>) -> Self {
Self {
config,
range,
formatting_disabled: false,
}
}
pub fn config(&self) -> Config {
self.config
}
pub fn check_toggle_formatting(&self, node: &impl Node) -> Self {
let leading_trivia = node.surrounding_trivia().0;
for trivia in leading_trivia {
let comment_lines = match trivia.token_type() {
TokenType::SingleLineComment { comment } => comment,
TokenType::MultiLineComment { comment, .. } => comment,
_ => continue,
}
.lines()
.map(|line| line.trim());
for line in comment_lines {
if line == "stylua: ignore start" && !self.formatting_disabled {
return Self {
formatting_disabled: true,
..*self
};
} else if line == "stylua: ignore end" && self.formatting_disabled {
return Self {
formatting_disabled: false,
..*self
};
}
}
}
*self
}
pub fn should_format_node(&self, node: &impl Node) -> bool {
if self.formatting_disabled {
return false;
}
let leading_trivia = node.surrounding_trivia().0;
for trivia in leading_trivia {
let comment_lines = match trivia.token_type() {
TokenType::SingleLineComment { comment } => comment,
TokenType::MultiLineComment { comment, .. } => comment,
_ => continue,
}
.lines()
.map(|line| line.trim());
for line in comment_lines {
if line == "stylua: ignore" {
return false;
}
}
}
if let Some(range) = self.range {
let mut in_range = true;
if let Some(start_bound) = range.start {
if let Some(node_start) = node.start_position() {
if node_start.bytes() < start_bound {
in_range = false;
}
}
}
if let Some(end_bound) = range.end {
if let Some(node_end) = node.end_position() {
if node_end.bytes() > end_bound {
in_range = false;
}
}
}
in_range
} else {
true
}
}
pub fn should_omit_string_parens(&self) -> bool {
self.config().no_call_parentheses
|| self.config().call_parentheses == CallParenType::None
|| self.config().call_parentheses == CallParenType::NoSingleString
}
pub fn should_omit_table_parens(&self) -> bool {
self.config().no_call_parentheses
|| self.config().call_parentheses == CallParenType::None
|| self.config().call_parentheses == CallParenType::NoSingleTable
}
}
#[macro_export]
macro_rules! check_should_format {
($ctx:expr, $token:expr) => {
if !$ctx.should_format_node($token) {
return $token.to_owned();
}
};
}
fn line_ending_character(line_endings: LineEndings) -> String {
match line_endings {
LineEndings::Unix => String::from("\n"),
LineEndings::Windows => String::from("\r\n"),
}
}
pub fn create_indent_trivia(ctx: &Context, shape: Shape) -> Token {
let indent_level = shape.indent().block_indent() + shape.indent().additional_indent();
create_plain_indent_trivia(ctx, indent_level)
}
pub fn create_plain_indent_trivia(ctx: &Context, indent_level: usize) -> Token {
match ctx.config().indent_type {
IndentType::Tabs => Token::new(TokenType::tabs(indent_level)),
IndentType::Spaces => {
Token::new(TokenType::spaces(indent_level * ctx.config().indent_width))
}
}
}
pub fn create_newline_trivia(ctx: &Context) -> Token {
Token::new(TokenType::Whitespace {
characters: line_ending_character(ctx.config().line_endings).into(),
})
}