···444444 .filter(|argument| argument.implicit.is_none())
445445 .find_map(|argument| argument.find_node(byte_index, constructor, arguments))
446446 .or_else(|| constructor.find_node(byte_index))
447447- .or_else(|| updated_record.find_node(byte_index))
447447+ .or_else(|| {
448448+ // If the updated record contains the index, then we update
449449+ // the found position to be `UpdatedRecord`
450450+ updated_record.find_node(byte_index).map(|found| {
451451+ if let Located::Expression {
452452+ expression,
453453+ position:
454454+ ExpressionPosition::ArgumentOrLabel { .. }
455455+ | ExpressionPosition::Expression,
456456+ } = found
457457+ {
458458+ Located::Expression {
459459+ expression,
460460+ position: ExpressionPosition::UpdatedRecord {
461461+ unchanged_record_fields: self
462462+ .unchanged_record_fields()
463463+ .unwrap_or_default(),
464464+ },
465465+ }
466466+ } else {
467467+ found
468468+ }
469469+ })
470470+ })
448471 .or_else(|| self.self_if_contains_location(byte_index)),
449472 }
450473 }
···1656167916571680 pub fn is_todo_with_no_message(&self) -> bool {
16581681 matches!(self, TypedExpr::Todo { message: None, .. })
16821682+ }
16831683+16841684+ /// If the expression is a record update, this returns a list with the names
16851685+ /// of its labelled arguments that are not being changed by the record
16861686+ /// update.
16871687+ pub fn unchanged_record_fields(&self) -> Option<Vec<(EcoString, Arc<Type>)>> {
16881688+ let TypedExpr::RecordUpdate { arguments, .. } = self else {
16891689+ return None;
16901690+ };
16911691+16921692+ let mut unchanged_arguments = vec![];
16931693+ for argument in arguments {
16941694+ // All arguments that stay the same are generated as "implicit"
16951695+ // arguments, we only care about them!
16961696+ if !argument.is_implicit() {
16971697+ continue;
16981698+ }
16991699+ // The label should always be present for these implicit arguments,
17001700+ // technically this will never fail, but rather than panicking I
17011701+ // just "continue".
17021702+ let Some(label) = argument.label.as_ref() else {
17031703+ continue;
17041704+ };
17051705+ unchanged_arguments.push((label.clone(), argument.value.type_()))
17061706+ }
17071707+ Some(unchanged_arguments)
16591708 }
16601709}
16611710
···426426/// The position of a located expression. Used to determine extra context,
427427/// such as whether to provide label completions if the expression is in
428428/// argument position.
429429-#[derive(Debug, Clone, Copy, PartialEq)]
429429+#[derive(Debug, Clone, PartialEq)]
430430pub enum ExpressionPosition<'a> {
431431 Expression,
432432 ArgumentOrLabel {
433433 called_function: &'a TypedExpr,
434434 function_arguments: &'a [TypedCallArg],
435435+ },
436436+ /// This is for an expression that is used in a record update spread:
437437+ /// ```gleam
438438+ /// Wibble(..wibble, a: 1)
439439+ /// // ^^^^^^^^ This!
440440+ /// ```
441441+ UpdatedRecord {
442442+ /// These are all the record fields that are not being updated in the
443443+ /// update.
444444+ unchanged_record_fields: Vec<(EcoString, Arc<Type>)>,
435445 },
436446}
437447