fix: Don't create intermediate variables when renaming a procedure argument. (#8723)

This commit is contained in:
Aaron Dodson
2025-01-08 09:37:28 -08:00
committed by GitHub
parent 151d21e50e
commit 4dcffa0914

View File

@@ -629,30 +629,49 @@ type ArgumentBlock = Block & ArgumentMixin;
interface ArgumentMixin extends ArgumentMixinType {} interface ArgumentMixin extends ArgumentMixinType {}
type ArgumentMixinType = typeof PROCEDURES_MUTATORARGUMENT; type ArgumentMixinType = typeof PROCEDURES_MUTATORARGUMENT;
// TODO(#6920): This is kludgy. /**
type FieldTextInputForArgument = FieldTextInput & { * Field responsible for editing procedure argument names.
oldShowEditorFn_(_e?: Event, quietInput?: boolean): void; */
createdVariables_: IVariableModel<IVariableState>[]; class ProcedureArgumentField extends FieldTextInput {
}; /**
* Whether or not this field is currently being edited interactively.
*/
editingInteractively = false;
/**
* The procedure argument variable whose name is being interactively edited.
*/
editingVariable?: IVariableModel<IVariableState>;
/**
* Displays the field editor.
*
* @param e The event that triggered display of the field editor.
*/
protected override showEditor_(e?: Event) {
super.showEditor_(e);
this.editingInteractively = true;
this.editingVariable = undefined;
}
/**
* Handles cleanup when the field editor is dismissed.
*/
override onFinishEditing_(value: string) {
super.onFinishEditing_(value);
this.editingInteractively = false;
}
}
const PROCEDURES_MUTATORARGUMENT = { const PROCEDURES_MUTATORARGUMENT = {
/** /**
* Mutator block for procedure argument. * Mutator block for procedure argument.
*/ */
init: function (this: ArgumentBlock) { init: function (this: ArgumentBlock) {
const field = fieldRegistry.fromJson({ const field = new ProcedureArgumentField(
type: 'field_input', Procedures.DEFAULT_ARG,
text: Procedures.DEFAULT_ARG, this.validator_,
}) as FieldTextInputForArgument; );
field.setValidator(this.validator_);
// Hack: override showEditor to do just a little bit more work.
// We don't have a good place to hook into the start of a text edit.
field.oldShowEditorFn_ = (field as AnyDuringMigration).showEditor_;
const newShowEditorFn = function (this: typeof field) {
this.createdVariables_ = [];
this.oldShowEditorFn_();
};
(field as AnyDuringMigration).showEditor_ = newShowEditorFn;
this.appendDummyInput() this.appendDummyInput()
.appendField(Msg['PROCEDURES_MUTATORARG_TITLE']) .appendField(Msg['PROCEDURES_MUTATORARG_TITLE'])
@@ -662,14 +681,6 @@ const PROCEDURES_MUTATORARGUMENT = {
this.setStyle('procedure_blocks'); this.setStyle('procedure_blocks');
this.setTooltip(Msg['PROCEDURES_MUTATORARG_TOOLTIP']); this.setTooltip(Msg['PROCEDURES_MUTATORARG_TOOLTIP']);
this.contextMenu = false; this.contextMenu = false;
// Create the default variable when we drag the block in from the flyout.
// Have to do this after installing the field on the block.
field.onFinishEditing_ = this.deleteIntermediateVars_;
// Create an empty list so onFinishEditing_ has something to look at, even
// though the editor was never opened.
field.createdVariables_ = [];
field.onFinishEditing_('x');
}, },
/** /**
@@ -683,11 +694,11 @@ const PROCEDURES_MUTATORARGUMENT = {
* @returns Valid name, or null if a name was not specified. * @returns Valid name, or null if a name was not specified.
*/ */
validator_: function ( validator_: function (
this: FieldTextInputForArgument, this: ProcedureArgumentField,
varName: string, varName: string,
): string | null { ): string | null {
const sourceBlock = this.getSourceBlock()!; const sourceBlock = this.getSourceBlock()!;
const outerWs = sourceBlock!.workspace.getRootWorkspace()!; const outerWs = sourceBlock.workspace.getRootWorkspace()!;
varName = varName.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, ''); varName = varName.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
if (!varName) { if (!varName) {
return null; return null;
@@ -716,43 +727,24 @@ const PROCEDURES_MUTATORARGUMENT = {
return varName; return varName;
} }
let model = outerWs.getVariable(varName, ''); const model = outerWs.getVariable(varName, '');
if (model && model.getName() !== varName) { if (model && model.getName() !== varName) {
// Rename the variable (case change) // Rename the variable (case change)
outerWs.renameVariableById(model.getId(), varName); outerWs.renameVariableById(model.getId(), varName);
} }
if (!model) { if (!model) {
model = outerWs.createVariable(varName, ''); if (this.editingInteractively) {
if (model && this.createdVariables_) { if (!this.editingVariable) {
this.createdVariables_.push(model); this.editingVariable = outerWs.createVariable(varName, '');
} else {
outerWs.renameVariableById(this.editingVariable.getId(), varName);
}
} else {
outerWs.createVariable(varName, '');
} }
} }
return varName; return varName;
}, },
/**
* Called when focusing away from the text field.
* Deletes all variables that were created as the user typed their intended
* variable name.
*
* @internal
* @param newText The new variable name.
*/
deleteIntermediateVars_: function (
this: FieldTextInputForArgument,
newText: string,
) {
const outerWs = this.getSourceBlock()!.workspace.getRootWorkspace();
if (!outerWs) {
return;
}
for (let i = 0; i < this.createdVariables_.length; i++) {
const model = this.createdVariables_[i];
if (model.getName() !== newText) {
outerWs.deleteVariableById(model.getId());
}
}
},
}; };
blocks['procedures_mutatorarg'] = PROCEDURES_MUTATORARGUMENT; blocks['procedures_mutatorarg'] = PROCEDURES_MUTATORARGUMENT;