Skip to content

Commit 3fa6db6

Browse files
authored
feat(gui): automatically deduplicate scope annotations (#135)
1 parent 261edc8 commit 3fa6db6

File tree

3 files changed

+104
-48
lines changed

3 files changed

+104
-48
lines changed

core/lang-server/src/document.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use arcstr::ArcStr;
2+
use cfgrammar::Span;
23
use lsp_document::{IndexedText, Pos, TextChange, TextMap, apply_change};
34
use tower_lsp_server::lsp_types::{Position, Range};
45

@@ -36,6 +37,13 @@ impl Document {
3637
pos2position(self.contents.offset_to_pos(offset).unwrap())
3738
}
3839

40+
pub(crate) fn span_to_range(&self, span: Span) -> Range {
41+
Range::new(
42+
self.offset_to_pos(span.start()),
43+
self.offset_to_pos(span.end()),
44+
)
45+
}
46+
3947
pub(crate) fn substr(&self, range: std::ops::Range<Position>) -> &str {
4048
self.contents
4149
.substr(position2pos(range.start)..position2pos(range.end))

core/lang-server/src/import.rs

Lines changed: 87 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ use compiler::{
1010
};
1111
use indexmap::IndexSet;
1212
use tower_lsp_server::lsp_types::{Range, TextEdit};
13+
use tracing::error;
1314

1415
use crate::document::Document;
1516

1617
pub(crate) struct ScopeAnnotationPass<'a> {
1718
ast: &'a AnnotatedAst<ParseMetadata>,
1819
content: Document,
19-
assigned_names: Vec<IndexSet<String>>,
20-
to_add: Vec<Vec<Range>>,
20+
annotations: Vec<Vec<(Option<Substr>, Range)>>,
2121
edits: Vec<TextEdit>,
2222
}
2323

@@ -26,8 +26,7 @@ impl<'a> ScopeAnnotationPass<'a> {
2626
Self {
2727
ast,
2828
content: Document::new(&ast.text, 0),
29-
assigned_names: vec![Default::default()],
30-
to_add: vec![Default::default()],
29+
annotations: vec![Default::default()],
3130
edits: vec![],
3231
}
3332
}
@@ -53,6 +52,36 @@ impl<'a> ScopeAnnotationPass<'a> {
5352
}
5453
}
5554

55+
fn increment_or_add_zero(input: &str) -> String {
56+
// Find the index where the trailing digits start
57+
let split_idx = input
58+
.char_indices()
59+
.rev()
60+
.take_while(|(_, c)| c.is_ascii_digit())
61+
.last()
62+
.map(|(i, _)| i);
63+
64+
match split_idx {
65+
Some(idx) => {
66+
let (prefix, suffix) = input.split_at(idx);
67+
68+
// Parse the number
69+
if let Ok(num) = suffix.parse::<u64>() {
70+
// Increment and maintain the original width for leading zeros
71+
let incremented = num + 1;
72+
format!("{}{:0width$}", prefix, incremented, width = suffix.len())
73+
} else {
74+
// Handle cases where the number is too large for u64
75+
format!("{}0", input)
76+
}
77+
}
78+
None => {
79+
// No digits at the end, just add '0'
80+
format!("{}0", input)
81+
}
82+
}
83+
}
84+
5685
impl<'a> AstTransformer for ScopeAnnotationPass<'a> {
5786
type InputMetadata = ParseMetadata;
5887
type OutputMetadata = ParseMetadata;
@@ -115,16 +144,16 @@ impl<'a> AstTransformer for ScopeAnnotationPass<'a> {
115144
_else_: &Scope<Substr, Self::OutputMetadata>,
116145
) -> <Self::OutputMetadata as AstMetadata>::IfExpr {
117146
if let Some(scope_annotation) = &input.scope_annotation {
118-
self.assigned_names
119-
.last_mut()
120-
.unwrap()
121-
.insert(scope_annotation.name.to_string());
147+
self.annotations.last_mut().unwrap().push((
148+
Some(scope_annotation.name.clone()),
149+
self.content.span_to_range(scope_annotation.span),
150+
));
122151
} else {
123152
let start = self.content.offset_to_pos(input.span.start());
124-
self.to_add
153+
self.annotations
125154
.last_mut()
126155
.unwrap()
127-
.push(Range::new(start, start));
156+
.push((None, Range::new(start, start)));
128157
}
129158
}
130159

@@ -185,47 +214,64 @@ impl<'a> AstTransformer for ScopeAnnotationPass<'a> {
185214
return;
186215
}
187216
if let Some(scope_annotation) = &input.scope_annotation {
188-
self.assigned_names
189-
.last_mut()
190-
.unwrap()
191-
.insert(scope_annotation.name.to_string());
217+
self.annotations.last_mut().unwrap().push((
218+
Some(scope_annotation.name.clone()),
219+
self.content.span_to_range(scope_annotation.span),
220+
));
192221
} else {
193222
let start = self.content.offset_to_pos(input.span.start());
194-
self.to_add
223+
self.annotations
195224
.last_mut()
196225
.unwrap()
197-
.push(Range::new(start, start));
226+
.push((None, Range::new(start, start)));
198227
}
199228
}
200229

201230
fn enter_scope(&mut self, _input: &Scope<Substr, Self::InputMetadata>) {
202-
self.assigned_names.push(Default::default());
203-
self.to_add.push(Default::default());
231+
self.annotations.push(Default::default());
204232
}
205233

206234
fn exit_scope(
207235
&mut self,
208236
_input: &Scope<Substr, Self::InputMetadata>,
209237
_output: &Scope<Substr, Self::OutputMetadata>,
210238
) {
211-
if let Some(mut to_add) = self.to_add.pop()
212-
&& let Some(mut assigned_names) = self.assigned_names.pop()
213-
{
214-
to_add.reverse();
215-
let mut id = 0;
216-
while let Some(range) = to_add.pop() {
217-
let name = loop {
218-
let name = format!("scope{}", id);
219-
id += 1;
220-
if !assigned_names.contains(&name) {
221-
assigned_names.insert(name.clone());
222-
break name;
239+
if let Some(mut annotations) = self.annotations.pop() {
240+
let mut assigned_names = IndexSet::new();
241+
for (annotation, range) in &annotations {
242+
if let Some(annotation) = annotation {
243+
if assigned_names.contains(annotation) {
244+
let mut new_name = increment_or_add_zero(annotation);
245+
while assigned_names.contains(new_name.as_str()) {
246+
new_name = increment_or_add_zero(&new_name);
247+
}
248+
self.edits.push(TextEdit {
249+
range: *range,
250+
new_text: new_name.clone(),
251+
});
252+
assigned_names.insert(Substr::from(new_name));
253+
} else {
254+
assigned_names.insert(annotation.clone());
223255
}
224-
};
225-
self.edits.push(TextEdit {
226-
range,
227-
new_text: format!("#{name} "),
228-
});
256+
}
257+
}
258+
annotations.reverse();
259+
let mut id = 0;
260+
for (annotation, range) in annotations {
261+
if annotation.is_none() {
262+
let name = loop {
263+
let name = Substr::from(format!("scope{}", id));
264+
id += 1;
265+
if !assigned_names.contains(&name) {
266+
assigned_names.insert(name.clone());
267+
break name;
268+
}
269+
};
270+
self.edits.push(TextEdit {
271+
range,
272+
new_text: format!("#{name} "),
273+
});
274+
}
229275
}
230276
}
231277
}
@@ -316,16 +362,16 @@ impl<'a> AstTransformer for ScopeAnnotationPass<'a> {
316362
Expr::StringLiteral(string_literal) => Expr::StringLiteral(string_literal.clone()),
317363
Expr::Scope(scope) => {
318364
if let Some(scope_annotation) = &scope.scope_annotation {
319-
self.assigned_names
320-
.last_mut()
321-
.unwrap()
322-
.insert(scope_annotation.name.to_string());
365+
self.annotations.last_mut().unwrap().push((
366+
Some(scope_annotation.name.clone()),
367+
self.content.span_to_range(scope_annotation.span),
368+
));
323369
} else {
324370
let start = self.content.offset_to_pos(scope.span.start());
325-
self.to_add
371+
self.annotations
326372
.last_mut()
327373
.unwrap()
328-
.push(Range::new(start, start));
374+
.push((None, Range::new(start, start)));
329375
}
330376
Expr::Scope(Box::new(self.transform_scope(scope)))
331377
}

core/lang-server/src/lib.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ impl StateMut {
144144
let static_output = compile::static_compile(&self.ast);
145145
// If GUI is connected, must annotate scopes.
146146
if self.gui_client.is_some() {
147+
let mut to_save = Vec::new();
147148
for (_, ast) in &self.ast {
148149
let scope_annotation = ScopeAnnotationPass::new(ast);
149150
let mut text_edits = scope_annotation.execute();
@@ -161,15 +162,16 @@ impl StateMut {
161162
.await
162163
.unwrap();
163164

164-
client
165-
.send_request::<ForceSave>(ast.path.clone())
166-
.await
167-
.unwrap();
168-
169-
// `compile` will be reinvoked upon save.
170-
return;
165+
to_save.push(ast.path.clone());
171166
}
172167
}
168+
let should_return = !to_save.is_empty();
169+
for path in to_save {
170+
client.send_request::<ForceSave>(path).await.unwrap();
171+
}
172+
if should_return {
173+
return;
174+
}
173175
}
174176

175177
let o = if let Some((ast, mut static_output)) = static_output {

0 commit comments

Comments
 (0)