From 5f0a1e67c64082c848418daa2b51020eb42c5c12 Mon Sep 17 00:00:00 2001 From: Akshay Date: Mon, 25 Oct 2021 22:08:52 +0530 Subject: rework cli, use subcommands instead --- bin/src/fix/all.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++ bin/src/fix/single.rs | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 bin/src/fix/all.rs create mode 100644 bin/src/fix/single.rs (limited to 'bin/src/fix') diff --git a/bin/src/fix/all.rs b/bin/src/fix/all.rs new file mode 100644 index 0000000..8c0770d --- /dev/null +++ b/bin/src/fix/all.rs @@ -0,0 +1,86 @@ +use std::borrow::Cow; + +use lib::{Report, LINTS}; +use rnix::{parser::ParseError as RnixParseErr, WalkEvent}; + +use crate::fix::{Fixed, FixResult}; + +fn collect_fixes(source: &str) -> Result, RnixParseErr> { + let parsed = rnix::parse(source).as_result()?; + + Ok(parsed + .node() + .preorder_with_tokens() + .filter_map(|event| match event { + WalkEvent::Enter(child) => LINTS.get(&child.kind()).map(|rules| { + rules + .iter() + .filter_map(|rule| rule.validate(&child)) + .filter(|report| report.total_suggestion_range().is_some()) + .collect::>() + }), + _ => None, + }) + .flatten() + .collect()) +} + +fn reorder(mut reports: Vec) -> Vec { + use std::collections::VecDeque; + + reports.sort_by(|a, b| { + let a_range = a.range(); + let b_range = b.range(); + a_range.end().partial_cmp(&b_range.end()).unwrap() + }); + + reports + .into_iter() + .fold(VecDeque::new(), |mut deque: VecDeque, new_elem| { + let front = deque.front(); + let new_range = new_elem.range(); + if let Some(front_range) = front.map(|f| f.range()) { + if new_range.start() > front_range.end() { + deque.push_front(new_elem); + } + } else { + deque.push_front(new_elem); + } + deque + }) + .into() +} + +impl<'a> Iterator for FixResult<'a> { + type Item = FixResult<'a>; + fn next(&mut self) -> Option { + let all_reports = collect_fixes(&self.src).ok()?; + if all_reports.is_empty() { + return None; + } + + let reordered = reorder(all_reports); + let fixed = reordered + .iter() + .map(|r| Fixed { + at: r.range(), + code: r.code, + }) + .collect::>(); + for report in reordered { + report.apply(self.src.to_mut()); + } + + Some(FixResult { + src: self.src.clone(), + fixed + }) + } +} + +pub fn all(src: &str) -> Option { + let src = Cow::from(src); + let _ = rnix::parse(&src).as_result().ok()?; + let initial = FixResult::empty(src); + initial.into_iter().last() +} diff --git a/bin/src/fix/single.rs b/bin/src/fix/single.rs new file mode 100644 index 0000000..d430693 --- /dev/null +++ b/bin/src/fix/single.rs @@ -0,0 +1,59 @@ +use std::{borrow::Cow, convert::TryFrom}; + +use lib::{Report, LINTS}; +use rnix::{TextRange, TextSize}; + +use crate::err::SingleFixErr; +use crate::fix::Source; + +pub struct SingleFixResult<'δ> { + pub src: Source<'δ>, +} + +fn pos_to_byte(line: usize, col: usize, src: &str) -> Result { + let mut byte: TextSize = TextSize::of(""); + for (_, l) in src.lines().enumerate().take_while(|(i, _)| i <= &line) { + byte += TextSize::of(l); + } + byte += TextSize::try_from(col).map_err(|_| SingleFixErr::Conversion(col))?; + + if usize::from(byte) >= src.len() { + Err(SingleFixErr::OutOfBounds(line, col)) + } else { + Ok(byte) + } +} + +fn find(offset: TextSize, src: &str) -> Result { + // we don't really need the source to form a completely parsed tree + let parsed = rnix::parse(src); + + let elem_at = parsed + .node() + .child_or_token_at_range(TextRange::empty(offset)) + .ok_or(SingleFixErr::NoOp)?; + + LINTS + .get(&elem_at.kind()) + .map(|rules| { + rules + .iter() + .filter_map(|rule| rule.validate(&elem_at)) + .filter(|report| report.total_suggestion_range().is_some()) + .next() + }) + .flatten() + .ok_or(SingleFixErr::NoOp) +} + +pub fn single(line: usize, col: usize, src: &str) -> Result { + let mut src = Cow::from(src); + let offset = pos_to_byte(line, col, &*src)?; + let report = find(offset, &*src)?; + + report.apply(src.to_mut()); + + Ok(SingleFixResult { + src + }) +} -- cgit v1.2.3