use crate::connector::DocIdent;
use anyhow::Result;
use pest::{Parser, Span, iterators::Pair};
use crate::ron::parse::{RonParser, Rule};
#[derive(Debug, Clone)]
pub enum Component {
Name(String),
Key(String),
Index(usize),
}
pub fn ron_path_to_string(path: &Vec<Component>) -> String {
let mut first = true;
let mut res = String::new();
for c in path {
match c {
Component::Name(s) => {
if !first {
res.push('.');
} else {
first = false;
}
res.push_str(s);
}
Component::Key(s) => {
res.push('[');
res.push_str(s);
res.push(']');
}
Component::Index(n) => {
res.push('[');
res.push_str(&n.to_string());
res.push(']');
}
}
}
res
}
pub fn path_at(src: &str, line: usize, col: usize) -> Result<Option<Vec<Component>>> {
let Some(byte_index) = pos_byte_index(line, col, src) else {
return Ok(None);
};
let mut pairs = RonParser::parse(Rule::ron, src)?;
let root = pairs.next().unwrap();
let mut trail = Vec::<Component>::new();
descend(root, byte_index, src, &mut trail)?;
Ok(Some(trail))
}
type RonPath = Vec<Component>;
#[derive(Default)]
pub struct RonStringReport {
pub root: Option<String>,
pub strings: Vec<(RonPath, String)>,
}
pub fn find_strings(src: &str) -> Result<RonStringReport> {
let mut pairs = RonParser::parse(Rule::ron, src)?;
let mut res = RonStringReport::default();
let root = pairs.next().unwrap();
let mut trail = Vec::new();
descend_find_strings(root, &mut res.root, src, &mut trail, &mut res.strings);
Ok(res)
}
pub fn ident_at(src: &str, line: usize, col: usize) -> Result<Option<DocIdent>> {
let Some(byte_index) = pos_byte_index(line, col, src) else {
return Ok(None);
};
let mut pairs = RonParser::parse(Rule::ron, src)?;
let root = pairs.next().unwrap();
let mut ident = None;
let mut parent = None;
descend_find_docident(root, byte_index, src, &mut parent, &mut ident)?;
Ok(ident)
}
fn descend_find_docident(
pair: Pair<Rule>,
cursor: usize,
_src: &str,
parent: &mut Option<DocIdent>,
ident: &mut Option<DocIdent>,
) -> Result<bool> {
let span = pair.as_span();
if !covers(span, cursor) {
return Ok(false);
}
match pair.as_rule() {
Rule::ron | Rule::value => {
for child in pair.into_inner() {
if descend_find_docident(child, cursor, _src, parent, ident)? {
break;
}
}
}
Rule::named_struct | Rule::tuple_struct | Rule::unit_struct => {
let mut it = pair.clone().into_inner();
if let Some(id_pair) = it.next()
&& id_pair.as_rule() == Rule::ident
{
if covers(id_pair.as_span(), cursor) {
*ident = Some(DocIdent::Struct {
name: id_pair.as_str().into(),
})
}
if covers(pair.as_span(), cursor) {
*parent = Some(DocIdent::Struct {
name: id_pair.as_str().into(),
})
}
}
for child in it {
if descend_find_docident(child, cursor, _src, parent, ident)? {
break;
}
}
}
Rule::named_field => {
let mut inner = pair.clone().into_inner();
let name = inner.next().unwrap(); if name.as_rule() == Rule::ident {
if covers(name.as_span(), cursor)
&& let Some(DocIdent::Struct { name: struct_name }) = parent
{
*ident = Some(DocIdent::Field {
parent: struct_name.to_string(),
name: name.as_str().into(),
})
}
for child in inner {
if descend_find_docident(child, cursor, _src, parent, ident)? {
break;
}
}
}
}
Rule::list | Rule::tuple => {
for val in pair.into_inner().filter(|p| p.as_rule() == Rule::value) {
if descend_find_docident(val, cursor, _src, parent, ident)? {
break;
}
}
}
Rule::map => {
for entry in pair.into_inner() {
let mut it = entry.into_inner();
let key = it.next().unwrap();
let val = it.next().unwrap();
if descend_find_docident(key.clone(), cursor, _src, parent, ident)? {
break;
}
if descend_find_docident(val, cursor, _src, parent, ident)? {
break;
}
}
}
Rule::enum_variant_named | Rule::enum_variant_tuple | Rule::enum_variant_unit => {
let mut it = pair.clone().into_inner();
let variant_name = it.next().unwrap(); if covers(variant_name.as_span(), cursor) {
return Ok(true);
}
for child in it {
if descend_find_docident(child, cursor, _src, parent, ident)? {
break;
}
}
}
_ => {
for child in pair.clone().into_inner() {
if descend_find_docident(child, cursor, _src, parent, ident)? {
return Ok(true);
}
}
}
}
Ok(true)
}
fn descend(pair: Pair<Rule>, cursor: usize, src: &str, trail: &mut Vec<Component>) -> Result<bool> {
let span = pair.as_span();
if !covers(span, cursor) {
return Ok(false);
}
match pair.as_rule() {
Rule::ron | Rule::value => {
for child in pair.into_inner() {
if descend(child, cursor, src, trail)? {
break;
}
}
}
Rule::named_struct | Rule::tuple_struct | Rule::unit_struct => {
let mut it = pair.clone().into_inner();
if let Some(id_pair) = it.next()
&& id_pair.as_rule() == Rule::ident
{
trail.push(Component::Name(slice(src, id_pair.as_span())));
}
for child in it {
if descend(child, cursor, src, trail)? {
break;
}
}
}
Rule::named_field => {
let mut inner = pair.clone().into_inner();
let name = inner.next().unwrap(); let val = inner.next().unwrap(); trail.push(Component::Name(slice(src, name.as_span())));
descend(val, cursor, src, trail)?;
}
Rule::list | Rule::tuple => {
for (idx, val) in pair.into_inner().filter(|p| p.as_rule() == Rule::value).enumerate() {
if descend(val, cursor, src, trail)? {
trail.push(Component::Index(idx));
break;
}
}
}
Rule::map => {
for entry in pair.into_inner() {
let mut it = entry.into_inner();
let key = it.next().unwrap();
let val = it.next().unwrap();
if descend(key.clone(), cursor, src, trail)? {
break;
}
if descend(val, cursor, src, trail)? {
break;
}
}
}
Rule::enum_variant_named | Rule::enum_variant_tuple | Rule::enum_variant_unit => {
let mut it = pair.clone().into_inner();
let variant_name = it.next().unwrap(); if covers(variant_name.as_span(), cursor) {
trail.push(Component::Name(slice(src, variant_name.as_span())));
return Ok(true);
}
for child in it {
if descend(child, cursor, src, trail)? {
trail.push(Component::Name(slice(src, variant_name.as_span())));
break;
}
}
}
_ => {
for child in pair.clone().into_inner() {
if descend(child, cursor, src, trail)? {
return Ok(true);
}
}
}
}
Ok(true)
}
fn descend_find_strings(
pair: Pair<Rule>,
root_name: &mut Option<String>,
src: &str,
trail: &mut Vec<Component>,
results: &mut Vec<(Vec<Component>, String)>,
) {
eprintln!("{pair:?}, {src}, {trail:?}");
match pair.as_rule() {
Rule::string_std => {
let raw = pair.as_str();
let content = raw[1..raw.len() - 1].to_owned();
results.push((trail.clone(), content));
}
Rule::ron | Rule::value => {
for child in pair.into_inner() {
descend_find_strings(child, root_name, src, trail, results);
}
}
Rule::named_struct | Rule::tuple_struct | Rule::unit_struct => {
let mut it = pair.into_inner().peekable();
let pushed = if it.peek().map(|p| p.as_rule()) == Some(Rule::ident) {
let id_pair = it.next().unwrap();
if root_name.is_none() {
*root_name = Some(slice(src, id_pair.as_span()));
}
true
} else {
false
};
for child in it {
descend_find_strings(child, root_name, src, trail, results);
}
if pushed {
trail.pop();
}
}
Rule::named_field => {
let mut inner = pair.into_inner();
let name = inner.next().unwrap();
trail.push(Component::Name(slice(src, name.as_span())));
for child in inner {
descend_find_strings(child, root_name, src, trail, results);
}
trail.pop();
}
Rule::list | Rule::tuple => {
for (idx, child) in pair.into_inner().filter(|p| p.as_rule() == Rule::value).enumerate() {
trail.push(Component::Index(idx));
descend_find_strings(child, root_name, src, trail, results);
trail.pop();
}
}
Rule::map => {
for entry in pair.into_inner() {
let mut it = entry.into_inner();
let key = it.next().unwrap();
let val = it.next().unwrap();
descend_find_strings(key.clone(), root_name, src, trail, results);
trail.push(Component::Key(slice(src, key.as_span())));
descend_find_strings(val, root_name, src, trail, results);
trail.pop();
}
}
Rule::enum_variant_named | Rule::enum_variant_tuple | Rule::enum_variant_unit => {
let mut it = pair.into_inner();
let variant_name = it.next().unwrap();
if root_name.is_none() {
*root_name = Some(slice(src, variant_name.as_span()));
}
for child in it {
descend_find_strings(child, root_name, src, trail, results);
}
trail.pop();
}
_ => {
for child in pair.into_inner() {
descend_find_strings(child, root_name, src, trail, results);
}
}
}
}
fn covers(span: Span, pos: usize) -> bool {
let (lo, hi) = (span.start(), span.end());
lo <= pos && pos < hi
}
fn slice<'s>(src: &'s str, span: Span<'s>) -> String {
src[span.start()..span.end()].to_owned()
}
pub fn pos_byte_index(line: usize, col: usize, s: &str) -> Option<usize> {
let mut line_no = 1;
let mut col_no = 1;
let mut i = 0;
if line_no == line && col_no == col {
return Some(i);
}
for (byte_idx, ch) in s.char_indices() {
if line_no == line && col_no == col {
return Some(i);
}
if ch == '\n' {
line_no += 1;
col_no = 1;
} else {
col_no += 1;
}
i = byte_idx;
}
if line_no == line && col_no == col {
return Some(i);
}
None
}