1use nu_protocol::{
2 DeclId, Span, SyntaxShape, VarId,
3 ast::{
4 Argument, Block, Expr, Expression, ExternalArgument, ImportPatternMember, ListItem,
5 MatchPattern, PathMember, Pattern, Pipeline, PipelineElement, PipelineRedirection,
6 RecordItem,
7 },
8 engine::StateWorkingSet,
9};
10use std::fmt::{Display, Formatter, Result};
11
12#[derive(Debug, Eq, PartialEq, Ord, Clone, PartialOrd)]
13pub enum FlatShape {
14 Binary,
15 Block,
16 Bool,
17 Closure,
18 Custom(DeclId),
19 DateTime,
20 Directory,
21 External,
22 ExternalArg,
23 ExternalResolved,
24 Filepath,
25 Flag,
26 Float,
27 Garbage,
28 GlobInterpolation,
29 GlobPattern,
30 Int,
31 InternalCall(DeclId),
32 Keyword,
33 List,
34 Literal,
35 MatchPattern,
36 Nothing,
37 Operator,
38 Pipe,
39 Range,
40 RawString,
41 Record,
42 Redirection,
43 Signature,
44 String,
45 StringInterpolation,
46 Table,
47 Variable(VarId),
48 VarDecl(VarId),
49}
50
51impl FlatShape {
52 pub fn as_str(&self) -> &str {
53 match self {
54 FlatShape::Binary => "shape_binary",
55 FlatShape::Block => "shape_block",
56 FlatShape::Bool => "shape_bool",
57 FlatShape::Closure => "shape_closure",
58 FlatShape::Custom(_) => "shape_custom",
59 FlatShape::DateTime => "shape_datetime",
60 FlatShape::Directory => "shape_directory",
61 FlatShape::External => "shape_external",
62 FlatShape::ExternalArg => "shape_externalarg",
63 FlatShape::ExternalResolved => "shape_external_resolved",
64 FlatShape::Filepath => "shape_filepath",
65 FlatShape::Flag => "shape_flag",
66 FlatShape::Float => "shape_float",
67 FlatShape::Garbage => "shape_garbage",
68 FlatShape::GlobInterpolation => "shape_glob_interpolation",
69 FlatShape::GlobPattern => "shape_globpattern",
70 FlatShape::Int => "shape_int",
71 FlatShape::InternalCall(_) => "shape_internalcall",
72 FlatShape::Keyword => "shape_keyword",
73 FlatShape::List => "shape_list",
74 FlatShape::Literal => "shape_literal",
75 FlatShape::MatchPattern => "shape_match_pattern",
76 FlatShape::Nothing => "shape_nothing",
77 FlatShape::Operator => "shape_operator",
78 FlatShape::Pipe => "shape_pipe",
79 FlatShape::Range => "shape_range",
80 FlatShape::RawString => "shape_raw_string",
81 FlatShape::Record => "shape_record",
82 FlatShape::Redirection => "shape_redirection",
83 FlatShape::Signature => "shape_signature",
84 FlatShape::String => "shape_string",
85 FlatShape::StringInterpolation => "shape_string_interpolation",
86 FlatShape::Table => "shape_table",
87 FlatShape::Variable(_) => "shape_variable",
88 FlatShape::VarDecl(_) => "shape_vardecl",
89 }
90 }
91}
92
93impl Display for FlatShape {
94 fn fmt(&self, f: &mut Formatter) -> Result {
95 f.write_str(self.as_str())
96 }
97}
98
99fn flatten_block_into(
107 working_set: &StateWorkingSet,
108 block: &Block,
109 output: &mut Vec<(Span, FlatShape)>,
110) {
111 for pipeline in &block.pipelines {
112 flatten_pipeline_into(working_set, pipeline, output);
113 }
114}
115
116fn flatten_pipeline_into(
117 working_set: &StateWorkingSet,
118 pipeline: &Pipeline,
119 output: &mut Vec<(Span, FlatShape)>,
120) {
121 for expr in &pipeline.elements {
122 flatten_pipeline_element_into(working_set, expr, output)
123 }
124}
125
126fn flatten_pipeline_element_into(
127 working_set: &StateWorkingSet,
128 pipeline_element: &PipelineElement,
129 output: &mut Vec<(Span, FlatShape)>,
130) {
131 if let Some(span) = pipeline_element.pipe {
132 output.push((span, FlatShape::Pipe));
133 }
134
135 flatten_expression_into(working_set, &pipeline_element.expr, output);
136
137 if let Some(redirection) = pipeline_element.redirection.as_ref() {
138 match redirection {
139 PipelineRedirection::Single { target, .. } => {
140 output.push((target.span(), FlatShape::Redirection));
141 if let Some(expr) = target.expr() {
142 flatten_expression_into(working_set, expr, output);
143 }
144 }
145 PipelineRedirection::Separate { out, err } => {
146 let (out, err) = if out.span() <= err.span() {
147 (out, err)
148 } else {
149 (err, out)
150 };
151
152 output.push((out.span(), FlatShape::Redirection));
153 if let Some(expr) = out.expr() {
154 flatten_expression_into(working_set, expr, output);
155 }
156 output.push((err.span(), FlatShape::Redirection));
157 if let Some(expr) = err.expr() {
158 flatten_expression_into(working_set, expr, output);
159 }
160 }
161 }
162 }
163}
164
165fn flatten_positional_arg_into(
166 working_set: &StateWorkingSet,
167 positional: &Expression,
168 shape: &SyntaxShape,
169 output: &mut Vec<(Span, FlatShape)>,
170) {
171 if matches!(shape, SyntaxShape::ExternalArgument)
172 && matches!(positional.expr, Expr::String(..) | Expr::GlobPattern(..))
173 {
174 output.push((positional.span, FlatShape::ExternalArg));
176 } else {
177 flatten_expression_into(working_set, positional, output)
178 }
179}
180
181fn flatten_expression_into(
182 working_set: &StateWorkingSet,
183 expr: &Expression,
184 output: &mut Vec<(Span, FlatShape)>,
185) {
186 if let Some(custom_completion) = &expr.custom_completion {
187 output.push((expr.span, FlatShape::Custom(*custom_completion)));
188 return;
189 }
190
191 match &expr.expr {
192 Expr::AttributeBlock(ab) => {
193 for attr in &ab.attributes {
194 flatten_expression_into(working_set, &attr.expr, output);
195 }
196 flatten_expression_into(working_set, &ab.item, output);
197 }
198 Expr::BinaryOp(lhs, op, rhs) => {
199 flatten_expression_into(working_set, lhs, output);
200 flatten_expression_into(working_set, op, output);
201 flatten_expression_into(working_set, rhs, output);
202 }
203 Expr::UnaryNot(not) => {
204 output.push((
205 Span::new(expr.span.start, expr.span.start + 3),
206 FlatShape::Operator,
207 ));
208 flatten_expression_into(working_set, not, output);
209 }
210 Expr::Collect(_, expr) => {
211 flatten_expression_into(working_set, expr, output);
212 }
213 Expr::Closure(block_id) => {
214 let outer_span = expr.span;
215
216 let block = working_set.get_block(*block_id);
217 let flattened = flatten_block(working_set, block);
218
219 if let Some(first) = flattened.first() {
220 if first.0.start > outer_span.start {
221 output.push((
222 Span::new(outer_span.start, first.0.start),
223 FlatShape::Closure,
224 ));
225 }
226 }
227
228 let last = if let Some(last) = flattened.last() {
229 if last.0.end < outer_span.end {
230 Some((Span::new(last.0.end, outer_span.end), FlatShape::Closure))
231 } else {
232 None
233 }
234 } else {
235 Some((outer_span, FlatShape::Closure))
237 };
238
239 output.extend(flattened);
240 if let Some(last) = last {
241 output.push(last);
242 }
243 }
244 Expr::Block(block_id) | Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
245 let outer_span = expr.span;
246
247 let flattened = flatten_block(working_set, working_set.get_block(*block_id));
248
249 if let Some(first) = flattened.first() {
250 if first.0.start > outer_span.start {
251 output.push((Span::new(outer_span.start, first.0.start), FlatShape::Block));
252 }
253 }
254
255 let last = if let Some(last) = flattened.last() {
256 if last.0.end < outer_span.end {
257 Some((Span::new(last.0.end, outer_span.end), FlatShape::Block))
258 } else {
259 None
260 }
261 } else {
262 None
263 };
264
265 output.extend(flattened);
266 if let Some(last) = last {
267 output.push(last);
268 }
269 }
270 Expr::Call(call) => {
271 let decl = working_set.get_decl(call.decl_id);
272
273 if call.head.end != 0 {
274 output.push((call.head, FlatShape::InternalCall(call.decl_id)));
276 }
277
278 let signature = decl.signature();
280 let mut positional_args = signature
281 .required_positional
282 .iter()
283 .chain(&signature.optional_positional);
284
285 let arg_start = output.len();
286 for arg in &call.arguments {
287 match arg {
288 Argument::Positional(positional) => {
289 let positional_arg = positional_args.next();
290 let shape = positional_arg
291 .or(signature.rest_positional.as_ref())
292 .map(|arg| &arg.shape)
293 .unwrap_or(&SyntaxShape::Any);
294
295 flatten_positional_arg_into(working_set, positional, shape, output)
296 }
297 Argument::Unknown(positional) => {
298 let shape = signature
299 .rest_positional
300 .as_ref()
301 .map(|arg| &arg.shape)
302 .unwrap_or(&SyntaxShape::Any);
303
304 flatten_positional_arg_into(working_set, positional, shape, output)
305 }
306 Argument::Named(named) => {
307 if named.0.span.end != 0 {
308 output.push((named.0.span, FlatShape::Flag));
310 }
311 if let Some(expr) = &named.2 {
312 flatten_expression_into(working_set, expr, output);
313 }
314 }
315 Argument::Spread(expr) => {
316 output.push((
317 Span::new(expr.span.start - 3, expr.span.start),
318 FlatShape::Operator,
319 ));
320 flatten_expression_into(working_set, expr, output);
321 }
322 }
323 }
324 output[arg_start..].sort();
326 }
327 Expr::ExternalCall(head, args) => {
328 if let Expr::String(..) | Expr::GlobPattern(..) = &head.expr {
329 output.push((head.span, FlatShape::External));
330 } else {
331 flatten_expression_into(working_set, head, output);
332 }
333
334 for arg in args.as_ref() {
335 match arg {
336 ExternalArgument::Regular(expr) => {
337 if let Expr::String(..) | Expr::GlobPattern(..) = &expr.expr {
338 output.push((expr.span, FlatShape::ExternalArg));
339 } else {
340 flatten_expression_into(working_set, expr, output);
341 }
342 }
343 ExternalArgument::Spread(expr) => {
344 output.push((
345 Span::new(expr.span.start - 3, expr.span.start),
346 FlatShape::Operator,
347 ));
348 flatten_expression_into(working_set, expr, output);
349 }
350 }
351 }
352 }
353 Expr::Garbage => output.push((expr.span, FlatShape::Garbage)),
354 Expr::Nothing => output.push((expr.span, FlatShape::Nothing)),
355 Expr::DateTime(_) => output.push((expr.span, FlatShape::DateTime)),
356 Expr::Binary(_) => output.push((expr.span, FlatShape::Binary)),
357 Expr::Int(_) => output.push((expr.span, FlatShape::Int)),
358 Expr::Float(_) => output.push((expr.span, FlatShape::Float)),
359 Expr::MatchBlock(matches) => {
360 for (pattern, expr) in matches {
361 flatten_pattern_into(pattern, output);
362 flatten_expression_into(working_set, expr, output);
363 }
364 }
365 Expr::ValueWithUnit(value) => {
366 flatten_expression_into(working_set, &value.expr, output);
367 output.push((value.unit.span, FlatShape::String));
368 }
369 Expr::CellPath(cell_path) => {
370 output.extend(cell_path.members.iter().map(|member| match *member {
371 PathMember::String { span, .. } => (span, FlatShape::String),
372 PathMember::Int { span, .. } => (span, FlatShape::Int),
373 }));
374 }
375 Expr::FullCellPath(cell_path) => {
376 flatten_expression_into(working_set, &cell_path.head, output);
377 output.extend(cell_path.tail.iter().map(|member| match *member {
378 PathMember::String { span, .. } => (span, FlatShape::String),
379 PathMember::Int { span, .. } => (span, FlatShape::Int),
380 }));
381 }
382 Expr::ImportPattern(import_pattern) => {
383 output.push((import_pattern.head.span, FlatShape::String));
384
385 for member in &import_pattern.members {
386 match member {
387 ImportPatternMember::Glob { span } => output.push((*span, FlatShape::String)),
388 ImportPatternMember::Name { span, .. } => {
389 output.push((*span, FlatShape::String))
390 }
391 ImportPatternMember::List { names } => {
392 output.extend(names.iter().map(|&(_, span)| (span, FlatShape::String)))
393 }
394 }
395 }
396 }
397 Expr::Overlay(_) => output.push((expr.span, FlatShape::String)),
398 Expr::Range(range) => {
399 if let Some(f) = &range.from {
400 flatten_expression_into(working_set, f, output);
401 }
402 if let Some(s) = &range.next {
403 output.push((range.operator.next_op_span, FlatShape::Operator));
404 flatten_expression_into(working_set, s, output);
405 }
406 output.push((range.operator.span, FlatShape::Operator));
407 if let Some(t) = &range.to {
408 flatten_expression_into(working_set, t, output);
409 }
410 }
411 Expr::Bool(_) => output.push((expr.span, FlatShape::Bool)),
412 Expr::Filepath(_, _) => output.push((expr.span, FlatShape::Filepath)),
413 Expr::Directory(_, _) => output.push((expr.span, FlatShape::Directory)),
414 Expr::GlobPattern(_, _) => output.push((expr.span, FlatShape::GlobPattern)),
415 Expr::List(list) => {
416 let outer_span = expr.span;
417 let mut last_end = outer_span.start;
418
419 for item in list {
420 match item {
421 ListItem::Item(expr) => {
422 let flattened = flatten_expression(working_set, expr);
423
424 if let Some(first) = flattened.first() {
425 if first.0.start > last_end {
426 output.push((Span::new(last_end, first.0.start), FlatShape::List));
427 }
428 }
429
430 if let Some(last) = flattened.last() {
431 last_end = last.0.end;
432 }
433
434 output.extend(flattened);
435 }
436 ListItem::Spread(op_span, expr) => {
437 if op_span.start > last_end {
438 output.push((Span::new(last_end, op_span.start), FlatShape::List));
439 }
440 output.push((*op_span, FlatShape::Operator));
441 last_end = op_span.end;
442
443 let flattened_inner = flatten_expression(working_set, expr);
444 if let Some(first) = flattened_inner.first() {
445 if first.0.start > last_end {
446 output.push((Span::new(last_end, first.0.start), FlatShape::List));
447 }
448 }
449 if let Some(last) = flattened_inner.last() {
450 last_end = last.0.end;
451 }
452 output.extend(flattened_inner);
453 }
454 }
455 }
456
457 if last_end < outer_span.end {
458 output.push((Span::new(last_end, outer_span.end), FlatShape::List));
459 }
460 }
461 Expr::StringInterpolation(exprs) => {
462 let mut flattened = vec![];
463 for expr in exprs {
464 flatten_expression_into(working_set, expr, &mut flattened);
465 }
466
467 if let Some(first) = flattened.first() {
468 if first.0.start != expr.span.start {
469 output.push((
471 Span::new(expr.span.start, expr.span.start + 2),
472 FlatShape::StringInterpolation,
473 ));
474 flattened.push((
475 Span::new(expr.span.end - 1, expr.span.end),
476 FlatShape::StringInterpolation,
477 ));
478 }
479 }
480 output.extend(flattened);
481 }
482 Expr::GlobInterpolation(exprs, quoted) => {
483 let mut flattened = vec![];
484 for expr in exprs {
485 flatten_expression_into(working_set, expr, &mut flattened);
486 }
487
488 if *quoted {
489 output.push((
491 Span::new(expr.span.start, expr.span.start + 2),
492 FlatShape::GlobInterpolation,
493 ));
494 flattened.push((
495 Span::new(expr.span.end - 1, expr.span.end),
496 FlatShape::GlobInterpolation,
497 ));
498 }
499 output.extend(flattened);
500 }
501 Expr::Record(list) => {
502 let outer_span = expr.span;
503 let mut last_end = outer_span.start;
504
505 for l in list {
506 match l {
507 RecordItem::Pair(key, val) => {
508 let flattened_lhs = flatten_expression(working_set, key);
509 let flattened_rhs = flatten_expression(working_set, val);
510
511 if let Some(first) = flattened_lhs.first() {
512 if first.0.start > last_end {
513 output
514 .push((Span::new(last_end, first.0.start), FlatShape::Record));
515 }
516 }
517 if let Some(last) = flattened_lhs.last() {
518 last_end = last.0.end;
519 }
520 output.extend(flattened_lhs);
521
522 if let Some(first) = flattened_rhs.first() {
523 if first.0.start > last_end {
524 output
525 .push((Span::new(last_end, first.0.start), FlatShape::Record));
526 }
527 }
528 if let Some(last) = flattened_rhs.last() {
529 last_end = last.0.end;
530 }
531
532 output.extend(flattened_rhs);
533 }
534 RecordItem::Spread(op_span, record) => {
535 if op_span.start > last_end {
536 output.push((Span::new(last_end, op_span.start), FlatShape::Record));
537 }
538 output.push((*op_span, FlatShape::Operator));
539 last_end = op_span.end;
540
541 let flattened = flatten_expression(working_set, record);
542 if let Some(first) = flattened.first() {
543 if first.0.start > last_end {
544 output
545 .push((Span::new(last_end, first.0.start), FlatShape::Record));
546 }
547 }
548 if let Some(last) = flattened.last() {
549 last_end = last.0.end;
550 }
551 output.extend(flattened);
552 }
553 }
554 }
555 if last_end < outer_span.end {
556 output.push((Span::new(last_end, outer_span.end), FlatShape::Record));
557 }
558 }
559 Expr::Keyword(kw) => {
560 output.push((kw.span, FlatShape::Keyword));
561 flatten_expression_into(working_set, &kw.expr, output);
562 }
563 Expr::Operator(_) => output.push((expr.span, FlatShape::Operator)),
564 Expr::Signature(_) => output.push((expr.span, FlatShape::Signature)),
565 Expr::String(_) => output.push((expr.span, FlatShape::String)),
566 Expr::RawString(_) => output.push((expr.span, FlatShape::RawString)),
567 Expr::Table(table) => {
568 let outer_span = expr.span;
569 let mut last_end = outer_span.start;
570
571 for col in table.columns.as_ref() {
572 let flattened = flatten_expression(working_set, col);
573 if let Some(first) = flattened.first() {
574 if first.0.start > last_end {
575 output.push((Span::new(last_end, first.0.start), FlatShape::Table));
576 }
577 }
578
579 if let Some(last) = flattened.last() {
580 last_end = last.0.end;
581 }
582
583 output.extend(flattened);
584 }
585 for row in table.rows.as_ref() {
586 for expr in row.as_ref() {
587 let flattened = flatten_expression(working_set, expr);
588 if let Some(first) = flattened.first() {
589 if first.0.start > last_end {
590 output.push((Span::new(last_end, first.0.start), FlatShape::Table));
591 }
592 }
593
594 if let Some(last) = flattened.last() {
595 last_end = last.0.end;
596 }
597
598 output.extend(flattened);
599 }
600 }
601
602 if last_end < outer_span.end {
603 output.push((Span::new(last_end, outer_span.end), FlatShape::Table));
604 }
605 }
606 Expr::Var(var_id) => output.push((expr.span, FlatShape::Variable(*var_id))),
607 Expr::VarDecl(var_id) => output.push((expr.span, FlatShape::VarDecl(*var_id))),
608 }
609}
610
611fn flatten_pattern_into(match_pattern: &MatchPattern, output: &mut Vec<(Span, FlatShape)>) {
612 match &match_pattern.pattern {
613 Pattern::Garbage => output.push((match_pattern.span, FlatShape::Garbage)),
614 Pattern::IgnoreValue => output.push((match_pattern.span, FlatShape::Nothing)),
615 Pattern::IgnoreRest => output.push((match_pattern.span, FlatShape::Nothing)),
616 Pattern::List(items) => {
617 if let Some(first) = items.first() {
618 if let Some(last) = items.last() {
619 output.push((
620 Span::new(match_pattern.span.start, first.span.start),
621 FlatShape::MatchPattern,
622 ));
623 for item in items {
624 flatten_pattern_into(item, output);
625 }
626 output.push((
627 Span::new(last.span.end, match_pattern.span.end),
628 FlatShape::MatchPattern,
629 ))
630 }
631 } else {
632 output.push((match_pattern.span, FlatShape::MatchPattern));
633 }
634 }
635 Pattern::Record(items) => {
636 if let Some(first) = items.first() {
637 if let Some(last) = items.last() {
638 output.push((
639 Span::new(match_pattern.span.start, first.1.span.start),
640 FlatShape::MatchPattern,
641 ));
642 for (_, pattern) in items {
643 flatten_pattern_into(pattern, output);
644 }
645 output.push((
646 Span::new(last.1.span.end, match_pattern.span.end),
647 FlatShape::MatchPattern,
648 ))
649 }
650 } else {
651 output.push((match_pattern.span, FlatShape::MatchPattern));
652 }
653 }
654 Pattern::Expression(_) | Pattern::Value(_) => {
655 output.push((match_pattern.span, FlatShape::MatchPattern))
656 }
657 Pattern::Variable(var_id) => output.push((match_pattern.span, FlatShape::VarDecl(*var_id))),
658 Pattern::Rest(var_id) => output.push((match_pattern.span, FlatShape::VarDecl(*var_id))),
659 Pattern::Or(patterns) => {
660 for pattern in patterns {
661 flatten_pattern_into(pattern, output);
662 }
663 }
664 }
665}
666
667pub fn flatten_block(working_set: &StateWorkingSet, block: &Block) -> Vec<(Span, FlatShape)> {
668 let mut output = Vec::new();
669 flatten_block_into(working_set, block, &mut output);
670 output
671}
672
673pub fn flatten_pipeline(
674 working_set: &StateWorkingSet,
675 pipeline: &Pipeline,
676) -> Vec<(Span, FlatShape)> {
677 let mut output = Vec::new();
678 flatten_pipeline_into(working_set, pipeline, &mut output);
679 output
680}
681
682pub fn flatten_pipeline_element(
683 working_set: &StateWorkingSet,
684 pipeline_element: &PipelineElement,
685) -> Vec<(Span, FlatShape)> {
686 let mut output = Vec::new();
687 flatten_pipeline_element_into(working_set, pipeline_element, &mut output);
688 output
689}
690
691pub fn flatten_expression(
692 working_set: &StateWorkingSet,
693 expr: &Expression,
694) -> Vec<(Span, FlatShape)> {
695 let mut output = Vec::new();
696 flatten_expression_into(working_set, expr, &mut output);
697 output
698}