diff --git a/frontend/src/components/QueryEditor.external-sql-save.test.tsx b/frontend/src/components/QueryEditor.external-sql-save.test.tsx index 4b7f545d..7883eebf 100644 --- a/frontend/src/components/QueryEditor.external-sql-save.test.tsx +++ b/frontend/src/components/QueryEditor.external-sql-save.test.tsx @@ -209,6 +209,7 @@ const editorState = vi.hoisted(() => { }, position: { lineNumber: 1, column: 1 }, selection: null as any, + scrollLeft: 0, providers: [] as any[], providerLanguages: [] as string[], hoverProviders: [] as any[], @@ -292,6 +293,10 @@ const editorState = vi.hoisted(() => { setSelections: vi.fn((selections: any[]) => { state.selection = Array.isArray(selections) ? selections[0] ?? null : null; }), + getScrollLeft: vi.fn(() => state.scrollLeft), + setScrollLeft: vi.fn((scrollLeft: number) => { + state.scrollLeft = scrollLeft; + }), executeEdits: vi.fn((_source: string, edits: any[]) => { edits.forEach((edit) => { const start = offsetAt({ lineNumber: edit.range.startLineNumber, column: edit.range.startColumn }); @@ -897,6 +902,7 @@ describe('QueryEditor external SQL save', () => { editorState.value = ''; editorState.position = { lineNumber: 1, column: 1 }; editorState.selection = null; + editorState.scrollLeft = 0; monacoEditorMockState.latestProps = null; editorState.domNode.style.cursor = ''; editorState.providers = []; @@ -919,6 +925,8 @@ describe('QueryEditor external SQL save', () => { editorState.editor.getModel().getValueLength.mockClear(); editorState.editor.setValue.mockClear(); editorState.editor.executeEdits.mockClear(); + editorState.editor.getScrollLeft.mockClear(); + editorState.editor.setScrollLeft.mockClear(); editorState.editor.deltaDecorations.mockClear(); editorState.editor.updateOptions.mockClear(); editorState.editor.pushUndoStop.mockClear(); @@ -4427,6 +4435,26 @@ describe('QueryEditor external SQL save', () => { }); }); + it('resets stale horizontal scroll after formatting a long single-line SQL statement', async () => { + let renderer!: ReactTestRenderer; + const longSql = `select ${Array.from({ length: 80 }, (_, index) => `column_${index + 1}`).join(', ')} from users where id=1`; + + await act(async () => { + renderer = create(); + }); + + editorState.scrollLeft = 2400; + + const formatButton = findButton(renderer, '美化'); + await act(async () => { + await formatButton.props.onClick(); + }); + + expect(editorState.editor.executeEdits).toHaveBeenCalled(); + expect(editorState.editor.setScrollLeft).toHaveBeenCalledWith(0); + expect(editorState.scrollLeft).toBe(0); + }); + it('formats only the selected SQL when a non-empty selection exists', async () => { let renderer!: ReactTestRenderer; const originalSql = 'select 1; select * from users where id=1'; diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx index fc8e71b5..6f4d18a5 100644 --- a/frontend/src/components/QueryEditor.tsx +++ b/frontend/src/components/QueryEditor.tsx @@ -6178,6 +6178,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc const nextValue = editor.getValue?.(); applyQueryState(typeof nextValue === 'string' ? nextValue : (formatSelection ? currentValue : formatted)); refreshObjectDecorations(); + editor.setScrollLeft?.(0); return; } if (formatSelection) {