Browse Source

Merge pull request #1513 from AtomicGameEngine/TSH-ATOMIC-VSCODEUPDATES

Changes to integrate better with VSCode
JoshEngebretson 8 years ago
parent
commit
8e6dcda11f

+ 7 - 4
Script/AtomicEditor/hostExtensions/coreExtensions/ProjectBasedExtensionLoader.ts

@@ -60,8 +60,8 @@ export default class ProjectBasedExtensionLoader extends Atomic.ScriptObject imp
      * file system and manually pull these in to provide to duktape
      * file system and manually pull these in to provide to duktape
      */
      */
     private rewriteModSearch() {
     private rewriteModSearch() {
-        Duktape.modSearch = (function(origModSearch) {
-            return function(id: string, require, exports, module) {
+        Duktape.modSearch = (function (origModSearch) {
+            return function (id: string, require, exports, module) {
                 let system = ToolCore.getToolSystem();
                 let system = ToolCore.getToolSystem();
                 if (id.indexOf(ProjectBasedExtensionLoader.duktapeRequirePrefix) == 0) {
                 if (id.indexOf(ProjectBasedExtensionLoader.duktapeRequirePrefix) == 0) {
                     let path = id.substr(ProjectBasedExtensionLoader.duktapeRequirePrefix.length) + ".js";
                     let path = id.substr(ProjectBasedExtensionLoader.duktapeRequirePrefix.length) + ".js";
@@ -75,7 +75,10 @@ export default class ProjectBasedExtensionLoader extends Atomic.ScriptObject imp
                         if (Atomic.fileSystem.fileExists(path)) {
                         if (Atomic.fileSystem.fileExists(path)) {
                             let include = new Atomic.File(path, Atomic.FileMode.FILE_READ);
                             let include = new Atomic.File(path, Atomic.FileMode.FILE_READ);
                             try {
                             try {
-                                return include.readText();
+                                // add a newline to handle situations where sourcemaps are used.  Duktape
+                                // doesn't like not having a trailing newline and the sourcemap process doesn't
+                                // add one.
+                                return include.readText() + "\n";
                             } finally {
                             } finally {
                                 include.close();
                                 include.close();
                             }
                             }
@@ -107,7 +110,7 @@ export default class ProjectBasedExtensionLoader extends Atomic.ScriptObject imp
                 filenames.forEach((filename) => {
                 filenames.forEach((filename) => {
                     // Filtered search in Atomic doesn't due true wildcarding, only handles extension filters
                     // Filtered search in Atomic doesn't due true wildcarding, only handles extension filters
                     // in the future this may be better handled with some kind of manifest file
                     // in the future this may be better handled with some kind of manifest file
-                    if (filename.toLowerCase().lastIndexOf(".plugin.js") >= 0) {
+                    if (filename.search(/\.plugin.js$/i) != -1) {
                         var extensionPath = editorScriptsPath + filename;
                         var extensionPath = editorScriptsPath + filename;
                         extensionPath = extensionPath.substring(0, extensionPath.length - 3);
                         extensionPath = extensionPath.substring(0, extensionPath.length - 3);
 
 

+ 179 - 82
Script/AtomicEditor/hostExtensions/languageExtensions/TypescriptLanguageExtension.ts

@@ -26,19 +26,22 @@
  * then the one in the project will overwrite these
  * then the one in the project will overwrite these
  * @type {ts.CompilerOptions}
  * @type {ts.CompilerOptions}
  */
  */
-const defaultCompilerOptions = {
-    noEmitOnError: true,
-    noImplicitAny: false,
-    target: "es5",
-    module: "commonjs",
-    declaration: false,
-    inlineSourceMap: false,
-    removeComments: false,
-    noLib: false,
-    forceConsistentCasingInFileNames: true,
-    allowJs: true,
-    lib: ["es5"]
-};
+function getDefaultCompilerOptions() {
+    return {
+        noEmitOnError: true,
+        noImplicitAny: false,
+        target: "es5",
+        module: "commonjs",
+        declaration: false,
+        inlineSourceMap: false,
+        sourceMap: false,
+        removeComments: false,
+        noLib: false,
+        forceConsistentCasingInFileNames: true,
+        allowJs: true,
+        lib: ["es5"]
+    };
+}
 
 
 /**
 /**
  * Resource extension that supports the web view typescript extension
  * Resource extension that supports the web view typescript extension
@@ -99,7 +102,7 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
             }
             }
         });
         });
 
 
-        let compilerOptions = defaultCompilerOptions;
+        let compilerOptions = getDefaultCompilerOptions();
         Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.js", Atomic.SCAN_FILES, true).forEach(filename => {
         Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.js", Atomic.SCAN_FILES, true).forEach(filename => {
             let fn = Atomic.addTrailingSlash(ToolCore.toolSystem.project.resourcePath) + filename;
             let fn = Atomic.addTrailingSlash(ToolCore.toolSystem.project.resourcePath) + filename;
             // if the .js file matches up to a .ts file already loaded, then skip it
             // if the .js file matches up to a .ts file already loaded, then skip it
@@ -177,17 +180,24 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
      * Configures the project to be a Typescript Project
      * Configures the project to be a Typescript Project
      * @return {[type]}
      * @return {[type]}
      */
      */
-    private configureTypescriptProjectMenu() {
-        if (this.isTypescriptProject && !this.menuCreated) {
-            const isCompileOnSave = this.serviceRegistry.projectServices.getUserPreference(this.name, "CompileOnSave", false);
-
-            // Build the menu - First build up an empty menu then manually add the items so we can have reference to them
-            const menu = this.serviceRegistry.uiServices.createPluginMenuItemSource("TypeScript", {});
-            this.compileOnSaveMenuItem = new Atomic.UIMenuItem(`Compile on Save: ${isCompileOnSave ? "On" : "Off"}`, `${this.name}.compileonsave`);
-            menu.addItem(this.compileOnSaveMenuItem);
-            menu.addItem(new Atomic.UIMenuItem("Compile Project", `${this.name}.compileproject`));
-            menu.addItem(new Atomic.UIMenuItem("Generate External Editor Project", `${this.name}.generateexternalproject`));
-            this.menuCreated = true;
+    private configureProjectMenu() {
+        if (!this.menuCreated) {
+            if (this.isTypescriptProject) {
+                const isCompileOnSave = this.serviceRegistry.projectServices.getUserPreference(this.name, "CompileOnSave", false);
+
+                // Build the menu - First build up an empty menu then manually add the items so we can have reference to them
+                const menu = this.serviceRegistry.uiServices.createPluginMenuItemSource("TypeScript", {});
+                this.compileOnSaveMenuItem = new Atomic.UIMenuItem(`Compile on Save: ${isCompileOnSave ? "On" : "Off"}`, `${this.name}.compileonsave`);
+                menu.addItem(this.compileOnSaveMenuItem);
+                menu.addItem(new Atomic.UIMenuItem("Compile Project", `${this.name}.compileproject`));
+                menu.addItem(new Atomic.UIMenuItem("Generate External Editor Project", `${this.name}.generateexternalproject`));
+                this.menuCreated = true;
+            } else {
+                // Build the menu - First build up an empty menu then manually add the items so we can have reference to them
+                const menu = this.serviceRegistry.uiServices.createPluginMenuItemSource("JavaScript", {});
+                menu.addItem(new Atomic.UIMenuItem("Generate External Editor Project", `${this.name}.generateexternalproject`));
+                this.menuCreated = true;
+            }
         }
         }
     }
     }
 
 
@@ -213,10 +223,7 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
             // update ts config in case we have a new resource
             // update ts config in case we have a new resource
             let tsConfig = this.buildTsConfig();
             let tsConfig = this.buildTsConfig();
             this.setTsConfigOnWebView(tsConfig);
             this.setTsConfigOnWebView(tsConfig);
-
-            if (this.isTypescriptProject) {
-                this.configureTypescriptProjectMenu();
-            }
+            this.configureProjectMenu();
         }
         }
     }
     }
 
 
@@ -235,7 +242,7 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
                 console.log(`${this.name}: deleting corresponding .js file`);
                 console.log(`${this.name}: deleting corresponding .js file`);
                 ToolCore.assetDatabase.deleteAsset(jsFileAsset);
                 ToolCore.assetDatabase.deleteAsset(jsFileAsset);
 
 
-                let eventData : Editor.EditorDeleteResourceNotificationEvent = {
+                let eventData: Editor.EditorDeleteResourceNotificationEvent = {
                     path: jsFile
                     path: jsFile
                 };
                 };
 
 
@@ -300,8 +307,9 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
         //scan all the files in the project for any typescript files so we can determine if this is a typescript project
         //scan all the files in the project for any typescript files so we can determine if this is a typescript project
         if (Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.ts", Atomic.SCAN_FILES, true).length > 0) {
         if (Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.ts", Atomic.SCAN_FILES, true).length > 0) {
             this.isTypescriptProject = true;
             this.isTypescriptProject = true;
-            this.configureTypescriptProjectMenu();
         };
         };
+
+        this.configureProjectMenu();
     }
     }
 
 
 
 
@@ -310,7 +318,12 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
      */
      */
     projectUnloaded() {
     projectUnloaded() {
         // Clean up
         // Clean up
-        this.serviceRegistry.uiServices.removePluginMenuItemSource("TypeScript");
+        if (this.isTypescriptProject) {
+            this.serviceRegistry.uiServices.removePluginMenuItemSource("TypeScript");
+        } else {
+            this.serviceRegistry.uiServices.removePluginMenuItemSource("JavaScript");
+        }
+
         this.compileOnSaveMenuItem = null;
         this.compileOnSaveMenuItem = null;
         this.menuCreated = false;
         this.menuCreated = false;
         this.isTypescriptProject = false;
         this.isTypescriptProject = false;
@@ -362,47 +375,131 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
         WebView.WebBrowserHost.setGlobalStringProperty("TypeScriptLanguageExtension", "tsConfig", JSON.stringify(tsConfig));
         WebView.WebBrowserHost.setGlobalStringProperty("TypeScriptLanguageExtension", "tsConfig", JSON.stringify(tsConfig));
     }
     }
 
 
-    generateExternalProject () {
-      const projectDir = ToolCore.toolSystem.project.projectPath;
-
-      // Create the typings folder in project root
-      const projectDirTypings = Atomic.addTrailingSlash(projectDir + "typings/ambient/atomicgameengine");
-      Atomic.getFileSystem().createDir(projectDirTypings);
-
-      // Copy the Atomic.d.ts definition file to the typings folder
-      const toolDataDir = ToolCore.toolEnvironment.toolDataDir;
-      const typescriptSupportDir = Atomic.addTrailingSlash(toolDataDir + "TypeScriptSupport");
-      Atomic.getFileSystem().copy(typescriptSupportDir + "Atomic.d.ts", projectDirTypings + "Atomic.d.ts");
-
-      // Generate a tsconfig.json file
-      const tsconfigFile = new Atomic.File(projectDir + "tsconfig.json", Atomic.FileMode.FILE_WRITE);
-      let tsconfig = {
-        compilerOptions: defaultCompilerOptions
-      };
-
-      // Don't use fully qualified path in the persistent tsconfig file, just use a relative path from the tsconfig
-      tsconfig.compilerOptions["baseUrl"] = "./Resources";
-      tsconfig.compilerOptions.allowJs = false;
-
-      tsconfigFile.writeString(JSON.stringify(tsconfig, null, 4));
-      tsconfigFile.close();
-
-      // Build out the vscode tasks.json
-      const tasks = {
-         "command": "tsc",
-         "isShellCommand": true,
-         "args": ["-p", "."],
-         "showOutput": "always",
-         "problemMatcher": "$tsc"
-     };
-
-     const tasksDir = Atomic.addTrailingSlash(projectDir + ".vscode");
-     Atomic.fileSystem.createDir(tasksDir);
-     const tasksFile = new Atomic.File(tasksDir + "tasks.json", Atomic.FileMode.FILE_WRITE);
-     tasksFile.writeString(JSON.stringify(tasks, null, 4));
-     tasksFile.close();
+    generateExternalProject() {
+        const projectDir = ToolCore.toolSystem.project.projectPath;
+
+        // Create the typings folder in project root
+        const projectDirTypings = Atomic.addTrailingSlash(projectDir + "typings/ambient/atomicgameengine");
+        Atomic.getFileSystem().createDir(projectDirTypings);
+
+        // Copy the Atomic.d.ts definition file to the typings folder
+        const toolDataDir = ToolCore.toolEnvironment.toolDataDir;
+        const typescriptSupportDir = Atomic.addTrailingSlash(toolDataDir + "TypeScriptSupport");
+        Atomic.getFileSystem().copy(typescriptSupportDir + "Atomic.d.ts", projectDirTypings + "Atomic.d.ts");
+
+        // Generate a tsconfig.json file
+        if (this.isTypescriptProject) {
+            const tsconfigFile = new Atomic.File(projectDir + "tsconfig.json", Atomic.FileMode.FILE_WRITE);
+
+            let tsconfig = {
+                compilerOptions: getDefaultCompilerOptions()
+            };
+
+            // Don't use fully qualified path in the persistent tsconfig file, just use a relative path from the tsconfig
+            tsconfig.compilerOptions["baseUrl"] = "./Resources";
+            tsconfig.compilerOptions.allowJs = false;
+            tsconfig.compilerOptions.sourceMap = true;
+
+            tsconfigFile.writeString(JSON.stringify(tsconfig, null, 4));
+            tsconfigFile.close();
+        } else {
+            const jsconfigFile = new Atomic.File(projectDir + "jsconfig.json", Atomic.FileMode.FILE_WRITE);
+            let jsconfig = {
+                "compilerOptions": {
+                    "target": "es5"
+                }
+            };
+
+            // Don't use fully qualified path in the persistent jsconfig file, just use a relative path from the jsconfig
+            jsconfig.compilerOptions["baseUrl"] = "./Resources";
 
 
+            jsconfigFile.writeString(JSON.stringify(jsconfig, null, 4));
+            jsconfigFile.close();
+        }
+
+        this.generateVsCodeFiles();
     }
     }
+
+    /**
+     * Generates the extra vscode files for launching the debugger, the player, etc
+     */
+    generateVsCodeFiles() {
+        const projectDir = ToolCore.toolSystem.project.projectPath;
+        const vscodeDir = Atomic.addTrailingSlash(projectDir + ".vscode");
+        Atomic.fileSystem.createDir(vscodeDir);
+
+        // Build out the vscode tasks.json
+        const taskFile = {
+            "version": "0.1.0",
+            "tasks": []
+        };
+
+        if (this.isTypescriptProject) {
+            taskFile.tasks.push({
+                "taskName": "Build",
+                "command": "tsc",
+                "isShellCommand": true,
+                "args": [
+                    "-p",
+                    "."
+                ],
+                "showOutput": "always",
+                "problemMatcher": "$tsc",
+                "isBuildCommand": true
+            });
+        };
+
+        taskFile.tasks.push({
+            "taskName": "Debug Atomic Player",
+            "command": `${ToolCore.toolEnvironment.editorBinary}`,
+            "args": [
+                "--player",
+                "--debug",
+                "--project",
+                "${workspaceRoot}"
+            ],
+            "isBackground": true
+        });
+
+        taskFile.tasks.push({
+            "taskName": "Launch Atomic Player",
+            "command": `${ToolCore.toolEnvironment.editorBinary}`,
+            "args": [
+                "--player",
+                "--project",
+                "${workspaceRoot}"
+            ],
+            "isBackground": true
+        });
+
+        const tasksFile = new Atomic.File(vscodeDir + "tasks.json", Atomic.FileMode.FILE_WRITE);
+        tasksFile.writeString(JSON.stringify(taskFile, null, 4));
+        tasksFile.close();
+
+        // Build vscode launch.json
+        const launch = {
+            "version": "0.2.0",
+            "configurations": [
+                {
+                    "name": "Attach",
+                    "type": "duk",
+                    "request": "attach",
+                    "address": "localhost",
+                    "port": 9091,
+                    "localRoot": "${workspaceRoot}/Resources",
+                    "sourceMaps": this.isTypescriptProject, // turn on source maps if we are a TypeScript project
+                    "outDir": "${workspaceRoot}/Resources",
+                    "stopOnEntry": false,
+                    "debugLog": true
+                }
+            ]
+        };
+
+        const launchFile = new Atomic.File(vscodeDir + "launch.json", Atomic.FileMode.FILE_WRITE);
+        launchFile.writeString(JSON.stringify(launch, null, 4));
+        launchFile.close();
+    }
+
     /**
     /**
      * Perform a full compile of the TypeScript
      * Perform a full compile of the TypeScript
      */
      */
@@ -410,9 +507,9 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
         const editor = this.serviceRegistry.uiServices.getCurrentResourceEditor();
         const editor = this.serviceRegistry.uiServices.getCurrentResourceEditor();
         if (editor && editor.typeName == "JSResourceEditor" && this.isValidFiletype(editor.fullPath)) {
         if (editor && editor.typeName == "JSResourceEditor" && this.isValidFiletype(editor.fullPath)) {
             this.sendEvent(Editor.EditorModalEventData({
             this.sendEvent(Editor.EditorModalEventData({
-              type: Editor.EDITOR_MODALINFO,
-              title: "Compiling TypeScript",
-              message: "Compiling TypeScript..."
+                type: Editor.EDITOR_MODALINFO,
+                title: "Compiling TypeScript",
+                message: "Compiling TypeScript..."
             }));
             }));
 
 
             const jsEditor = <Editor.JSResourceEditor>editor;
             const jsEditor = <Editor.JSResourceEditor>editor;
@@ -501,15 +598,15 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
 
 
         if (errors) {
         if (errors) {
             this.sendEvent(Editor.EditorModalEventData({
             this.sendEvent(Editor.EditorModalEventData({
-              type: Editor.EDITOR_MODALINFO,
-              title: "Compiling TypeScript",
-              message: "Errors detected while compiling TypeScript."
+                type: Editor.EDITOR_MODALINFO,
+                title: "Compiling TypeScript",
+                message: "Errors detected while compiling TypeScript."
             }));
             }));
         } else {
         } else {
             this.sendEvent(Editor.EditorModalEventData({
             this.sendEvent(Editor.EditorModalEventData({
-              type: Editor.EDITOR_MODALINFO,
-              title: "Compiling TypeScript",
-              message: "Successfully compiled TypeScript."
+                type: Editor.EDITOR_MODALINFO,
+                title: "Compiling TypeScript",
+                message: "Successfully compiled TypeScript."
             }));
             }));
         }
         }
 
 
@@ -522,10 +619,10 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
             `Compilation Completed in ${results.duration}ms`
             `Compilation Completed in ${results.duration}ms`
         ].join("\n");
         ].join("\n");
 
 
-        let window = this.serviceRegistry.uiServices.showNonModalWindow("TypeScript Compilation Results", "AtomicEditor/editor/ui/typescriptresults.tb.txt", (ev:Atomic.UIWidgetEvent) => {
+        let window = this.serviceRegistry.uiServices.showNonModalWindow("TypeScript Compilation Results", "AtomicEditor/editor/ui/typescriptresults.tb.txt", (ev: Atomic.UIWidgetEvent) => {
             if (ev.type == Atomic.UI_EVENT_TYPE.UI_EVENT_TYPE_CLICK) {
             if (ev.type == Atomic.UI_EVENT_TYPE.UI_EVENT_TYPE_CLICK) {
                 if (ev.target.id == "close") {
                 if (ev.target.id == "close") {
-                  window.close();
+                    window.close();
                 } else {
                 } else {
                     let diag = links[ev.target.id];
                     let diag = links[ev.target.id];
                     if (diag) {
                     if (diag) {