/src/fileConverter.ts

https://github.com/Togusa09/vscode-tmlanguage · TypeScript · 239 lines · 195 code · 38 blank · 6 comment · 47 complexity · 14a5c6687aac5eb6daaf299055e9d230 MD5 · raw file

  1. import * as vscode from 'vscode';
  2. import * as path from 'path';
  3. import * as fs from "fs";
  4. import * as plist from 'plist';
  5. import * as json from 'format-json';
  6. import * as YAML from 'yamljs';
  7. import {SupportedLanguage} from './languages'
  8. export interface IConfig {
  9. replaceExistingFile : boolean
  10. }
  11. export class FileConverter{
  12. public convertFileToJsonTml() : Thenable<boolean> {
  13. return this.ConvertFile( "json-tmlanguage");
  14. }
  15. public convertFileToYamlTml() : Thenable<boolean>{
  16. return this.ConvertFile( "yaml-tmlanguage");
  17. }
  18. public convertFileToTml() : Thenable<boolean>{
  19. return this.ConvertFile( "tmlanguage");
  20. }
  21. public convertFileToAuto() : Thenable<boolean>{
  22. return this.ConvertFile( "yaml-tmlanguage");
  23. }
  24. public dispose(): void {
  25. // nothing to do
  26. }
  27. private fileExtensionFor(destinationLanguage: string) : string {
  28. switch (destinationLanguage.toLowerCase()) {
  29. case "json-tmlanguage":
  30. return "JSON-tmLanguage";
  31. case "yaml-tmlanguage":
  32. return "YAML-tmLanguage";
  33. case "tmlanguage":
  34. return "tmLanguage";
  35. default:
  36. return undefined;
  37. }
  38. }
  39. private ConvertFile(destinationLanguage: string) : Thenable<boolean>{
  40. let editor : vscode.TextEditor = vscode.window.activeTextEditor;
  41. if (!editor){
  42. return;
  43. }
  44. const extension: string = this.fileExtensionFor(destinationLanguage);
  45. let doc : vscode.TextDocument = editor.document;
  46. var parsedFilePath: path.ParsedPath = path.parse(doc.fileName);
  47. if (!extension) {
  48. console.log(`Unable to map destination language (${destinationLanguage}) to file extension`);
  49. vscode.window.showErrorMessage(`Unable to map destination language (${destinationLanguage}) to file extension`);
  50. return;
  51. }
  52. try{
  53. const destLanguage: SupportedLanguage = destinationLanguage as SupportedLanguage;
  54. const doc: vscode.TextDocument = editor.document;
  55. const parsedFilePath: path.ParsedPath = path.parse(doc.fileName);
  56. let documentText: string = doc.getText();
  57. console.log(`Converting to ${extension}`);
  58. // Some of the sublime tmlanguage variant files had comments as hints for auto conversion
  59. if (documentText.startsWith("//")){
  60. var lastLineRange : vscode.Range = doc.lineAt(doc.lineCount - 1).range;
  61. documentText = doc.getText(new vscode.Range(new vscode.Position(1, 0), lastLineRange.end));
  62. }
  63. const sourceLanguage: SupportedLanguage = doc.languageId as SupportedLanguage;
  64. const cfg = vscode.workspace.getConfiguration().get<IConfig>("tmLanguage")
  65. if(cfg.replaceExistingFile){
  66. return this.createFileReplacingExisting(parsedFilePath, extension, sourceLanguage, destinationLanguage, documentText);
  67. }else{
  68. return this.createFileWithUniqueName(parsedFilePath, extension, sourceLanguage, destinationLanguage, documentText)
  69. }
  70. } catch(err) {
  71. console.log(err);
  72. }
  73. }
  74. private createFileReplacingExisting(parsedFilePath: path.ParsedPath, extension: string, sourceLanguage: SupportedLanguage,
  75. destinationLanguage: string, documentText: string) : Thenable<boolean>
  76. {
  77. const newFilePath: string = path.join(parsedFilePath.dir, "./" + parsedFilePath.name + "." + extension);
  78. return this.ifExists(newFilePath)
  79. .then((existed: boolean) => {
  80. return this.openEditor(sourceLanguage, destinationLanguage, documentText, newFilePath, existed);
  81. }, (reason: any) => {
  82. reason = reason || "Error writing output file";
  83. console.log("Error writing output", reason);
  84. vscode.window.showErrorMessage(reason.toString());
  85. return Promise.resolve(false);
  86. });
  87. }
  88. private createFileWithUniqueName(parsedFilePath: path.ParsedPath, extension: string, sourceLanguage: SupportedLanguage,
  89. destinationLanguage: string, documentText: string) : Thenable<boolean>{
  90. // check to see if file already exists
  91. return vscode.workspace.findFiles(parsedFilePath.name + "*." + extension, "ABC")
  92. .then(matchingFiles => {
  93. var paths = matchingFiles.map(p => p.fsPath);
  94. var editorWindows = vscode.window.visibleTextEditors.map(x => x.document.fileName);
  95. paths = paths.concat(editorWindows);
  96. var newFilePath = path.join(parsedFilePath.dir, './' + parsedFilePath.name + '.' + extension);
  97. if (matchingFiles.length != 0){
  98. var counter = 1;
  99. while (paths.indexOf(newFilePath) >= 0){
  100. newFilePath = path.join(parsedFilePath.dir, './' + parsedFilePath.name + '(' + counter +').' + extension);
  101. counter++;
  102. }
  103. }
  104. return this.openEditor(sourceLanguage, destinationLanguage, documentText, newFilePath, false);
  105. });;
  106. }
  107. private ifExists(filePath: string): Promise<boolean> {
  108. return new Promise((resolve?: (value: boolean) => void, reject?: (reason: any) => void) => {
  109. try {
  110. fs.exists(filePath, (exists: boolean) => {
  111. resolve(exists);
  112. });
  113. } catch (err) {
  114. reject(err);
  115. }
  116. });
  117. }
  118. private parse(sourceLanguage: SupportedLanguage, documentText: string): any {
  119. switch (sourceLanguage) {
  120. case "xml":
  121. case "tmlanguage":
  122. return plist.parse(documentText);
  123. case "json":
  124. case "json-tmlanguage":
  125. return JSON.parse(documentText);
  126. case "yaml":
  127. case "yaml-tmlanguage":
  128. return YAML.parse(documentText);
  129. default:
  130. return undefined;
  131. }
  132. }
  133. private build(destinationLanguage: SupportedLanguage, parsed: any): string {
  134. switch (destinationLanguage) {
  135. case "xml":
  136. case "tmlanguage":
  137. return plist.build(parsed);
  138. case "json":
  139. case "json-tmlanguage":
  140. return json.plain(parsed);
  141. case "yaml":
  142. case "yaml-tmlanguage":
  143. return YAML.stringify(parsed, 6);
  144. default:
  145. return undefined;
  146. }
  147. }
  148. private uriFor(filePath: string, existing: boolean): vscode.Uri {
  149. if (existing) {
  150. return vscode.Uri.file(filePath);
  151. }
  152. return vscode.Uri.parse("untitled:" + filePath);
  153. }
  154. private DoEditStuff(edit: vscode.TextEditorEdit, editor: vscode.TextEditor, sourceLanguage: SupportedLanguage, destinationLanguage: string, documentText: string,
  155. exists: boolean, textDoc: vscode.TextDocument) : void{
  156. var parsed: string;
  157. parsed = this.parse(sourceLanguage, documentText);
  158. if (!parsed){
  159. // Display a message?
  160. console.log("Could not parse source");
  161. //return Promise.reject("Could not parse source");
  162. }
  163. const destLanguage: SupportedLanguage = destinationLanguage as SupportedLanguage;
  164. var built = this.build(destLanguage, parsed);
  165. if (exists) {
  166. const was: vscode.Selection = editor.selection;
  167. const lastLineRange: vscode.Range = textDoc.lineAt(textDoc.lineCount - 1).range;
  168. const beginning: vscode.Position = new vscode.Position(0, 0);
  169. const end: vscode.Position = new vscode.Position(textDoc.lineCount - 1, lastLineRange.end.character);
  170. const entire: vscode.Range = new vscode.Range(beginning, end);
  171. edit.replace(entire, built);
  172. editor.selection = was;
  173. } else {
  174. edit.insert(new vscode.Position(0, 0), built);
  175. }
  176. //return Promise.resolve(true);
  177. }
  178. private openEditor(sourceLanguage: SupportedLanguage, destinationLanguage:string, documentText: string, path: string, exists: boolean)
  179. : Thenable<boolean>
  180. {
  181. const uri: vscode.Uri = this.uriFor(path, exists);
  182. var textDoc : vscode.TextDocument;
  183. var docEditor : vscode.TextEditor;
  184. return vscode.workspace.openTextDocument(uri)
  185. .then((doc: vscode.TextDocument) => {
  186. textDoc = doc;
  187. return vscode.window.showTextDocument(doc)
  188. })
  189. .then((editor: vscode.TextEditor) => {
  190. docEditor = editor;
  191. return editor.edit((edit: vscode.TextEditorEdit) => {
  192. this.DoEditStuff(edit, editor, sourceLanguage, destinationLanguage, documentText, exists, textDoc);
  193. });
  194. },
  195. (reason: any) => {
  196. reason = reason || "Error opening editor for output file";
  197. console.log("Error opening editor for file", reason);
  198. vscode.window.showErrorMessage(reason.toString());
  199. });
  200. }
  201. }