@@ -32,70 +32,130 @@ interface PreviewRangesWithContent {
3232}
3333
3434export function wrapWithAbbreviation ( args : any ) {
35+ return doWrapping ( false , args ) ;
36+ }
37+
38+ export function wrapIndividualLinesWithAbbreviation ( args : any ) {
39+ return doWrapping ( true , args ) ;
40+ }
41+
42+ function doWrapping ( individualLines : boolean , args : any ) {
3543 if ( ! validate ( false ) || ! vscode . window . activeTextEditor ) {
3644 return ;
3745 }
3846
3947 const editor = vscode . window . activeTextEditor ;
40- let rootNode = parseDocument ( editor . document , false ) ;
41-
42- const syntax = getSyntaxFromArgs ( { language : editor . document . languageId } ) || '' ;
48+ const rootNode = parseDocument ( editor . document , false ) ;
49+ if ( individualLines ) {
50+ if ( editor . selections . length === 1 && editor . selection . isEmpty ) {
51+ vscode . window . showInformationMessage ( 'Select more than 1 line and try again.' ) ;
52+ return ;
53+ }
54+ if ( editor . selections . find ( x => x . isEmpty ) ) {
55+ vscode . window . showInformationMessage ( 'Select more than 1 line in each selection and try again.' ) ;
56+ return ;
57+ }
58+ }
59+ const syntax = getSyntaxFromArgs ( { language : editor . document . languageId } ) ;
4360 if ( ! syntax ) {
4461 return ;
4562 }
4663
4764 let inPreview = false ;
65+ let currentValue = '' ;
66+ const helper = getEmmetHelper ( ) ;
4867
4968 // Fetch general information for the succesive expansions. i.e. the ranges to replace and its contents
50- let rangesToReplace : PreviewRangesWithContent [ ] = [ ] ;
51-
52- editor . selections . sort ( ( a : vscode . Selection , b : vscode . Selection ) => { return a . start . line - b . start . line ; } ) . forEach ( selection => {
69+ let rangesToReplace : PreviewRangesWithContent [ ] = editor . selections . sort ( ( a : vscode . Selection , b : vscode . Selection ) => { return a . start . compareTo ( b . start ) ; } ) . map ( selection => {
5370 let rangeToReplace : vscode . Range = selection . isReversed ? new vscode . Range ( selection . active , selection . anchor ) : selection ;
5471 if ( ! rangeToReplace . isSingleLine && rangeToReplace . end . character === 0 ) {
55- let previousLine = rangeToReplace . end . line - 1 ;
56- let lastChar = editor . document . lineAt ( previousLine ) . text . length ;
72+ const previousLine = rangeToReplace . end . line - 1 ;
73+ const lastChar = editor . document . lineAt ( previousLine ) . text . length ;
5774 rangeToReplace = new vscode . Range ( rangeToReplace . start , new vscode . Position ( previousLine , lastChar ) ) ;
5875 } else if ( rangeToReplace . isEmpty ) {
59- let { active } = selection ;
60- let currentNode = getNode ( rootNode , active , true ) ;
76+ const { active } = selection ;
77+ const currentNode = getNode ( rootNode , active , true ) ;
6178 if ( currentNode && ( currentNode . start . line === active . line || currentNode . end . line === active . line ) ) {
6279 rangeToReplace = new vscode . Range ( currentNode . start , currentNode . end ) ;
6380 } else {
6481 rangeToReplace = new vscode . Range ( rangeToReplace . start . line , 0 , rangeToReplace . start . line , editor . document . lineAt ( rangeToReplace . start . line ) . text . length ) ;
6582 }
6683 }
6784
68- rangeToReplace = ignoreExtraWhitespaceSelected ( rangeToReplace , editor . document ) ;
85+ const firstLineOfSelection = editor . document . lineAt ( rangeToReplace . start ) . text . substr ( rangeToReplace . start . character ) ;
86+ const matches = firstLineOfSelection . match ( / ^ ( \s * ) / ) ;
87+ const extraWhiteSpaceSelected = matches ? matches [ 1 ] . length : 0 ;
88+ rangeToReplace = new vscode . Range ( rangeToReplace . start . line , rangeToReplace . start . character + extraWhiteSpaceSelected , rangeToReplace . end . line , rangeToReplace . end . character ) ;
6989
70- const wholeFirstLine = editor . document . lineAt ( rangeToReplace . start ) . text ;
71- const otherMatches = wholeFirstLine . match ( / ^ ( \s * ) / ) ;
72- const preceedingWhiteSpace = otherMatches ? otherMatches [ 1 ] : '' ;
90+ let textToWrapInPreview : string [ ] ;
7391 let textToReplace = editor . document . getText ( rangeToReplace ) ;
74- let textToWrapInPreview = rangeToReplace . isSingleLine ? [ textToReplace ] : [ '\n\t' + textToReplace . split ( '\n' + preceedingWhiteSpace ) . join ( '\n\t' ) + '\n' ] ;
75- rangesToReplace . push ( { previewRange : rangeToReplace , originalRange : rangeToReplace , originalContent : textToReplace , textToWrapInPreview } ) ;
92+ if ( individualLines ) {
93+ textToWrapInPreview = textToReplace . split ( '\n' ) . map ( x => x . trim ( ) ) ;
94+ } else {
95+ const wholeFirstLine = editor . document . lineAt ( rangeToReplace . start ) . text ;
96+ const otherMatches = wholeFirstLine . match ( / ^ ( \s * ) / ) ;
97+ const preceedingWhiteSpace = otherMatches ? otherMatches [ 1 ] : '' ;
98+ textToWrapInPreview = rangeToReplace . isSingleLine ? [ textToReplace ] : [ '\n\t' + textToReplace . split ( '\n' + preceedingWhiteSpace ) . join ( '\n\t' ) + '\n' ] ;
99+ }
100+
101+ return {
102+ previewRange : rangeToReplace ,
103+ originalRange : rangeToReplace ,
104+ originalContent : textToReplace ,
105+ textToWrapInPreview
106+ } ;
76107 } ) ;
77108
78- let abbreviationPromise ;
79- let currentValue = '' ;
109+ function revertPreview ( ) : Thenable < any > {
110+ return editor . edit ( builder => {
111+ for ( let i = 0 ; i < rangesToReplace . length ; i ++ ) {
112+ builder . replace ( rangesToReplace [ i ] . previewRange , rangesToReplace [ i ] . originalContent ) ;
113+ rangesToReplace [ i ] . previewRange = rangesToReplace [ i ] . originalRange ;
114+ }
115+ } , { undoStopBefore : false , undoStopAfter : false } ) ;
116+ }
80117
81- function inputChanged ( value : string ) : string {
82- if ( value !== currentValue ) {
83- currentValue = value ;
84- makeChanges ( value , inPreview , false ) . then ( ( out ) => {
85- if ( typeof out === 'boolean' ) {
86- inPreview = out ;
118+ function applyPreview ( expandAbbrList : ExpandAbbreviationInput [ ] ) : Thenable < boolean > {
119+ let totalLinesInserted = 0 ;
120+
121+ return editor . edit ( builder => {
122+ for ( let i = 0 ; i < rangesToReplace . length ; i ++ ) {
123+ const expandedText = expandAbbr ( expandAbbrList [ i ] ) || '' ;
124+ if ( ! expandedText ) {
125+ // Failed to expand text. We already showed an error inside expandAbbr.
126+ break ;
87127 }
88- } ) ;
89- }
90- return '' ;
91- }
92128
93- abbreviationPromise = ( args && args [ 'abbreviation' ] ) ? Promise . resolve ( args [ 'abbreviation' ] ) : vscode . window . showInputBox ( { prompt : 'Enter Abbreviation' , validateInput : inputChanged } ) ;
94- const helper = getEmmetHelper ( ) ;
129+ const oldPreviewRange = rangesToReplace [ i ] . previewRange ;
130+ const preceedingText = editor . document . getText ( new vscode . Range ( oldPreviewRange . start . line , 0 , oldPreviewRange . start . line , oldPreviewRange . start . character ) ) ;
131+ const indentPrefix = ( preceedingText . match ( / ^ ( \s * ) / ) || [ '' , '' ] ) [ 1 ] ;
132+
133+ let newText = expandedText . replace ( / \n / g, '\n' + indentPrefix ) ; // Adding indentation on each line of expanded text
134+ newText = newText . replace ( / \$ \{ [ \d ] * \} / g, '|' ) ; // Removing Tabstops
135+ newText = newText . replace ( / \$ \{ [ \d ] * ( : [ ^ } ] * ) ? \} / g, ( match ) => { // Replacing Placeholders
136+ return match . replace ( / ^ \$ \{ [ \d ] * : / , '' ) . replace ( '}' , '' ) ;
137+ } ) ;
138+ builder . replace ( oldPreviewRange , newText ) ;
139+
140+ const expandedTextLines = newText . split ( '\n' ) ;
141+ const oldPreviewLines = oldPreviewRange . end . line - oldPreviewRange . start . line + 1 ;
142+ const newLinesInserted = expandedTextLines . length - oldPreviewLines ;
143+
144+ let lastLineEnd = expandedTextLines [ expandedTextLines . length - 1 ] . length ;
145+ if ( expandedTextLines . length === 1 ) {
146+ // If the expandedText is single line, add the length of preceeding whitespace as it will not be included in line length.
147+ lastLineEnd += oldPreviewRange . start . character ;
148+ }
149+
150+ rangesToReplace [ i ] . previewRange = new vscode . Range ( oldPreviewRange . start . line + totalLinesInserted , oldPreviewRange . start . character , oldPreviewRange . end . line + totalLinesInserted + newLinesInserted , lastLineEnd ) ;
151+ totalLinesInserted += newLinesInserted ;
152+ }
153+ } , { undoStopBefore : false , undoStopAfter : false } ) ;
154+ }
95155
96- function makeChanges ( inputAbbreviation : string | undefined , inPreview : boolean , definitive : boolean ) : Thenable < boolean > {
156+ function makeChanges ( inputAbbreviation : string | undefined , definitive : boolean ) : Thenable < boolean > {
97157 if ( ! inputAbbreviation || ! inputAbbreviation . trim ( ) || ! helper . isAbbreviationValid ( syntax , inputAbbreviation ) ) {
98- return inPreview ? revertPreview ( editor , rangesToReplace ) . then ( ( ) => { return false ; } ) : Promise . resolve ( inPreview ) ;
158+ return inPreview ? revertPreview ( ) . then ( ( ) => { return false ; } ) : Promise . resolve ( inPreview ) ;
99159 }
100160
101161 let extractedResults = helper . extractAbbreviationFromText ( inputAbbreviation ) ;
@@ -107,145 +167,44 @@ export function wrapWithAbbreviation(args: any) {
107167
108168 let { abbreviation, filter } = extractedResults ;
109169 if ( definitive ) {
110- const revertPromise = inPreview ? revertPreview ( editor , rangesToReplace ) : Promise . resolve ( ) ;
170+ const revertPromise = inPreview ? revertPreview ( ) : Promise . resolve ( ) ;
111171 return revertPromise . then ( ( ) => {
112172 const expandAbbrList : ExpandAbbreviationInput [ ] = rangesToReplace . map ( rangesAndContent => {
113173 let rangeToReplace = rangesAndContent . originalRange ;
114- let textToWrap = rangeToReplace . isSingleLine ? [ '$TM_SELECTED_TEXT' ] : [ '\n\t$TM_SELECTED_TEXT\n' ] ;
115- return { syntax, abbreviation, rangeToReplace, textToWrap, filter } ;
174+ let textToWrap : string [ ] ;
175+ if ( individualLines ) {
176+ textToWrap = rangesAndContent . textToWrapInPreview ;
177+ } else {
178+ textToWrap = rangeToReplace . isSingleLine ? [ '$TM_SELECTED_TEXT' ] : [ '\n\t$TM_SELECTED_TEXT\n' ] ;
179+ }
180+ return { syntax : syntax || '' , abbreviation, rangeToReplace, textToWrap, filter } ;
116181 } ) ;
117- return expandAbbreviationInRange ( editor , expandAbbrList , true ) . then ( ( ) => { return true ; } ) ;
182+ return expandAbbreviationInRange ( editor , expandAbbrList , ! individualLines ) . then ( ( ) => { return true ; } ) ;
118183 } ) ;
119184 }
120185
121186 const expandAbbrList : ExpandAbbreviationInput [ ] = rangesToReplace . map ( rangesAndContent => {
122- return { syntax, abbreviation, rangeToReplace : rangesAndContent . originalRange , textToWrap : rangesAndContent . textToWrapInPreview , filter } ;
187+ return { syntax : syntax || '' , abbreviation, rangeToReplace : rangesAndContent . originalRange , textToWrap : rangesAndContent . textToWrapInPreview , filter } ;
123188 } ) ;
124189
125- return applyPreview ( editor , expandAbbrList , rangesToReplace ) ;
190+ return applyPreview ( expandAbbrList ) ;
126191 }
127192
128- // On inputBox closing
129- return abbreviationPromise . then ( inputAbbreviation => {
130- return makeChanges ( inputAbbreviation , inPreview , true ) ;
131- } ) ;
132- }
133-
134- function revertPreview ( editor : vscode . TextEditor , rangesToReplace : PreviewRangesWithContent [ ] ) : Thenable < any > {
135- return editor . edit ( builder => {
136- for ( let i = 0 ; i < rangesToReplace . length ; i ++ ) {
137- builder . replace ( rangesToReplace [ i ] . previewRange , rangesToReplace [ i ] . originalContent ) ;
138- rangesToReplace [ i ] . previewRange = rangesToReplace [ i ] . originalRange ;
139- }
140- } , { undoStopBefore : false , undoStopAfter : false } ) ;
141- }
142-
143- function applyPreview ( editor : vscode . TextEditor , expandAbbrList : ExpandAbbreviationInput [ ] , rangesToReplace : PreviewRangesWithContent [ ] ) : Thenable < boolean > {
144- let totalLinesInserted = 0 ;
145-
146- return editor . edit ( builder => {
147- for ( let i = 0 ; i < rangesToReplace . length ; i ++ ) {
148- const expandedText = expandAbbr ( expandAbbrList [ i ] ) || '' ;
149- if ( ! expandedText ) {
150- // Failed to expand text. We already showed an error inside expandAbbr.
151- break ;
152- }
153-
154- const oldPreviewRange = rangesToReplace [ i ] . previewRange ;
155- const preceedingText = editor . document . getText ( new vscode . Range ( oldPreviewRange . start . line , 0 , oldPreviewRange . start . line , oldPreviewRange . start . character ) ) ;
156- const indentPrefix = ( preceedingText . match ( / ^ ( \s * ) / ) || [ '' , '' ] ) [ 1 ] ;
157-
158- let newText = expandedText . replace ( / \n / g, '\n' + indentPrefix ) ; // Adding indentation on each line of expanded text
159- newText = newText . replace ( / \$ \{ [ \d ] * \} / g, '|' ) ; // Removing Tabstops
160- newText = newText . replace ( / \$ \{ [ \d ] * ( : [ ^ } ] * ) ? \} / g, ( match ) => { // Replacing Placeholders
161- return match . replace ( / ^ \$ \{ [ \d ] * : / , '' ) . replace ( '}' , '' ) ;
193+ function inputChanged ( value : string ) : string {
194+ if ( value !== currentValue ) {
195+ currentValue = value ;
196+ makeChanges ( value , false ) . then ( ( out ) => {
197+ if ( typeof out === 'boolean' ) {
198+ inPreview = out ;
199+ }
162200 } ) ;
163- builder . replace ( oldPreviewRange , newText ) ;
164-
165- const expandedTextLines = newText . split ( '\n' ) ;
166- const oldPreviewLines = oldPreviewRange . end . line - oldPreviewRange . start . line + 1 ;
167- const newLinesInserted = expandedTextLines . length - oldPreviewLines ;
168-
169- let lastLineEnd = expandedTextLines [ expandedTextLines . length - 1 ] . length ;
170- if ( expandedTextLines . length === 1 ) {
171- // If the expandedText is single line, add the length of preceeding whitespace as it will not be included in line length.
172- lastLineEnd += oldPreviewRange . start . character ;
173- }
174-
175- rangesToReplace [ i ] . previewRange = new vscode . Range ( oldPreviewRange . start . line + totalLinesInserted , oldPreviewRange . start . character , oldPreviewRange . end . line + totalLinesInserted + newLinesInserted , lastLineEnd ) ;
176- totalLinesInserted += newLinesInserted ;
177201 }
178- } , { undoStopBefore : false , undoStopAfter : false } ) ;
179- }
180-
181- export function wrapIndividualLinesWithAbbreviation ( args : any ) {
182- if ( ! validate ( false ) || ! vscode . window . activeTextEditor ) {
183- return ;
184- }
185-
186- const editor = vscode . window . activeTextEditor ;
187- if ( editor . selections . length === 1 && editor . selection . isEmpty ) {
188- vscode . window . showInformationMessage ( 'Select more than 1 line and try again.' ) ;
189- return ;
190- }
191- if ( editor . selections . find ( x => x . isEmpty ) ) {
192- vscode . window . showInformationMessage ( 'Select more than 1 line in each selection and try again.' ) ;
193- return ;
194- }
195- let rangesToReplace : vscode . Range [ ] = [ ] ;
196- editor . selections . forEach ( selection => {
197- let rangeToReplace : vscode . Range = selection . isReversed ? new vscode . Range ( selection . active , selection . anchor ) : selection ;
198- if ( ! rangeToReplace . isSingleLine && rangeToReplace . end . character === 0 ) {
199- let previousLine = rangeToReplace . end . line - 1 ;
200- let lastChar = editor . document . lineAt ( previousLine ) . text . length ;
201- rangeToReplace = new vscode . Range ( rangeToReplace . start , new vscode . Position ( previousLine , lastChar ) ) ;
202- }
203-
204- rangeToReplace = ignoreExtraWhitespaceSelected ( rangeToReplace , editor . document ) ;
205- rangesToReplace . push ( rangeToReplace ) ;
206- } ) ;
207-
208- const syntax = getSyntaxFromArgs ( { language : editor . document . languageId } ) ;
209- if ( ! syntax ) {
210- return ;
202+ return '' ;
211203 }
212-
213- const abbreviationPromise = ( args && args [ 'abbreviation' ] ) ? Promise . resolve ( args [ 'abbreviation' ] ) : vscode . window . showInputBox ( { prompt : 'Enter Abbreviation' } ) ;
214- const helper = getEmmetHelper ( ) ;
215-
204+ const abbreviationPromise = ( args && args [ 'abbreviation' ] ) ? Promise . resolve ( args [ 'abbreviation' ] ) : vscode . window . showInputBox ( { prompt : 'Enter Abbreviation' , validateInput : inputChanged } ) ;
216205 return abbreviationPromise . then ( inputAbbreviation => {
217- let expandAbbrInput : ExpandAbbreviationInput [ ] = [ ] ;
218- if ( ! inputAbbreviation || ! inputAbbreviation . trim ( ) || ! helper . isAbbreviationValid ( syntax , inputAbbreviation ) ) { return false ; }
219-
220- let extractedResults = helper . extractAbbreviationFromText ( inputAbbreviation ) ;
221- if ( ! extractedResults ) {
222- return false ;
223- }
224- rangesToReplace . forEach ( rangeToReplace => {
225- let lines = editor . document . getText ( rangeToReplace ) . split ( '\n' ) . map ( x => x . trim ( ) ) ;
226-
227- let { abbreviation, filter } = extractedResults ;
228- let input : ExpandAbbreviationInput = {
229- syntax,
230- abbreviation,
231- rangeToReplace,
232- textToWrap : lines ,
233- filter
234- } ;
235- expandAbbrInput . push ( input ) ;
236- } ) ;
237-
238- return expandAbbreviationInRange ( editor , expandAbbrInput , false ) ;
206+ return makeChanges ( inputAbbreviation , true ) ;
239207 } ) ;
240-
241- }
242-
243- function ignoreExtraWhitespaceSelected ( range : vscode . Range , document : vscode . TextDocument ) : vscode . Range {
244- const firstLineOfSelection = document . lineAt ( range . start ) . text . substr ( range . start . character ) ;
245- const matches = firstLineOfSelection . match ( / ^ ( \s * ) / ) ;
246- const extraWhiteSpaceSelected = matches ? matches [ 1 ] . length : 0 ;
247-
248- return new vscode . Range ( range . start . line , range . start . character + extraWhiteSpaceSelected , range . end . line , range . end . character ) ;
249208}
250209
251210export function expandEmmetAbbreviation ( args : any ) : Thenable < boolean | undefined > {
0 commit comments