diffr_plugin_sdk/
draft.rs1use crate::apply::{find, trees_ref, Applier};
10use crate::tree::{siblings_of, walk, Node, Pairing, Region, Source};
11use crate::types::{Cut, Move, Visibility};
12use anyhow::{anyhow, ensure};
13
14pub struct Draft {
15 sides: Pairing<Source>,
17 file: Visibility,
18 applier: Applier,
19 moves: Vec<Move>,
20}
21
22impl Draft {
23 pub fn new(sides: &Pairing<Source>) -> Self {
24 let sides = sides.clone().map(|source| Source {
25 text: String::new(),
26 regions: source.regions,
27 });
28 Self {
29 applier: Applier::new(&sides),
30 sides,
31 file: Visibility::default(),
32 moves: Vec::new(),
33 }
34 }
35
36 pub fn push(&mut self, next: Move) -> anyhow::Result<()> {
39 self.applier
40 .apply(next.clone(), &mut self.sides, &mut self.file)?;
41 self.moves.push(next);
42 Ok(())
43 }
44
45 pub fn into_moves(self) -> Vec<Move> {
46 self.moves
47 }
48
49 fn region(&self, id: u32) -> anyhow::Result<&Region> {
50 trees_ref(&self.sides)
51 .into_iter()
52 .find_map(|tree| find(tree, id))
53 .ok_or_else(|| anyhow!("no region {id}"))
54 }
55
56 fn regions(&self, visit: &mut impl FnMut(&Region)) {
57 for tree in trees_ref(&self.sides) {
58 walk(tree, visit);
59 }
60 }
61
62 pub fn paired_leaf(&self, id: u32) -> anyhow::Result<Option<u32>> {
64 let alignment = self.region(id)?.alignment_id();
65 let mut paired = None;
66 self.regions(&mut |region| {
67 if region.id != id && alignment.is_some() && region.alignment_id() == alignment {
68 paired = Some(region.id);
69 }
70 });
71 Ok(paired)
72 }
73
74 pub fn collapse(&mut self, id: u32, label: String) -> anyhow::Result<()> {
79 let target = self.region(id)?;
80 let (state, alignment) = (target.fold_state_id, target.alignment_id());
81 let (mut named, mut cleared) = (vec![id], Vec::new());
82 self.regions(&mut |region| {
83 if region.fold_state_id != state || region.id == id {
84 return;
85 }
86 if alignment.is_some() && region.alignment_id() == alignment {
87 named.push(region.id);
88 } else if !region.visibility.collapsed && !region.visibility.label.is_empty() {
89 cleared.push(region.id);
90 }
91 });
92 self.push(Move::SetCollapsed((id, true)))?;
93 for region in named {
94 self.push(Move::SetLabel((region, Some(label.clone()))))?;
95 }
96 for region in cleared {
97 self.push(Move::SetLabel((region, None)))?;
98 }
99 Ok(())
100 }
101
102 pub fn link(&mut self, ids: &[u32]) -> anyhow::Result<()> {
106 let states = ids
107 .iter()
108 .map(|id| Ok(self.region(*id)?.fold_state_id))
109 .collect::<anyhow::Result<Vec<u32>>>()?;
110 let first_collapsed = self.region(ids[0])?.visibility.collapsed;
111 let (mut collapsed, mut cleared) = (false, Vec::new());
112 self.regions(&mut |region| {
113 if !states.contains(®ion.fold_state_id) {
114 return;
115 }
116 collapsed |= region.visibility.collapsed;
117 if !region.visibility.collapsed && !region.visibility.label.is_empty() {
118 cleared.push(region.id);
119 }
120 });
121 self.push(Move::LinkFoldState(ids.to_vec()))?;
122 if !collapsed {
123 return Ok(());
124 }
125 if !first_collapsed {
126 self.push(Move::SetCollapsed((ids[0], true)))?;
127 }
128 for region in cleared {
129 self.push(Move::SetLabel((region, None)))?;
130 }
131 Ok(())
132 }
133
134 pub fn cut_lines(&mut self, id: u32, start: u32, end: u32) -> anyhow::Result<u32> {
139 let leaf = self.region(id)?;
140 ensure!(
141 leaf.alignment_id().is_some(),
142 "region {id} is a fold; only a leaf can be cut to lines"
143 );
144 let len = leaf.range.lines().len() as u32;
145 ensure!(
146 start < end && end <= len,
147 "lines {start}..{end} are outside region {id}, which has {len} lines"
148 );
149 let mut piece = id;
150 if start > 0 {
151 self.push(Move::Cut(Cut {
152 region: id,
153 at: start,
154 }))?;
155 piece = self.next_sibling(id)?;
156 }
157 if end < len {
158 self.push(Move::Cut(Cut {
159 region: piece,
160 at: end - start,
161 }))?;
162 }
163 Ok(piece)
164 }
165
166 fn next_sibling(&self, id: u32) -> anyhow::Result<u32> {
168 trees_ref(&self.sides)
169 .into_iter()
170 .find_map(|tree| {
171 let siblings = siblings_of(tree, id)?;
172 let index = siblings.iter().position(|region| region.id == id)?;
173 siblings.get(index + 1).map(|region| region.id)
174 })
175 .ok_or_else(|| anyhow!("region {id} has no next sibling"))
176 }
177
178 pub fn group(&mut self, ids: Vec<u32>, label: String) -> anyhow::Result<()> {
181 self.push(Move::JoinFolds(ids.clone()))?;
182 let mut folds = Vec::new();
183 for tree in trees_ref(&self.sides) {
184 let mut parent = None;
185 walk(tree, &mut |region| {
186 if let Node::Fold { children } = ®ion.node {
187 if children.iter().any(|child| ids.contains(&child.id)) {
188 parent = Some(region.id);
189 }
190 }
191 });
192 folds.extend(parent);
193 }
194 let first = *folds.first().expect("a join adds a fold on some side");
195 self.push(Move::SetCollapsed((first, true)))?;
196 for region in folds {
197 self.push(Move::SetLabel((region, Some(label.clone()))))?;
198 }
199 Ok(())
200 }
201}