Lints and suggestions for the Nix programming language
at main 60 lines 1.8 kB view raw
1use std::{borrow::Cow, convert::TryFrom}; 2 3use lib::Report; 4use rnix::{Root, TextSize, WalkEvent}; 5 6use crate::{err::SingleFixErr, fix::Source, utils}; 7 8pub struct SingleFixResult<'δ> { 9 pub src: Source<'δ>, 10} 11 12fn pos_to_byte(line: usize, col: usize, src: &str) -> Result<TextSize, SingleFixErr> { 13 let mut byte: TextSize = TextSize::of(""); 14 for (l, _) in src 15 .split_inclusive('\n') 16 .zip(1..) 17 .take_while(|(_, i)| *i < line) 18 { 19 byte += TextSize::of(l); 20 } 21 byte += TextSize::try_from(col).map_err(|_| SingleFixErr::Conversion(col))?; 22 23 if usize::from(byte) >= src.len() { 24 Err(SingleFixErr::OutOfBounds(line, col)) 25 } else { 26 Ok(byte) 27 } 28} 29 30fn find(offset: TextSize, src: &str) -> Result<Report, SingleFixErr> { 31 // we don't really need the source to form a completely parsed tree 32 let parsed = Root::parse(src); 33 let lints = utils::lint_map(); 34 35 parsed 36 .syntax() 37 .preorder_with_tokens() 38 .filter_map(|event| match event { 39 WalkEvent::Enter(child) => lints.get(&child.kind()).map(|rules| { 40 rules 41 .iter() 42 .filter_map(|rule| rule.validate(&child)) 43 .find(|report| report.total_suggestion_range().is_some()) 44 }), 45 WalkEvent::Leave(_) => None, 46 }) 47 .flatten() 48 .find(|report| report.total_diagnostic_range().unwrap().contains(offset)) 49 .ok_or(SingleFixErr::NoOp) 50} 51 52pub fn single(line: usize, col: usize, src: &str) -> Result<SingleFixResult<'_>, SingleFixErr> { 53 let mut src = Cow::from(src); 54 let offset = pos_to_byte(line, col, &src)?; 55 let report = find(offset, &src)?; 56 57 report.apply(src.to_mut()); 58 59 Ok(SingleFixResult { src }) 60}