use std::borrow::Cow;
use std::fmt::Debug;
use std::ops::Range;
use cow_utils::CowUtils;
use derive_builder::Builder;
use itertools::Itertools;
use nested_intervals::IntervalSet;
use rowan::{TextRange, TextSize};
use crate::syntax::ast::AstToken;
use crate::syntax::ast::tokens::{DoubleQuotedValue, SingleQuotedValue};
use crate::syntax::make;
use crate::syntax::syntax_kind::SyntaxKind;
use crate::syntax::syntax_node::SyntaxToken;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum QuotedValue {
SingleQuoted(SingleQuotedValue),
DoubleQuoted(DoubleQuotedValue),
}
impl AstToken for QuotedValue {
fn can_cast(token: SyntaxKind) -> bool
where
Self: Sized,
{
match token {
SyntaxKind::DoubleQuotedValue | SyntaxKind::SingleQuotedValue => true,
_ => false,
}
}
fn cast(syntax: SyntaxToken) -> Option<Self>
where
Self: Sized,
{
let ret = match syntax.kind() {
SyntaxKind::DoubleQuotedValue => {
QuotedValue::DoubleQuoted(DoubleQuotedValue { syntax })
}
SyntaxKind::SingleQuotedValue => {
QuotedValue::SingleQuoted(SingleQuotedValue { syntax })
}
_ => return None,
};
Some(ret)
}
fn syntax(&self) -> &SyntaxToken {
match self {
QuotedValue::DoubleQuoted(it) => &it.syntax,
QuotedValue::SingleQuoted(it) => &it.syntax,
}
}
}
impl QuotedValue {
pub fn text_range_between_quotes(&self) -> TextRange {
let range = self.syntax().text_range();
let quote_size = TextSize::of("\"");
let (start, end) = (range.start() + quote_size, range.end() - quote_size);
TextRange::new(start, end)
}
pub fn raw_value(&self) -> &str {
&self.text()[self.text_range_between_quotes() - self.syntax().text_range().start()]
}
pub fn lines(&self) -> impl Iterator<Item = &str> {
self.line_ranges().into_iter().map(move |range| {
let start_offset = self.syntax().text_range().start();
let absolute_range = range - start_offset;
&self.text()[absolute_range]
})
}
pub fn sorted_lines(&self) -> Self {
let vals = self
.lines()
.filter_map(|v| match v.trim() {
"" => None,
v => Some(v.to_owned()),
})
.sorted()
.collect::<Vec<_>>();
make::quoted_value_from_slice(&vals)
}
pub fn value(&self) -> Cow<str> {
self.raw_value().cow_replace("\\\n", "")
}
pub fn line_ranges(&self) -> Vec<TextRange> {
let range = self.text_range_between_quotes();
let start_offset = self.syntax().text_range().start();
let relative_range =
TextRange::new(range.start() - start_offset, range.end() - start_offset);
assert_eq!(u32::from(relative_range.start()), 1);
assert_eq!(
u32::from(relative_range.end()),
self.text().len() as u32 - 1
);
let escaped_newline_size = TextSize::of("\\\n");
let intervals = self
.raw_value()
.match_indices("\\\n")
.map(|m| {
let start = m.0 as u32 + 1;
start..start + u32::from(escaped_newline_size)
})
.collect::<Vec<Range<u32>>>();
let interval = IntervalSet::new(intervals.as_slice()).unwrap();
interval
.invert(
u32::from(relative_range.start()),
u32::from(relative_range.end()),
)
.iter()
.map(|(range, _)| {
TextRange::new(
TextSize::from(range.start) + start_offset,
TextSize::from(range.end) + start_offset,
)
})
.collect::<Vec<_>>()
}
}
#[derive(Debug, Builder, Clone)]
#[builder(setter(into))]
pub struct QuotedValueFormat {
packing: SubValuePacking,
closing_quote_on_own_line: bool,
alignment: SubValueAlignment,
}
impl Default for QuotedValueFormat {
fn default() -> Self {
Self {
alignment: SubValueAlignment::default(),
closing_quote_on_own_line: true,
packing: SubValuePacking::default(),
}
}
}
#[derive(Debug, Default, Clone)]
pub enum SubValuePacking {
Off,
#[default]
OnePerLine,
Wrapped {
line_width: usize,
},
}
#[derive(Debug, Clone)]
pub enum SubValueAlignment {
WithOperator,
WithFirstSubValue,
Indented { indent: usize },
}
impl Default for SubValueAlignment {
fn default() -> Self {
SubValueAlignment::Indented { indent: 4 }
}
}
pub struct QuotedValueBuilder {
sub_values: Vec<String>,
}
fn ff() {
let _b = QuotedValueFormatBuilder::default()
.alignment(SubValueAlignment::WithFirstSubValue)
.build();
}