[Task #3061771] Correct render order issues on undo (#1785)

* fix #3061771 by correcting render order issues on undo

* clarifying comment

* fix lints

* push an undo stop before executing edits

* tidy up some unnecessary comments
This commit is contained in:
Ashley Stanton-Nurse 2024-04-04 09:17:09 -07:00 committed by GitHub
parent f533eeb0fc
commit db50f42832
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 30 additions and 10 deletions

View File

@ -46,9 +46,21 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
}, 100); }, 100);
} }
public componentDidUpdate(previous: EditorReactProps) { public componentDidUpdate() {
if (this.props.content !== previous.content) { if (!this.editor) {
this.editor?.setValue(this.props.content); return;
}
const existingContent = this.editor.getModel().getValue();
if (this.props.content !== existingContent) {
this.editor.pushUndoStop();
this.editor.executeEdits("", [
{
range: this.editor.getModel().getFullModelRange(),
text: this.props.content,
},
]);
} }
} }
@ -71,9 +83,14 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
protected configureEditor(editor: monaco.editor.IStandaloneCodeEditor) { protected configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
this.editor = editor; this.editor = editor;
const queryEditorModel = this.editor.getModel();
if (!this.props.isReadOnly && this.props.onContentChanged) { if (!this.props.isReadOnly && this.props.onContentChanged) {
queryEditorModel.onDidChangeContent(() => { // Hooking the model's onDidChangeContent event because of some event ordering issues.
// If a single user input causes BOTH the editor content to change AND the cursor selection to change (which is likely),
// then there are some inconsistencies as to which event fires first.
// But the editor.onDidChangeModelContent event seems to always fire before the cursor selection event.
// (This is NOT true for the model's onDidChangeContent event, which sometimes fires after the cursor selection event.)
// If the cursor selection event fires first, then the calling component may re-render the component with old content, so we want to ensure the model content changed event always fires first.
this.editor.onDidChangeModelContent(() => {
const queryEditorModel = this.editor.getModel(); const queryEditorModel = this.editor.getModel();
this.props.onContentChanged(queryEditorModel.getValue()); this.props.onContentChanged(queryEditorModel.getValue());
}); });

View File

@ -496,13 +496,16 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
}; };
public onChangeContent(newContent: string): void { public onChangeContent(newContent: string): void {
// The copilot store's active query takes precedence over the local state,
// and we can't update both states in a single operation.
// So, we update the copilot store's state first, then update the local state.
if (this.state.copilotActive) {
this.props.copilotStore?.setQuery(newContent);
}
this.setState({ this.setState({
sqlQueryEditorContent: newContent, sqlQueryEditorContent: newContent,
queryCopilotGeneratedQuery: "", queryCopilotGeneratedQuery: "",
}); });
if (this.state.copilotActive) {
this.props.copilotStore?.setQuery(newContent);
}
if (this.isPreferredApiMongoDB) { if (this.isPreferredApiMongoDB) {
if (newContent.length > 0) { if (newContent.length > 0) {
this.executeQueryButton = { this.executeQueryButton = {
@ -544,7 +547,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
useCommandBar.getState().setContextButtons(this.getTabsButtons()); useCommandBar.getState().setContextButtons(this.getTabsButtons());
} }
public setEditorContent(): string { public getEditorContent(): string {
if (this.isCopilotTabActive && this.state.queryCopilotGeneratedQuery) { if (this.isCopilotTabActive && this.state.queryCopilotGeneratedQuery) {
return this.state.queryCopilotGeneratedQuery; return this.state.queryCopilotGeneratedQuery;
} }
@ -601,7 +604,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
<div className="queryEditor" style={{ height: "100%" }}> <div className="queryEditor" style={{ height: "100%" }}>
<EditorReact <EditorReact
language={"sql"} language={"sql"}
content={this.setEditorContent()} content={this.getEditorContent()}
isReadOnly={false} isReadOnly={false}
wordWrap={"on"} wordWrap={"on"}
ariaLabel={"Editing Query"} ariaLabel={"Editing Query"}