#![warn(
rust_2018_idioms,
future_incompatible,
unused,
box_pointers,
macro_use_extern_crate,
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unused_import_braces,
unused_lifetimes,
unused_qualifications,
unused_results,
clippy::nursery,
clippy::pedantic,
clippy::restriction
)]
#![allow(clippy::suspicious_op_assign_impl, clippy::suspicious_arithmetic_impl)]
#![doc(html_root_url = "https://docs.rs/paper/0.3.0")]
#![allow(clippy::missing_inline_in_public_items)]
pub mod num;
pub mod ui;
mod engine;
mod storage;
pub use engine::Outcome;
pub use storage::{Explorer, File};
use engine::{Failure, Mode};
use pancurses::Input;
use rec::ChCls::{Any, Digit, End, Not, Sign, Whitespace};
use rec::{lazy_some, opt, some, tkn, var, Element, Pattern};
use std::borrow::Borrow;
use std::cmp::{self, Ordering};
use std::collections::HashMap;
use std::error;
use std::fmt::{self, Debug, Display, Formatter};
use std::iter;
use std::ops::{Add, AddAssign, Shr, ShrAssign, Sub};
use try_from::{TryFrom, TryFromIntError};
use ui::{
Address, Change, Color, Edit, Index, IndexType, Length, Region, UserInterface, BACKSPACE,
ENTER, ESC,
};
const NEGATIVE_ONE: IndexType = -1;
#[derive(Debug)]
pub struct Paper<'a> {
ui: &'a dyn UserInterface,
view: View<'a>,
sketch: String,
signals: Vec<Section>,
noises: Vec<Section>,
marks: Vec<Mark>,
filters: PaperFilters,
command_pattern: Pattern,
mode: Mode,
first_feature_pattern: Pattern,
}
impl<'a> Paper<'a> {
#[inline]
pub fn new(ui: &'a dyn UserInterface) -> Self {
Self {
ui,
..Self::default()
}
}
pub fn with_file(ui: &'a dyn UserInterface, file: File<'a>) -> Self {
Self {
ui,
view: View::with_file(file).unwrap_or_default(),
..Self::default()
}
}
#[inline]
pub fn run(&mut self) -> Outcome<()> {
self.ui.init()?;
loop {
if let Err(Failure::Quit) = self.step() {
break;
}
}
self.ui.close()?;
Ok(())
}
pub fn step(&mut self) -> Outcome<()> {
match (self.mode, self.ui.receive_input()) {
(Mode::Display, Some(Input::Character(c))) => match c {
'.' => {
self.draw_sketch()?;
self.change_mode(Mode::Command);
}
'#' | '/' => {
self.sketch.push(c);
self.draw_sketch()?;
self.change_mode(Mode::Filter);
}
'j' => {
let movement = IndexType::try_from(self.scroll_height()?)?;
if self.scroll(movement) {
self.display_view()?;
}
}
'k' => {
let mut movement = IndexType::try_from(self.scroll_height()?)?;
movement = movement
.checked_neg()
.ok_or(Failure::Conversion(TryFromIntError::Overflow))?;
if self.scroll(movement) {
self.display_view()?;
}
}
_ => {}
},
(Mode::Command, Some(Input::Character(c))) => match c {
ENTER => {
let command = self.sketch.clone();
let command_tokens = self.command_pattern.tokenize(&command);
match command_tokens.get("command") {
Some("see") => {
if let Some(path) = command_tokens.get("args") {
self.change_view(path)?;
}
}
Some("put") => {
self.save_view()?;
}
Some("end") => {
return Err(Failure::Quit);
}
Some(_) | None => {}
}
self.sketch.clear();
self.display_view()?;
self.change_mode(Mode::Display);
}
ESC => {
self.sketch.clear();
self.display_view()?;
self.change_mode(Mode::Display);
}
_ => {
if c == BACKSPACE {
if self.sketch.pop().is_none() {
self.ui.flash()?;
}
} else {
self.sketch.push(c);
}
self.draw_sketch()?;
}
},
(Mode::Filter, Some(Input::Character(c))) => match c {
ENTER => {
self.change_mode(Mode::Action);
}
'\t' => {
self.reduce_noise();
self.sketch.push_str("&&");
self.draw_sketch()?;
let filter = self.sketch.clone();
if let Some(last_feature) = self
.first_feature_pattern
.tokenize_iter(&filter)
.last()
.and_then(|tokens| tokens.get("feature"))
{
self.filter_signals(last_feature)?;
}
self.clear_background()?;
self.draw_filter_backgrounds()?;
}
ESC => {
self.sketch.clear();
self.display_view()?;
self.change_mode(Mode::Display);
}
_ => {
if let BACKSPACE = c {
if self.sketch.pop().is_none() {
self.ui.flash()?;
}
} else {
self.sketch.push(c);
}
self.draw_sketch()?;
let filter = self.sketch.clone();
if let Some(last_feature) = self
.first_feature_pattern
.tokenize_iter(&filter)
.last()
.and_then(|tokens| tokens.get("feature"))
{
self.filter_signals(last_feature)?;
}
self.clear_background()?;
self.draw_filter_backgrounds()?;
}
},
(Mode::Action, Some(Input::Character(c))) => match c {
ESC => {
self.sketch.clear();
self.display_view()?;
self.change_mode(Mode::Display);
}
'i' => {
self.set_marks(Edge::Start);
self.display_view()?;
self.change_mode(Mode::Edit);
}
'I' => {
self.set_marks(Edge::End);
self.display_view()?;
self.change_mode(Mode::Edit);
}
_ => {}
},
(Mode::Edit, Some(Input::Character(c))) => {
if c == ESC {
self.sketch.clear();
self.display_view()?;
self.change_mode(Mode::Display);
} else {
self.update_view(c)?;
}
}
(_, _) => {}
}
Ok(())
}
fn display_view(&self) -> Outcome<()> {
for edit in self.view.redraw_edits().take(self.ui.grid_height()?) {
self.ui.apply(edit)?;
}
Ok(())
}
fn change_view(&mut self, path: &str) -> Outcome<()> {
self.view = View::with_file(File::new(&storage::Local, String::from(path)))?;
self.noises.clear();
for line in 1..=self.view.line_count {
if let Some(noise) = LineNumber::new(line).map(Section::line) {
self.noises.push(noise);
}
}
Ok(())
}
fn save_view(&self) -> Outcome<()> {
self.view.put()
}
fn reduce_noise(&mut self) {
self.noises = self.signals.clone();
}
fn filter_signals(&mut self, feature: &str) -> Result<(), TryFromIntError> {
self.signals = self.noises.clone();
if let Some(id) = feature.chars().nth(0) {
for filter in self.filters.iter() {
if id == filter.id() {
return filter.extract(feature, &mut self.signals, &self.view);
}
}
}
Ok(())
}
fn draw_sketch(&self) -> Outcome<()> {
self.ui.apply(Edit::new(
Region::with_row(0)?,
Change::Row(self.sketch.clone()),
))?;
Ok(())
}
fn clear_background(&self) -> Outcome<()> {
for row in 0..self.ui.grid_height()? {
self.format_region(Region::with_row(row)?, Color::Default)?;
}
Ok(())
}
fn set_marks(&mut self, edge: Edge) {
self.marks.clear();
for signal in &self.signals {
let mut place = signal.start;
if edge == Edge::End {
place.column += Index::try_from(signal.length)
.unwrap_or_else(|_| self.view.line_length(signal.start).unwrap_or_default())
}
let pointer = Pointer(
self.view
.line_indices()
.nth(place.line.row())
.and_then(|index_value| Index::try_from(index_value).ok()),
) + place.column;
self.marks.push(Mark { place, pointer });
}
}
fn scroll(&mut self, movement: IndexType) -> IsChanging {
self.view.scroll(movement)
}
fn draw_filter_backgrounds(&self) -> ui::Outcome {
for noise in &self.noises {
self.format_section(noise, Color::Blue)?;
}
for signal in &self.signals {
self.format_section(signal, Color::Red)?;
}
Ok(())
}
fn format_section(&self, section: &Section, color: Color) -> ui::Outcome {
if let Some(region) = self.view.region_at(section) {
self.format_region(region, color)?;
}
Ok(())
}
fn format_region(&self, region: Region, color: Color) -> ui::Outcome {
self.ui.apply(Edit::new(region, Change::Format(color)))
}
fn update_view(&mut self, c: char) -> Outcome<()> {
let mut adjustment = Adjustment::default();
for mark in &mut self.marks {
if let Some(new_adjustment) = Adjustment::create(c, mark.place, &self.view) {
adjustment += new_adjustment;
if adjustment.change != Change::Clear {
if let Some(region) = self.view.region_at(&mark.place) {
self.ui
.apply(Edit::new(region, adjustment.change.clone()))?;
}
}
mark.adjust(&adjustment);
self.view.add(mark, c)?;
}
}
if adjustment.change == Change::Clear {
self.view.clean();
self.display_view()?;
}
Ok(())
}
fn change_mode(&mut self, mode: Mode) {
self.mode = mode;
}
#[allow(clippy::integer_arithmetic)]
fn scroll_height(&self) -> Result<usize, TryFromIntError> {
self.ui.grid_height().map(|height| height / 4)
}
}
impl Default for Paper<'_> {
fn default() -> Self {
Self {
ui: &ui::NullUserInterface,
view: View::default(),
sketch: String::default(),
signals: Vec::default(),
noises: Vec::default(),
marks: Vec::default(),
filters: PaperFilters::default(),
mode: Mode::default(),
command_pattern: Pattern::define(
tkn!(lazy_some(Any) => "command")
+ (End | (some(Whitespace) + tkn!(var(Any) => "args"))),
),
first_feature_pattern: Pattern::define(tkn!(var(Not("&")) => "feature") + opt("&&")),
}
}
}
#[derive(Debug, Default)]
struct PaperFilters {
line: LineFilter,
pattern: PatternFilter,
}
impl PaperFilters {
fn iter(&self) -> PaperFiltersIter<'_> {
PaperFiltersIter {
index: 0,
filters: self,
}
}
}
struct PaperFiltersIter<'a> {
index: usize,
filters: &'a PaperFilters,
}
impl<'a> Iterator for PaperFiltersIter<'a> {
type Item = &'a dyn Filter;
fn next(&mut self) -> Option<Self::Item> {
self.index += 1;
match self.index {
1 => Some(&self.filters.line),
2 => Some(&self.filters.pattern),
_ => None,
}
}
}
#[derive(Clone, Debug, Default)]
struct View<'a> {
data: String,
first_line: LineNumber,
margin_width: usize,
line_count: usize,
file: File<'a>,
}
impl<'a> View<'a> {
fn with_file(file: File<'a>) -> Outcome<Self> {
let mut view = Self {
data: file.read()?,
file,
..Self::default()
};
view.clean();
Ok(view)
}
fn add(&mut self, mark: &Mark, c: char) -> Result<(), TryFromIntError> {
if let Some(index) = mark.pointer.0 {
let data_index = usize::try_from(index)?;
if let BACKSPACE = c {
match self.data.remove(data_index) {
_ => {}
}
} else {
self.data.insert(data_index.saturating_sub(1), c);
}
}
Ok(())
}
fn line_indices(&self) -> impl Iterator<Item = IndexType> + '_ {
iter::once(0).chain(self.data.match_indices(ENTER).flat_map(|(index, _)| {
index
.checked_add(1)
.and_then(|i| IndexType::try_from(i).ok())
.into_iter()
}))
}
#[allow(clippy::integer_arithmetic)]
fn first_data_column(&self) -> Result<Index, TryFromIntError> {
Index::try_from(self.margin_width + 1)
}
fn address_at(&self, place: Place) -> Option<Address> {
match Index::try_from(place.line - self.first_line) {
Ok(row) => self
.first_data_column()
.ok()
.map(|origin| Address::new(row, place.column + origin)),
_ => None,
}
}
fn region_at<T: Area>(&self, area: &T) -> Option<Region> {
self.address_at(area.start())
.map(|address| Region::new(address, area.length()))
}
fn redraw_edits(&self) -> impl Iterator<Item = Edit> + '_ {
iter::once(Edit::new(Region::default(), Change::Clear)).chain(
self.first_line
.into_iter()
.zip(self.lines().skip(self.first_line.row()))
.flat_map(move |(line_number, line)| {
self.region_at(&Section::line(line_number))
.map(|region| {
Edit::new(
region,
Change::Row(format!(
"{:>width$} {}",
line_number,
line,
width = self.margin_width
)),
)
})
.into_iter()
}),
)
}
fn lines(&self) -> std::str::Lines<'_> {
self.data.lines()
}
fn line(&self, line_number: LineNumber) -> Option<&str> {
self.lines().nth(line_number.row())
}
fn clean(&mut self) {
self.line_count = self.lines().count();
self.update_margin_width()
}
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::cast_sign_loss)]
fn update_margin_width(&mut self) {
self.margin_width = (((self.line_count.saturating_add(1)) as f64).log10().ceil()) as usize;
}
fn scroll(&mut self, movement: IndexType) -> IsChanging {
let new_first_line = cmp::min(
self.first_line + movement,
LineNumber::new(self.line_count).unwrap_or_default(),
);
if new_first_line == self.first_line {
false
} else {
self.first_line = new_first_line;
true
}
}
fn line_length(&self, place: Place) -> Option<Index> {
self.line(place.line)
.and_then(|x| Index::try_from(x.len()).ok())
}
fn put(&self) -> Outcome<()> {
self.file.write(&self.data)
}
}
type IsChanging = bool;
#[derive(Clone, Debug, Default)]
struct Adjustment {
shift: IndexType,
line_change: IndexType,
indexes_changed: HashMap<LineNumber, IndexType>,
change: Change,
}
impl Adjustment {
fn new(line: LineNumber, shift: IndexType, index_change: IndexType, change: Change) -> Self {
let line_change = if change == Change::Clear { shift } else { 0 };
Self {
shift,
line_change,
indexes_changed: [(line + line_change, index_change)]
.iter()
.cloned()
.collect(),
change,
}
}
fn create(c: char, place: Place, view: &View<'_>) -> Option<Self> {
match c {
BACKSPACE => {
if place.column == 0 {
view.line_length(place).map(|x| {
Self::new(place.line, NEGATIVE_ONE, IndexType::from(x), Change::Clear)
})
} else {
Some(Self::new(
place.line,
NEGATIVE_ONE,
NEGATIVE_ONE,
Change::Backspace,
))
}
}
ENTER => Some(Self::new(
place.line,
1,
place.column.negate(),
Change::Clear,
)),
_ => Some(Self::new(place.line, 1, 1, Change::Insert(c))),
}
}
}
impl AddAssign for Adjustment {
fn add_assign(&mut self, other: Self) {
self.shift += other.shift;
self.line_change += other.line_change;
for (line, change) in other.indexes_changed {
*self.indexes_changed.entry(line).or_default() += change;
}
if self.change != Change::Clear {
self.change = other.change
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Edge {
Start,
End,
}
impl Default for Edge {
#[inline]
fn default() -> Self {
Edge::Start
}
}
impl Display for Edge {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Edge::Start => write!(f, "Starting edge"),
Edge::End => write!(f, "Ending edge"),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
struct Mark {
pointer: Pointer,
place: Place,
}
impl Mark {
fn adjust(&mut self, adjustment: &Adjustment) {
self.pointer += adjustment.shift;
self.place.line = self.place.line + adjustment.line_change;
for (&line, &change) in &adjustment.indexes_changed {
if line == self.place.line {
self.place >>= change;
}
}
}
}
impl Display for Mark {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.place, self.pointer)
}
}
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Debug)]
struct Pointer(Option<Index>);
impl PartialEq<IndexType> for Pointer {
fn eq(&self, other: &IndexType) -> bool {
self.0.map_or(false, |x| x == *other)
}
}
impl PartialOrd<IndexType> for Pointer {
fn partial_cmp(&self, other: &IndexType) -> Option<Ordering> {
self.0.and_then(|x| x.partial_cmp(other))
}
}
impl<T: Borrow<IndexType>> Add<T> for Pointer {
type Output = Self;
fn add(self, other: T) -> Self::Output {
Pointer(self.0.map(|x| x + *other.borrow()))
}
}
impl<T: Borrow<IndexType>> AddAssign<T> for Pointer {
fn add_assign(&mut self, other: T) {
self.0 = self.0.map(|x| x + *other.borrow());
}
}
impl Default for Pointer {
fn default() -> Self {
Pointer(Some(Index::from(0)))
}
}
impl Display for Pointer {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"[{}]",
match self.0 {
None => String::from("None"),
Some(i) => format!("{}", i),
}
)
}
}
impl PartialEq<Pointer> for IndexType {
#[inline]
fn eq(&self, other: &Pointer) -> bool {
other == self
}
}
impl PartialOrd<Pointer> for IndexType {
#[inline]
fn partial_cmp(&self, other: &Pointer) -> Option<Ordering> {
other.partial_cmp(self).map(|x| x.reverse())
}
}
trait Area {
fn start(&self) -> Place;
fn length(&self) -> Length;
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct Section {
start: Place,
length: Length,
}
impl Section {
#[inline]
fn line(line: LineNumber) -> Self {
Self {
start: Place {
line,
column: Index::from(0),
},
length: Length::End,
}
}
}
impl Area for Section {
fn start(&self) -> Place {
self.start
}
fn length(&self) -> Length {
self.length
}
}
impl Display for Section {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}->{}", self.start, self.length)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct Place {
line: LineNumber,
column: Index,
}
impl Area for Place {
fn start(&self) -> Place {
*self
}
fn length(&self) -> Length {
Length::from(1)
}
}
impl Shr<IndexType> for Place {
type Output = Self;
#[inline]
fn shr(self, rhs: IndexType) -> Self {
let mut new_place = self;
new_place >>= rhs;
new_place
}
}
impl ShrAssign<IndexType> for Place {
#[inline]
fn shr_assign(&mut self, rhs: IndexType) {
self.column += rhs;
}
}
impl Display for Place {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "ln {}, idx {}", self.line, self.column)
}
}
type LineNumberType = u32;
#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Debug)]
struct LineNumber(LineNumberType);
impl LineNumber {
fn new(value: usize) -> Option<Self> {
if value == 0 {
None
} else {
LineNumberType::try_from(value).ok().map(LineNumber)
}
}
#[allow(clippy::integer_arithmetic)]
fn row(self) -> usize {
(self.0 - 1) as usize
}
}
impl Add<IndexType> for LineNumber {
type Output = Self;
fn add(self, other: IndexType) -> Self::Output {
#[allow(clippy::integer_arithmetic)]
match usize::try_from(i64::from(self.0) + i64::from(other)) {
Ok(sum) => Self::new(sum).unwrap_or_default(),
Err(TryFromIntError::Underflow) => Self::default(),
Err(TryFromIntError::Overflow) => Self(LineNumberType::max_value()),
}
}
}
impl Sub for LineNumber {
type Output = i64;
#[allow(clippy::integer_arithmetic)]
fn sub(self, other: Self) -> Self::Output {
i64::from(self.0) - i64::from(other.0)
}
}
impl Display for LineNumber {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Default for LineNumber {
#[inline]
fn default() -> Self {
LineNumber(1)
}
}
impl std::str::FromStr for LineNumber {
type Err = ParseLineNumberError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s.parse::<usize>()?).ok_or(ParseLineNumberError::InvalidValue)
}
}
impl IntoIterator for LineNumber {
type Item = Self;
type IntoIter = LineNumberIterator;
fn into_iter(self) -> Self::IntoIter {
LineNumberIterator { current: self }
}
}
struct LineNumberIterator {
current: LineNumber,
}
impl Iterator for LineNumberIterator {
type Item = LineNumber;
fn next(&mut self) -> Option<Self::Item> {
let line_number = LineNumber(self.current.0);
self.current.0 += 1;
Some(line_number)
}
}
#[derive(Debug)]
enum ParseLineNumberError {
InvalidValue,
ParseInt(std::num::ParseIntError),
}
impl std::error::Error for ParseLineNumberError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
ParseLineNumberError::InvalidValue => None,
ParseLineNumberError::ParseInt(ref err) => Some(err),
}
}
}
impl Display for ParseLineNumberError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
ParseLineNumberError::InvalidValue => write!(f, "Invalid line number provided."),
ParseLineNumberError::ParseInt(ref err) => write!(f, "{}", err),
}
}
}
impl From<std::num::ParseIntError> for ParseLineNumberError {
fn from(error: std::num::ParseIntError) -> Self {
ParseLineNumberError::ParseInt(error)
}
}
trait Filter: Debug {
fn id(&self) -> char;
fn extract(
&self,
feature: &str,
sections: &mut Vec<Section>,
view: &View<'_>,
) -> Result<(), TryFromIntError>;
}
#[derive(Debug)]
struct LineFilter {
pattern: Pattern,
}
impl Default for LineFilter {
fn default() -> Self {
Self {
pattern: Pattern::define(
"#" + ((tkn!(some(Digit) => "line") + End)
| (tkn!(some(Digit) => "start") + "." + tkn!(some(Digit) => "end"))
| (tkn!(some(Digit) => "origin") + tkn!(Sign + some(Digit) => "movement"))),
),
}
}
}
impl Filter for LineFilter {
fn id(&self) -> char {
'#'
}
fn extract(
&self,
feature: &str,
sections: &mut Vec<Section>,
_view: &View<'_>,
) -> Result<(), TryFromIntError> {
let tokens = self.pattern.tokenize(feature);
if let Ok(line) = tokens.parse::<LineNumber>("line") {
sections.retain(|&x| x.start.line == line);
} else if let (Ok(start), Ok(end)) = (
tokens.parse::<LineNumber>("start"),
tokens.parse::<LineNumber>("end"),
) {
let top = cmp::min(start, end);
let bottom = cmp::max(start, end);
sections.retain(|&x| {
let row = x.start.line;
row >= top && row <= bottom
})
} else if let (Ok(origin), Ok(movement)) = (
tokens.parse::<LineNumber>("origin"),
tokens.parse::<IndexType>("movement"),
) {
let end = origin + movement;
let top = cmp::min(origin, end);
let bottom = cmp::max(origin, end);
sections.retain(|&x| {
let row = x.start.line;
row >= top && row <= bottom
})
}
Ok(())
}
}
#[derive(Debug)]
struct PatternFilter {
pattern: Pattern,
}
impl Default for PatternFilter {
fn default() -> Self {
Self {
pattern: Pattern::define("/" + tkn!(some(Any) => "pattern")),
}
}
}
impl Filter for PatternFilter {
fn id(&self) -> char {
'/'
}
fn extract(
&self,
feature: &str,
sections: &mut Vec<Section>,
view: &View<'_>,
) -> Result<(), TryFromIntError> {
if let Some(user_pattern) = self.pattern.tokenize(feature).get("pattern") {
if let Ok(search_pattern) = Pattern::load(user_pattern) {
let target_sections = sections.clone();
sections.clear();
for target_section in target_sections {
let start = usize::try_from(target_section.start.column)?;
if let Some(target) = view
.line(target_section.start.line)
.map(|x| x.chars().skip(start).collect::<String>())
{
for location in search_pattern.locate_iter(&target) {
sections.push(Section {
start: target_section.start
>> IndexType::try_from(location.start())?,
length: Length::try_from(location.length())?,
});
}
}
}
}
}
Ok(())
}
}