From d0065185d7ea503cd96250216dcb79c33d98966c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:06:40 +0000 Subject: [PATCH 1/3] Initial plan From 4cad7e902afb437c05709cf530e83a2c863d7fa8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:17:17 +0000 Subject: [PATCH 2/3] Cache source map writer columns Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- tsc/internal/compiler/emit_test.go | 2 +- tsc/internal/printer/textwriter.go | 13 +++++++-- tsc/internal/printer/textwriter_test.go | 38 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tsc/internal/printer/textwriter_test.go diff --git a/tsc/internal/compiler/emit_test.go b/tsc/internal/compiler/emit_test.go index 0e74a10cbf0c1..49e9cb2896310 100644 --- a/tsc/internal/compiler/emit_test.go +++ b/tsc/internal/compiler/emit_test.go @@ -16,7 +16,7 @@ import ( // generateLongLineTS generates TypeScript source code that produces a single very long line. // This simulates generated code (e.g., from code generators) that has no line breaks, // which triggers O(n²) behavior in source map generation due to -// GetECMALineAndUTF16CharacterOfPosition scanning from line start for each position. +// the emit writer scanning from line start for each generated position. func generateLongLineTS(numProperties int) string { // Build a large object literal all on one line, with no line breaks. var b strings.Builder diff --git a/tsc/internal/printer/textwriter.go b/tsc/internal/printer/textwriter.go index e92ad3ccf2cc7..484f0b00bb3bd 100644 --- a/tsc/internal/printer/textwriter.go +++ b/tsc/internal/printer/textwriter.go @@ -20,6 +20,8 @@ type textWriter struct { lineStart bool lineCount int linePos int + columnPos int + column core.UTF16Offset hasTrailingCommentState bool } @@ -41,9 +43,10 @@ func (w *textWriter) GetColumn() core.UTF16Offset { if w.lineStart { return core.UTF16Offset(w.indent * w.indentSize) } - // Count UTF-16 code units from the last line start. - // For ASCII-only output (the common case), this equals the byte count. - return core.UTF16Len(w.builder.String()[w.linePos:]) + textPos := w.builder.Len() + w.column += core.UTF16Len(w.builder.String()[w.columnPos:textPos]) + w.columnPos = textPos + return w.column } func (w *textWriter) GetIndent() int { @@ -107,6 +110,8 @@ func (w *textWriter) updateLineCountAndPosFor(s string) { w.lineCount += count - 1 curLen := w.builder.Len() w.linePos = curLen - len(s) + int(lastLineStart) + w.columnPos = w.linePos + w.column = 0 w.lineStart = (w.linePos - curLen) == 0 return } @@ -163,6 +168,8 @@ func (w *textWriter) writeLineRaw() { w.lastWritten = w.newLine w.lineCount++ w.linePos = w.builder.Len() + w.columnPos = w.linePos + w.column = 0 w.lineStart = true w.hasTrailingCommentState = false } diff --git a/tsc/internal/printer/textwriter_test.go b/tsc/internal/printer/textwriter_test.go new file mode 100644 index 0000000000000..44ed345f5ee8c --- /dev/null +++ b/tsc/internal/printer/textwriter_test.go @@ -0,0 +1,38 @@ +package printer + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/core" + "gotest.tools/v3/assert" +) + +func TestTextWriterCachesUTF16Column(t *testing.T) { + t.Parallel() + + w := NewTextWriter("\n", 4).(*textWriter) + w.Write("a𝟘") + assert.Equal(t, w.GetColumn(), core.UTF16Offset(3)) + assert.Equal(t, w.columnPos, w.builder.Len()) + + w.Write("𝟙b") + assert.Equal(t, w.GetColumn(), core.UTF16Offset(6)) + assert.Equal(t, w.columnPos, w.builder.Len()) +} + +func TestTextWriterResetsCachedColumnAfterNewLine(t *testing.T) { + t.Parallel() + + w := NewTextWriter("\n", 4).(*textWriter) + w.Write("previous line") + w.GetColumn() + w.RawWrite("\n𝟘") + assert.Equal(t, w.GetColumn(), core.UTF16Offset(2)) + assert.Equal(t, w.columnPos, w.builder.Len()) + + w.WriteLine() + w.IncreaseIndent() + assert.Equal(t, w.GetColumn(), core.UTF16Offset(4)) + w.Write("x") + assert.Equal(t, w.GetColumn(), core.UTF16Offset(5)) +} From 7d2d4eb89b046dbd41e6fd87c508ad56f6433b46 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:26:58 +0000 Subject: [PATCH 3/3] Format source map column cache Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- tsc/internal/printer/textwriter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsc/internal/printer/textwriter.go b/tsc/internal/printer/textwriter.go index 484f0b00bb3bd..53c2418b149df 100644 --- a/tsc/internal/printer/textwriter.go +++ b/tsc/internal/printer/textwriter.go @@ -21,7 +21,7 @@ type textWriter struct { lineCount int linePos int columnPos int - column core.UTF16Offset + column core.UTF16Offset hasTrailingCommentState bool }