Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

fix extract function inside pipelines

author
Giacomo Cavalieri
committer
Louis Pilfold
date (Mar 31, 2026, 1:41 PM +0100) commit f76c0872 parent 51d962d9 change-id xxxvlltu
+854 -55
+26 -2
compiler-core/src/ast.rs
··· 3480 3480 pub enum AssignmentKind<Expression> { 3481 3481 /// let x = ... 3482 3482 Let, 3483 - /// This is a let assignment generated by the compiler for intermediate variables 3484 - /// needed by record updates and `use`. 3483 + /// This is a let assignment generated by the compiler for intermediate 3484 + /// variables needed by record updates and `use`. 3485 3485 /// Like a regular `Let` assignment this can never fail. 3486 3486 /// 3487 3487 Generated, ··· 4564 4564 /// 4565 4565 #[derive(Debug, Clone, PartialEq, Eq)] 4566 4566 pub struct TypedPipelineAssignment { 4567 + /// This is the location of the corresponding pipeline step. 4568 + /// 4569 + /// Take this pipeline: 4570 + /// 4571 + /// ```gleam 4572 + /// wibble |> wobble |> woo 4573 + /// ``` 4574 + /// 4575 + /// It's made of two steps and a final expression: 4576 + /// 4577 + /// ```gleam 4578 + /// let step_0 = wibble 4579 + /// let step_1 = wobble(step_0) 4580 + /// woo(step_1) 4581 + /// ``` 4582 + /// 4583 + /// The locations of each step would be the following: 4584 + /// 4585 + /// ```gleam 4586 + /// wibble |> wobble |> woo 4587 + /// ^^^^^^ location of first step 4588 + /// ^^^^^^ location of second step 4589 + /// ``` 4590 + /// 4567 4591 pub location: SrcSpan, 4568 4592 pub name: EcoString, 4569 4593 pub value: Box<TypedExpr>,
+12 -5
compiler-core/src/type_/pipe.rs
··· 22 22 fn new(expr_typer: &'a mut ExprTyper<'b, 'c>, size: usize, first: TypedExpr, end: u32) -> Self { 23 23 let first_type = first.type_(); 24 24 let first_location = first.location(); 25 - let first_value = new_pipeline_assignment(expr_typer, first); 25 + let first_value = new_pipeline_assignment(expr_typer, first, first_location); 26 26 Self { 27 27 size, 28 28 expr_typer, ··· 82 82 let mut finally = None; 83 83 84 84 for (i, call) in expressions.into_iter().enumerate() { 85 + let step_location = call.location(); 86 + 85 87 if self.expr_typer.previous_panics { 86 88 self.expr_typer 87 89 .warn_for_unreachable_code(call.location(), PanicPosition::PreviousExpression); ··· 212 214 if i + 2 == self.size { 213 215 finally = Some((call, kind)); 214 216 } else { 215 - self.push_assignment(call, kind); 217 + self.push_assignment(call, step_location, kind); 216 218 } 217 219 } 218 220 ··· 269 271 } 270 272 271 273 /// Push an assignment for the value on the left hand side of the pipe 272 - fn push_assignment(&mut self, expression: TypedExpr, kind: PipelineAssignmentKind) { 274 + fn push_assignment( 275 + &mut self, 276 + expression: TypedExpr, 277 + call_location: SrcSpan, 278 + kind: PipelineAssignmentKind, 279 + ) { 273 280 self.argument_type = expression.type_(); 274 281 self.argument_location = expression.location(); 275 - let assignment = new_pipeline_assignment(self.expr_typer, expression); 282 + let assignment = new_pipeline_assignment(self.expr_typer, expression, call_location); 276 283 self.assignments.push((assignment, kind)); 277 284 } 278 285 ··· 428 435 fn new_pipeline_assignment( 429 436 expr_typer: &mut ExprTyper<'_, '_>, 430 437 expression: TypedExpr, 438 + location: SrcSpan, 431 439 ) -> TypedPipelineAssignment { 432 - let location = expression.location(); 433 440 // Insert the variable for use in type checking the rest of the pipeline 434 441 expr_typer.environment.insert_local_variable( 435 442 PIPE_VARIABLE.into(),
+316 -45
language-server/src/code_action.rs
··· 31 31 use lsp_types::{CodeAction, CodeActionKind, CodeActionParams, Position, Range, TextEdit, Url}; 32 32 use vec1::{Vec1, vec1}; 33 33 34 + use crate::engine::{completely_within, position_within}; 35 + 34 36 use super::{ 35 37 TextEdits, 36 38 compiler::LspProjectCompiler, ··· 9716 9718 /// a statement is the last in a block or function, we need to track that 9717 9719 /// manually. 9718 9720 last_statement_location: Option<SrcSpan>, 9721 + /// When visiting a pipeline step, this will hold the type of the value 9722 + /// returned by the previous step (if any!) 9723 + previous_pipeline_assignment_type: Option<Arc<Type>>, 9719 9724 } 9720 9725 9721 9726 /// Information about a section of code we are extracting as a function. 9727 + #[derive(Debug)] 9722 9728 struct ExtractedFunction<'a> { 9723 9729 /// A list of parameters which need to be passed to the extracted function. 9724 9730 /// These are any variables used in the extracted code, which are defined ··· 9745 9751 fn location(&self) -> SrcSpan { 9746 9752 match &self.value { 9747 9753 ExtractedValue::Expression(expression) => expression.location(), 9748 - ExtractedValue::Statements { location, .. } => *location, 9754 + ExtractedValue::Statements { location, .. } 9755 + | ExtractedValue::PipelineSteps { location, .. } => *location, 9756 + } 9757 + } 9758 + 9759 + /// If the extracted function is a series of pipeline steps, this adds to it 9760 + /// the given pipeline step, otherwise leaving it unchanged. 9761 + /// If the extracted function was indeed a pipeline, this will return `true`, 9762 + /// otherwise it returns `false`. 9763 + /// 9764 + fn try_add_pipeline_step(&mut self, step_type: Arc<Type>, step_location: SrcSpan) { 9765 + if let ExtractedFunction { 9766 + value: 9767 + ExtractedValue::PipelineSteps { 9768 + location, 9769 + before_first: _, 9770 + return_type, 9771 + }, 9772 + .. 9773 + } = self 9774 + { 9775 + // If we're extracting this pipeline and the final step is included 9776 + // in the selection we want to add it to the extracted steps 9777 + *return_type = step_type; 9778 + *location = location.merge(&step_location); 9749 9779 } 9750 9780 } 9751 9781 } ··· 9757 9787 location: SrcSpan, 9758 9788 position: StatementPosition, 9759 9789 }, 9790 + PipelineSteps { 9791 + location: SrcSpan, 9792 + /// The type of the value produced by the pipeline steps that will be 9793 + /// piped into the extracted function. Could be none if the steps we're 9794 + /// extracting include the first step, in that case there would be 9795 + /// nothing that is fed into it. 9796 + before_first: Option<Arc<Type>>, 9797 + /// The type returned by the extracted steps. 9798 + return_type: Arc<Type>, 9799 + }, 9800 + } 9801 + 9802 + impl ExtractedValue<'_> { 9803 + fn location(&self) -> SrcSpan { 9804 + match self { 9805 + ExtractedValue::Expression(typed_expr) => typed_expr.location(), 9806 + ExtractedValue::Statements { location, .. } 9807 + | ExtractedValue::PipelineSteps { location, .. } => *location, 9808 + } 9809 + } 9760 9810 } 9761 9811 9762 9812 /// When we are extracting multiple statements, there are two possible cases: ··· 9819 9869 function: None, 9820 9870 function_end_position: None, 9821 9871 last_statement_location: None, 9872 + previous_pipeline_assignment_type: None, 9822 9873 } 9823 9874 } 9824 9875 ··· 9841 9892 }; 9842 9893 9843 9894 match extracted.value { 9844 - // If we extract a block, it isn't very helpful to have the body of the 9845 - // extracted function just be a single block expression, so instead we 9846 - // extract the statements inside the block. For example, the following 9847 - // code: 9895 + // If we extract a block, it isn't very helpful to have the body of 9896 + // the extracted function just be a single block expression, so 9897 + // instead we extract the statements inside the block. For example, 9898 + // the following code: 9848 9899 // 9849 9900 // ```gleam 9850 9901 // pub fn main() { ··· 9973 10024 type_, 9974 10025 extracted.parameters, 9975 10026 end, 10027 + ), 10028 + ExtractedValue::PipelineSteps { 10029 + location, 10030 + before_first, 10031 + return_type, 10032 + } => self.extract_pipeline_steps( 10033 + location, 10034 + end, 10035 + extracted.parameters, 10036 + before_first, 10037 + return_type, 9976 10038 ), 9977 10039 } 9978 10040 ··· 10346 10408 self.edits.insert(function_end, function); 10347 10409 } 10348 10410 10411 + fn extract_pipeline_steps( 10412 + &mut self, 10413 + location: SrcSpan, 10414 + function_end: u32, 10415 + parameters: Vec<(EcoString, Arc<Type>)>, 10416 + before_first: Option<Arc<Type>>, 10417 + return_type: Arc<Type>, 10418 + ) { 10419 + let name = self.function_name(); 10420 + let code = code_at(self.module, location); 10421 + let arguments = parameters.iter().map(|(name, _)| name.clone()).join(", "); 10422 + let replacement = match before_first { 10423 + Some(_) if parameters.is_empty() => format!("{name}"), 10424 + Some(_) | None => format!("{name}({arguments})"), 10425 + }; 10426 + self.edits.replace(location, replacement); 10427 + 10428 + // When extracting something out of the middle of a pipeline the 10429 + // function we produce will produce a single value as output but could 10430 + // take multiple values as input: 10431 + // 10432 + // ```gleam 10433 + // wibble 10434 + // |> wobble(a) // extracting this 10435 + // |> woo(b) // 10436 + // |> something 10437 + // ``` 10438 + // 10439 + // It will take the type returned by `wibble`, `a`, and `b` as input, 10440 + // and produce the value returned by `woo` as output: 10441 + // 10442 + // ```gleam 10443 + // wibble 10444 + // |> function(a, b) 10445 + // |> something 10446 + // ``` 10447 + // 10448 + // If the steps extracted are at the beginning of the pipeline, then it 10449 + // won't take that additional argument! 10450 + // 10451 + // ```gleam 10452 + // wibble // extracting these 10453 + // |> wobble(a) // 10454 + // |> woo(b) // 10455 + // |> something 10456 + // ``` 10457 + // 10458 + // Becomes: 10459 + // 10460 + // ```gleam 10461 + // function(a, b) 10462 + // |> something 10463 + // ``` 10464 + 10465 + let mut type_printer = Printer::new(&self.module.ast.names); 10466 + let return_type = type_printer.print_type(&return_type); 10467 + let first_argument = before_first.map(|type_of_first_argument| { 10468 + let mut generator = NameGenerator::new(); 10469 + for (name, _) in parameters.iter() { 10470 + generator.add_used_name(name.clone()); 10471 + } 10472 + let first_argument_name = generator.generate_name_from_type(&type_of_first_argument); 10473 + (first_argument_name, type_of_first_argument.clone()) 10474 + }); 10475 + let parameters = first_argument 10476 + .clone() 10477 + .into_iter() 10478 + .chain(parameters) 10479 + .map(|(name, type_)| eco_format!("{name}: {}", type_printer.print_type(&type_))) 10480 + .join(", "); 10481 + 10482 + let code = if let Some((first_argument_name, _)) = first_argument { 10483 + format!("{first_argument_name}\n |> {code}") 10484 + } else { 10485 + format!("{}", code.trim_start_matches("|>")) 10486 + }; 10487 + let function = format!( 10488 + "\n\nfn {name}({parameters}) -> {return_type} {{ 10489 + {code} 10490 + }}" 10491 + ); 10492 + 10493 + self.edits.insert(function_end, function); 10494 + } 10495 + 10349 10496 /// When a variable is referenced, we need to decide if we need to do anything 10350 10497 /// to ensure that the reference is still valid after extracting a function. 10351 10498 /// If the variable is defined outside the extracted function, but used inside ··· 10396 10543 variables.push((name.clone(), type_.clone())); 10397 10544 } 10398 10545 10399 - fn can_extract(&self, location: SrcSpan) -> bool { 10400 - let expression_range = self.edits.src_span_to_lsp_range(location); 10546 + fn can_extract_expression(&self, expression: &TypedExpr) -> bool { 10547 + let expression_range = self.edits.src_span_to_lsp_range(expression.location()); 10401 10548 let selected_range = self.params.range; 10402 10549 10403 10550 // If the selected range doesn't touch the expression at all, then there ··· 10406 10553 return false; 10407 10554 } 10408 10555 10409 - // Determine whether the selected range falls completely within the 10410 - // expression. For example: 10411 - // ```gleam 10412 - // pub fn main() { 10413 - // let something = { 10414 - // let a = 1 10415 - // let b = 2 10416 - // let c = a + b 10417 - // //^ The user has selected from here 10418 - // let d = a * b 10419 - // c / d 10420 - // // ^ Until here 10421 - // } 10422 - // } 10423 - // ``` 10424 - // 10425 - // Here, the selected range does overlap with the `let something` 10426 - // statement; but we don't want to extract that whole statement! The 10427 - // user only wanted to extract the statements inside the block. So if 10428 - // the selected range falls completely within the expression, we ignore 10429 - // it and traverse the tree further until we find exactly what the user 10430 - // selected. 10431 - // 10432 - let selected_within_expression = selected_range.start > expression_range.start 10433 - && selected_range.start < expression_range.end 10434 - && selected_range.end > expression_range.start 10435 - && selected_range.end < expression_range.end; 10556 + match expression { 10557 + TypedExpr::Pipeline { 10558 + first_value, 10559 + finally, 10560 + .. 10561 + } => { 10562 + // We can extract a pipeline as a whole only if the selection 10563 + // spans all of its steps! 10564 + let first_step = self.edits.src_span_to_lsp_range(first_value.location); 10565 + let last_step = self.edits.src_span_to_lsp_range(finally.location()); 10566 + position_within(selected_range.start, first_step) 10567 + && position_within(selected_range.end, last_step) 10568 + } 10569 + 10570 + TypedExpr::Int { .. } 10571 + | TypedExpr::Float { .. } 10572 + | TypedExpr::String { .. } 10573 + | TypedExpr::Block { .. } 10574 + | TypedExpr::Var { .. } 10575 + | TypedExpr::Fn { .. } 10576 + | TypedExpr::List { .. } 10577 + | TypedExpr::Call { .. } 10578 + | TypedExpr::BinOp { .. } 10579 + | TypedExpr::Case { .. } 10580 + | TypedExpr::RecordAccess { .. } 10581 + | TypedExpr::PositionalAccess { .. } 10582 + | TypedExpr::ModuleSelect { .. } 10583 + | TypedExpr::Tuple { .. } 10584 + | TypedExpr::TupleIndex { .. } 10585 + | TypedExpr::Todo { .. } 10586 + | TypedExpr::Panic { .. } 10587 + | TypedExpr::Echo { .. } 10588 + | TypedExpr::BitArray { .. } 10589 + | TypedExpr::RecordUpdate { .. } 10590 + | TypedExpr::NegateBool { .. } 10591 + | TypedExpr::NegateInt { .. } 10592 + | TypedExpr::Invalid { .. } => !completely_within(selected_range, expression_range), 10593 + } 10594 + } 10436 10595 10437 - // If the selected range is completely within the expression, we don't 10438 - // want to extract it. 10439 - !selected_within_expression 10596 + fn can_extract_statement(&self, statement: &TypedStatement) -> bool { 10597 + match statement { 10598 + ast::Statement::Expression(expression) => self.can_extract_expression(expression), 10599 + 10600 + // Determine whether the selected range falls completely within the 10601 + // expression. For example: 10602 + // ```gleam 10603 + // pub fn main() { 10604 + // let something = { 10605 + // let a = 1 10606 + // let b = 2 10607 + // let c = a + b 10608 + // //^ The user has selected from here 10609 + // let d = a * b 10610 + // c / d 10611 + // // ^ Until here 10612 + // } 10613 + // } 10614 + // ``` 10615 + // 10616 + // Here, the selected range does overlap with the `let something` 10617 + // statement; but we don't want to extract that whole statement! The 10618 + // user only wanted to extract the statements inside the block. So if 10619 + // the selected range falls completely within the expression, we ignore 10620 + // it and traverse the tree further until we find exactly what the user 10621 + // selected. 10622 + // 10623 + // If the selected range is completely within the expression, we don't 10624 + // want to extract it. 10625 + ast::Statement::Assignment(_) | ast::Statement::Use(_) | ast::Statement::Assert(_) => { 10626 + let statement_range = self.edits.src_span_to_lsp_range(statement.location()); 10627 + let selected_range = self.params.range; 10628 + 10629 + // If the selected range doesn't touch the statement at all, then there 10630 + // is no reason to extract it. 10631 + if !overlaps(statement_range, selected_range) { 10632 + return false; 10633 + } 10634 + !completely_within(selected_range, statement_range) 10635 + } 10636 + } 10440 10637 } 10441 10638 } 10442 10639 ··· 10473 10670 // not desired. 10474 10671 if self.function.is_none() { 10475 10672 // If this expression is fully selected, we mark it as being extracted. 10476 - if self.can_extract(expression.location()) { 10673 + if self.can_extract_expression(expression) { 10477 10674 self.function = Some(ExtractedFunction::new(ExtractedValue::Expression( 10478 10675 expression, 10479 10676 ))); ··· 10485 10682 fn visit_typed_statement(&mut self, statement: &'ast TypedStatement) { 10486 10683 let statement_location = statement.location(); 10487 10684 10488 - if self.can_extract(statement_location) { 10685 + if self.can_extract_statement(statement) { 10489 10686 let is_in_tail_position = 10490 10687 self.last_statement_location 10491 10688 .is_some_and(|last_statement_location| { ··· 10505 10702 10506 10703 match &mut self.function { 10507 10704 None => { 10508 - self.function = Some(ExtractedFunction::new(ExtractedValue::Statements { 10509 - location: statement_location, 10510 - position, 10511 - })); 10705 + self.function = match statement { 10706 + TypedStatement::Expression(TypedExpr::Pipeline { .. }) => None, 10707 + TypedStatement::Assert(_) 10708 + | TypedStatement::Assignment(_) 10709 + | TypedStatement::Expression(_) 10710 + | TypedStatement::Use(_) => { 10711 + Some(ExtractedFunction::new(ExtractedValue::Statements { 10712 + location: statement_location, 10713 + position, 10714 + })) 10715 + } 10716 + } 10512 10717 } 10718 + 10513 10719 // If we have already chosen an expression to extract, that means 10514 10720 // that this statement is within the already extracted expression, 10515 10721 // so we don't want to extract this instead. ··· 10518 10724 .. 10519 10725 }) => {} 10520 10726 // If we are selecting multiple statements, this statement should 10521 - // be included within list, so we merge the spans to ensure it 10522 - // is included. 10727 + // be included within that list, so we merge the spans to ensure 10728 + // it is included. 10523 10729 Some(ExtractedFunction { 10524 10730 value: 10525 10731 ExtractedValue::Statements { ··· 10531 10737 *location = location.merge(&statement_location); 10532 10738 *extracted_position = position; 10533 10739 } 10740 + Some(ExtractedFunction { 10741 + value: value @ ExtractedValue::PipelineSteps { .. }, 10742 + .. 10743 + }) => { 10744 + // If we were extracting a pipeline, but end up selecting 10745 + // some statement that is not part of it, then we go back to 10746 + // selecting a batch of statements. 10747 + *value = ExtractedValue::Statements { 10748 + location: value.location(), 10749 + position, 10750 + } 10751 + } 10534 10752 } 10535 10753 } 10536 10754 ast::visit::visit_typed_statement(self, statement); 10755 + } 10756 + 10757 + fn visit_typed_expr_pipeline( 10758 + &mut self, 10759 + _location: &'ast SrcSpan, 10760 + first_value: &'ast TypedPipelineAssignment, 10761 + assignments: &'ast [(TypedPipelineAssignment, PipelineAssignmentKind)], 10762 + finally: &'ast TypedExpr, 10763 + _finally_kind: &'ast PipelineAssignmentKind, 10764 + ) { 10765 + self.previous_pipeline_assignment_type = None; 10766 + self.visit_typed_pipeline_assignment(first_value); 10767 + 10768 + self.previous_pipeline_assignment_type = Some(first_value.type_()); 10769 + for (assignment, _kind) in assignments { 10770 + self.visit_typed_pipeline_assignment(assignment); 10771 + self.previous_pipeline_assignment_type = Some(assignment.type_()); 10772 + } 10773 + 10774 + // If we're selecting a pipeline and the selection ends on its final step 10775 + // we want to include that as well into the extracted bit. 10776 + let final_step_range = self.edits.src_span_to_lsp_range(finally.location()); 10777 + if let Some(extracted_function) = &mut self.function 10778 + && position_within(self.params.range.end, final_step_range) 10779 + { 10780 + extracted_function.try_add_pipeline_step(finally.type_(), finally.location()); 10781 + }; 10782 + 10783 + self.visit_typed_expr(finally); 10784 + self.previous_pipeline_assignment_type = None; 10785 + } 10786 + 10787 + fn visit_typed_pipeline_assignment(&mut self, assignment: &'ast TypedPipelineAssignment) { 10788 + // In order to be extracted, a pipeline step must be overlapping with 10789 + // the cursor selection! 10790 + let assignment_range = self.edits.src_span_to_lsp_range(assignment.location); 10791 + if !overlaps(self.params.range, assignment_range) { 10792 + return; 10793 + } 10794 + 10795 + match &mut self.function { 10796 + None => { 10797 + self.function = Some(ExtractedFunction::new(ExtractedValue::PipelineSteps { 10798 + location: assignment.location, 10799 + before_first: self.previous_pipeline_assignment_type.clone(), 10800 + return_type: assignment.type_(), 10801 + })); 10802 + } 10803 + Some(extracted_function) => { 10804 + extracted_function.try_add_pipeline_step(assignment.type_(), assignment.location) 10805 + } 10806 + } 10807 + ast::visit::visit_typed_pipeline_assignment(self, assignment); 10537 10808 } 10538 10809 10539 10810 fn visit_typed_expr_var(
+55 -3
language-server/src/engine.rs
··· 1689 1689 } 1690 1690 } 1691 1691 1692 - // Returns true if any part of either range overlaps with the other. 1692 + /// Returns true if any part of either range overlaps with the other. 1693 1693 pub fn overlaps(a: Range, b: Range) -> bool { 1694 1694 position_within(a.start, b) 1695 1695 || position_within(a.end, b) ··· 1697 1697 || position_within(b.end, a) 1698 1698 } 1699 1699 1700 - // Returns true if a range is contained within another. 1700 + /// Returns true if the first range is within the second range. 1701 + /// The ranges might touch on their extremes and still be considered one within 1702 + /// the other! 1703 + /// 1704 + /// `within(a, b)` is true in all of these cases: 1705 + /// 1706 + /// - ```txt 1707 + /// |------| b 1708 + /// |---| a 1709 + /// ``` 1710 + /// - ```txt 1711 + /// |------| b 1712 + /// |---| a 1713 + /// ``` 1714 + /// - ```txt 1715 + /// |------| b 1716 + /// |---| a 1717 + /// ``` 1718 + /// - ```txt 1719 + /// |------| b 1720 + /// |------| a 1721 + /// ``` 1722 + /// 1701 1723 pub fn within(a: Range, b: Range) -> bool { 1702 1724 position_within(a.start, b) && position_within(a.end, b) 1703 1725 } 1704 1726 1727 + /// Returns true if the first range is completely within the second range. 1728 + /// The range cannot have any extreme in common to be considered completely 1729 + /// within another. 1730 + /// 1731 + /// `completely_within(a, b)` is true this case: 1732 + /// 1733 + /// - ```txt 1734 + /// |------| b 1735 + /// |---| a 1736 + /// ``` 1737 + /// 1738 + /// And `completely_within(a, b)` is false in all these cases: 1739 + /// 1740 + /// - ```txt 1741 + /// |------| b 1742 + /// |---| a 1743 + /// ``` 1744 + /// - ```txt 1745 + /// |------| b 1746 + /// |---| a 1747 + /// ``` 1748 + /// - ```txt 1749 + /// |------| b 1750 + /// |------| a 1751 + /// ``` 1752 + /// 1753 + pub fn completely_within(a: Range, b: Range) -> bool { 1754 + a.start > b.start && a.start < b.end && a.end > b.start && a.end < b.end 1755 + } 1756 + 1705 1757 // Returns true if a position is within a range. 1706 - fn position_within(position: Position, range: Range) -> bool { 1758 + pub fn position_within(position: Position, range: Range) -> bool { 1707 1759 position >= range.start && position <= range.end 1708 1760 } 1709 1761
+196
language-server/src/tests/action.rs
··· 11506 11506 } 11507 11507 11508 11508 #[test] 11509 + fn extract_entire_pipeline() { 11510 + assert_code_action!( 11511 + EXTRACT_FUNCTION, 11512 + r#" 11513 + pub fn main() { 11514 + True 11515 + |> wibble 11516 + |> wobble 11517 + } 11518 + 11519 + fn wibble(_) { 1 } 11520 + fn wobble(_) { 1.0 } 11521 + "#, 11522 + find_position_of("True").select_until(find_position_of("wobble")) 11523 + ); 11524 + } 11525 + 11526 + #[test] 11527 + fn extract_starting_steps_of_pipeline() { 11528 + assert_code_action!( 11529 + EXTRACT_FUNCTION, 11530 + r#" 11531 + pub fn main() { 11532 + True 11533 + |> wibble 11534 + |> wobble 11535 + } 11536 + 11537 + fn wibble(_) { 1 } 11538 + fn wobble(_) { 1.0 } 11539 + "#, 11540 + find_position_of("True").select_until(find_position_of("wibble")) 11541 + ); 11542 + } 11543 + 11544 + #[test] 11545 + fn extract_starting_steps_of_pipeline_with_argument() { 11546 + assert_code_action!( 11547 + EXTRACT_FUNCTION, 11548 + r#" 11549 + pub fn main() { 11550 + let something = 1 11551 + 11552 + True 11553 + |> wibble(something) 11554 + |> wobble 11555 + } 11556 + 11557 + fn wibble(_, _) { 1 } 11558 + fn wobble(_) { 1.0 } 11559 + "#, 11560 + find_position_of("True").select_until(find_position_of("wibble")) 11561 + ); 11562 + } 11563 + 11564 + #[test] 11565 + fn extract_final_steps_of_pipeline() { 11566 + assert_code_action!( 11567 + EXTRACT_FUNCTION, 11568 + r#" 11569 + pub fn main() { 11570 + True 11571 + |> wibble 11572 + |> wobble 11573 + } 11574 + 11575 + fn wibble(_) { 1 } 11576 + fn wobble(_) { 1.0 } 11577 + "#, 11578 + find_position_of("wibble").select_until(find_position_of("wobble")) 11579 + ); 11580 + } 11581 + 11582 + #[test] 11583 + fn extract_final_steps_of_pipeline_with_arguments() { 11584 + assert_code_action!( 11585 + EXTRACT_FUNCTION, 11586 + r#" 11587 + pub fn main() { 11588 + let something = 1 11589 + 11590 + True 11591 + |> wibble 11592 + |> wobble(something) 11593 + } 11594 + 11595 + fn wibble(_) { 1 } 11596 + fn wobble(_, _) { 1.0 } 11597 + "#, 11598 + find_position_of("wibble").select_until(find_position_of("wobble")) 11599 + ); 11600 + } 11601 + 11602 + #[test] 11603 + fn extract_middle_steps_of_pipeline() { 11604 + assert_code_action!( 11605 + EXTRACT_FUNCTION, 11606 + r#" 11607 + pub fn main() { 11608 + True 11609 + |> wibble 11610 + |> wobble 11611 + |> woo 11612 + } 11613 + 11614 + fn wibble(_) { 1 } 11615 + fn wobble(_) { 1.0 } 11616 + fn woo(_) { [] } 11617 + "#, 11618 + find_position_of("wibble").select_until(find_position_of("wobble")) 11619 + ); 11620 + } 11621 + 11622 + #[test] 11623 + fn extract_middle_steps_of_pipeline_with_arguments() { 11624 + assert_code_action!( 11625 + EXTRACT_FUNCTION, 11626 + r#" 11627 + pub fn main() { 11628 + let something = 1 11629 + 11630 + True 11631 + |> wibble(1) 11632 + |> wobble(something) 11633 + |> woo 11634 + } 11635 + 11636 + fn wibble(_, _) { 1 } 11637 + fn wobble(_, _) { 1.0 } 11638 + fn woo(_) { [] } 11639 + "#, 11640 + find_position_of("wibble").select_until(find_position_of("wobble")) 11641 + ); 11642 + } 11643 + 11644 + #[test] 11645 + fn cannot_extract_a_single_starting_step_as_function() { 11646 + assert_no_code_actions!( 11647 + EXTRACT_FUNCTION, 11648 + r#" 11649 + pub fn main() { 11650 + True 11651 + |> wibble 11652 + |> wobble 11653 + |> woo 11654 + } 11655 + 11656 + fn wibble(_, _) { 1 } 11657 + fn wobble(_, _) { 1.0 } 11658 + fn woo(_) { [] } 11659 + "#, 11660 + find_position_of("True").to_selection() 11661 + ); 11662 + } 11663 + 11664 + #[test] 11665 + fn cannot_extract_a_single_middle_step_as_function() { 11666 + assert_no_code_actions!( 11667 + EXTRACT_FUNCTION, 11668 + r#" 11669 + pub fn main() { 11670 + True 11671 + |> wibble 11672 + |> wobble 11673 + |> woo 11674 + } 11675 + 11676 + fn wibble(_, _) { 1 } 11677 + fn wobble(_, _) { 1.0 } 11678 + fn woo(_) { [] } 11679 + "#, 11680 + find_position_of("wibble").to_selection() 11681 + ); 11682 + } 11683 + 11684 + #[test] 11685 + fn cannot_extract_a_single_final_step_as_function() { 11686 + assert_no_code_actions!( 11687 + EXTRACT_FUNCTION, 11688 + r#" 11689 + pub fn main() { 11690 + True 11691 + |> wibble 11692 + |> wobble 11693 + |> woo 11694 + } 11695 + 11696 + fn wibble(_, _) { 1 } 11697 + fn wobble(_, _) { 1.0 } 11698 + fn woo(_) { [] } 11699 + "#, 11700 + find_position_of("woo").to_selection() 11701 + ); 11702 + } 11703 + 11704 + #[test] 11509 11705 fn no_code_action_to_extract_function_when_expression_is_not_fully_selected() { 11510 11706 assert_no_code_actions!( 11511 11707 EXTRACT_FUNCTION,
+33
language-server/src/tests/snapshots/gleam_language_server__tests__action__extract_entire_pipeline.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn main() {\n True\n |> wibble\n |> wobble\n}\n\nfn wibble(_) { 1 }\nfn wobble(_) { 1.0 }\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn main() { 8 + True 9 + ▔▔▔▔ 10 + |> wibble 11 + ▔▔▔▔▔▔▔▔▔▔▔ 12 + |> wobble 13 + ▔▔▔▔▔↑ 14 + } 15 + 16 + fn wibble(_) { 1 } 17 + fn wobble(_) { 1.0 } 18 + 19 + 20 + ----- AFTER ACTION 21 + 22 + pub fn main() { 23 + function() 24 + } 25 + 26 + fn function() -> Float { 27 + True 28 + |> wibble 29 + |> wobble 30 + } 31 + 32 + fn wibble(_) { 1 } 33 + fn wobble(_) { 1.0 }
+33
language-server/src/tests/snapshots/gleam_language_server__tests__action__extract_final_steps_of_pipeline.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn main() {\n True\n |> wibble\n |> wobble\n}\n\nfn wibble(_) { 1 }\nfn wobble(_) { 1.0 }\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn main() { 8 + True 9 + |> wibble 10 + ▔▔▔▔▔▔ 11 + |> wobble 12 + ▔▔▔▔▔↑ 13 + } 14 + 15 + fn wibble(_) { 1 } 16 + fn wobble(_) { 1.0 } 17 + 18 + 19 + ----- AFTER ACTION 20 + 21 + pub fn main() { 22 + True 23 + |> function 24 + } 25 + 26 + fn function(bool: Bool) -> Float { 27 + bool 28 + |> wibble 29 + |> wobble 30 + } 31 + 32 + fn wibble(_) { 1 } 33 + fn wobble(_) { 1.0 }
+37
language-server/src/tests/snapshots/gleam_language_server__tests__action__extract_final_steps_of_pipeline_with_arguments.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn main() {\n let something = 1\n\n True\n |> wibble\n |> wobble(something)\n}\n\nfn wibble(_) { 1 }\nfn wobble(_, _) { 1.0 }\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn main() { 8 + let something = 1 9 + 10 + True 11 + |> wibble 12 + ▔▔▔▔▔▔ 13 + |> wobble(something) 14 + ▔▔▔▔▔↑ 15 + } 16 + 17 + fn wibble(_) { 1 } 18 + fn wobble(_, _) { 1.0 } 19 + 20 + 21 + ----- AFTER ACTION 22 + 23 + pub fn main() { 24 + let something = 1 25 + 26 + True 27 + |> function(something) 28 + } 29 + 30 + fn function(bool: Bool, something: Int) -> Float { 31 + bool 32 + |> wibble 33 + |> wobble(something) 34 + } 35 + 36 + fn wibble(_) { 1 } 37 + fn wobble(_, _) { 1.0 }
+37
language-server/src/tests/snapshots/gleam_language_server__tests__action__extract_middle_steps_of_pipeline.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn main() {\n True\n |> wibble\n |> wobble\n |> woo\n}\n\nfn wibble(_) { 1 }\nfn wobble(_) { 1.0 }\nfn woo(_) { [] }\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn main() { 8 + True 9 + |> wibble 10 + ▔▔▔▔▔▔ 11 + |> wobble 12 + ▔▔▔▔▔↑ 13 + |> woo 14 + } 15 + 16 + fn wibble(_) { 1 } 17 + fn wobble(_) { 1.0 } 18 + fn woo(_) { [] } 19 + 20 + 21 + ----- AFTER ACTION 22 + 23 + pub fn main() { 24 + True 25 + |> function 26 + |> woo 27 + } 28 + 29 + fn function(bool: Bool) -> Float { 30 + bool 31 + |> wibble 32 + |> wobble 33 + } 34 + 35 + fn wibble(_) { 1 } 36 + fn wobble(_) { 1.0 } 37 + fn woo(_) { [] }
+41
language-server/src/tests/snapshots/gleam_language_server__tests__action__extract_middle_steps_of_pipeline_with_arguments.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn main() {\n let something = 1\n\n True\n |> wibble(1)\n |> wobble(something)\n |> woo\n}\n\nfn wibble(_, _) { 1 }\nfn wobble(_, _) { 1.0 }\nfn woo(_) { [] }\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn main() { 8 + let something = 1 9 + 10 + True 11 + |> wibble(1) 12 + ▔▔▔▔▔▔▔▔▔ 13 + |> wobble(something) 14 + ▔▔▔▔▔↑ 15 + |> woo 16 + } 17 + 18 + fn wibble(_, _) { 1 } 19 + fn wobble(_, _) { 1.0 } 20 + fn woo(_) { [] } 21 + 22 + 23 + ----- AFTER ACTION 24 + 25 + pub fn main() { 26 + let something = 1 27 + 28 + True 29 + |> function(something) 30 + |> woo 31 + } 32 + 33 + fn function(bool: Bool, something: Int) -> Float { 34 + bool 35 + |> wibble(1) 36 + |> wobble(something) 37 + } 38 + 39 + fn wibble(_, _) { 1 } 40 + fn wobble(_, _) { 1.0 } 41 + fn woo(_) { [] }
+32
language-server/src/tests/snapshots/gleam_language_server__tests__action__extract_starting_steps_of_pipeline.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn main() {\n True\n |> wibble\n |> wobble\n}\n\nfn wibble(_) { 1 }\nfn wobble(_) { 1.0 }\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn main() { 8 + True 9 + ▔▔▔▔ 10 + |> wibble 11 + ▔▔▔▔▔↑ 12 + |> wobble 13 + } 14 + 15 + fn wibble(_) { 1 } 16 + fn wobble(_) { 1.0 } 17 + 18 + 19 + ----- AFTER ACTION 20 + 21 + pub fn main() { 22 + function() 23 + |> wobble 24 + } 25 + 26 + fn function() -> Int { 27 + True 28 + |> wibble 29 + } 30 + 31 + fn wibble(_) { 1 } 32 + fn wobble(_) { 1.0 }
+36
language-server/src/tests/snapshots/gleam_language_server__tests__action__extract_starting_steps_of_pipeline_with_argument.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn main() {\n let something = 1\n\n True\n |> wibble(something)\n |> wobble\n}\n\nfn wibble(_, _) { 1 }\nfn wobble(_) { 1.0 }\n" 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn main() { 8 + let something = 1 9 + 10 + True 11 + ▔▔▔▔ 12 + |> wibble(something) 13 + ▔▔▔▔▔↑ 14 + |> wobble 15 + } 16 + 17 + fn wibble(_, _) { 1 } 18 + fn wobble(_) { 1.0 } 19 + 20 + 21 + ----- AFTER ACTION 22 + 23 + pub fn main() { 24 + let something = 1 25 + 26 + function(something) 27 + |> wobble 28 + } 29 + 30 + fn function(something: Int) -> Int { 31 + True 32 + |> wibble(something) 33 + } 34 + 35 + fn wibble(_, _) { 1 } 36 + fn wobble(_) { 1.0 }