diff --git a/packages/ts-plugin/src/index.ts b/packages/ts-plugin/src/index.ts index 68f2f40..30f03f8 100644 --- a/packages/ts-plugin/src/index.ts +++ b/packages/ts-plugin/src/index.ts @@ -232,20 +232,98 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => { ts.createSourceFile = createSourceFile; } + { + const oldUpdateSourceFile = ts.updateSourceFile; + + function commonPrefixLength(a: string, b: string): number { + const limit = Math.min(a.length, b.length); + for (let i = 0; i < limit; i++) { + if (a[i] !== b[i]) return i; + } + return limit; + } + + function commonSuffixLength(a: string, b: string): number { + const limit = Math.min(a.length, b.length); + for (let i = 0; i < limit; i++) { + if (a[a.length - i - 1] !== b[b.length - i - 1]) return i; + } + return limit; + } + + function updateSourceFile( + sourceFile: ts.SourceFile, + newText: string, + textChangeRange: ts.TextChangeRange, + aggressiveChecks?: boolean, + ): ts.SourceFile { + return withFileName( + sourceFile.fileName, + () => oldUpdateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks), + (fx) => { + const oldInfo = fx.info; + return expandFile( + sourceFile.fileName, + oldInfo.originalSource, + (newExpandedText) => { + const oldExpandedText = oldInfo.sourceFile.text; + if (oldExpandedText === newExpandedText) { + // console.log('updateSourceFile', 'no change'); + return oldInfo.sourceFile; + } else { + const prefix = commonPrefixLength(oldExpandedText, newExpandedText); + const suffix = + commonSuffixLength(oldExpandedText.substring(prefix), + newExpandedText.substring(prefix)); + const newChangeRange = { + span: { + start: prefix, + length: oldExpandedText.length - prefix - suffix, + }, + newLength: newExpandedText.length - prefix - suffix, + } satisfies ts.TextChangeRange; + // console.log('updateSourceFile', JSON.stringify({ + // oldSyndicateText: oldInfo.originalSource, + // newSyndicateText: newText, + // oldExpandedText, + // newExpandedText, + // oldLength: oldExpandedText.length, + // newLength: newExpandedText.length, + // oldChangeRange: textChangeRange, + // newChangeRange, + // prefix, + // suffix, + // }, void 0, 2)); + return oldUpdateSourceFile( + sourceFile, newExpandedText, newChangeRange, aggressiveChecks); + } + }); + }); + } + + ts.updateSourceFile = updateSourceFile; + } + { const oldEditContent = ts.server.ScriptInfo.prototype.editContent; ts.server.ScriptInfo.prototype.editContent = function (start: number, end: number, newText: string): void { - // console.log('SyndicateScriptInfo.editContent', this.fileName, start, end, newText); + // console.log('SyndicateScriptInfo.editContent', JSON.stringify({ + // fileName: this.fileName, + // start, + // end, + // newText, + // oldText: (this as any).textStorage.text ?? ["MISSING"], + // }, void 0, 2)); withFileName(this.fileName, - () => oldEditContent(start, end, newText), + () => oldEditContent.call(this, start, end, newText), (fx) => { const info = fx.info; const revised = info.originalSource.substring(0, start) + newText + info.originalSource.substring(end); info.originalSource = revised; - this.open(revised); + oldEditContent.call(this, start, end, newText); }); }; }