Browse Source

Changes to integrate better with VSCode
- generate vscode tasks file for launching the player in debug mode or non-debug mode
- generate vscode for utilizing the duktape debugger vscode extension
- added a menu item to allow pure JavaScript projects to build out the vscode project for editing under vscode. Latest vscode actually reads d.ts files for completions, even for JavaScript projects
- modify the project based extension loader to not try to load .map files as extensions [bug]
- modify the project based extension loader to handle loading files that have the source map line on the last line of the file w/o an extra line feed

Shaddock Heath 8 years ago
parent
commit
31af81245b

+ 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
      */
     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();
                 if (id.indexOf(ProjectBasedExtensionLoader.duktapeRequirePrefix) == 0) {
                     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)) {
                             let include = new Atomic.File(path, Atomic.FileMode.FILE_READ);
                             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 {
                                 include.close();
                             }
@@ -107,7 +110,7 @@ export default class ProjectBasedExtensionLoader extends Atomic.ScriptObject imp
                 filenames.forEach((filename) => {
                     // 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
-                    if (filename.toLowerCase().lastIndexOf(".plugin.js") >= 0) {
+                    if (filename.search(/\.plugin.js$/i) != -1) {
                         var extensionPath = editorScriptsPath + filename;
                         extensionPath = extensionPath.substring(0, extensionPath.length - 3);
 

+ 143 - 68
Script/AtomicEditor/hostExtensions/languageExtensions/TypescriptLanguageExtension.ts

@@ -177,17 +177,24 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
      * Configures the project to be a Typescript Project
      * @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 +220,7 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
             // update ts config in case we have a new resource
             let tsConfig = this.buildTsConfig();
             this.setTsConfigOnWebView(tsConfig);
-
-            if (this.isTypescriptProject) {
-                this.configureTypescriptProjectMenu();
-            }
+            this.configureProjectMenu();
         }
     }
 
@@ -235,7 +239,7 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
                 console.log(`${this.name}: deleting corresponding .js file`);
                 ToolCore.assetDatabase.deleteAsset(jsFileAsset);
 
-                let eventData : Editor.EditorDeleteResourceNotificationEvent = {
+                let eventData: Editor.EditorDeleteResourceNotificationEvent = {
                     path: jsFile
                 };
 
@@ -300,8 +304,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
         if (Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.ts", Atomic.SCAN_FILES, true).length > 0) {
             this.isTypescriptProject = true;
-            this.configureTypescriptProjectMenu();
         };
+
+        this.configureProjectMenu();
     }
 
 
@@ -310,7 +315,12 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
      */
     projectUnloaded() {
         // 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.menuCreated = false;
         this.isTypescriptProject = false;
@@ -362,47 +372,112 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
         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: 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();
+        }
+
+        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 tasks = {
+            "version": "0.1.0",
+            "tasks": [
+                {
+                    "taskName": "Build",
+                    "command": "tsc",
+                    "isShellCommand": true,
+                    "args": [
+                        "-p",
+                        "."
+                    ],
+                    "showOutput": "always",
+                    "problemMatcher": "$tsc",
+                    "isBuildCommand": true
+                },
+                {
+                    "taskName": "Debug Atomic Player",
+                    "command": `${ToolCore.toolEnvironment.editorBinary}`,
+                    "args": [
+                        "--player",
+                        "--debug",
+                        "--project",
+                        "${workspaceRoot}"
+                    ],
+                    "isBackground": true
+                },
+                {
+                    "taskName": "Lauch 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(tasks, 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": false,
+                    "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
      */
@@ -410,9 +485,9 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
         const editor = this.serviceRegistry.uiServices.getCurrentResourceEditor();
         if (editor && editor.typeName == "JSResourceEditor" && this.isValidFiletype(editor.fullPath)) {
             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;
@@ -501,15 +576,15 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
 
         if (errors) {
             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 {
             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 +597,10 @@ export default class TypescriptLanguageExtension extends Atomic.ScriptObject imp
             `Compilation Completed in ${results.duration}ms`
         ].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.target.id == "close") {
-                  window.close();
+                    window.close();
                 } else {
                     let diag = links[ev.target.id];
                     if (diag) {