@@ -14,11 +14,10 @@ export async function resolveContextExpression(): Promise<void> {
1414 }
1515
1616 const { document, selection } = editor ;
17- const contextExpression = selection . isEmpty
18- ? document . lineAt ( selection . active . line ) . text . trim ( )
19- : document . getText ( selection ) . trim ( ) ;
17+ const contextInfo = getContextExpressionInfo ( document , selection ) ;
18+ const contextExpression = contextInfo . text ;
2019
21- if ( ! contextExpression ) {
20+ if ( ! contextExpression . trim ( ) ) {
2221 void vscode . window . showErrorMessage ( "Context expression is empty." ) ;
2322 return ;
2423 }
@@ -41,35 +40,16 @@ export async function resolveContextExpression(): Promise<void> {
4140 }
4241
4342 const textExpression = normalizedTextExpression . replace ( / \r ? \n / g, eol ) ;
44- let formattedTextExpression = textExpression ;
43+ const formattedTextExpression = textExpression ;
4544
4645 let rangeToReplace : vscode . Range ;
4746 if ( selection . isEmpty ) {
4847 const fallbackLine = document . lineAt ( selection . active . line ) ;
49- const fallbackRange = fallbackLine . range ;
50-
51- rangeToReplace = getRangeToReplaceForLine ( document , selection . active . line , contextExpression ) ?? fallbackRange ;
52-
53- const preservedPrefix = document . getText ( new vscode . Range ( fallbackLine . range . start , rangeToReplace . start ) ) ;
54-
55- formattedTextExpression = normalizeInsertionWithPrefix ( formattedTextExpression , preservedPrefix , eol ) ;
48+ rangeToReplace = fallbackLine . range ;
5649 } else {
57- // Multi-line or partial selection
58- const firstSelLine = document . lineAt ( selection . start . line ) ;
59- const preservedPrefix = document . getText ( new vscode . Range ( firstSelLine . range . start , selection . start ) ) ;
60- const leadingWS = firstSelLine . text . match ( / ^ [ \t ] * / ) ?. [ 0 ] ?? "" ;
61-
62- // 1) Normalize snippet to avoid duplicating "."/";" according to the prefix that will remain in the file
63- formattedTextExpression = normalizeInsertionWithPrefix ( formattedTextExpression , preservedPrefix , eol ) ;
64-
65- // 2) Only prefix indentation if the selection started at column 0 (i.e., NO preserved prefix)
66- formattedTextExpression = maybePrefixFirstLineIndent (
67- formattedTextExpression ,
68- preservedPrefix . length === 0 ? leadingWS : "" ,
69- eol
70- ) ;
71-
72- rangeToReplace = new vscode . Range ( selection . start , selection . end ) ;
50+ const start = document . lineAt ( selection . start . line ) . range . start ;
51+ const replacementEnd = contextInfo . replacementEnd ?? document . lineAt ( selection . end . line ) . range . end ;
52+ rangeToReplace = new vscode . Range ( start , replacementEnd ) ;
7353 }
7454
7555 await editor . edit ( ( editBuilder ) => {
@@ -92,112 +72,32 @@ export async function resolveContextExpression(): Promise<void> {
9272 }
9373}
9474
95- function getRangeToReplaceForLine (
96- document : vscode . TextDocument ,
97- lineNumber : number ,
98- contextExpression : string
99- ) : vscode . Range | undefined {
100- if ( ! contextExpression ) {
101- return undefined ;
102- }
75+ type ContextExpressionInfo = {
76+ text : string ;
77+ replacementEnd ?: vscode . Position ;
78+ } ;
10379
104- const line = document . lineAt ( lineNumber ) ;
105- const expressionIndex = line . text . indexOf ( contextExpression ) ;
106- if ( expressionIndex === - 1 ) {
107- return undefined ;
80+ function getContextExpressionInfo ( document : vscode . TextDocument , selection : vscode . Selection ) : ContextExpressionInfo {
81+ if ( selection . isEmpty ) {
82+ return {
83+ text : document . lineAt ( selection . active . line ) . text ,
84+ } ;
10885 }
10986
110- const prefixLength = getPrefixLengthToPreserve ( contextExpression ) ;
111- const startCharacter = expressionIndex + prefixLength ;
112- const endCharacter = expressionIndex + contextExpression . length ;
113-
114- const start = line . range . start . translate ( 0 , startCharacter ) ;
115- const end = line . range . start . translate ( 0 , endCharacter ) ;
116- return new vscode . Range ( start , end ) ;
117- }
118-
119- /**
120- * Based on the preserved line prefix, remove from the BEGINNING of the snippet's first line:
121- * - if the prefix ends with ";": remove ^[\t ]*(?:\.\s*)*;\s*
122- * - otherwise, if it ends with dots: remove ^[\t ]*(?:\.\s*)+
123- * - neutral case: try to remove comment; otherwise remove dots
124- */
125- function normalizeInsertionWithPrefix ( text : string , preservedPrefix : string , eol : string ) : string {
126- const lines = text . split ( / \r ? \n / ) ;
127- if ( lines . length === 0 ) return text ;
128-
129- const preservedEnd = preservedPrefix . replace ( / \s + $ / g, "" ) ;
130-
131- const endsWithSemicolon = / (?: \. \s * ) * ; \s * $ / . test ( preservedEnd ) ;
132- const endsWithDotsOnly = ! endsWithSemicolon && / (?: \. \s * ) + $ / . test ( preservedEnd ) ;
87+ const startLine = selection . start . line ;
88+ const start = document . lineAt ( startLine ) . range . start ;
13389
134- if ( endsWithSemicolon ) {
135- lines [ 0 ] = lines [ 0 ] . replace ( / ^ [ \t ] * (?: \. \s * ) * ; \s * / , "" ) ;
136- } else if ( endsWithDotsOnly ) {
137- lines [ 0 ] = lines [ 0 ] . replace ( / ^ [ \t ] * (?: \. \s * ) + / , "" ) ;
138- } else {
139- const removedComment = lines [ 0 ] . replace ( / ^ [ \t ] * (?: \. \s * ) ? ; \s * / , "" ) ;
140- if ( removedComment !== lines [ 0 ] ) {
141- lines [ 0 ] = removedComment ;
142- } else {
143- lines [ 0 ] = lines [ 0 ] . replace ( / ^ [ \t ] * (?: \. \s * ) + / , "" ) ;
144- }
90+ let lastLine = selection . end . line ;
91+ if ( selection . end . character === 0 && selection . end . line > selection . start . line ) {
92+ lastLine = selection . end . line - 1 ;
14593 }
14694
147- return lines . join ( eol ) ;
148- }
149-
150- /**
151- * Prefix indentation (tabs/spaces) ONLY if provided.
152- * Useful when the selection started at column 0 (no preserved prefix).
153- */
154- function maybePrefixFirstLineIndent ( text : string , leadingWS : string , eol : string ) : string {
155- if ( ! text || ! leadingWS ) return text ;
156- const lines = text . split ( / \r ? \n / ) ;
157- if ( lines . length === 0 ) return text ;
158-
159- // Do not force replacement if there is already some whitespace; just prefix it.
160- lines [ 0 ] = leadingWS + lines [ 0 ] ;
161- return lines . join ( eol ) ;
162- }
163-
164- /**
165- * Keep: preserve level dots / indentation and, if present, '; ' before the typed content.
166- * Returns how many characters of the contextExpression belong to that prefix.
167- */
168- function getPrefixLengthToPreserve ( contextExpression : string ) : number {
169- let index = 0 ;
170-
171- while ( index < contextExpression . length ) {
172- const char = contextExpression [ index ] ;
173-
174- if ( char === "." ) {
175- index ++ ;
176- while ( index < contextExpression . length && contextExpression [ index ] === " " ) {
177- index ++ ;
178- }
179- continue ;
180- }
181-
182- if ( char === " " || char === "\t" ) {
183- index ++ ;
184- continue ;
185- }
186-
187- break ;
188- }
189-
190- if ( index < contextExpression . length && contextExpression [ index ] === ";" ) {
191- index ++ ;
192- while (
193- index < contextExpression . length &&
194- ( contextExpression [ index ] === " " || contextExpression [ index ] === "\t" )
195- ) {
196- index ++ ;
197- }
198- }
95+ const end = document . lineAt ( lastLine ) . range . end ;
19996
200- return index ;
97+ return {
98+ text : document . getText ( new vscode . Range ( start , end ) ) ,
99+ replacementEnd : end ,
100+ } ;
201101}
202102
203103function extractGifUri ( text : string ) : {
0 commit comments