From 284d3979a404c1455b8d3005f8d406ed69a24a81 Mon Sep 17 00:00:00 2001 From: MayerTim Date: Wed, 10 Jun 2026 23:55:23 +0200 Subject: [PATCH] refactor(formatter): isolate protected text helpers --- src/formatter/keywordCase.ts | 17 +++------ .../cleanup/statementContinuationCleanup.ts | 26 ++++++-------- .../passes/structural/inlineIfFormatting.ts | 9 ++--- .../structural/parenthesisFormatting.ts | 21 ++--------- .../structural/queryClauseFormatting.ts | 18 ++-------- src/formatter/sqlLineScanner.ts | 35 +++++++++++++++++++ 6 files changed, 58 insertions(+), 68 deletions(-) diff --git a/src/formatter/keywordCase.ts b/src/formatter/keywordCase.ts index b2eb891..8730319 100644 --- a/src/formatter/keywordCase.ts +++ b/src/formatter/keywordCase.ts @@ -3,6 +3,7 @@ import type { KeywordCase } from './options'; import { createInitialSqlLineScanState, scanSqlLineOutsideLiteralsAndComments, + rewriteSqlLineOutsideLiteralsAndComments, type SqlLineScanState, } from './sqlLineScanner'; @@ -40,19 +41,11 @@ export function applyKeywordCaseToLine( return { line, nextState: scanResult.nextState }; } - const scanResult = scanSqlLineOutsideLiteralsAndComments(line, scanState); - let cursor = 0; - let nextLine = ''; - - for (const segment of scanResult.outsideSegments) { - nextLine += line.slice(cursor, segment.start); - nextLine += transformSqlWords(line.slice(segment.start, segment.end), dialect, keywordCase); - cursor = segment.end; - } - - nextLine += line.slice(cursor); + const result = rewriteSqlLineOutsideLiteralsAndComments(line, scanState, (segmentText) => + transformSqlWords(segmentText, dialect, keywordCase), + ); - return { line: nextLine, nextState: scanResult.nextState }; + return { line: result.line, nextState: result.nextState }; } export function collectSqlWordsOutsideLiteralsAndComments( diff --git a/src/formatter/passes/cleanup/statementContinuationCleanup.ts b/src/formatter/passes/cleanup/statementContinuationCleanup.ts index e2b4d04..88756ba 100644 --- a/src/formatter/passes/cleanup/statementContinuationCleanup.ts +++ b/src/formatter/passes/cleanup/statementContinuationCleanup.ts @@ -1,6 +1,7 @@ import { createInitialSqlLineScanState, - scanSqlLineOutsideLiteralsAndComments, + rewriteSqlLineOutsideLiteralsAndComments, + type SqlLineScanState, } from '../../sqlLineScanner'; export function cleanupWatcomStatementContinuations( @@ -57,22 +58,17 @@ export function cleanupWatcomStatementContinuations( function rewriteOutsideSqlText( line: string, - scanState: ReturnType, + scanState: SqlLineScanState, rewrite: (line: string, start: number, end: number) => string, -): { line: string } { - const result = scanSqlLineOutsideLiteralsAndComments(line, scanState); - let rewritten = ''; - let cursor = 0; - - for (const segment of result.outsideSegments) { - rewritten += line.slice(cursor, segment.start); - rewritten += rewrite(line, segment.start, segment.end); - cursor = segment.end; - } - - rewritten += line.slice(cursor); +): { readonly line: string } { + const result = rewriteSqlLineOutsideLiteralsAndComments( + line, + scanState, + (_segmentText, segment) => rewrite(line, segment.start, segment.end), + ); scanState.inBlockComment = result.nextState.inBlockComment; - return { line: rewritten }; + + return { line: result.line }; } function cleanupStatementOutsideSqlText(line: string, start: number, end: number): string { diff --git a/src/formatter/passes/structural/inlineIfFormatting.ts b/src/formatter/passes/structural/inlineIfFormatting.ts index 4130ba8..3abe964 100644 --- a/src/formatter/passes/structural/inlineIfFormatting.ts +++ b/src/formatter/passes/structural/inlineIfFormatting.ts @@ -1,6 +1,7 @@ import type { SqlDialect } from '../../../dialects'; import { cloneSqlLineScanState, + createSqlOutsideLookup, scanSqlLineOutsideLiteralsAndComments, type SqlLineScanState, type SqlOutsideSegment, @@ -167,13 +168,7 @@ function splitConditionOnLogicalOperators(condition: string): string[] { condition, cloneSqlLineScanState({ inBlockComment: false }), ); - const outside = new Array(condition.length).fill(false); - - for (const segment of scanResult.outsideSegments) { - for (let index = segment.start; index < segment.end; index += 1) { - outside[index] = true; - } - } + const outside = createSqlOutsideLookup(condition.length, scanResult.outsideSegments); const parts: string[] = []; let lastSplit = 0; diff --git a/src/formatter/passes/structural/parenthesisFormatting.ts b/src/formatter/passes/structural/parenthesisFormatting.ts index 83c5f75..c5e286d 100644 --- a/src/formatter/passes/structural/parenthesisFormatting.ts +++ b/src/formatter/passes/structural/parenthesisFormatting.ts @@ -1,8 +1,8 @@ import { cloneSqlLineScanState, + createSqlOutsideLookup, scanSqlLineOutsideLiteralsAndComments, type SqlLineScanState, - type SqlOutsideSegment, } from '../../sqlLineScanner'; export interface ParenthesisFormattingState { @@ -70,7 +70,7 @@ export function expandParenthesesInLine( initialState: ParenthesisFormattingState, ): ParenthesisExpansionResult { const scanResult = scanSqlLineOutsideLiteralsAndComments(line, initialState.scanState); - const outside = createOutsideLookup(line.length, scanResult.outsideSegments); + const outside = createSqlOutsideLookup(line.length, scanResult.outsideSegments); if (!lineContainsSplittableParenthesis(line, outside, initialState.parenthesisDepth)) { return { @@ -173,7 +173,7 @@ export function analyzeParenthesesForIndent( initialState: SqlLineScanState, ): ParenthesisIndentAnalysis { const scanResult = scanSqlLineOutsideLiteralsAndComments(line, initialState); - const outside = createOutsideLookup(line.length, scanResult.outsideSegments); + const outside = createSqlOutsideLookup(line.length, scanResult.outsideSegments); let leadingClosingParentheses = 0; let depthDelta = 0; let hasSeenNonWhitespace = false; @@ -364,21 +364,6 @@ function readWordBefore(line: string, index: number): string | undefined { return line.slice(cursor + 1, end); } -function createOutsideLookup( - length: number, - outsideSegments: readonly SqlOutsideSegment[], -): boolean[] { - const outside = new Array(length).fill(false); - - for (const segment of outsideSegments) { - for (let index = segment.start; index < segment.end; index += 1) { - outside[index] = true; - } - } - - return outside; -} - function skipWhitespace(line: string, start: number): number { let index = start; diff --git a/src/formatter/passes/structural/queryClauseFormatting.ts b/src/formatter/passes/structural/queryClauseFormatting.ts index f5db437..ffd925c 100644 --- a/src/formatter/passes/structural/queryClauseFormatting.ts +++ b/src/formatter/passes/structural/queryClauseFormatting.ts @@ -1,6 +1,7 @@ import type { SqlDialect } from '../../../dialects'; import { cloneSqlLineScanState, + createSqlOutsideLookup, scanSqlLineOutsideLiteralsAndComments, type SqlLineScanState, type SqlOutsideSegment, @@ -48,7 +49,7 @@ export function expandWatcomQueryClauseLine( initialState: QueryClauseFormattingState, ): ExpandedLineResult { const scanResult = scanSqlLineOutsideLiteralsAndComments(line, initialState.scanState); - const outside = createOutsideLookup(line.length, scanResult.outsideSegments); + const outside = createSqlOutsideLookup(line.length, scanResult.outsideSegments); const nextState: QueryClauseFormattingState = { scanState: scanResult.nextState, parenthesisDepth: updateParenthesisDepth(line, outside, initialState.parenthesisDepth), @@ -268,21 +269,6 @@ function updateParenthesisDepth( return depth; } -function createOutsideLookup( - length: number, - outsideSegments: readonly SqlOutsideSegment[], -): boolean[] { - const outside = new Array(length).fill(false); - - for (const segment of outsideSegments) { - for (let index = segment.start; index < segment.end; index += 1) { - outside[index] = true; - } - } - - return outside; -} - function isPhrase( words: readonly WordMatch[], index: number, diff --git a/src/formatter/sqlLineScanner.ts b/src/formatter/sqlLineScanner.ts index 6106bbe..8b6bd69 100644 --- a/src/formatter/sqlLineScanner.ts +++ b/src/formatter/sqlLineScanner.ts @@ -20,6 +20,41 @@ export function cloneSqlLineScanState(state: SqlLineScanState): SqlLineScanState return { inBlockComment: state.inBlockComment }; } +export function createSqlOutsideLookup( + length: number, + outsideSegments: readonly SqlOutsideSegment[], +): readonly boolean[] { + const outside = new Array(length).fill(false); + + for (const segment of outsideSegments) { + for (let index = segment.start; index < segment.end; index += 1) { + outside[index] = true; + } + } + + return outside; +} + +export function rewriteSqlLineOutsideLiteralsAndComments( + line: string, + initialState: SqlLineScanState, + rewrite: (segmentText: string, segment: SqlOutsideSegment) => string, +): { readonly line: string; readonly nextState: SqlLineScanState } { + const scanResult = scanSqlLineOutsideLiteralsAndComments(line, initialState); + let rewritten = ''; + let cursor = 0; + + for (const segment of scanResult.outsideSegments) { + rewritten += line.slice(cursor, segment.start); + rewritten += rewrite(line.slice(segment.start, segment.end), segment); + cursor = segment.end; + } + + rewritten += line.slice(cursor); + + return { line: rewritten, nextState: scanResult.nextState }; +} + export function scanSqlLineOutsideLiteralsAndComments( line: string, initialState: SqlLineScanState,