fix: bypass sql type injection during formatting to prevent offset corruption (#8786)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Diego Imbert
2026-04-10 16:26:33 +02:00
committed by GitHub
parent 3c64a4282d
commit 8957d8f19b

View File

@@ -53,6 +53,8 @@ class SqlAwareTypeScriptWorker extends TypeScriptWorker {
// Cache of transformed code and offset maps per file version
// Structure: fileUri -> {version, originalText, transformed, offsetMap, offsetMapEntries}
this._transformedCodeCache = new Map()
// When true, getScriptSnapshot returns original code (used during formatting)
this._bypassTransform = false
}
/**
@@ -110,6 +112,10 @@ class SqlAwareTypeScriptWorker extends TypeScriptWorker {
* This is called by TypeScript when it needs to read source files
*/
getScriptSnapshot(fileName) {
if (this._bypassTransform) {
return super.getScriptSnapshot(fileName)
}
const cached = this._getTransformResult(fileName)
if (!cached) {
@@ -718,6 +724,39 @@ class SqlAwareTypeScriptWorker extends TypeScriptWorker {
return [...mappedDiagnostics, ...sqlDiagnostics]
}
/**
* Override formatting methods to operate on original code (without injected SQL types).
* The formatter only cares about syntax/spacing, not types, so the original code is correct.
* Without this, formatting edits use positions from the transformed code, causing
* character offsets to be wrong and truncating SQL strings.
*/
async getFormattingEditsForDocument(fileName, options) {
this._bypassTransform = true
try {
return await super.getFormattingEditsForDocument(fileName, options)
} finally {
this._bypassTransform = false
}
}
async getFormattingEditsForRange(fileName, start, end, options) {
this._bypassTransform = true
try {
return await super.getFormattingEditsForRange(fileName, start, end, options)
} finally {
this._bypassTransform = false
}
}
async getFormattingEditsAfterKeystroke(fileName, position, ch, options) {
this._bypassTransform = true
try {
return await super.getFormattingEditsAfterKeystroke(fileName, position, ch, options)
} finally {
this._bypassTransform = false
}
}
async getSuggestionDiagnostics(fileName) {
const diagnostics = await super.getSuggestionDiagnostics(fileName)
return this._mapDiagnostics(diagnostics, fileName)