mxmlextrema_as3parser/compilation_unit/
location.rsuse std::cmp::Ordering;
use std::fmt::Debug;
use serde::{Serialize, Deserialize, Serializer};
use std::rc::Rc;
use crate::compilation_unit::*;
use crate::util::{CharacterReader, count_first_whitespace_characters};
use crate::parser::CharacterValidator;
#[derive(Clone, Deserialize)]
pub struct Location {
#[serde(skip)]
pub(crate) compilation_unit: Rc<CompilationUnit>,
#[serde(skip)]
pub(crate) first_offset: usize,
#[serde(skip)]
pub(crate) last_offset: usize,
}
impl Serialize for Location {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("{}:{}-{}:{}", self.first_line_number(), self.first_column() + 1, self.last_line_number(), self.last_column() + 1))
}
}
impl Debug for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,
"Location(first_line_number={}, first_column={}, first_offset={}, last_line_number={}, last_column={}, last_offset={})",
self.first_line_number(),
self.first_column(),
self.first_offset,
self.last_line_number(),
self.last_column(),
self.last_offset
)
}
}
impl Eq for Location {}
impl PartialEq for Location {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.compilation_unit, &other.compilation_unit) &&
self.first_offset == other.first_offset &&
self.last_offset == other.last_offset
}
}
impl Ord for Location {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_or(Ordering::Equal)
}
}
impl PartialOrd for Location {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.first_offset.partial_cmp(&other.first_offset)
}
}
impl Location {
pub fn with_offsets(
compilation_unit: &Rc<CompilationUnit>,
first_offset: usize,
last_offset: usize,
) -> Self {
Self {
compilation_unit: compilation_unit.clone(),
first_offset,
last_offset,
}
}
pub fn with_offset(compilation_unit: &Rc<CompilationUnit>, offset: usize) -> Self {
Self::with_offsets(compilation_unit, offset, offset)
}
pub fn combine_with(&self, other: Location) -> Self {
Self {
compilation_unit: self.compilation_unit.clone(),
first_offset: self.first_offset,
last_offset: other.last_offset,
}
}
pub fn combine_with_start_of(&self, other: Location) -> Self {
Self {
compilation_unit: self.compilation_unit.clone(),
first_offset: self.first_offset,
last_offset: other.first_offset,
}
}
pub fn compilation_unit(&self) -> Rc<CompilationUnit> {
self.compilation_unit.clone()
}
pub fn first_line_number(&self) -> usize {
self.compilation_unit.get_line_number(self.first_offset)
}
pub fn last_line_number(&self) -> usize {
self.compilation_unit.get_line_number(self.last_offset)
}
pub fn first_offset(&self) -> usize {
self.first_offset
}
pub fn last_offset(&self) -> usize {
self.last_offset
}
pub fn first_column(&self) -> usize {
self.compilation_unit.get_column(self.first_offset)
}
pub fn last_column(&self) -> usize {
self.compilation_unit.get_column(self.last_offset)
}
pub fn character_count(&self) -> usize {
self.compilation_unit.text()[self.first_offset..self.last_offset].chars().count()
}
pub fn line_break(&self, other: &Self) -> bool {
self.last_line_number() != other.first_line_number()
}
pub fn text(&self) -> String {
self.compilation_unit.text()[self.first_offset..self.last_offset].to_owned()
}
pub fn shift_until_eof(&self, count: usize) -> Location {
let mut ch = CharacterReader::from(&self.compilation_unit.text()[self.first_offset..]);
for _ in 0..count {
if ch.next().is_none() {
break;
}
}
Self::with_offsets(&self.compilation_unit, self.first_offset + ch.index(), self.last_offset)
}
pub fn shift_whitespace(&self, text: &str) -> Location {
self.shift_until_eof(count_first_whitespace_characters(text))
}
pub fn show_code(&self) -> String {
let cu = self.compilation_unit();
let start = cu.get_line_offset(self.first_line_number());
if let Some(start) = start {
let end = cu.get_line_offset(self.first_line_number() + 1).unwrap_or(cu.text().len());
let mut subregion = &cu.text()[start..end];
let mut indent_length = 0usize;
for ch in subregion.chars() {
if !CharacterValidator::is_whitespace(ch) {
break;
}
indent_length += ch.len_utf8();
}
let mut line_terminator_length = 0usize;
for ch in subregion.chars().rev() {
if CharacterValidator::is_line_terminator(ch) {
line_terminator_length += ch.len_utf8();
}
}
subregion = &subregion[indent_length..(subregion.len() - line_terminator_length)];
if subregion.len() >= 150 {
subregion = &subregion[..149];
}
let mut pointer = " ".repeat(cu.text()[(start + indent_length)..self.first_offset()].chars().count()) + "^";
if pointer.len() >= 150 {
pointer = pointer[..149].to_owned();
}
let line = format!("{} | {}", self.first_line_number(), subregion);
let pointer = format!("{} {}", " ".repeat(self.first_line_number().to_string().chars().count()), pointer);
return format!("{line}\n{pointer}");
}
"".into()
}
}