From 43348e1bf8c0c76680812a45247152e0fa3a147c Mon Sep 17 00:00:00 2001 From: David Ichim Date: Sun, 25 May 2025 22:39:41 +0300 Subject: [PATCH 1/9] fix(non-stop): Improve fixup insertion and docstring placement - Enhanced FixupController to handle indentation correctly when inserting code snippets, especially for Python docstrings. - Adjusted docstring placement logic in `getLanguageSpecificQueryWrappers` to ensure correct positioning above symbols. - Added a special case to handle Python documentation indentation. - Fixed an issue where the LLM returns the entire code block instead of only the docstring. ## Test plan - Tested with various code snippets and docstrings to ensure correct indentation and placement. - Verified that the fix addresses the issue of the LLM returning the entire code block instead of only the docstring. --- vscode/src/non-stop/FixupController.ts | 70 +++++++++++++++++++++----- vscode/src/tree-sitter/query-sdk.ts | 8 +-- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/vscode/src/non-stop/FixupController.ts b/vscode/src/non-stop/FixupController.ts index dfb880761eee..f0b523ed3367 100644 --- a/vscode/src/non-stop/FixupController.ts +++ b/vscode/src/non-stop/FixupController.ts @@ -18,6 +18,7 @@ import { PersistenceTracker } from '../common/persistence-tracker' import { lines } from '../completions/text-processing' import { executeEdit } from '../edit/execute' import { type QuickPickInput, getInput } from '../edit/input/get-input' + import { type EditIntent, EditIntentTelemetryMetadataMapping, @@ -942,26 +943,69 @@ export class FixupController return false } - // Get the index of the first non-whitespace character on the line where the insertion point is. - const nonEmptyStartIndex = document.lineAt( - task.insertionPoint.line - ).firstNonWhitespaceCharacterIndex - // Split the text into lines and prepend each line with spaces to match the indentation level - // of the line where the insertion point is. - const textLines = text.split('\n').map(line => ' '.repeat(nonEmptyStartIndex) + line) - // Join the lines back into a single string with newline characters - // Remove any leading whitespace from the first line, as we are inserting at the insertionPoint - // Keep any trailing whitespace on the last line to preserve the original indentation. - const replacementText = textLines.join('\n').trimStart() + // Get the indentation context for the insertion point + const insertionLine = document.lineAt(task.insertionPoint.line) + let targetIndentSize = insertionLine.firstNonWhitespaceCharacterIndex + + // Special case for Python documentation + if (targetIndentSize === 0 && task.document.languageId === 'python' && task.intent === 'doc') { + targetIndentSize = 4 + } + + // Calculate indentation adjustments + const textIndentSize = text.search(/\S/) || 0 + const needsIndentAdjustment = targetIndentSize > textIndentSize + const indentDifference = needsIndentAdjustment ? targetIndentSize - textIndentSize : 0 + + // Process the text lines + const textLines = text.split('\n') + const processedLines = textLines.map((line, index) => { + // Don't add extra indentation to empty lines + if (line.trim() === '') { + return line + } + + // For the first line, only add indentation if we're at the start of a line + if ( + index === 0 && + task.insertionPoint.character > 0 && + line.startsWith(' '.repeat(textIndentSize)) + ) { + return line.trimStart() + } + + // Add the calculated indentation difference + return ' '.repeat(indentDifference) + line + }) + + // Ensure proper line ending and handle insertion at line start + const proposedText = + processedLines.join('\n') + + (task.document.languageId !== 'python' + ? task.insertionPoint.character > 0 + ? ' '.repeat(targetIndentSize) + : '' + : '') + + const replacementText = proposedText + const startLine = task.insertionPoint.line > 0 ? task.insertionPoint.line - 1 : 0 + const startLineText = document.lineAt(startLine).text - // Insert the updated text at the specified insertionPoint. + // Insert the updated text at the specified insertionPoint if (edit instanceof vscode.WorkspaceEdit) { edit.insert(document.uri, task.insertionPoint, replacementText) return vscode.workspace.applyEdit(edit) } return edit(editBuilder => { - editBuilder.insert(task.insertionPoint, replacementText) + // Replace the code block if the start line matches the start of the text + // This happens sometimes with python document code action where instead + // of adding only the docstring, the LLM returns the entire code block + if (startLine > 0 && startLineText && text.startsWith(startLineText)) { + editBuilder.replace(task.originalRange, replacementText) + } else { + editBuilder.insert(task.insertionPoint, replacementText) + } }, options) } diff --git a/vscode/src/tree-sitter/query-sdk.ts b/vscode/src/tree-sitter/query-sdk.ts index 82cd41925474..a67d8f569ccf 100644 --- a/vscode/src/tree-sitter/query-sdk.ts +++ b/vscode/src/tree-sitter/query-sdk.ts @@ -186,7 +186,7 @@ function getLanguageSpecificQueryWrappers( const symbol = findLast(symbolCaptures, ({ node }) => { return ( node.startPosition.row === start.row && - (node.startPosition.column <= start.column || node.startPosition.row < start.row) && + (node.startPosition.column <= start.column || node.startPosition.row <= start.row) && (start.column <= node.endPosition.column || start.row < node.endPosition.row) ) }) @@ -227,8 +227,10 @@ function getLanguageSpecificQueryWrappers( * For all other cases, docstrings should be attached above the symbol range, use this. */ const docStringLine = - languageId === 'python' && insertionPoint - ? insertionPoint.node.startPosition.row + 1 + languageId === 'python' + ? insertionPoint + ? insertionPoint.node.startPosition.row + 1 + : start.row + 1 : start.row - 1 const docstringCaptures = queries.documentableNodes.compiled .captures(root, { From 48e5619ecd8a4f1666e48396e2e518107680e5f3 Mon Sep 17 00:00:00 2001 From: David Ichim Date: Sun, 8 Jun 2025 15:39:05 +0300 Subject: [PATCH 2/9] fix(tree-sitter): Adjust symbol capture condition in query wrappers - Modified the symbol capture condition in `getLanguageSpecificQueryWrappers` to correctly identify symbols based on their position relative to the start position. - Changed `node.startPosition.row <= start.row` to `node.startPosition.row < start.row` to refine the symbol matching logic. This fixes the tests from `vscode/src/tree-sitter/query-tests/documentable-nodes.test.ts` --- vscode/src/tree-sitter/query-sdk.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode/src/tree-sitter/query-sdk.ts b/vscode/src/tree-sitter/query-sdk.ts index a67d8f569ccf..f8d12db6eee8 100644 --- a/vscode/src/tree-sitter/query-sdk.ts +++ b/vscode/src/tree-sitter/query-sdk.ts @@ -186,7 +186,7 @@ function getLanguageSpecificQueryWrappers( const symbol = findLast(symbolCaptures, ({ node }) => { return ( node.startPosition.row === start.row && - (node.startPosition.column <= start.column || node.startPosition.row <= start.row) && + (node.startPosition.column <= start.column || node.startPosition.row < start.row) && (start.column <= node.endPosition.column || start.row < node.endPosition.row) ) }) From 4382b4ed8b89106996e6678d579384e0ddde8851 Mon Sep 17 00:00:00 2001 From: David Ichim Date: Sun, 8 Jun 2025 22:39:24 +0300 Subject: [PATCH 3/9] fix(edit): Improve code indentation and docstring generation for fixups - Adjusted indentation logic in `FixupController.ts` to handle cases where the target indentation is greater than the text indentation. - Added a special case for Python documentation to avoid incorrect indentation. - Modified `claude.ts` to prevent empty responses from Claude when generating docstrings for Python functions by excluding the function definition line from the stop sequences. - Fixed a bug in `FixupController.ts` where the entire code block was being returned instead of just the docstring for Python document code actions. Now, the code correctly replaces the original range with the generated text. --- vscode/src/edit/prompt/models/claude.ts | 4 +++- vscode/src/non-stop/FixupController.ts | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/vscode/src/edit/prompt/models/claude.ts b/vscode/src/edit/prompt/models/claude.ts index a5dcf73c0db1..7bf4a7e226a1 100644 --- a/vscode/src/edit/prompt/models/claude.ts +++ b/vscode/src/edit/prompt/models/claude.ts @@ -22,7 +22,9 @@ export const claude: EditLLMInteraction = { getDoc(options) { const docStopSequences = [...SHARED_PARAMETERS.stopSequences] const firstLine = options.selectedText.toString().split('\n')[0] - if (firstLine.trim().length > 0) { + // when claude receives a def function line, it will return an empty response + // if we add that first line to the stop sequences + if (firstLine.trim().length > 0 && options.document.languageId !== 'python') { docStopSequences.push(firstLine) } diff --git a/vscode/src/non-stop/FixupController.ts b/vscode/src/non-stop/FixupController.ts index a6a5502eff70..0230dd0278fa 100644 --- a/vscode/src/non-stop/FixupController.ts +++ b/vscode/src/non-stop/FixupController.ts @@ -24,6 +24,7 @@ import { type EditMode, EditModeTelemetryMetadataMapping, } from '../edit/types' +import { isStreamedIntent } from '../edit/utils/edit-intent' import { getOverriddenModelForIntent } from '../edit/utils/edit-models' import type { ExtensionClient } from '../extension-client' import { isRunningInsideAgent } from '../jsonrpc/isRunningInsideAgent' @@ -31,8 +32,6 @@ import { logDebug } from '../output-channel-logger' import { charactersLogger } from '../services/CharactersLogger' import { splitSafeMetadata } from '../services/telemetry-v2' import { countCode } from '../services/utils/code-count' - -import { isStreamedIntent } from '../edit/utils/edit-intent' import { FixupDocumentEditObserver } from './FixupDocumentEditObserver' import type { FixupFile } from './FixupFile' import { FixupFileObserver } from './FixupFileObserver' @@ -955,6 +954,7 @@ export class FixupController // Get the indentation context for the insertion point const insertionLine = document.lineAt(task.insertionPoint.line) let targetIndentSize = insertionLine.firstNonWhitespaceCharacterIndex + const textIndentSize = text.search(/\S/) || 0 // Special case for Python documentation if (targetIndentSize === 0 && task.document.languageId === 'python' && task.intent === 'doc') { @@ -962,7 +962,6 @@ export class FixupController } // Calculate indentation adjustments - const textIndentSize = text.search(/\S/) || 0 const needsIndentAdjustment = targetIndentSize > textIndentSize const indentDifference = needsIndentAdjustment ? targetIndentSize - textIndentSize : 0 @@ -1011,7 +1010,7 @@ export class FixupController // This happens sometimes with python document code action where instead // of adding only the docstring, the LLM returns the entire code block if (startLine > 0 && startLineText && text.startsWith(startLineText)) { - editBuilder.replace(task.originalRange, replacementText) + editBuilder.replace(task.originalRange, text) } else { editBuilder.insert(task.insertionPoint, replacementText) } From 2756d157a2c5b2e121ae29f32f23cf586dc46ebe Mon Sep 17 00:00:00 2001 From: David Ichim Date: Mon, 9 Jun 2025 09:39:51 +0300 Subject: [PATCH 4/9] Update test snapshot for document code. - With the fixes made to the white space, we get a better spacing distribution than what we had before. --- agent/src/__snapshots__/document-code.test.ts.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/src/__snapshots__/document-code.test.ts.snap b/agent/src/__snapshots__/document-code.test.ts.snap index fbc06f4c3164..eb5cab8d4b8b 100644 --- a/agent/src/__snapshots__/document-code.test.ts.snap +++ b/agent/src/__snapshots__/document-code.test.ts.snap @@ -51,8 +51,8 @@ export class TestClass { constructor(private shouldGreet: boolean) {} /** - * Prints a greeting message if shouldGreet is true. - */ + * Prints a greeting message if shouldGreet is true. + */ public functionName() { if (this.shouldGreet) { console.log(/* CURSOR */ 'Hello World!') From 989e34f303e3790b4a8e9f93211fc0f450ae0a39 Mon Sep 17 00:00:00 2001 From: David Ichim Date: Sun, 22 Jun 2025 14:24:04 +0300 Subject: [PATCH 5/9] Fix: Handle null arguments in vscode.commands.executeCommand and add test coverage (#8080) This commit addresses an issue where `vscode.commands.executeCommand` would throw a TypeError when invoked with null arguments. The fix involves adding a null check before accessing `Symbol.iterator` when handling arguments. Additionally, this commit includes comprehensive test coverage for `vscode.commands.executeCommand`, specifically focusing on handling null, undefined, and iterable arguments correctly. The following changes were made: - Added null check in `vscode-shim.ts` to prevent TypeError when `args[0]` is null. - Added tests in `vscode-shim.test.ts` to verify correct handling of null, undefined, and iterable arguments. - Added no-op handlers for `textEditor/selection` and `textEditor/revealRange` requests in `TestClient.ts` to support testing scenarios. Test plan: - Run `document-code.test.ts` and vscode-shim.test.ts` to ensure all tests pass. --- agent/src/TestClient.ts | 8 ++++ agent/src/vscode-shim.test.ts | 73 +++++++++++++++++++++++++++++++++++ agent/src/vscode-shim.ts | 3 +- 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/agent/src/TestClient.ts b/agent/src/TestClient.ts index 4346ac4f4a36..8d1555ad6149 100644 --- a/agent/src/TestClient.ts +++ b/agent/src/TestClient.ts @@ -357,6 +357,14 @@ export class TestClient extends MessageHandler { this.registerRequest('textDocument/show', () => { return Promise.resolve(true) }) + this.registerRequest('textEditor/selection', () => { + // No-op handler for textEditor/selection requests in test environment + return Promise.resolve(null) + }) + this.registerRequest('textEditor/revealRange', () => { + // No-op handler for textEditor/revealRange requests in test environment + return Promise.resolve(null) + }) this.registerRequest('editTask/getUserInput', async params => { return { instruction: this.userInput.instruction ?? params.instruction, diff --git a/agent/src/vscode-shim.test.ts b/agent/src/vscode-shim.test.ts index 5e45acbcf98f..3e9eeaa621b8 100644 --- a/agent/src/vscode-shim.test.ts +++ b/agent/src/vscode-shim.test.ts @@ -434,6 +434,79 @@ describe('vscode.workspace.getConfiguration', () => { }) }) +describe('vscode.commands.executeCommand', () => { + it('handles null arguments without throwing TypeError', async () => { + // This test demonstrates the fix for the issue found in document-code.test.ts + // where args[0] could be null, causing a TypeError when checking Symbol.iterator + + let callbackInvoked = false + let receivedArg: any + + // Register a test command that accepts null arguments + const disposable = vscode.commands.registerCommand('test.command.with.null', (arg) => { + callbackInvoked = true + receivedArg = arg + return 'success' + }) + + try { + // Execute command with null argument - this should not throw + const result = await vscode.commands.executeCommand('test.command.with.null', null) + + expect(callbackInvoked).toBe(true) + expect(receivedArg).toBe(null) + expect(result).toBe('success') + } finally { + disposable.dispose() + } + }) + + it('handles undefined arguments without throwing TypeError', async () => { + let callbackInvoked = false + let receivedArg: any + + const disposable = vscode.commands.registerCommand('test.command.with.undefined', (arg) => { + callbackInvoked = true + receivedArg = arg + return 'success' + }) + + try { + // Execute command with undefined argument + const result = await vscode.commands.executeCommand('test.command.with.undefined', undefined) + + expect(callbackInvoked).toBe(true) + expect(receivedArg).toBe(undefined) + expect(result).toBe('success') + } finally { + disposable.dispose() + } + }) + + it('handles iterable objects correctly', async () => { + let callbackInvoked = false + let receivedArgs: any[] = [] + + const disposable = vscode.commands.registerCommand('test.command.with.iterable', (...args) => { + callbackInvoked = true + receivedArgs = args + return 'success' + }) + + try { + // Execute command with an iterable array - should spread the arguments + const iterableArg = ['arg1', 'arg2', 'arg3'] + const result = await vscode.commands.executeCommand('test.command.with.iterable', iterableArg) + + expect(callbackInvoked).toBe(true) + expect(receivedArgs).toEqual(['arg1', 'arg2', 'arg3']) + expect(result).toBe('success') + } finally { + disposable.dispose() + } + }) +}) + describe('workspaces', () => { const workspaces = [vscode.Uri.parse('file:///a'), vscode.Uri.parse('file:///b')] diff --git a/agent/src/vscode-shim.ts b/agent/src/vscode-shim.ts index 5c86733b94c7..4097d1a9b1d0 100644 --- a/agent/src/vscode-shim.ts +++ b/agent/src/vscode-shim.ts @@ -1043,7 +1043,8 @@ const _commands: Partial = { try { // Handle the case where a single object is passed if (args.length === 1) { - if (typeof args[0] === 'object' && typeof args[0][Symbol.iterator] === 'function') { + // Check for null before accessing Symbol.iterator to avoid TypeError + if (typeof args[0] === 'object' && args[0] !== null && typeof args[0][Symbol.iterator] === 'function') { return promisify(registered.callback(...args[0])) } return promisify(registered.callback(args[0])) From 4468e7cb6eca86f803dcb3d8695f4ba7c31dd59e Mon Sep 17 00:00:00 2001 From: David Ichim Date: Sun, 22 Jun 2025 14:42:44 +0300 Subject: [PATCH 6/9] pnpm biome --- agent/src/vscode-shim.test.ts | 9 ++++++--- agent/src/vscode-shim.ts | 6 +++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/agent/src/vscode-shim.test.ts b/agent/src/vscode-shim.test.ts index 3e9eeaa621b8..3b2f82fd4ed4 100644 --- a/agent/src/vscode-shim.test.ts +++ b/agent/src/vscode-shim.test.ts @@ -443,7 +443,7 @@ describe('vscode.commands.executeCommand', () => { let receivedArg: any // Register a test command that accepts null arguments - const disposable = vscode.commands.registerCommand('test.command.with.null', (arg) => { + const disposable = vscode.commands.registerCommand('test.command.with.null', arg => { callbackInvoked = true receivedArg = arg return 'success' @@ -465,7 +465,7 @@ describe('vscode.commands.executeCommand', () => { let callbackInvoked = false let receivedArg: any - const disposable = vscode.commands.registerCommand('test.command.with.undefined', (arg) => { + const disposable = vscode.commands.registerCommand('test.command.with.undefined', arg => { callbackInvoked = true receivedArg = arg return 'success' @@ -496,7 +496,10 @@ describe('vscode.commands.executeCommand', () => { try { // Execute command with an iterable array - should spread the arguments const iterableArg = ['arg1', 'arg2', 'arg3'] - const result = await vscode.commands.executeCommand('test.command.with.iterable', iterableArg) + const result = await vscode.commands.executeCommand( + 'test.command.with.iterable', + iterableArg + ) expect(callbackInvoked).toBe(true) expect(receivedArgs).toEqual(['arg1', 'arg2', 'arg3']) diff --git a/agent/src/vscode-shim.ts b/agent/src/vscode-shim.ts index 4097d1a9b1d0..b46aeb535983 100644 --- a/agent/src/vscode-shim.ts +++ b/agent/src/vscode-shim.ts @@ -1044,7 +1044,11 @@ const _commands: Partial = { // Handle the case where a single object is passed if (args.length === 1) { // Check for null before accessing Symbol.iterator to avoid TypeError - if (typeof args[0] === 'object' && args[0] !== null && typeof args[0][Symbol.iterator] === 'function') { + if ( + typeof args[0] === 'object' && + args[0] !== null && + typeof args[0][Symbol.iterator] === 'function' + ) { return promisify(registered.callback(...args[0])) } return promisify(registered.callback(args[0])) From 013161787f3e0e77701e69403cbc0b71ec436675 Mon Sep 17 00:00:00 2001 From: David Ichim Date: Sun, 22 Jun 2025 22:40:18 +0300 Subject: [PATCH 7/9] Fix: Improve Python docstring insertion in FixupController This commit improves the handling of Python docstring insertion within the FixupController. Specifically, it addresses scenarios where the docstring should be inserted on the line following the function or class definition. The changes include: - Introduce `isPythonFile` and `isDocIntent` variables for better readability. - Add a condition to increment the insertion point line by one when inserting docstrings for Python files if the start line text is empty. This ensures the docstring is placed on the next line after the function or class definition. - Update the `editBuilder.insert` call to use the potentially adjusted `insertionPoint`. --- vscode/src/non-stop/FixupController.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/vscode/src/non-stop/FixupController.ts b/vscode/src/non-stop/FixupController.ts index 0230dd0278fa..f5e9fd14e917 100644 --- a/vscode/src/non-stop/FixupController.ts +++ b/vscode/src/non-stop/FixupController.ts @@ -956,8 +956,11 @@ export class FixupController let targetIndentSize = insertionLine.firstNonWhitespaceCharacterIndex const textIndentSize = text.search(/\S/) || 0 + const isPythonFile = task.document.languageId === 'python' + const isDocIntent = task.intent === 'doc' + // Special case for Python documentation - if (targetIndentSize === 0 && task.document.languageId === 'python' && task.intent === 'doc') { + if (targetIndentSize === 0 && isPythonFile && isDocIntent) { targetIndentSize = 4 } @@ -989,7 +992,7 @@ export class FixupController // Ensure proper line ending and handle insertion at line start const proposedText = processedLines.join('\n') + - (task.document.languageId !== 'python' + (!isPythonFile ? task.insertionPoint.character > 0 ? ' '.repeat(targetIndentSize) : '' @@ -1005,6 +1008,15 @@ export class FixupController return vscode.workspace.applyEdit(edit) } + // If we have a doc intent with python file and no start line text, + // we want to insert the docstring after the insertion point. + // This is because we need the docstring to be on the next line + // after the function or class definition. + const insertionPoint = new vscode.Position( + isPythonFile && isDocIntent && !startLineText ? task.insertionPoint.line + 1 : task.insertionPoint.line, + task.insertionPoint.character + ) + return edit(editBuilder => { // Replace the code block if the start line matches the start of the text // This happens sometimes with python document code action where instead @@ -1012,7 +1024,7 @@ export class FixupController if (startLine > 0 && startLineText && text.startsWith(startLineText)) { editBuilder.replace(task.originalRange, text) } else { - editBuilder.insert(task.insertionPoint, replacementText) + editBuilder.insert(insertionPoint, replacementText) } }, options) } From ae8943b5488c9163fcaaad0ff195c7c141c614c1 Mon Sep 17 00:00:00 2001 From: David Ichim Date: Mon, 23 Jun 2025 11:35:07 +0300 Subject: [PATCH 8/9] biome fix --- vscode/src/non-stop/FixupController.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vscode/src/non-stop/FixupController.ts b/vscode/src/non-stop/FixupController.ts index f5e9fd14e917..5c095cff3d12 100644 --- a/vscode/src/non-stop/FixupController.ts +++ b/vscode/src/non-stop/FixupController.ts @@ -1013,7 +1013,9 @@ export class FixupController // This is because we need the docstring to be on the next line // after the function or class definition. const insertionPoint = new vscode.Position( - isPythonFile && isDocIntent && !startLineText ? task.insertionPoint.line + 1 : task.insertionPoint.line, + isPythonFile && isDocIntent && !startLineText + ? task.insertionPoint.line + 1 + : task.insertionPoint.line, task.insertionPoint.character ) From 3c12d6a0599ea7ab7277eda47eb166e38b22bb94 Mon Sep 17 00:00:00 2001 From: David Ichim Date: Wed, 2 Jul 2025 22:50:23 +0300 Subject: [PATCH 9/9] Fix: Use editor tab size for Python docstring indentation in FixupController This commit fixes an issue where the FixupController was hardcoding the indentation size for Python docstrings to 4 spaces, regardless of the editor's tab size settings. The change replaces the hardcoded value with a call to `getEditorTabSize()` to ensure that the docstring indentation matches the user's preferred tab size. This improves consistency and code style for Python projects. --- vscode/src/non-stop/FixupController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vscode/src/non-stop/FixupController.ts b/vscode/src/non-stop/FixupController.ts index 5c095cff3d12..2ef96b8824e0 100644 --- a/vscode/src/non-stop/FixupController.ts +++ b/vscode/src/non-stop/FixupController.ts @@ -15,7 +15,7 @@ import { import type { SmartApplyResult } from '../chat/protocol' import { PersistenceTracker } from '../common/persistence-tracker' -import { lines } from '../completions/text-processing' +import { getEditorTabSize, lines } from '../completions/text-processing' import { executeEdit } from '../edit/execute' import type { EditInput } from '../edit/input/get-input' import { @@ -961,7 +961,7 @@ export class FixupController // Special case for Python documentation if (targetIndentSize === 0 && isPythonFile && isDocIntent) { - targetIndentSize = 4 + targetIndentSize = getEditorTabSize() } // Calculate indentation adjustments