@@ -3,20 +3,22 @@ import * as YAML from 'yaml';
33import * as XML from 'plist' ;
44import * as CSON from 'cson-parser' ;
55import * as DATE from 'date-and-time' ;
6+ import CSON2JSON from 'cson2json' ;
67
78
89export function initFileConverter ( context : vscode . ExtensionContext ) {
910 context . subscriptions . push (
1011 // TODO: use registerTextEditorCommand instead. https://github.com/microsoft/vscode/issues/153164
11- vscode . commands . registerCommand ( 'extension .convertFileToJSON' , async ( document : vscode . TextDocument ) => await convertFileTo ( 'JSON' , document ) ) ,
12- vscode . commands . registerCommand ( 'extension .convertFileToYAML' , async ( document : vscode . TextDocument ) => await convertFileTo ( 'YAML' , document ) ) ,
13- vscode . commands . registerCommand ( 'extension .convertFileToXML' , async ( document : vscode . TextDocument ) => await convertFileTo ( 'XML' , document ) ) ,
14- vscode . commands . registerCommand ( 'extension.convertFileToPLIST ' , async ( document : vscode . TextDocument ) => await convertFileTo ( 'ASCII' , document ) ) ,
15- vscode . commands . registerCommand ( 'extension .convertFileToCSON' , async ( document : vscode . TextDocument ) => await convertFileTo ( 'CSON' , document ) ) ,
12+ vscode . commands . registerCommand ( 'textmate .convertFileToJSON' , async ( document ? : vscode . TextDocument ) => await convertFileTo ( 'JSON' , document ) ) ,
13+ vscode . commands . registerCommand ( 'textmate .convertFileToYAML' , async ( document ? : vscode . TextDocument ) => await convertFileTo ( 'YAML' , document ) ) ,
14+ vscode . commands . registerCommand ( 'textmate .convertFileToXML' , async ( document ? : vscode . TextDocument ) => await convertFileTo ( 'XML' , document ) ) ,
15+ vscode . commands . registerCommand ( 'textmate.convertFileToASCII ' , async ( document ? : vscode . TextDocument ) => await convertFileTo ( 'ASCII' , document ) ) ,
16+ vscode . commands . registerCommand ( 'textmate .convertFileToCSON' , async ( document ? : vscode . TextDocument ) => await convertFileTo ( 'CSON' , document ) ) ,
1617 ) ;
1718}
1819
1920type Language = 'JSON' | 'YAML' | 'XML' | 'ASCII' | 'CSON' ;
21+
2022async function convertFileTo ( newLanguage : Language , document ?: vscode . TextDocument ) {
2123 if ( ! document ) {
2224 const activeTextEditor = vscode . window . activeTextEditor ;
@@ -45,15 +47,15 @@ async function convertFileTo(newLanguage: Language, document?: vscode.TextDocume
4547 parsedDocument = parseAsciiPLIST ( text ) ;
4648 break ;
4749 case 'CSON' :
48- if ( ! CSON . parse ) {
49- vscode . window . showWarningMessage ( "TextMate: CSON conversion not available in VSCode Web atm" ) ;
50+ if ( CSON . parse ) {
51+ parsedDocument = CSON . parse ( text ) ;
5052 break ;
5153 }
52- parsedDocument = CSON . parse ( text ) ;
54+ parsedDocument = CSON2JSON . default ( text ) ;
5355 break ;
5456 }
5557 } catch ( error : any ) {
56- vscode . window . showWarningMessage ( `TextMate: Error converting file from ${ language } :\n${ error . toString ( ) } ` ) ;
58+ throw new Error ( `TextMate: Error converting file from ${ language } :\n${ error . toString ( ) } ` ) ;
5759 }
5860 // console.log(`parsedDocument: ${language}\n`, parsedDocument);
5961 if ( parsedDocument != null ) {
@@ -90,24 +92,25 @@ async function convertFileTo(newLanguage: Language, document?: vscode.TextDocume
9092 } ) ;
9193 break ;
9294 case 'XML' :
95+ if ( typeof navigator !== 'undefined' ) {
96+ throw new Error ( "TextMate: Conversion to XML is not available in VSCode Web atm" ) ;
97+ }
9398 newText = XML . build ( parsedDocument , { indent : indent } ) ;
9499 break ;
95100 case 'ASCII' :
96101 newText = stringifyAsciiPLIST ( parsedDocument , indent ) ;
97102 break ;
98103 case 'CSON' :
99104 if ( ! CSON . stringify ) {
100- vscode . window . showWarningMessage ( "TextMate: CSON conversion not available in VSCode Web atm" ) ;
101- return ;
105+ throw new Error ( "TextMate: Conversion to CSON is not available in VSCode Web atm" ) ;
102106 }
103107 newText = CSON . stringify ( parsedDocument , undefined , indent ) ;
104108 break ;
105109 default :
106110 return ;
107111 }
108112 } catch ( error : any ) {
109- vscode . window . showWarningMessage ( `TextMate: Error converting file to ${ newLanguage } :\n${ error . toString ( ) } ` ) ;
110- return ;
113+ throw new Error ( `TextMate: Error converting file to ${ newLanguage } :\n${ error ?. message || error . toString ( ) } ` ) ;
111114 }
112115
113116 const newDocument = await vscode . workspace . openTextDocument ( { content : newText , language : documentLanguage } ) ;
@@ -127,33 +130,36 @@ function rankLanguages(document: vscode.TextDocument): Language[] {
127130 CSON : 0 ,
128131 } ;
129132
130- const documentLanguage = document . languageId ;
131- if ( / J S O N / i. test ( documentLanguage ) ) {
133+ const languageId = document . languageId ;
134+ if ( / J S O N / i. test ( languageId ) ) {
132135 languageScores . JSON += 10 ;
133136 }
134- if ( / Y A ? M L / i. test ( documentLanguage ) ) {
137+ if ( / Y A ? M L / i. test ( languageId ) ) {
135138 languageScores . YAML += 10 ;
136139 }
137- if ( / X M L / i. test ( documentLanguage ) ) {
140+ if ( / X M L / i. test ( languageId ) ) {
138141 languageScores . XML += 10 ;
139142 }
140- if ( / A S C I I - T E X T M A T E / i. test ( documentLanguage ) ) {
143+ if ( / A S C I I - T E X T M A T E / i. test ( languageId ) ) {
141144 languageScores . ASCII += 10 ;
142145 }
143- if ( / c s o n | c o f f e e s c r i p t / i. test ( documentLanguage ) ) {
146+ if ( / c s o n | c o f f e e s c r i p t / i. test ( languageId ) ) {
144147 languageScores . CSON += 10 ;
145148 }
146149
147150 const fileName = document . fileName ;
148151 if ( / J S O N [ C L ] ? $ / i. test ( fileName ) ) {
149152 languageScores . JSON += 6 ;
150153 }
151- if ( / Y A ? M L / i. test ( fileName ) ) {
152- languageScores . YAML += 5 ;
154+ if ( / Y A ? M L (?: - t m L a n g u a g e ) ? $ / i. test ( fileName ) ) {
155+ languageScores . YAML += 6 ;
153156 }
154157 if ( / X M L $ / i. test ( fileName ) ) {
155158 languageScores . XML += 6 ;
156159 }
160+ if ( / (?: c s o n | c o f f e e s c r i p t ) (?: - t m L a n g u a g e ) ? $ / i. test ( fileName ) ) {
161+ languageScores . CSON += 6 ;
162+ }
157163 if ( / P L I S T $ / i. test ( fileName ) ) {
158164 languageScores . ASCII += 5 ;
159165 }
@@ -164,9 +170,6 @@ function rankLanguages(document: vscode.TextDocument): Language[] {
164170 if ( / t e x t m a t e $ / i. test ( fileName ) ) {
165171 languageScores . ASCII += 3 ;
166172 }
167- if ( / c s o n $ | c o f f e e s c r i p t $ / i. test ( fileName ) ) {
168- languageScores . CSON += 6 ;
169- }
170173
171174 const text = document . getText ( ) ;
172175 if ( / ^ \s * { \s * $ | ^ \s * { \s * " / i. test ( text ) ) {
@@ -185,9 +188,12 @@ function rankLanguages(document: vscode.TextDocument): Language[] {
185188 if ( / ^ \s * { \s * [ ' " ] ? \w + [ ' " ] ? \s * = / i. test ( text ) ) {
186189 languageScores . ASCII += 3 ;
187190 }
188- if ( / ^ \s * # | ^ \s * \w + : \s / i. test ( text ) ) {
191+ if ( / ^ \s * # | ^ \s * [ ' " ] ? \w + [ ' " ] ? : \s / i. test ( text ) ) {
189192 languageScores . CSON += 3 ;
190193 }
194+ if ( / ^ \s * \w + : \s + \[ / i. test ( text ) ) {
195+ languageScores . CSON += 2 ;
196+ }
191197
192198 for ( const language in languageScores ) {
193199 if ( languageScores [ language as Language ] === 0 ) {
@@ -196,16 +202,16 @@ function rankLanguages(document: vscode.TextDocument): Language[] {
196202 }
197203
198204 const rankedLanguages = ( Object . keys ( languageScores ) as Language [ ] ) . sort ( ( a , b ) => languageScores [ b ] - languageScores [ a ] ) ;
199- console . log ( 'TextMate: rankedLanguages: ' , rankedLanguages , ' ' , languageScores ) ;
200- if ( rankLanguages . length === 0 ) {
201- vscode . window . showWarningMessage ( "TextMate: FileConverter:\nCannot determine document's language" ) ;
205+ // console.log('TextMate: FileConverter: Ranked Languages: Order: ', rankedLanguages, 'Scores: ', languageScores);
206+ if ( rankedLanguages . length === 0 ) {
207+ throw new Error ( "TextMate: FileConverter:\nCannot determine document's language" ) ;
202208 }
203209 return rankedLanguages ;
204210}
205211
206212
207213
208- /* == ASCI PLIST == */
214+ /* == ASCII PLIST == */
209215
210216function stringifyAsciiPLIST ( value : Value , space : string = '\t' , indent : number = 0 , parent ?: Value ) : string {
211217 // https://github.com/textmate/textmate/blob/master/Frameworks/plist/src/to_s.cc
@@ -243,7 +249,7 @@ function stringifyAsciiPLIST(value: Value, space: string = '\t', indent: number
243249 return value . toString ( ) ;
244250
245251 case 'boolean' :
246- // Apple 's custom plist (ascii) boolean
252+ // TextMate2.0 's custom boolean
247253 return value ? ':true' : ':false' ;
248254
249255 case 'object' :
@@ -259,36 +265,37 @@ function stringifyAsciiPLIST(value: Value, space: string = '\t', indent: number
259265 }
260266
261267 if ( value . length === 1 ) {
262- if ( value [ 0 ] != null ) {
263- const firstValue = value [ 0 ] ;
264- switch ( typeof firstValue ) {
265- case 'object' :
266- if ( ! ( firstValue instanceof Date ) ) {
267- break ;
268- }
269- case 'string' :
270- case 'bigint' :
271- case 'number' :
272- case 'boolean' :
273- return `( ${ stringifyAsciiPLIST ( firstValue ) } )` ;
274- }
268+ const firstValue = value [ 0 ] ;
269+ switch ( typeof firstValue ) {
270+ case 'object' :
271+ if ( ! ( firstValue instanceof Date ) ) {
272+ break ;
273+ }
274+ case 'string' :
275+ case 'bigint' :
276+ case 'number' :
277+ case 'boolean' :
278+ if ( firstValue == null ) {
279+ break ;
280+ }
281+ return `( ${ stringifyAsciiPLIST ( firstValue ) } )` ;
275282 }
276-
277283 }
278- return `(${ value
279- . map ( element =>
280- `\n${ space . repeat ( indent + 1 ) } ${ stringifyAsciiPLIST ( element , space , indent + 1 , value ) } ,`
281- ) . join ( '' )
282- } \n${ space . repeat ( indent ) } )`;
284+ const elements = value . map ( element => `\n${ space . repeat ( indent + 1 ) } ${ stringifyAsciiPLIST ( element , space , indent + 1 , value ) } ,` ) ;
285+ return `(${ elements . join ( '' ) } \n${ space . repeat ( indent ) } )` ;
283286 }
284287
285- if ( Object . keys ( value ) . length === 0 ) {
288+ const keys = Object . keys ( value ) ;
289+ if ( keys . length === 0 ) {
286290 return '{ }' ;
287291 }
288- if ( Object . keys ( value ) . length === 1 && typeof Object . values ( value ) [ 0 ] !== 'object' ) {
289- return `{ ${ stringifyAsciiPLIST ( Object . keys ( value ) [ 0 ] ) } = ${ stringifyAsciiPLIST ( Object . values ( value ) [ 0 ] , space , indent + 1 , value ) } ; }` ;
292+ if ( keys . length === 1 ) {
293+ const values = Object . values ( value ) ;
294+ if ( typeof values [ 0 ] !== 'object' ) {
295+ return `{ ${ stringifyAsciiPLIST ( keys [ 0 ] ) } = ${ stringifyAsciiPLIST ( Object . values ( value ) [ 0 ] , space , indent + 1 , value ) } ; }` ;
296+ }
290297 }
291- return `{${ ( Array . isArray ( parent ) || ! parent ) ? space . startsWith ( ' ' ) ? space . slice ( 1 ) || ' ' : space : `\n${ space . repeat ( indent + 1 ) } ` } ${ Object . keys ( value )
298+ return `{${ ( ! parent || Array . isArray ( parent ) ) ? space . startsWith ( ' ' ) ? space . slice ( 1 ) || ' ' : space : `\n${ space . repeat ( indent + 1 ) } ` } ${ keys
292299 . map ( key =>
293300 `${ stringifyAsciiPLIST ( key ) } = ${ stringifyAsciiPLIST ( value [ key ] , space , indent + 1 , value ) } ;`
294301 ) . join ( `\n${ space . repeat ( indent + 1 ) } ` )
@@ -322,7 +329,7 @@ function nextToken(regex: RegExp, string: string): match {
322329 // const matchKey = Object.keys(match)[matchIndex] as NonNullable<match>['key'];
323330 // const matchValue = Object.values(match)[matchIndex]!;
324331 // return { key: matchKey, value: matchValue };
325- const matchedGroup = Object . entries ( match ) . find ( group => group [ 1 ] ) as [ NonNullable < match > [ 'key' ] , string ] ;
332+ const matchedGroup = Object . entries ( match ) . find ( group => group [ 1 ] ) as [ NonNullable < match > [ 'key' ] , string ] | undefined ;
326333 if ( matchedGroup === undefined ) {
327334 return null ;
328335 }
@@ -333,10 +340,10 @@ function parseElement(regex: RegExp, string: string, match: match = nextToken(re
333340 return ;
334341 }
335342 switch ( match . key ) {
336- // case 'whitespace':
337- // case 'comment':
338- // case 'forwardSlash':
339- // continue;
343+ // case 'whitespace':
344+ // case 'comment':
345+ // case 'forwardSlash':
346+ // continue;
340347
341348 case 'curlyOpen' :
342349 const object : Dictionary = { } ;
@@ -433,7 +440,9 @@ function parseElement(regex: RegExp, string: string, match: match = nextToken(re
433440 // return;
434441 // }
435442 const date = DATE . parse ( match . value , '@YYYY-MM-DD HH:mm:ss Z' , true ) ;
436- if ( date . getUTCFullYear ( ) < 1970 ) { }
443+ if ( date . getUTCFullYear ( ) < 1970 ) {
444+ // TextMate2.0 can't handle dates before 1970
445+ }
437446 return date ;
438447
439448 case 'curlyClose' :
@@ -487,7 +496,7 @@ function parseAsciiPLIST(string: string): Value {
487496 [
488497 / (?< whitespace > [ \t \r \n ] + ) / ,
489498 / (?< comment > \/ \/ .* $ | \/ \* .* ?\* \/ ) / ,
490- / (?< forwardSlash > \/ (? ! \s ) ) / , // for some reason, Apple ignores single forward slashes before any non-whitespace token
499+ / (?< forwardSlash > \/ (? ! \s ) ) / , // TextMate2.0 doesn't backtrack matching a single forward slash when attempting to match a comment. Effectively ignores a single forward slash before any non-whitespace token
491500 / (?< curlyOpen > { ) / ,
492501 / (?< curlyClose > } ) / ,
493502 / (?< parenOpen > \( ) / ,
0 commit comments