Browse Source

Merge pull request #614 from shaddockh/TSH-ATOMIC-TYPESCRIPT

[WIP] Typescript Compilation + Exploring editor extensions
JoshEngebretson 9 years ago
parent
commit
518fef5042
56 changed files with 6038 additions and 828 deletions
  1. 11 0
      Build/Scripts/BuildCommon.js
  2. 220 170
      Build/node_modules/typescript/lib/tsc.js
  3. 277 168
      Build/node_modules/typescript/lib/tsserver.js
  4. 7 8
      Build/node_modules/typescript/lib/typescript.d.ts
  5. 276 171
      Build/node_modules/typescript/lib/typescript.js
  6. 7 8
      Build/node_modules/typescript/lib/typescriptServices.d.ts
  7. 276 171
      Build/node_modules/typescript/lib/typescriptServices.js
  8. 19 15
      Build/node_modules/typescript/package.json
  9. 3 3
      Build/npm-shrinkwrap.json
  10. 3 2
      Build/package.json
  11. 1 1
      Data/AtomicEditor/CodeEditor/.gitignore
  12. 14 3
      Data/AtomicEditor/CodeEditor/Editor.html
  13. 3 0
      Data/AtomicEditor/CodeEditor/source/systemjs/system.js
  14. 1 0
      Data/AtomicEditor/TypeScriptSupport/.gitignore
  15. 9 0
      Data/AtomicEditor/TypeScriptSupport/duktape.d.ts
  16. 7 2
      Script/AtomicEditor/editor/Editor.ts
  17. 40 3
      Script/AtomicEditor/editor/EditorEvents.ts
  18. 171 0
      Script/AtomicEditor/hostExtensions/HostExtensionServices.ts
  19. 72 0
      Script/AtomicEditor/hostExtensions/ServiceLocator.ts
  20. 185 0
      Script/AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts
  21. 6 1
      Script/AtomicEditor/ui/frames/MainFrame.ts
  22. 1 1
      Script/AtomicEditor/ui/frames/ProjectFrame.ts
  23. 26 1
      Script/AtomicEditor/ui/frames/ResourceFrame.ts
  24. 1 1
      Script/AtomicEditor/ui/frames/inspector/InspectorFrame.ts
  25. 1 1
      Script/AtomicEditor/ui/modal/CreateProject.ts
  26. 26 4
      Script/AtomicEditor/ui/modal/UIResourceOps.ts
  27. 18 0
      Script/AtomicWebViewEditor/clientExtensions/ClientExtensionEventNames.ts
  28. 169 0
      Script/AtomicWebViewEditor/clientExtensions/ClientExtensionServices.ts
  29. 74 0
      Script/AtomicWebViewEditor/clientExtensions/ServiceLocator.ts
  30. 50 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/javascript/JavascriptLanguageExtension.ts
  31. 268 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/TypescriptLanguageExtension.ts
  32. 353 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageService.ts
  33. 347 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageServiceWebWorker.ts
  34. 45 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerLoader.ts
  35. 60 0
      Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerProcessTypes.ts
  36. 7 0
      Script/AtomicWebViewEditor/editor/editor.ts
  37. 94 0
      Script/AtomicWebViewEditor/editor/editorCommands.ts
  38. 0 20
      Script/AtomicWebViewEditor/editor/editorConfig.ts
  39. 179 50
      Script/AtomicWebViewEditor/interop.ts
  40. 2167 0
      Script/AtomicWebViewEditor/modules/typescript.d.ts
  41. 25 4
      Script/AtomicWebViewEditor/tsconfig.json
  42. 0 5
      Script/AtomicWebViewEditor/typings/AtomicQuery.d.ts
  43. 19 0
      Script/AtomicWebViewEditor/typings/WindowExt.d.ts
  44. 21 0
      Script/AtomicWebViewEditor/typings/systemjs.d.ts
  45. 29 0
      Script/AtomicWebViewEditor/typings/webworkers.d.ts
  46. 310 0
      Script/TypeScript/EditorWork.d.ts
  47. 13 2
      Script/tsconfig.json
  48. 0 2
      Script/tslint.json
  49. 21 0
      Source/Atomic/Resource/ResourceEvents.h
  50. 2 0
      Source/AtomicEditor/EditorMode/AEEditorMode.cpp
  51. 71 6
      Source/AtomicEditor/Editors/JSResourceEditor.cpp
  52. 5 1
      Source/AtomicEditor/Editors/JSResourceEditor.h
  53. 17 1
      Source/AtomicEditor/Editors/ResourceEditor.cpp
  54. 4 1
      Source/AtomicEditor/Editors/ResourceEditor.h
  55. 3 0
      Source/AtomicWebView/WebBrowserHost.cpp
  56. 4 2
      Source/AtomicWebView/WebSchemeHandler.cpp

+ 11 - 0
Build/Scripts/BuildCommon.js

@@ -88,6 +88,17 @@ namespace('build', function() {
             console.log("\n\nLint: Typescript linting complete.\n\n");
             jake.exec(cmds, function() {
 
+                // copy some external dependencies into the editor modules directory
+               var editorModulesDir = "./Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts/AtomicEditor/modules";
+               var webeditorModulesDir = "./Data/AtomicEditor/CodeEditor/source/editorCore/modules";
+               var nodeModulesDir = "./Build/node_modules";
+               fs.mkdirsSync(editorModulesDir);
+               // TypeScript
+               fs.copySync(nodeModulesDir + "/typescript/lib/typescript.js", webeditorModulesDir + "/typescript.js")
+
+               // copy lib.core.d.ts into the tool data directory
+               fs.mkdirsSync("./Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts/AtomicEditor/TypeScriptSupport");
+               fs.copySync("./Build/node_modules/typescript/lib/lib.core.d.ts","./Data/AtomicEditor/TypeScriptSupport/lib.core.d.ts")
                complete();
 
             }, {

File diff suppressed because it is too large
+ 220 - 170
Build/node_modules/typescript/lib/tsc.js


File diff suppressed because it is too large
+ 277 - 168
Build/node_modules/typescript/lib/tsserver.js


+ 7 - 8
Build/node_modules/typescript/lib/typescript.d.ts

@@ -1178,7 +1178,8 @@ declare namespace ts {
         buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void;
         buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
-        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags): void;
+        buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
@@ -1218,17 +1219,18 @@ declare namespace ts {
         This = 0,
         Identifier = 1,
     }
-    interface TypePredicate {
+    interface TypePredicateBase {
         kind: TypePredicateKind;
         type: Type;
     }
-    interface ThisTypePredicate extends TypePredicate {
+    interface ThisTypePredicate extends TypePredicateBase {
         _thisTypePredicateBrand: any;
     }
-    interface IdentifierTypePredicate extends TypePredicate {
+    interface IdentifierTypePredicate extends TypePredicateBase {
         parameterName: string;
         parameterIndex: number;
     }
+    type TypePredicate = IdentifierTypePredicate | ThisTypePredicate;
     enum SymbolFlags {
         None = 0,
         FunctionScopedVariable = 1,
@@ -1329,7 +1331,6 @@ declare namespace ts {
         ESSymbol = 16777216,
         ThisType = 33554432,
         ObjectLiteralPatternWithComputedProperties = 67108864,
-        PredicateType = 134217728,
         StringLike = 258,
         NumberLike = 132,
         ObjectType = 80896,
@@ -1342,9 +1343,6 @@ declare namespace ts {
         symbol?: Symbol;
         pattern?: DestructuringPattern;
     }
-    interface PredicateType extends Type {
-        predicate: ThisTypePredicate | IdentifierTypePredicate;
-    }
     interface StringLiteralType extends Type {
         text: string;
     }
@@ -1479,6 +1477,7 @@ declare namespace ts {
         forceConsistentCasingInFileNames?: boolean;
         allowSyntheticDefaultImports?: boolean;
         allowJs?: boolean;
+        noImplicitUseStrict?: boolean;
         [option: string]: string | number | boolean;
     }
     enum ModuleKind {

File diff suppressed because it is too large
+ 276 - 171
Build/node_modules/typescript/lib/typescript.js


+ 7 - 8
Build/node_modules/typescript/lib/typescriptServices.d.ts

@@ -1178,7 +1178,8 @@ declare namespace ts {
         buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void;
         buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
-        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags): void;
+        buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
         buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
@@ -1218,17 +1219,18 @@ declare namespace ts {
         This = 0,
         Identifier = 1,
     }
-    interface TypePredicate {
+    interface TypePredicateBase {
         kind: TypePredicateKind;
         type: Type;
     }
-    interface ThisTypePredicate extends TypePredicate {
+    interface ThisTypePredicate extends TypePredicateBase {
         _thisTypePredicateBrand: any;
     }
-    interface IdentifierTypePredicate extends TypePredicate {
+    interface IdentifierTypePredicate extends TypePredicateBase {
         parameterName: string;
         parameterIndex: number;
     }
+    type TypePredicate = IdentifierTypePredicate | ThisTypePredicate;
     enum SymbolFlags {
         None = 0,
         FunctionScopedVariable = 1,
@@ -1329,7 +1331,6 @@ declare namespace ts {
         ESSymbol = 16777216,
         ThisType = 33554432,
         ObjectLiteralPatternWithComputedProperties = 67108864,
-        PredicateType = 134217728,
         StringLike = 258,
         NumberLike = 132,
         ObjectType = 80896,
@@ -1342,9 +1343,6 @@ declare namespace ts {
         symbol?: Symbol;
         pattern?: DestructuringPattern;
     }
-    interface PredicateType extends Type {
-        predicate: ThisTypePredicate | IdentifierTypePredicate;
-    }
     interface StringLiteralType extends Type {
         text: string;
     }
@@ -1479,6 +1477,7 @@ declare namespace ts {
         forceConsistentCasingInFileNames?: boolean;
         allowSyntheticDefaultImports?: boolean;
         allowJs?: boolean;
+        noImplicitUseStrict?: boolean;
         [option: string]: string | number | boolean;
     }
     enum ModuleKind {

File diff suppressed because it is too large
+ 276 - 171
Build/node_modules/typescript/lib/typescriptServices.js


+ 19 - 15
Build/node_modules/typescript/package.json

@@ -1,15 +1,19 @@
 {
   "_args": [
     [
-      "[email protected]",
-      "/Users/shaddockh/Development/AtomicProjects/AtomicGameEngineFork/AtomicGameEngine/Build"
+      "typescript@^1.8.0",
+      "/Users/shaddockh/Development/Atomic/AtomicFork/AtomicGameEngine/Build"
     ]
   ],
-  "_from": "typescript@>=1.8.0 <1.9.0",
-  "_id": "[email protected].0",
+  "_from": "typescript@>=1.8.0 <2.0.0",
+  "_id": "[email protected].2",
   "_inCache": true,
   "_installable": true,
   "_location": "/typescript",
+  "_npmOperationalInternal": {
+    "host": "packages-6-west.internal.npmjs.com",
+    "tmp": "tmp/typescript-1.8.2.tgz_1456180363305_0.6699868906289339"
+  },
   "_npmUser": {
     "email": "[email protected]",
     "name": "typescript"
@@ -18,20 +22,20 @@
   "_phantomChildren": {},
   "_requested": {
     "name": "typescript",
-    "raw": "[email protected]",
-    "rawSpec": "1.8",
+    "raw": "typescript@^1.8.0",
+    "rawSpec": "^1.8.0",
     "scope": null,
-    "spec": ">=1.8.0 <1.9.0",
+    "spec": ">=1.8.0 <2.0.0",
     "type": "range"
   },
   "_requiredBy": [
     "/"
   ],
-  "_resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.0.tgz",
-  "_shasum": "cc5bc63d7f7d84ea26debd7adb774c0362b0ec11",
+  "_resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.2.tgz",
+  "_shasum": "4d2ad7db172be67a913d09862b510133bad61b33",
   "_shrinkwrap": null,
-  "_spec": "[email protected]",
-  "_where": "/Users/shaddockh/Development/AtomicProjects/AtomicGameEngineFork/AtomicGameEngine/Build",
+  "_spec": "typescript@^1.8.0",
+  "_where": "/Users/shaddockh/Development/Atomic/AtomicFork/AtomicGameEngine/Build",
   "author": {
     "name": "Microsoft Corp."
   },
@@ -63,13 +67,13 @@
   },
   "directories": {},
   "dist": {
-    "shasum": "cc5bc63d7f7d84ea26debd7adb774c0362b0ec11",
-    "tarball": "http://registry.npmjs.org/typescript/-/typescript-1.8.0.tgz"
+    "shasum": "4d2ad7db172be67a913d09862b510133bad61b33",
+    "tarball": "http://registry.npmjs.org/typescript/-/typescript-1.8.2.tgz"
   },
   "engines": {
     "node": ">=0.8.0"
   },
-  "gitHead": "a288b84632c1400806df55025ca6b568cfa4d00e",
+  "gitHead": "e5dd34f9e69f517182abfc996a10b8312b14e015",
   "homepage": "http://typescriptlang.org/",
   "keywords": [
     "Microsoft",
@@ -105,5 +109,5 @@
     "test": "jake runtests"
   },
   "typings": "./lib/typescript.d.ts",
-  "version": "1.8.0"
+  "version": "1.8.2"
 }

+ 3 - 3
Build/npm-shrinkwrap.json

@@ -163,9 +163,9 @@
       "resolved": "https://registry.npmjs.org/tslint/-/tslint-3.2.1.tgz"
     },
     "typescript": {
-      "version": "1.8.0",
-      "from": "typescript@>=1.8.0 <1.9.0",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.0.tgz"
+      "version": "1.8.2",
+      "from": "typescript@>=1.8.0 <2.0.0",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.8.2.tgz"
     },
     "underscore.string": {
       "version": "3.1.1",

+ 3 - 2
Build/package.json

@@ -5,6 +5,7 @@
     "glob": "^6.0.3",
     "jake": "^8.0.12",
     "tslint": "^3.2.0",
-    "typescript": "^1.8.0"
-  }
+    "typescript": "^1.8.2"
+  },
+  "devDependencies": {}
 }

+ 1 - 1
Data/AtomicEditor/CodeEditor/.gitignore

@@ -1 +1 @@
-editor.bundle.*
+source/editorCore

+ 14 - 3
Data/AtomicEditor/CodeEditor/Editor.html

@@ -25,10 +25,21 @@
 
   <script src="./source/ace/ace.js" type="text/javascript" charset="utf-8"></script>
   <script src="./source/ace/ext-language_tools.js"></script>
-  <script src="./source/systemjs/system-register-only.js" type="text/javascript" charset="utf-8"></script>
-  <script src="./editor.bundle.js" type="text/javascript" charset="utf-8"></script>
+  <script src="./source/systemjs/system.js" type="text/javascript" charset="utf-8"></script>
   <script>
-    System.import('./interop');
+    System.config({
+        "baseURL": "/",
+        "defaultJSExtensions": true,
+        // TODO: figure out how to make this be loaded in by the extension instead of being hard-coded in the html page
+        meta: {
+        './source/editorCore/modules/typescript.js': {
+          format: 'global',
+          exports: 'ts',
+        }
+      }
+    });
+
+    System.import('./source/editorCore/interop');
   </script>
 
 </body>

File diff suppressed because it is too large
+ 3 - 0
Data/AtomicEditor/CodeEditor/source/systemjs/system.js


+ 1 - 0
Data/AtomicEditor/TypeScriptSupport/.gitignore

@@ -0,0 +1 @@
+lib.core.d.ts

+ 9 - 0
Data/AtomicEditor/TypeScriptSupport/duktape.d.ts

@@ -0,0 +1,9 @@
+// Duktape built-ins
+
+// extracted from lib.d.ts
+declare interface Console {
+	log(message?: any, ...optionalParams: any[]): void;
+}
+
+declare var console:Console;
+

+ 7 - 2
Script/AtomicEditor/editor/Editor.ts

@@ -63,7 +63,7 @@ class Editor extends Atomic.ScriptObject {
 
         this.subscribeToEvent(EditorEvents.LoadProject, (data) => this.handleEditorLoadProject(data));
         this.subscribeToEvent(EditorEvents.CloseProject, (data) => this.handleEditorCloseProject(data));
-        this.subscribeToEvent("ProjectUnloaded", (data) => {
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => {
             Atomic.graphics.windowTitle = "AtomicEditor";
             this.handleProjectUnloaded(data);
         });
@@ -149,7 +149,11 @@ class Editor extends Atomic.ScriptObject {
             return false;
 
         }
-        return system.loadProject(event.path);
+        const loaded = system.loadProject(event.path);
+        if (loaded) {
+            this.sendEvent(EditorEvents.LoadProjectNotification, event);
+        }
+        return loaded;
     }
 
     closeAllResourceEditors() {
@@ -171,6 +175,7 @@ class Editor extends Atomic.ScriptObject {
 
     handleEditorCloseProject(event) {
         this.projectCloseRequested = true;
+        this.sendEvent(EditorEvents.ProjectUnloadedNotification, event);
         this.closeAllResourceEditors();
     }
 

+ 40 - 3
Script/AtomicEditor/editor/EditorEvents.ts

@@ -20,6 +20,7 @@
 // THE SOFTWARE.
 //
 
+// TODO: migrate these interfaces out to the d.ts and migrate the static strings to some common location
 export const ModalError = "ModalError";
 export interface ModalErrorEvent {
 
@@ -27,7 +28,7 @@ export interface ModalErrorEvent {
   message: string;
 
 }
-
+export const PlayerStartRequest = "EditorPlayRequest";
 export const PlayerStarted = "EditorPlayerStarted";
 export const PlayerStopped = "EditorPlayerStopped";
 export const PlayerPaused = "EditorPlayerPaused";
@@ -65,8 +66,10 @@ export interface ContentFolderChangedEvent {
 
 export const CloseProject = "EditorCloseProject";
 export const ProjectClosed = "EditorProjectClosed";
+export const ProjectUnloadedNotification = "ProjectUnloadedNotification";
 
 export const LoadProject = "EditorLoadProject";
+export const LoadProjectNotification = "EditorLoadProjectNotification";
 export interface LoadProjectEvent {
 
   // The full path to the .atomic file
@@ -77,10 +80,14 @@ export interface LoadProjectEvent {
 export const SaveAllResources = "EditorSaveAllResources";
 
 export const SaveResource = "EditorSaveResource";
+/**
+ * Called once the resource has been saved
+ * @type {String}
+ */
+export const SaveResourceNotification = "EditorSaveResourceNotification";
 export interface SaveResourceEvent {
 
-  // The full path to the resource to save
-  // empty or undefined for current
+  // The full path to the resource to save / empty or undefined for current
   path: string;
 
 }
@@ -101,6 +108,36 @@ export interface EditResourceEvent {
 
 }
 
+export const DeleteResource = "EditorDeleteResource";
+/**
+ * Called once the resource has been deleted
+ * @type {String}
+ */
+export const DeleteResourceNotification = "DeleteResourceNotification";
+export interface DeleteResourceEvent {
+
+  // The full path to the resource to edit
+  path: string;
+
+}
+
+export const RenameResource = "EditorRenameResource";
+/**
+ * Called once the resource has been renamed
+ * @type {String}
+ */
+export const RenameResourceNotification = "RenameResourceNotification";
+export interface RenameResourceEvent {
+
+  // The full path to the resource to edit
+  path: string;
+  newPath: string;
+  newName: string;
+
+  // the asset to delete
+  asset: ToolCore.Asset;
+}
+
 export const SceneEditStateChange = "SceneEditStateChange";
 export interface SceneEditStateChangeEvent {
 

+ 171 - 0
Script/AtomicEditor/hostExtensions/HostExtensionServices.ts

@@ -0,0 +1,171 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+import * as EditorEvents from "../editor/EditorEvents";
+import * as EditorUI from "../ui/EditorUI";
+
+
+/**
+ * Generic registry for storing Editor Extension Services
+ */
+class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Editor.Extensions.ServiceRegistry<T> {
+    registeredServices: T[] = [];
+
+    /**
+     * Adds a service to the registered services list for this type of service
+     * @param  {T}      service the service to register
+     */
+    register(service: T) {
+        this.registeredServices.push(service);
+    }
+
+}
+
+interface ServiceEventSubscriber {
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
+     */
+    subscribeToEvents(topLevelWindow: Atomic.UIWidget);
+}
+
+/**
+ * Registry for service extensions that are concerned about project events
+ */
+export class ProjectServiceRegistry extends ServiceRegistry<Editor.HostExtensions.ProjectService> implements ServiceEventSubscriber {
+    constructor() {
+        super();
+    }
+
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
+     */
+    subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
+        eventDispatcher.subscribeToEvent(EditorEvents.LoadProjectNotification, (ev) => this.projectLoaded(ev));
+        eventDispatcher.subscribeToEvent(EditorEvents.CloseProject, (ev) => this.projectUnloaded(ev));
+        eventDispatcher.subscribeToEvent(EditorEvents.PlayerStartRequest, () => this.playerStarted());
+    }
+
+    /**
+     * Called when the project is unloaded
+     * @param  {[type]} data Event info from the project unloaded event
+     */
+    projectUnloaded(data) {
+        this.registeredServices.forEach((service) => {
+            // Notify services that the project has been unloaded
+            try {
+                if (service.projectUnloaded) {
+                    service.projectUnloaded();
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when the project is loaded
+     * @param  {[type]} data Event info from the project unloaded event
+     */
+    projectLoaded(ev: Editor.EditorEvents.LoadProjectEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Notify services that the project has just been loaded
+                if (service.projectLoaded) {
+                    service.projectLoaded(ev);
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    playerStarted() {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Notify services that the project has just been loaded
+                if (service.playerStarted) {
+                    service.playerStarted();
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+}
+
+/**
+ * Registry for service extensions that are concerned about Resources
+ */
+export class ResourceServiceRegistry extends ServiceRegistry<Editor.HostExtensions.ResourceService> {
+    constructor() {
+        super();
+    }
+
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {Atomic.UIWidget} topLevelWindow The top level window that will be receiving these events
+     */
+    subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
+        eventDispatcher.subscribeToEvent(EditorEvents.SaveResourceNotification, (ev) => this.saveResource(ev));
+        eventDispatcher.subscribeToEvent(EditorEvents.DeleteResourceNotification, (ev) => this.deleteResource(ev));
+        eventDispatcher.subscribeToEvent(EditorEvents.RenameResourceNotification, (ev) => this.renameResource(ev));
+    }
+
+    /**
+     * Called after a resource has been saved
+     * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+     */
+    saveResource(ev: Editor.EditorEvents.SaveResourceEvent) {
+        // run through and find any services that can handle this.
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can save
+                if (service.save) {
+                    service.save(ev);
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when a resource has been deleted
+     */
+    deleteResource(ev: Editor.EditorEvents.DeleteResourceEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can delete
+                if (service.delete) {
+                    service.delete(ev);
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n ${e}\n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when a resource has been renamed
+     * @param  {Editor.EditorEvents.RenameResourceEvent} ev
+     */
+    renameResource(ev: Editor.EditorEvents.RenameResourceEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can handle the rename
+                if (service.rename) {
+                    service.rename(ev);
+                }
+            } catch (e) {
+                EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+}

+ 72 - 0
Script/AtomicEditor/hostExtensions/ServiceLocator.ts

@@ -0,0 +1,72 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+import * as HostExtensionServices from "./HostExtensionServices";
+import * as EditorUI from "../ui/EditorUI";
+import TypescriptLanguageExtension from "./languageExtensions/TypscriptLanguageExtension";
+
+/**
+ * Generic service locator of editor services that may be injected by either a plugin
+ * or by the editor itself.
+ */
+export class ServiceLocatorType implements Editor.HostExtensions.HostServiceLocator {
+
+    constructor() {
+        this.resourceServices = new HostExtensionServices.ResourceServiceRegistry();
+        this.projectServices = new HostExtensionServices.ProjectServiceRegistry();
+    }
+
+    private eventDispatcher: Atomic.UIWidget = null;
+
+    resourceServices: HostExtensionServices.ResourceServiceRegistry;
+    projectServices: HostExtensionServices.ProjectServiceRegistry;
+
+    loadService(service: Editor.HostExtensions.HostEditorService) {
+        try {
+            service.initialize(this);
+        } catch (e) {
+            EditorUI.showModalError("Extension Error", `Error detected in extension ${service.name}\n \n ${e.stack}`);
+        }
+    }
+
+    /**
+     * This is where the top level window will allow the service locator to listen for events and act on them.
+     * @param  {Atomic.UIWidget} frame
+     */
+    subscribeToEvents(frame: Atomic.UIWidget) {
+        this.eventDispatcher = frame;
+        this.resourceServices.subscribeToEvents(this);
+        this.projectServices.subscribeToEvents(this);
+    }
+
+    /**
+     * Send a custom event.  This can be used by services to publish custom events
+     * @param  {string} eventType
+     * @param  {any} data
+     */
+    sendEvent(eventType: string, data: any) {
+        if (this.eventDispatcher) {
+            this.eventDispatcher.sendEvent(eventType, data);
+        }
+    }
+
+    /**
+     * Subscribe to an event and provide a callback.  This can be used by services to subscribe to custom events
+     * @param  {string} eventType
+     * @param  {any} callback
+     */
+    subscribeToEvent(eventType: string, callback: (data: any) => void) {
+        if (this.eventDispatcher) {
+            this.eventDispatcher.subscribeToEvent(eventType, callback);
+        }
+    }
+}
+// Singleton service locator that can be referenced
+const serviceLocator = new ServiceLocatorType();
+export default serviceLocator;
+
+// Load up all the internal services
+serviceLocator.loadService(new TypescriptLanguageExtension());

+ 185 - 0
Script/AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts

@@ -0,0 +1,185 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+import * as EditorEvents from "../../editor/EditorEvents";
+
+/**
+ * Resource extension that supports the web view typescript extension
+ */
+export default class TypescriptLanguageExtension implements Editor.HostExtensions.ResourceService, Editor.HostExtensions.ProjectService {
+    name: string = "HostTypeScriptLanguageExtension";
+    description: string = "This service supports the typscript webview extension.";
+
+    /**
+     * Indicates if this project contains typescript files.
+     * @type {Boolean}
+     */
+    private isTypescriptProject = false;
+    private serviceRegistry: Editor.HostExtensions.HostServiceLocator = null;
+    /**
+     * Determines if the file name/path provided is something we care about
+     * @param  {string} path
+     * @return {boolean}
+     */
+    private isValidFiletype(path: string): boolean {
+        if (this.isTypescriptProject) {
+            const ext = Atomic.getExtension(path);
+            if (ext == ".ts") {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Seed the language service with all of the relevant files in the project.  This updates the tsconifg.atomic file in
+     * the root of the resources directory.
+     */
+    private loadProjectFiles() {
+        let projectFiles: Array<string> = [];
+
+        //scan all the files in the project for any typescript files so we can determine if this is a typescript project
+        Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.ts", Atomic.SCAN_FILES, true).forEach(filename => {
+            projectFiles.push(Atomic.addTrailingSlash(ToolCore.toolSystem.project.resourcePath) + filename);
+            this.isTypescriptProject = true;
+        });
+
+        // only build out a tsconfig.atomic if we actually have typescript files in the project
+        if (this.isTypescriptProject) {
+            // First we need to load in a copy of the lib.core.d.ts that is necessary for the hosted typescript compiler
+            projectFiles.push(Atomic.addTrailingSlash(Atomic.addTrailingSlash(ToolCore.toolEnvironment.toolDataDir) + "TypeScriptSupport") + "lib.core.d.ts");
+
+            // Load up a copy of the duktape.d.ts
+            projectFiles.push(Atomic.addTrailingSlash(Atomic.addTrailingSlash(ToolCore.toolEnvironment.toolDataDir) + "TypeScriptSupport") + "duktape.d.ts");
+
+
+            // Look in a 'typings' directory for any typescript definition files
+            const typingsDir = Atomic.addTrailingSlash(ToolCore.toolSystem.project.projectPath) + "typings";
+            Atomic.fileSystem.scanDir(typingsDir, "*.d.ts", Atomic.SCAN_FILES, true).forEach(filename => {
+                projectFiles.push(Atomic.addTrailingSlash(typingsDir) + filename);
+            });
+
+            let files = projectFiles.map((f: string) => {
+                if (f.indexOf(ToolCore.toolSystem.project.resourcePath) != -1) {
+                    // if we are in the resources directory, just pass back the path from resources down
+                    return f.replace(Atomic.addTrailingSlash(ToolCore.toolSystem.project.projectPath), "");
+                } else {
+                    // otherwise return the full path
+                    return f;
+                }
+            });
+
+            let tsConfig = {
+                files: files
+            };
+
+            let filename = Atomic.addTrailingSlash(ToolCore.toolSystem.project.resourcePath) + "tsconfig.atomic";
+            let script = new Atomic.File(filename, Atomic.FILE_WRITE);
+            try {
+                script.writeString(JSON.stringify(tsConfig));
+                script.flush();
+            } finally {
+                script.close();
+            }
+        }
+    }
+
+    /**
+     * Inject this language service into the registry
+     * @return {[type]}             True if successful
+     */
+    initialize(serviceRegistry: Editor.HostExtensions.HostServiceLocator) {
+        // We care about both resource events as well as project events
+        serviceRegistry.resourceServices.register(this);
+        serviceRegistry.projectServices.register(this);
+        this.serviceRegistry = serviceRegistry;
+    }
+
+    /**
+     * Handle the delete.  This should delete the corresponding javascript file
+     * @param  {Editor.EditorEvents.DeleteResourceEvent} ev
+     */
+    delete(ev: Editor.EditorEvents.DeleteResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+            console.log(`${this.name}: received a delete resource event`);
+
+            // Delete the corresponding js file
+            let jsFile = ev.path.replace(/\.ts$/, ".js");
+            let jsFileAsset = ToolCore.assetDatabase.getAssetByPath(jsFile);
+            if (jsFileAsset) {
+                console.log(`${this.name}: deleting corresponding .js file`);
+                ToolCore.assetDatabase.deleteAsset(jsFileAsset);
+
+                let eventData: EditorEvents.DeleteResourceEvent = {
+                    path: jsFile
+                };
+
+                this.serviceRegistry.sendEvent(EditorEvents.DeleteResourceNotification, eventData);
+                
+                // rebuild the tsconfig.atomic
+                this.loadProjectFiles();
+            }
+        }
+    }
+
+    /**
+     * Handle the rename.  Should rename the corresponding .js file
+     * @param  {Editor.EditorEvents.RenameResourceEvent} ev
+     */
+    rename(ev: Editor.EditorEvents.RenameResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+            console.log(`${this.name}: received a rename resource event`);
+
+            // Rename the corresponding js file
+            let jsFile = ev.path.replace(/\.ts$/, ".js");
+            let jsFileNew = ev.newPath.replace(/\.ts$/, ".js"); // rename doesn't want extension
+            let jsFileAsset = ToolCore.assetDatabase.getAssetByPath(jsFile);
+            if (jsFileAsset) {
+                console.log(`${this.name}: renaming corresponding .js file`);
+                jsFileAsset.rename(ev.newName);
+
+                let eventData: EditorEvents.RenameResourceEvent = {
+                    path: jsFile,
+                    newPath: jsFileNew,
+                    newName: ev.newName,
+                    asset: jsFileAsset
+                };
+
+                this.serviceRegistry.sendEvent(EditorEvents.RenameResourceNotification, eventData);
+
+                // rebuild the tsconfig.atomic
+                this.loadProjectFiles();
+            }
+        }
+    }
+
+    /**
+     * Handles the save event and detects if a typescript file has been added to a non-typescript project
+     * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+     * @return {[type]}
+     */
+    save(ev: Editor.EditorEvents.SaveResourceEvent) {
+        // let's check to see if we have created a typescript file
+        if (!this.isTypescriptProject) {
+            if (Atomic.getExtension(ev.path) == ".ts") {
+                this.isTypescriptProject = true;
+                this.loadProjectFiles();
+            }
+        }
+    }
+
+    /*** ProjectService implementation ****/
+
+    /**
+     * Called when the project is being loaded to allow the typscript language service to reset and
+     * possibly compile
+     */
+    projectLoaded(ev: Editor.EditorEvents.LoadProjectEvent) {
+        // got a load, we need to reset the language service
+        console.log(`${this.name}: received a project loaded event for project at ${ev.path}`);
+        this.loadProjectFiles();
+    }
+}

+ 6 - 1
Script/AtomicEditor/ui/frames/MainFrame.ts

@@ -33,6 +33,8 @@ import ScriptWidget = require("ui/ScriptWidget");
 import MainFrameMenu = require("./menus/MainFrameMenu");
 
 import MenuItemSources = require("./menus/MenuItemSources");
+import ServiceLocator from "../../hostExtensions/ServiceLocator";
+import * as EditorEvents from "../../editor/EditorEvents";
 
 class MainFrame extends ScriptWidget {
 
@@ -68,11 +70,14 @@ class MainFrame extends ScriptWidget {
             this.enableProjectMenus();
         });
 
-        this.subscribeToEvent("ProjectUnloaded", (data) => {
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => {
             this.showWelcomeFrame(true);
             this.disableProjectMenus();
         });
 
+        // Allow the service locator to hook into the event system
+        ServiceLocator.subscribeToEvents(this);
+
         this.showWelcomeFrame(true);
 
     }

+ 1 - 1
Script/AtomicEditor/ui/frames/ProjectFrame.ts

@@ -69,7 +69,7 @@ class ProjectFrame extends ScriptWidget {
 
         // events
         this.subscribeToEvent("ProjectLoaded", (data) => this.handleProjectLoaded(data));
-        this.subscribeToEvent("ProjectUnloaded", (data) => this.handleProjectUnloaded(data));
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => this.handleProjectUnloaded(data));
         this.subscribeToEvent("DragEnded", (data: Atomic.DragEndedEvent) => this.handleDragEnded(data));
 
         this.subscribeToEvent("ResourceAdded", (ev: ToolCore.ResourceAddedEvent) => this.handleResourceAdded(ev));

+ 26 - 1
Script/AtomicEditor/ui/frames/ResourceFrame.ts

@@ -55,14 +55,29 @@ class ResourceFrame extends ScriptWidget {
 
         if (this.currentResourceEditor) {
             this.currentResourceEditor.save();
+            // Grab the path to this file and pass it to the save resource
+            this.sendEvent(EditorEvents.SaveResourceNotification, {
+                path: ev.path || this.currentResourceEditor.fullPath
+            });
         }
 
     }
 
+    handleDeleteResource(ev: EditorEvents.DeleteResourceEvent) {
+        var editor = this.editors[ev.path];
+        if (editor) {
+            editor.close(true);
+            delete this.editors[ev.path];
+        }
+    }
+
     handleSaveAllResources(data) {
 
         for (var i in this.editors) {
             this.editors[i].save();
+            this.sendEvent(EditorEvents.SaveResourceNotification, {
+                path: this.editors[i].fullPath
+            });
         }
 
     }
@@ -203,6 +218,14 @@ class ResourceFrame extends ScriptWidget {
 
     }
 
+    handleRenameResource(ev:EditorEvents.RenameResourceEvent) {
+        var editor = this.editors[ev.path];
+        if (editor) {
+            this.editors[ev.newPath] = editor;
+            delete this.editors[ev.path];
+        }
+    }
+
     handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
 
         if (ev.type == Atomic.UI_EVENT_TYPE_TAB_CHANGED && ev.target == this.tabcontainer) {
@@ -268,11 +291,13 @@ class ResourceFrame extends ScriptWidget {
 
         this.resourceViewContainer.addChild(this);
 
-        this.subscribeToEvent("ProjectUnloaded", (data) => this.handleProjectUnloaded(data));
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => this.handleProjectUnloaded(data));
         this.subscribeToEvent(EditorEvents.EditResource, (data) => this.handleEditResource(data));
         this.subscribeToEvent(EditorEvents.SaveResource, (data) => this.handleSaveResource(data));
         this.subscribeToEvent(EditorEvents.SaveAllResources, (data) => this.handleSaveAllResources(data));
         this.subscribeToEvent(EditorEvents.EditorResourceClose, (ev: EditorEvents.EditorCloseResourceEvent) => this.handleCloseResource(ev));
+        this.subscribeToEvent(EditorEvents.RenameResourceNotification, (ev: EditorEvents.RenameResourceEvent) => this.handleRenameResource(ev));
+        this.subscribeToEvent(EditorEvents.DeleteResourceNotification, (data) => this.handleDeleteResource(data));
 
         this.subscribeToEvent(UIEvents.ResourceEditorChanged, (data) => this.handleResourceEditorChanged(data));
 

+ 1 - 1
Script/AtomicEditor/ui/frames/inspector/InspectorFrame.ts

@@ -54,7 +54,7 @@ class InspectorFrame extends ScriptWidget {
         var container = this.getWidget("inspectorcontainer");
 
         this.subscribeToEvent(EditorEvents.EditResource, (data) => this.handleEditResource(data));
-        this.subscribeToEvent("ProjectUnloaded", (data) => this.handleProjectUnloaded(data));
+        this.subscribeToEvent(EditorEvents.ProjectUnloadedNotification, (data) => this.handleProjectUnloaded(data));
 
         this.subscribeToEvent(EditorEvents.ActiveSceneEditorChange, (data) => this.handleActiveSceneEditorChanged(data));
 

+ 1 - 1
Script/AtomicEditor/ui/modal/CreateProject.ts

@@ -24,7 +24,7 @@ import EditorEvents = require("../../editor/EditorEvents");
 import EditorUI = require("../EditorUI");
 import ModalWindow = require("./ModalWindow");
 
-import ProjectTemplates = require("../resources/ProjectTemplates");
+import ProjectTemplates = require("../../resources/ProjectTemplates");
 
 class CreateProject extends ModalWindow {
 

+ 26 - 4
Script/AtomicEditor/ui/modal/UIResourceOps.ts

@@ -56,9 +56,15 @@ export class ResourceDelete extends ModalWindow {
 
                 this.hide();
 
+                let eventData = {
+                    path: this.asset.path
+                };
+
                 var db = ToolCore.getAssetDatabase();
                 db.deleteAsset(this.asset);
 
+                this.sendEvent(EditorEvents.DeleteResourceNotification, eventData);
+
                 return true;
             }
 
@@ -146,8 +152,10 @@ export class CreateComponent extends ModalWindow {
                 var componentName = this.nameField.text;
                 var outputFile = Atomic.addTrailingSlash(this.resourcePath) + componentName;
 
-                if (outputFile.indexOf(".js") == -1) outputFile += ".js";
-
+                // Check to see if we have a file extension.  If we don't then assume .js
+                if (outputFile.indexOf(".") == -1) {
+                    outputFile += ".js";
+                }
 
                 if (ResourceOps.CreateNewComponent(outputFile, componentName)) {
 
@@ -199,7 +207,10 @@ export class CreateScript extends ModalWindow {
                 var scriptName = this.nameField.text;
                 var outputFile = Atomic.addTrailingSlash(this.resourcePath) + scriptName;
 
-                if (outputFile.indexOf(".js") == -1) outputFile += ".js";
+                // Check to see if we have a file extension.  If we don't then assume .js
+                if (outputFile.indexOf(".") == -1) {
+                    outputFile += ".js";
+                }
 
 
                 if (ResourceOps.CreateNewScript(outputFile, scriptName)) {
@@ -364,9 +375,20 @@ export class RenameAsset extends ModalWindow {
 
                 this.hide();
 
-                if (this.asset.name != this.nameEdit.text)
+                if (this.asset.name != this.nameEdit.text) {
+                    let oldPath = this.asset.path;
                     this.asset.rename(this.nameEdit.text);
 
+                    let eventData: EditorEvents.RenameResourceEvent = {
+                        path: oldPath,
+                        newPath: this.asset.path,
+                        newName: this.nameEdit.text,
+                        asset: this.asset
+                    };
+
+                    this.sendEvent(EditorEvents.RenameResourceNotification, eventData);
+                }
+
                 return true;
             }
 

+ 18 - 0
Script/AtomicWebViewEditor/clientExtensions/ClientExtensionEventNames.ts

@@ -0,0 +1,18 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+/**
+ * All of the publicly available events that client extensions listen to
+ */
+export default class ClientExtensionEventNames {
+    static CodeLoadedEvent = "CodeLoadedEvent";
+    static ConfigureEditorEvent = "ConfigureEditorEvent";
+    static ResourceSavedEvent = "ResourceSavedEvent";
+    static ResourceRenamedEvent = "ResourceRenamedEvent";
+    static ResourceDeletedEvent = "ResourceDeletedEvent";
+    static ProjectUnloadedEvent = "ProjectUnloadedEvent";
+}

+ 169 - 0
Script/AtomicWebViewEditor/clientExtensions/ClientExtensionServices.ts

@@ -0,0 +1,169 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+import ClientExtensionEventNames from "./ClientExtensionEventNames";
+
+// Entry point for web view extensions -- extensions that live inside the web view
+interface EventSubscription {
+    eventName: string;
+    callback: (data: any) => any;
+}
+
+/**
+ * Implements an event dispatcher for the client services
+ */
+export class EventDispatcher implements Editor.Extensions.EventDispatcher {
+    private subscriptions: EventSubscription[] = [];
+
+    sendEvent(eventType: string, data: any) {
+        this.subscriptions.forEach(sub => {
+            if (sub.eventName == eventType) {
+                sub.callback(data);
+            }
+        });
+    }
+
+    subscribeToEvent(eventType, callback) {
+        this.subscriptions.push({
+            eventName: eventType,
+            callback: callback
+        });
+    }
+}
+
+
+/**
+ * Generic registry for storing Editor Extension Services
+ */
+class ServiceRegistry<T extends Editor.Extensions.EditorService> implements Editor.Extensions.ServiceRegistry<T> {
+    registeredServices: T[] = [];
+
+    /**
+     * Adds a service to the registered services list for this type of service
+     * @param  {T}      service the service to register
+     */
+    register(service: T) {
+        this.registeredServices.push(service);
+    }
+}
+
+export class ExtensionServiceRegistry extends ServiceRegistry<Editor.ClientExtensions.WebViewService> {
+
+    /**
+     * Allow this service registry to subscribe to events that it is interested in
+     * @param  {EventDispatcher} eventDispatcher The global event dispatcher
+     */
+    subscribeToEvents(eventDispatcher: Editor.Extensions.EventDispatcher) {
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.CodeLoadedEvent, (ev) => this.codeLoaded(ev));
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ConfigureEditorEvent, (ev) => this.configureEditor(ev));
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ResourceRenamedEvent, (ev) => this.renameResource(ev));
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ProjectUnloadedEvent, (ev) => this.projectUnloaded());
+        eventDispatcher.subscribeToEvent(ClientExtensionEventNames.ResourceDeletedEvent, (ev) => this.deleteResource(ev));
+    }
+
+    /**
+     * Called when code is loaded
+     * @param  {Editor.EditorEvents.CodeLoadedEvent} ev Event info about the file that is being loaded
+     */
+    codeLoaded(ev: Editor.EditorEvents.CodeLoadedEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Notify services that the project has just been loaded
+                if (service.codeLoaded) {
+                    service.codeLoaded(ev);
+                }
+            } catch (e) {
+                alert(`Extension Error:\n Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called after a resource has been saved
+     * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+     */
+    saveResource(ev: Editor.EditorEvents.SaveResourceEvent) {
+        // run through and find any services that can handle this.
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can save
+                if (service.save) {
+                    service.save(ev);
+                }
+            } catch (e) {
+                alert(`Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when a resource has been deleted
+     */
+    deleteResource(ev: Editor.EditorEvents.DeleteResourceEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can delete
+                if (service.delete) {
+                    service.delete(ev);
+                }
+            } catch (e) {
+                alert(`Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when a resource has been renamed
+     * @param  {Editor.EditorEvents.RenameResourceEvent} ev
+     */
+    renameResource(ev: Editor.EditorEvents.RenameResourceEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Verify that the service contains the appropriate methods and that it can handle the rename
+                if (service.rename) {
+                    service.rename(ev);
+                }
+            } catch (e) {
+                alert(`Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+    /**
+     * Called when the editor is requesting to be configured for a particular file
+     * @param  {Editor.EditorEvents.EditorFileEvent} ev
+     */
+    configureEditor(ev: Editor.EditorEvents.EditorFileEvent) {
+        this.registeredServices.forEach((service) => {
+            try {
+                // Notify services that the project has just been loaded
+                if (service.configureEditor) {
+                    service.configureEditor(ev);
+                }
+            } catch (e) {
+                alert(`Extension Error:\n Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+
+
+    /**
+     * Called when the project is unloaded
+     */
+    projectUnloaded() {
+        this.registeredServices.forEach((service) => {
+            // Notify services that the project has been unloaded
+            try {
+                if (service.projectUnloaded) {
+                    service.projectUnloaded();
+                }
+            } catch (e) {
+                alert(`Extension Error:\n Error detected in extension ${service.name}\n \n ${e.stack}`);
+            }
+        });
+    }
+}

+ 74 - 0
Script/AtomicWebViewEditor/clientExtensions/ServiceLocator.ts

@@ -0,0 +1,74 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+import HostInteropType from "../interop";
+import * as ClientExtensionServices from "./ClientExtensionServices";
+
+// Initialize and configure the extensions
+import tsExtension from "./languageExtensions/typescript/TypescriptLanguageExtension";
+import jsExtension from "./languageExtensions/javascript/JavascriptLanguageExtension";
+
+/**
+ * Generic service locator of editor services that may be injected by either a plugin
+ * or by the editor itself.
+ */
+export class ClientServiceLocatorType implements Editor.ClientExtensions.ClientServiceLocator {
+
+    constructor() {
+        this.services = new ClientExtensionServices.ExtensionServiceRegistry();
+        this.services.subscribeToEvents(this);
+    }
+
+    private services: ClientExtensionServices.ExtensionServiceRegistry;
+    private eventDispatcher: Editor.Extensions.EventDispatcher = new ClientExtensionServices.EventDispatcher();
+
+    /**
+     * Returns the Host Interop module
+     * @return {Editor.ClientExtensions.HostInterop}
+     */
+    getHostInterop(): Editor.ClientExtensions.HostInterop {
+        return HostInteropType.getInstance();
+    }
+
+    loadService(service: Editor.ClientExtensions.ClientEditorService) {
+        try {
+            this.services.register(service);
+            service.initialize(this);
+        } catch (e) {
+            alert(`Extension Error:\n Error detected in extension ${service.name}\n \n ${e.stack}`);
+        }
+    }
+
+    /**
+     * Send a custom event.  This can be used by services to publish custom events
+     * @param  {string} eventType
+     * @param  {any} data
+     */
+    sendEvent(eventType: string, data: any) {
+        if (this.eventDispatcher) {
+            this.eventDispatcher.sendEvent(eventType, data);
+        }
+    }
+
+    /**
+     * Subscribe to an event and provide a callback.  This can be used by services to subscribe to custom events
+     * @param  {string} eventType
+     * @param  {any} callback
+     */
+    subscribeToEvent(eventType: string, callback: (data: any) => void) {
+        if (this.eventDispatcher) {
+            this.eventDispatcher.subscribeToEvent(eventType, callback);
+        }
+    }
+}
+
+// Singleton service locator that can be referenced
+const serviceLocator = new ClientServiceLocatorType();
+export default serviceLocator;
+
+// Load up all the internal services
+serviceLocator.loadService(new tsExtension());
+serviceLocator.loadService(new jsExtension());

+ 50 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/javascript/JavascriptLanguageExtension.ts

@@ -0,0 +1,50 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+
+/**
+ * Resource extension that handles configuring the editor for Javascript
+ */
+export default class JavascriptLanguageExtension implements Editor.ClientExtensions.WebViewService {
+    name: string = "ClientJavascriptLanguageExtension";
+    description: string = "Javascript language services for the editor.";
+
+    private serviceLocator: Editor.ClientExtensions.ClientServiceLocator;
+
+    /**
+    * Initialize the language service
+     * @param  {Editor.ClientExtensions.ClientServiceLocator} serviceLocator
+     */
+    initialize(serviceLocator: Editor.ClientExtensions.ClientServiceLocator) {
+        // initialize the language service
+        this.serviceLocator = serviceLocator;
+    }
+
+    /**
+     * Determines if the file name/path provided is something we care about
+     * @param  {string} path
+     * @return {boolean}
+     */
+    private isValidFiletype(path: string): boolean {
+        let ext = path.split(".").pop();
+        return ext == "js";
+    }
+
+    /**
+     * Called when the editor needs to be configured for a particular file
+     * @param  {Editor.EditorEvents.EditorFileEvent} ev
+     */
+    configureEditor(ev: Editor.EditorEvents.EditorFileEvent) {
+        if (this.isValidFiletype(ev.filename)) {
+            let editor = <AceAjax.Editor>ev.editor;
+            editor.session.setMode("ace/mode/javascript");
+
+            editor.setOptions({
+                enableBasicAutocompletion: true,
+                enableLiveAutocompletion: true
+            });
+        }
+    }
+}

+ 268 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/TypescriptLanguageExtension.ts

@@ -0,0 +1,268 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+// Based upon the TypeScript language services example at https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#incremental-build-support-using-the-language-services
+
+import * as ts from "../../../modules/typescript";
+import * as WorkerProcessTypes from "./workerprocess/workerProcessTypes";
+import ClientExtensionEventNames from "../../ClientExtensionEventNames";
+
+/**
+ * Resource extension that handles compiling or transpling typescript on file save.
+ */
+export default class TypescriptLanguageExtension implements Editor.ClientExtensions.WebViewService {
+    name: string = "ClientTypescriptLanguageExtension";
+    description: string = "This extension handles typescript language features such as completion, compilation, etc.";
+
+    /**
+     * current filename
+     * @type {string}
+     */
+    private filename: string;
+
+    private serviceLocator: Editor.ClientExtensions.ClientServiceLocator;
+
+    private worker: SharedWorker.SharedWorker;
+
+    private editor;
+
+    /**
+     * Perform a full compile on save, or just transpile the current file
+     * @type {boolean}
+     */
+    fullCompile: boolean = true;
+
+    /**
+    * Inject this language service into the registry
+     * @param  {Editor.ClientExtensions.ClientServiceLocator} serviceLocator
+     */
+    initialize(serviceLocator: Editor.ClientExtensions.ClientServiceLocator) {
+        // initialize the language service
+        this.serviceLocator = serviceLocator;
+    }
+
+    /**
+     * Determines if the file name/path provided is something we care about
+     * @param  {string} path
+     * @return {boolean}
+     */
+    private isValidFiletype(path: string): boolean {
+        let ext = path.split(".").pop();
+        return ext == "ts";
+    }
+
+    /**
+     * Utility function that handles sending a request to the worker process and then when
+     * a response is received pass it back to the caller.  Since this is all handled async,
+     * it will be facilitated by passing back a promise
+     * @param  {string} responseChannel The unique string name of the response message channel
+     * @param  {any} message
+     * @return {PromiseLike}
+     */
+    private workerRequest(responseChannel: string, message: any): PromiseLike<{}> {
+        let worker = this.worker;
+
+        return new Promise((resolve, reject) => {
+            const responseCallback = function(e: WorkerProcessTypes.WorkerProcessMessage<any>) {
+                if (e.data.command == responseChannel) {
+                    worker.port.removeEventListener("message", responseCallback);
+                    resolve(e.data);
+                }
+            };
+            this.worker.port.addEventListener("message", responseCallback);
+            this.worker.port.postMessage(message);
+        });
+    }
+
+    /**
+     * Called when the editor needs to be configured for a particular file
+     * @param  {Editor.EditorEvents.EditorFileEvent} ev
+     */
+    configureEditor(ev: Editor.EditorEvents.EditorFileEvent) {
+        if (this.isValidFiletype(ev.filename)) {
+            let editor = <AceAjax.Editor>ev.editor;
+            editor.session.setMode("ace/mode/typescript");
+
+            editor.setOptions({
+                enableBasicAutocompletion: true,
+                enableLiveAutocompletion: true
+            });
+
+            this.editor = editor; // cache this so that we can reference it later
+        }
+    }
+
+    /**
+     * Called when code is first loaded into the editor
+     * @param  {CodeLoadedEvent} ev
+     * @return {[type]}
+     */
+    codeLoaded(ev: Editor.EditorEvents.CodeLoadedEvent) {
+        if (this.isValidFiletype(ev.filename)) {
+            this.filename = ev.filename;
+
+            let editor = ev.editor;
+
+            // we only want the typescript completer, otherwise we get a LOT of noise
+            editor.completers = [this.buildWordCompleter(ev.filename)];
+
+            // Build our worker
+            this.buildWorker();
+
+            // post a message to the shared web worker
+            this.worker.port.postMessage({ command: WorkerProcessTypes.Connect, sender: "Typescript Language Extension", filename: ev.filename });
+        }
+    }
+
+    /**
+     * Handler for any messages initiated by the worker process
+     * @param  {WorkerProcessTypes.WorkerProcessMessage} e
+     */
+    handleWorkerMessage(e: WorkerProcessTypes.WorkerProcessMessage<any>) {
+        switch (e.data.command) {
+            case WorkerProcessTypes.Message:
+                console.log(e.data.message);
+                break;
+            case WorkerProcessTypes.Alert:
+                alert(e.data.message);
+                break;
+        }
+    }
+
+    /**
+     * Build/Attach to the shared web worker
+     */
+    buildWorker() {
+
+        this.worker = new SharedWorker("./source/editorCore/clientExtensions/languageExtensions/typescript/workerprocess/workerLoader.js");
+
+        // hook up the event listener
+        this.worker.port.addEventListener("message", this.handleWorkerMessage.bind(this), false);
+
+        // Tell the SharedWorker we're closing
+        addEventListener("beforeunload", () => {
+            this.worker.port.postMessage({ command: WorkerProcessTypes.Disconnect });
+        });
+
+        this.worker.port.start();
+    }
+
+    /**
+     * Builds the word completer for the Ace Editor.  This will handle determining which items to display in the popup and in which order
+     * @param  {string} filename the filename of the current file
+     * @return {[type]} returns a completer
+     */
+    private buildWordCompleter(filename: string): {
+        getDocTooltip?: (selected: WorkerProcessTypes.WordCompletion) => void,
+        getCompletions: (editor, session, pos, prefix, callback) => void
+    } {
+        let extension = this;
+        let wordCompleter = {
+            getDocTooltip: function(selected: WorkerProcessTypes.WordCompletion) {
+                const message: WorkerProcessTypes.GetDocTooltipMessageData = {
+                    command: WorkerProcessTypes.GetDocTooltip,
+                    filename: extension.filename,
+                    completionItem: selected,
+                    pos: selected.pos
+                };
+
+                // Since the doc tooltip built in function of Ace doesn't support async calls to retrieve the tootip,
+                // we need to go ahead and call the worker and then force the display of the tooltip when we get
+                // a result back
+                extension.workerRequest(WorkerProcessTypes.DocTooltipResponse, message)
+                    .then((e: WorkerProcessTypes.GetDocTooltipResponseMessageData) => {
+                    extension.editor.completer.showDocTooltip(e);
+                });
+            },
+
+            getCompletions: function(editor, session, pos, prefix, callback) {
+                const message: WorkerProcessTypes.GetCompletionsMessageData = {
+                    command: WorkerProcessTypes.GetCompletions,
+                    filename: extension.filename,
+                    pos: pos,
+                    sourceText: editor.session.getValue(),
+                    prefix: prefix
+                };
+
+                extension.workerRequest(WorkerProcessTypes.CompletionResponse, message)
+                    .then((e: WorkerProcessTypes.GetCompletionsResponseMessageData) => {
+                    callback(null, e.completions);
+                });
+            }
+        };
+        return wordCompleter;
+    }
+
+    /**
+     * Called once a resource has been saved
+     * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+     */
+    save(ev: Editor.EditorEvents.SaveResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+            console.log(`${this.name}: received a save resource event for ${ev.path}`);
+
+            const message: WorkerProcessTypes.SaveMessageData = {
+                command: ClientExtensionEventNames.ResourceSavedEvent,
+                path: ev.path
+            };
+
+            this.worker.port.postMessage(message);
+        }
+    }
+
+    /**
+     * Handle the delete.  This should delete the corresponding javascript file
+     * @param  {Editor.EditorEvents.DeleteResourceEvent} ev
+     */
+    delete(ev: Editor.EditorEvents.DeleteResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+            console.log(`${this.name}: received a delete resource event for ${ev.path}`);
+
+            // notify the typescript language service that the file has been deleted
+            const message: WorkerProcessTypes.DeleteMessageData = {
+                command: ClientExtensionEventNames.ResourceDeletedEvent,
+                path: ev.path
+            };
+
+            this.worker.port.postMessage(message);
+        }
+    }
+
+    /**
+     * Handle the rename.  Should rename the corresponding .js file
+     * @param  {Editor.EditorEvents.RenameResourceEvent} ev
+     */
+    rename(ev: Editor.EditorEvents.RenameResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+            console.log(`${this.name}: received a rename resource event for ${ev.path} -> ${ev.newPath}`);
+
+            // notify the typescript language service that the file has been renamed
+            const message: WorkerProcessTypes.RenameMessageData = {
+                command: ClientExtensionEventNames.ResourceRenamedEvent,
+                path: ev.path,
+                newPath: ev.newPath
+            };
+
+            this.worker.port.postMessage(message);
+        }
+    }
+
+    /**
+     * Handle when the project is unloaded so that resources can be freed
+     */
+    projectUnloaded() {
+        if (this.worker) {
+
+            console.log(`${this.name}: received a project unloaded event`);
+
+            const message: WorkerProcessTypes.WorkerProcessMessageData = {
+                command: ClientExtensionEventNames.ProjectUnloadedEvent
+            };
+
+            this.worker.port.postMessage(message);
+        }
+    }
+}

+ 353 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageService.ts

@@ -0,0 +1,353 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+// Based upon the TypeScript language services example at https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#incremental-build-support-using-the-language-services
+
+import * as ts from "../../../../modules/typescript";
+
+/**
+ * Abstraction over the file system
+ */
+export interface FileSystemInterface {
+    /**
+     * Deterimine if the particular file exists in the resources
+     * @param  {string} filename
+     * @return {boolean}
+     */
+    fileExists(filename: string): boolean;
+    /**
+     * Grab the contents of the file
+     * @param  {string} filename
+     * @return {string}
+     */
+    getFile(filename: string): string;
+    /**
+     * Write the contents to the file specified
+     * @param  {string} filename
+     * @param  {string} contents
+     */
+    writeFile(filename: string, contents: string);
+
+    /**
+     * Returns the current directory / root of the source tree
+     * @return {string}
+     */
+    getCurrentDirectory(): string;
+}
+
+/**
+ * Resource extension that handles compiling or transpling typescript on file save.
+ */
+export class TypescriptLanguageService {
+
+    constructor(fs: FileSystemInterface) {
+        this.fs = fs;
+
+        // Create the language service files
+        this.documentRegistry = ts.createDocumentRegistry();
+        this.createLanguageService(this.documentRegistry);
+    }
+
+    private fs: FileSystemInterface = null;
+    private languageService: ts.LanguageService = null;
+    private documentRegistry: ts.DocumentRegistry = null;
+
+    public compilerOptions: ts.CompilerOptions = {
+        noEmitOnError: true,
+        noImplicitAny: false,
+        target: ts.ScriptTarget.ES5,
+        module: ts.ModuleKind.CommonJS,
+        noLib: true
+    };
+
+    name: string = "TypescriptLanguageService";
+
+    /**
+     * Perform a full compile on save, or just transpile the current file
+     * @type {boolean}
+     */
+    fullCompile: boolean = true;
+
+    private projectFiles: string[] = [];
+    private versionMap: ts.Map<{ version: number, snapshot?: ts.IScriptSnapshot }> = {};
+
+    private createLanguageService(documentRegistry: ts.DocumentRegistry) {
+
+        // Create the language service host to allow the LS to communicate with the host
+        const servicesHost: ts.LanguageServiceHost = {
+            getScriptFileNames: () => this.projectFiles,
+            getScriptVersion: (fileName) => this.versionMap[fileName] && this.versionMap[fileName].version.toString(),
+            getScriptSnapshot: (filename) => {
+                const scriptVersion = this.versionMap[filename];
+
+                // Grab the cached version
+                if (scriptVersion) {
+                    if (scriptVersion.snapshot) {
+                        return scriptVersion.snapshot;
+                    } else {
+                        console.log(`!!! creating snapshot for ${filename}`);
+                        let sourceFile = this.documentRegistry.acquireDocument(filename, this.compilerOptions, ts.ScriptSnapshot.fromString(""), scriptVersion.version.toString());
+                        return ts.ScriptSnapshot.fromString(sourceFile.text);
+                    }
+                }
+            },
+            getCurrentDirectory: () => this.fs.getCurrentDirectory(),
+            getCompilationSettings: () => this.compilerOptions,
+            getDefaultLibFileName: (options) => undefined
+        };
+
+        this.languageService = ts.createLanguageService(servicesHost, documentRegistry);
+    }
+
+    /**
+     * Adds a file to the internal project cache
+     * @param  {string} filename the full path of the file to add
+     * @param [{sring}] fileContents optional file contents.  If not provided, the filesystem object will be queried
+     */
+    addProjectFile(filename: string, fileContents?: string) {
+        if (this.projectFiles.indexOf(filename) == -1) {
+            console.log("Added project file: " + filename);
+            this.versionMap[filename] = {
+                version: 0,
+                snapshot: ts.ScriptSnapshot.fromString(fileContents || this.fs.getFile(filename))
+            };
+            this.projectFiles.push(filename);
+            this.documentRegistry.acquireDocument(
+                filename,
+                this.compilerOptions,
+                this.versionMap[filename].snapshot,
+                "0");
+        }
+    }
+
+    /**
+     * Updates the internal file representation.
+     * @param  {string} filename name of the file
+     * @param  {string} fileContents optional contents of the file.  If not provided, the file system object will be queried
+     * @return {ts.SourceFile}
+     */
+    updateProjectFile(filename: string, fileContents?: string): ts.SourceFile {
+        console.log("Updated project file: " + filename);
+        this.versionMap[filename].version++;
+        this.versionMap[filename].snapshot = ts.ScriptSnapshot.fromString(fileContents || this.fs.getFile(filename));
+        return this.documentRegistry.updateDocument(
+            filename,
+            this.compilerOptions,
+            this.versionMap[filename].snapshot,
+            this.versionMap[filename].version.toString());
+    }
+
+    /**
+     * Returns the list of project files
+     * @return {string[]}
+     */
+    getProjectFiles(): string[] {
+        return this.projectFiles;
+    }
+    /**
+     * Simply transpile the typescript file.  This is much faster and only checks for syntax errors
+     * @param {string[]}           fileNames array of files to transpile
+     * @param {ts.CompilerOptions} options   compiler options
+     */
+    transpile(fileNames: string[], options?: ts.CompilerOptions): void {
+        options = options || this.compilerOptions;
+        this.compilerOptions = options;
+
+        fileNames.forEach((fileName) => {
+            console.log(`${this.name}:  Transpiling ${fileName}`);
+            let script = this.fs.getFile(fileName);
+            let diagnostics: ts.Diagnostic[] = [];
+            let result = ts.transpile(script, options, fileName, diagnostics);
+            if (diagnostics.length) {
+                this.logErrors(diagnostics);
+            }
+            if (diagnostics.length == 0) {
+                this.fs.writeFile(fileName.replace(".ts", ".js"), result);
+            }
+        });
+    }
+
+    /**
+     * Converts the line and character offset to a position
+     * @param  {ts.SourceFile} file
+     * @param  {number} line
+     * @param  {number} character
+     * @return {number}
+     */
+    getPositionOfLineAndCharacter(file: ts.SourceFile, line: number, character: number): number {
+        return ts.getPositionOfLineAndCharacter(file, line, character);
+    }
+
+    /**
+     * Returns the completions available at the provided position
+     * @param  {string} filename
+     * @param  {number} position
+     * @return {ts.CompletionInfo}
+     */
+    getCompletions(filename: string, pos: number): ts.CompletionInfo {
+        return this.languageService.getCompletionsAtPosition(filename, pos);
+    }
+
+    /**
+     * Returns details of this completion entry
+     * @param  {string} filename
+     * @param  {number} pos
+     * @param  {string} entryname
+     * @return {ts.CompletionEntryDetails}
+     */
+    getCompletionEntryDetails(filename: string, pos: number, entryname: string): ts.CompletionEntryDetails {
+        return this.languageService.getCompletionEntryDetails(filename, pos, entryname);
+    }
+    /**
+     * Compile the provided file to javascript with full type checking etc
+     * @param  {string}  a list of file names to compile
+     * @param  {ts.CompilerOptions} options for the compiler
+     */
+    compile(files: string[], options?: ts.CompilerOptions): void {
+        let start = new Date().getTime();
+
+        options = options || this.compilerOptions;
+
+        //Make sure we have these files in the project
+        files.forEach((file) => {
+            this.addProjectFile(file);
+        });
+
+        let errors: ts.Diagnostic[] = [];
+
+        if (files.length == 0) {
+            // We haven't passed any files in, so let's compile them all
+            this.projectFiles.forEach(filename => {
+                errors = errors.concat(this.compileFile(filename));
+            });
+        } else {
+            // Only compile the files that are newly edited
+            files.forEach(filename => {
+                // increment the version number since we changed
+                this.versionMap[filename].version++;
+                this.versionMap[filename].snapshot = null;
+                errors = errors.concat(this.compileFile(filename));
+            });
+        }
+
+        if (errors.length) {
+            this.logErrors(errors);
+        }
+
+        console.log(`${this.name}: Compiling complete after ${new Date().getTime() - start} ms`);
+    }
+
+    /**
+     * Delete a file from the project store
+     * @param  {string} filepath
+     */
+    deleteProjectFile(filepath: string) {
+        if (this.versionMap[filepath]) {
+            delete this.versionMap[filepath];
+        }
+        let idx = this.projectFiles.indexOf(filepath);
+        if (idx > -1) {
+            console.log(`delete project file from ${filepath}`);
+            this.projectFiles.splice(idx, 1);
+        }
+    }
+
+    /**
+     * rename a file in the project store
+     * @param  {string} filepath the old path to the file
+     * @param  {string} newpath the new path to the file
+     */
+    renameProjectFile(filepath: string, newpath: string): void {
+        let oldFile = this.versionMap[filepath];
+        if (oldFile) {
+            console.log(`Rename project file from ${filepath} to ${newpath}`);
+            delete this.versionMap[filepath];
+            this.versionMap[newpath] = oldFile;
+        }
+        let idx = this.projectFiles.indexOf(filepath);
+        if (idx > -1) {
+            console.log(`Update project files array from ${filepath} to ${newpath}`)
+            this.projectFiles[idx] = newpath;
+        }
+    }
+
+    /**
+     * clear out any caches, etc.
+     */
+    reset() {
+        this.projectFiles = [];
+        this.versionMap = {};
+        //this.languageService = null;
+    }
+
+    /**
+     * Compile an individual file
+     * @param  {string} filename the file to compile
+     * @return {[ts.Diagnostic]} a list of any errors
+     */
+    private compileFile(filename: string): ts.Diagnostic[] {
+        console.log(`${this.name}: Compiling version ${this.versionMap[filename].version} of ${filename}`);
+        //if (!filename.match("\.d\.ts$")) {
+        try {
+            return this.emitFile(filename);
+        } catch (err) {
+            console.log(`${this.name}: problem encountered compiling ${filename}: ${err}`);
+            return [];
+            // console.log(err.stack);
+        }
+        //}
+    }
+
+    /**
+     * writes out a file from the compile process
+     * @param  {string} filename [description]
+     * @return {[ts.Diagnostic]} a list of any errors
+     */
+    private emitFile(filename: string): ts.Diagnostic[] {
+        let output = this.languageService.getEmitOutput(filename);
+        let allDiagnostics: ts.Diagnostic[] = [];
+        if (output.emitSkipped) {
+            console.log(`${this.name}: Failure Emitting ${filename}`);
+            allDiagnostics = this.languageService.getCompilerOptionsDiagnostics()
+                .concat(this.languageService.getSyntacticDiagnostics(filename))
+                .concat(this.languageService.getSemanticDiagnostics(filename));
+        }
+
+        output.outputFiles.forEach(o => {
+            this.fs.writeFile(o.name, o.text);
+        });
+        return allDiagnostics;
+    }
+
+    /**
+     * Logs errors from the diagnostics returned from the compile/transpile process
+     * @param {ts.Diagnostic} diagnostics information about the errors
+     * @return {[type]}          [description]
+     */
+    private logErrors(diagnostics: ts.Diagnostic[]) {
+        let msg = [];
+
+        diagnostics.forEach(diagnostic => {
+            let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
+            if (diagnostic.file) {
+                let d = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
+                let line = d.line;
+                let character = d.character;
+
+                // NOTE: Destructuring throws an error in chrome web view
+                //let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
+
+                msg.push(`${this.name}:  Error ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
+            }
+            else {
+                msg.push(`${this.name}  Error: ${message}`);
+            }
+        });
+        console.log(`TypeScript Errors:\n${msg.join("\n") }`);
+        throw new Error(`TypeScript Errors:\n${msg.join("\n") }`);
+    }
+
+}

+ 347 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageServiceWebWorker.ts

@@ -0,0 +1,347 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+/**
+ * Typescript Language Worker - Handles bringing down the source, providing completions, and compiling.
+ * since this is a shared web worker, each editor tab will be sharing the same data.
+ */
+import * as ts from "../../../../modules/typescript";
+import {TypescriptLanguageService, FileSystemInterface} from "./TypescriptLanguageService";
+import * as WorkerProcessTypes from "./workerProcessTypes";
+import ClientExtensionEventNames from "../../../ClientExtensionEventNames";
+
+interface TSConfigFile {
+    compilerOptions?: ts.CompilerOptions;
+    files: Array<string>;
+}
+
+/**
+ * Queries the host for a particular resource and returns it in a promise
+ * @param  {string} codeUrl
+ * @return {Promise}
+ */
+function getResource(codeUrl: string): Promise<{}> {
+    return new Promise(function(resolve, reject) {
+        const xmlHttp = new XMLHttpRequest();
+        xmlHttp.onreadystatechange = () => {
+            if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
+                resolve(xmlHttp.responseText);
+            }
+        };
+        xmlHttp.open("GET", codeUrl, true); // true for asynchronous
+        xmlHttp.send(null);
+    });
+}
+
+/**
+ * Returns a file resource from the resources directory
+ * @param  {string} filename name and path of file under the project directory or a fully qualified file name
+ * @return {Promise}
+ */
+function getFileResource(filename: string): Promise<{}> {
+    return getResource(`atomic://${filename}`);
+}
+
+/**
+ * Class that provides access to the Atomic filesystem routines
+ */
+class WebFileSystem implements FileSystemInterface {
+
+    private fileCache = {};
+    /**
+     * Deterimine if the particular file exists in the resources
+     * @param  {string} filename
+     * @return {boolean}
+     */
+    fileExists(filename: string): boolean {
+        return this.fileCache[filename] != null;
+    }
+
+    /**
+     * Cache a file in the filesystem
+     * @param  {string} filename
+     * @param  {string} file
+     */
+    cacheFile(filename: string, file: string) {
+        this.fileCache[filename] = file;
+    }
+
+    /**
+     * Grab the contents of the file
+     * @param  {string} filename
+     * @return {string}
+     */
+    getFile(filename: string): string {
+        console.log("FS.GETFILE!!");
+        // return HostInterop.getResource("atomic:" + filename);
+        return this.fileCache[filename];
+    }
+
+    /**
+     * Write the contents to the file specified
+     * @param  {string} filename
+     * @param  {string} contents
+     */
+    writeFile(filename: string, contents: string) {
+        //TODO:
+        /*let script = new Atomic.File(filename, Atomic.FILE_WRITE);
+        try {
+            script.writeString(contents);
+            script.flush();
+        } finally {
+            script.close();
+        }
+        */
+    }
+
+    /**
+     * Returns the current directory / root of the source tree
+     * @return {string}
+     */
+    getCurrentDirectory(): string {
+        return "";
+    }
+
+}
+
+export default class TypescriptLanguageServiceWebWorker {
+    /**
+     * Number of editors connected to this shared worker
+     * @type {Number}
+     */
+    connections = 0;
+
+    /**
+     * The language service that will handle building
+     * @type {TypescriptLanguageService}
+     */
+    languageService: TypescriptLanguageService = null;
+
+    fs: WebFileSystem; // needed?
+
+    projectLoaded = false;
+
+    constructor() {
+        this.fs = new WebFileSystem();
+        this.languageService = new TypescriptLanguageService(this.fs);
+    }
+
+    /**
+     * Handle when an editor connects to this worker
+     * @param  {any} e
+     */
+    connect(e) {
+        let port: MessagePort = e.ports[0];
+        this.connections++;
+
+        port.addEventListener("message", (e: WorkerProcessTypes.WorkerProcessMessage<any>) => {
+            switch (e.data.command) {
+                case WorkerProcessTypes.Connect:
+                    this.handleHELO(port, e.data);
+                    break;
+                case WorkerProcessTypes.Disconnect:
+                    this.handleCLOSE(port, e.data);
+                    break;
+                case WorkerProcessTypes.GetCompletions:
+                    this.handleGetCompletions(port, e.data);
+                    break;
+                case WorkerProcessTypes.GetDocTooltip:
+                    this.handleGetDocTooltip(port, e.data);
+                    break;
+                case ClientExtensionEventNames.ResourceSavedEvent:
+                    this.handleSave(port, e.data);
+                    break;
+                case ClientExtensionEventNames.ResourceRenamedEvent:
+                    this.handleRename(port, e.data);
+                    break;
+                case ClientExtensionEventNames.ResourceDeletedEvent:
+                    this.handleDelete(port, e.data);
+                    break;
+                case ClientExtensionEventNames.ProjectUnloadedEvent:
+                    this.handleProjectUnloaded(port);
+                    break;
+            }
+
+        }, false);
+
+        port.start();
+    }
+
+    /**
+     * Called when we want to reset the language service
+     * @return {[type]}
+     */
+    reset() {
+        this.languageService.reset();
+        this.projectLoaded = false;
+    }
+
+    /**
+     * Seed the language service with all of the relevant files in the project
+     * @return {Promise}
+     */
+    private loadProjectFiles() {
+        // Let's query the backend and get a list of the current files
+        // and delete any that may be been removed and sync up
+        return getFileResource("resources/tsconfig.atomic").then((jsonTsConfig: string) => {
+            let promises: PromiseLike<void>[] = [];
+            let tsConfig: TSConfigFile = JSON.parse(jsonTsConfig);
+
+            if (tsConfig.compilerOptions) {
+                this.languageService.compilerOptions = tsConfig.compilerOptions;
+            };
+
+            let existingFiles = this.languageService.getProjectFiles();
+
+            // see if anything was deleted
+            existingFiles.forEach((f) => {
+                if (tsConfig.files.indexOf(f) == -1) {
+                    this.languageService.deleteProjectFile(f);
+                }
+            });
+
+            // load up any new files that may have been added
+            tsConfig.files.forEach((f) => {
+                if (existingFiles.indexOf(f) == -1) {
+                    promises.push(getFileResource(f).then((code: string) => {
+                        this.languageService.addProjectFile(f, code);
+                    }));
+                }
+            });
+            return Promise.all(promises);
+        }).then(() => {
+            // Let's seed the compiler state
+            // this.languageService.compile([this.filename]);
+            this.projectLoaded = true;
+        });
+    }
+
+    /**
+     * Handle initial tab connection
+     * @param  {MessagePort} port
+     * @param  {any} eventData
+     */
+    handleHELO(port: MessagePort, eventData: any | {
+        sender: string,
+        filename: string
+    }) {
+        port.postMessage({ command: WorkerProcessTypes.Message, message: "Hello " + eventData.sender + " (port #" + this.connections + ")" });
+        this.loadProjectFiles().then(() => {
+            this.languageService.compile([eventData.filename]);
+        });
+    }
+
+    /**
+     * Handle when a tab closes
+     * @param  {MessagePort} port
+     * @param  {any} eventData
+     */
+    handleCLOSE(port: MessagePort, eventData: any) {
+        this.connections--;
+        if (this.connections <= 0) {
+            this.reset();
+        }
+        console.log("Got a close");
+    }
+
+    /**
+     * Get completions
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.GetCompletionsMessage} eventData
+     */
+    handleGetCompletions(port: MessagePort, eventData: WorkerProcessTypes.GetCompletionsMessageData) {
+        let sourceFile = this.languageService.updateProjectFile(eventData.filename, eventData.sourceText);
+
+        let newpos = this.languageService.getPositionOfLineAndCharacter(sourceFile, eventData.pos.row, eventData.pos.column);
+        let completions = this.languageService.getCompletions(eventData.filename, newpos);
+
+        let message: WorkerProcessTypes.GetCompletionsResponseMessageData = {
+            command: WorkerProcessTypes.CompletionResponse,
+            completions: []
+        };
+        let langService = this.languageService;
+
+        if (completions) {
+            message.completions = completions.entries.map((completion: ts.CompletionEntry) => {
+                let value = completion.name;
+                let completionItem: WorkerProcessTypes.WordCompletion = {
+                    caption: completion.name,
+                    value: value,
+                    score: 100 - parseInt(completion.sortText, 0),
+                    meta: completion.kind,
+                    pos: newpos
+                };
+
+                //completionItem.docTooltip = this.getDocTooltip(eventData.filename, newpos, completionItem);
+                return completionItem;
+            });
+        }
+
+        port.postMessage(message);
+    }
+
+    /**
+     * Get the documentation popup information for the current completion item
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.GetDocTooltipMessageData} eventData
+     * @return {[type]}
+     */
+    handleGetDocTooltip(port: MessagePort, eventData: WorkerProcessTypes.GetDocTooltipMessageData) {
+        let message: WorkerProcessTypes.GetDocTooltipResponseMessageData = {
+            command: WorkerProcessTypes.DocTooltipResponse
+        };
+        const details = this.languageService.getCompletionEntryDetails(eventData.filename, eventData.pos, eventData.completionItem.caption);
+        if (details) {
+            let docs = details.displayParts.map(part => part.text).join("");
+            if (details.documentation) {
+                docs += "<br/" + details.documentation.map(part => part.text).join("");
+            }
+
+            message.docHTML = docs;
+        }
+
+        port.postMessage(message);
+    }
+
+    /**
+     * Called when the file has been saved.
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.SaveMessageData} eventData
+     */
+    handleSave(port: MessagePort, eventData: WorkerProcessTypes.SaveMessageData) {
+        // let's reload the file
+        getFileResource(eventData.path).then((code: string) => {
+            this.languageService.updateProjectFile(eventData.path, code);
+        });
+    }
+
+    /**
+     * Called when a file has been deleted
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.DeleteMessageData} eventData
+     */
+    handleDelete(port: MessagePort, eventData: WorkerProcessTypes.DeleteMessageData) {
+        this.languageService.deleteProjectFile(eventData.path);
+    }
+
+    /**
+     * Called when a file has been renamed
+     * @param  {MessagePort} port
+     * @param  {WorkerProcessCommands.RenameMessageData} eventData
+     */
+    handleRename(port: MessagePort, eventData: WorkerProcessTypes.RenameMessageData) {
+        this.languageService.renameProjectFile(eventData.path, eventData.newPath);
+    }
+
+    /**
+     * Called when the project has been closed
+     * @param  {MessagePort} port
+     */
+    handleProjectUnloaded(port: MessagePort) {
+        this.reset();
+    }
+}

+ 45 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerLoader.ts

@@ -0,0 +1,45 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+/**
+ * Typescript Language Worker - Handles bringing down the source, providing completions, and compiling.
+ * since this is a shared web worker, each editor tab will be sharing the same data.
+ */
+"use strict";
+
+// Bring in the system js library so we can import the modules we need.  The path here is based upon the location
+// of this SharedWorker in the directory tree
+importScripts("../../../../../systemjs/system.js");
+System.config({
+    defaultJSExtensions: true,
+    meta: {
+        "../../../../modules/typescript.js": {
+            format: "global",
+            exports: "ts"
+        }
+    }
+});
+
+// hook into the first time a tab connects...need to do some set up and some async pulling down of required modules
+function firstTimeConnect(e: any) {
+    // Going low-level to use the systemjs import system.  import will return a promise that will resolve
+    // to the module being required in.  Once this is loaded in and instantiated, everything downstream can just
+    // use normal typescript style imports to bring in other modules.
+    System.import("./TypescriptLanguageServiceWebWorker").then((TypescriptLanguageServiceWebWorker) => {
+        const worker = new TypescriptLanguageServiceWebWorker.default();
+
+        // remove the event listener for the first connect
+        self.removeEventListener("connect", firstTimeConnect);
+
+        // hook up the permanent event listener
+        self.addEventListener("connect", worker.connect.bind(worker));
+
+        // forward the connect call
+        worker.connect(e);
+    });
+}
+self.addEventListener("connect", firstTimeConnect);

+ 60 - 0
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/workerProcessTypes.ts

@@ -0,0 +1,60 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+export interface WorkerProcessMessage<T> extends MessageEvent {
+    data: T;
+}
+
+export interface WorkerProcessMessageData {
+    command: string;
+}
+
+export const GetCompletions = "COMPLETIONS";
+export const CompletionResponse = "COMPLETION_RESPONSE";
+export interface WordCompletion {
+    caption: string;
+    meta: string;
+    score: number;
+    value: string;
+    pos: number;
+    snippet?: string;
+    docHTML?: string;
+    docText?: string;
+}
+
+export interface GetCompletionsMessageData extends WorkerProcessMessageData {
+    sourceText: string;
+    filename: string;
+    pos: { row: number, column: number };
+    prefix: string;
+}
+
+export interface GetCompletionsResponseMessageData extends WorkerProcessMessageData {
+    completions: Array<WordCompletion>;
+}
+
+export const GetDocTooltip = "DOC_TOOLTIP";
+export const DocTooltipResponse = "DOC_TOOLTIP_RESPONSE";
+export interface GetDocTooltipMessageData extends WorkerProcessMessageData {
+    completionItem: WordCompletion;
+    filename: string;
+    pos: number;
+}
+
+export interface GetDocTooltipResponseMessageData extends WorkerProcessMessageData {
+    docText?: string;
+    docHTML?: string;
+}
+
+export const Connect = "HELO";
+export const Disconnect = "CLOSE";
+export const Message = "MESSAGE";
+export const Alert = "ALERT";
+
+export interface SaveMessageData extends WorkerProcessMessageData, Editor.EditorEvents.SaveResourceEvent { }
+export interface DeleteMessageData extends WorkerProcessMessageData, Editor.EditorEvents.DeleteResourceEvent {}
+export interface RenameMessageData extends WorkerProcessMessageData, Editor.EditorEvents.RenameResourceEvent {}

+ 7 - 0
Script/AtomicWebViewEditor/editor/editor.ts

@@ -1,3 +1,10 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
 // Instantiate the editor
 const editor = ace.edit("editor");
 

+ 94 - 0
Script/AtomicWebViewEditor/editor/editorCommands.ts

@@ -0,0 +1,94 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+import editor from "./editor";
+import serviceLocator from "../clientExtensions/ServiceLocator";
+import HostInterop from "../interop";
+import ClientExtensionEventNames from "../clientExtensions/ClientExtensionEventNames";
+
+
+/**
+ * Set the editor theme and configuration based upon the file extension
+ * @param  {string} fileExt
+ */
+export function configure(fileExt: string, filename: string) {
+
+    // set a default theme
+    editor.setTheme("ace/theme/monokai");
+
+    // set a default mode
+    editor.session.setMode("ace/mode/javascript");
+
+    // give the language extensions the opportunity to configure the editor based upon the file type
+    serviceLocator.sendEvent(ClientExtensionEventNames.ConfigureEditorEvent, {
+        fileExt: fileExt,
+        filename: filename,
+        editor: editor
+    });
+}
+
+/**
+ * Returns the text in the editor instance
+ * @return {string}
+ */
+export function getSourceText() : string {
+    return editor.session.getValue();
+}
+
+/**
+ * Loads a file of code into the editor and wires up the change events
+ * @param  {string} code
+ * @param {string} filename
+ * @param  {string} fileExt
+ */
+export function loadCodeIntoEditor(code: string, filename: string, fileExt: string) {
+    editor.session.setValue(code);
+    editor.gotoLine(0);
+
+    editor.getSession().on("change", function(e) {
+        HostInterop.getInstance().notifyEditorChange();
+    });
+
+    serviceLocator.sendEvent(ClientExtensionEventNames.CodeLoadedEvent, {
+        code: code,
+        filename: filename,
+        fileExt: fileExt,
+        editor: editor
+    });
+
+}
+
+/**
+ * Called when the project is getting unloaded
+ */
+export function projectUnloaded() {
+    serviceLocator.sendEvent(ClientExtensionEventNames.ProjectUnloadedEvent, null);
+}
+
+/**
+ * Called when a resource is getting renamed
+ * @param  {string} path
+ * @param  {string} newPath
+ */
+export function resourceRenamed(path: string, newPath: string) {
+    let data:Editor.EditorEvents.RenameResourceEvent = {
+        path: path,
+        newPath: newPath
+    };
+    serviceLocator.sendEvent(ClientExtensionEventNames.ResourceRenamedEvent, data);
+}
+
+/**
+ * Called when a resource is getting deleted
+ * @param  {string} path
+ */
+export function resourceDeleted(path: string) {
+    let data:Editor.EditorEvents.DeleteResourceEvent = {
+        path: path
+    };
+    serviceLocator.sendEvent(ClientExtensionEventNames.ResourceDeletedEvent, data);
+}

+ 0 - 20
Script/AtomicWebViewEditor/editor/editorConfig.ts

@@ -1,20 +0,0 @@
-import editor from "./editor";
-
-/**
- * Set the editor theme and configuration based upon the file extension
- * @param  {string} fileExt
- */
-export function configure(fileExt: string) {
-    editor.setTheme("ace/theme/monokai");
-    switch (fileExt.toLowerCase()) {
-        case "ts":
-            editor.session.setMode("ace/mode/typescript");
-            break;
-        case "js":
-            editor.session.setMode("ace/mode/javascript");
-            break;
-        default:
-            editor.session.setMode("ace/mode/javascript");
-            break;
-    }
-}

+ 179 - 50
Script/AtomicWebViewEditor/interop.ts

@@ -1,60 +1,189 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
 // This is the interop file, exposing functions that can be called by the host game engine
-import editor from "./editor/editor";
-import * as editorConfig from "./editor/editorConfig";
+import * as editorCommands from "./editor/editorCommands";
 
-export function saveCode() {
+/**
+ * Port to attach Chrome Dev Tools to
+ * @type {Number}
+ */
+const DEBUG_PORT = 3335;
 
-  const data = {
-    message: "saveCode",
-    payload: editor.session.getValue()
-  };
+/**
+ * Display "Attach dev tools now" alert on startup if this is set to true
+ * @type {Boolean}
+ */
+const DEBUG_ALERT = false;
 
-  window.atomicQuery({
-    request: JSON.stringify(data),
-    persistent: false,
-    onSuccess: function(response) {/**/ },
-    onFailure: function(error_code, error_message) {
-      console.log("Error getting code");
-    }
-  });
-}
+/**
+ * Promise version of atomic query
+ * @param  {string} message the query to use to pass to atomicQuery.  If there is no payload, this will be passed directly, otherwise it will be passed in a data object
+ * @param  {any} payload optional data to send
+ * @return {Promise}
+ */
+function atomicQueryPromise(message: any): Promise<{}> {
+    return new Promise(function(resolve, reject) {
+        let queryMessage = message;
+
+        // if message is coming in as an object then let's stringify it
+        if (typeof (message) != "string") {
+            queryMessage = JSON.stringify(message);
+        }
 
-export function codeLoaded(value: string, fileExt: string) {
-  editor.session.setValue(value);
-  editor.gotoLine(0);
-
-  editor.getSession().on("change", function(e) {
-    window.atomicQuery({
-      request: "change",
-      persistent: false,
-      onSuccess: (response) => {/**/ },
-      onFailure: (error_code, error_message) => {
-        console.log("Error on change");
-      }
+        window.atomicQuery({
+            request: queryMessage,
+            persistent: false,
+            onSuccess: resolve,
+            onFailure: (error_code, error_message) => reject({ error_code: error_code, error_message: error_message })
+        });
     });
-  });
 }
 
-export function loadCode(codeUrl: string) {
-  const fileExt = codeUrl.split(".").pop();
-
-  // go ahead and set the theme prior to pulling the file across
-  editorConfig.configure(fileExt);
-
-  const p = new Promise(function(resolve, reject) {
-    const xmlHttp = new XMLHttpRequest();
-    xmlHttp.onreadystatechange = () => {
-      if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
-        resolve(xmlHttp.responseText);
-      }
-    };
-    xmlHttp.open("GET", codeUrl, true); // true for asynchronous
-    xmlHttp.send(null);
-  }).then(function(src: string) {
-    codeLoaded(src, fileExt);
-  });
+export default class HostInteropType {
+
+    private static _inst: HostInteropType = null;
+    static getInstance(): HostInteropType {
+        if (HostInteropType._inst == null) {
+            HostInteropType._inst = new HostInteropType();
+        }
+        return HostInteropType._inst;
+    }
+
+    static EDITOR_SAVE_CODE = "editorSaveCode";
+    static EDITOR_SAVE_FILE = "editorSaveFile";
+    static EDITOR_LOAD_COMPLETE = "editorLoadComplete";
+    static EDITOR_CHANGE = "editorChange";
+
+    constructor() {
+        // Set up the window object so the host can call into it
+        window.HOST_loadCode = this.loadCode.bind(this);
+        window.HOST_saveCode = this.saveCode.bind(this);
+
+        window.HOST_projectUnloaded = this.projectUnloaded.bind(this);
+        window.HOST_resourceRenamed = this.resourceRenamed.bind(this);
+        window.HOST_resourceDeleted = this.resourceDeleted.bind(this);
+    }
+
+    /**
+     * Called from the host to notify the client what file to load
+     * @param  {string} codeUrl
+     */
+    loadCode(codeUrl: string) {
+        console.log("Load Code called for :" + codeUrl);
+        const fileExt = codeUrl.split(".").pop();
+        const filename = codeUrl.replace("atomic://", "");
+
+        // go ahead and set the theme prior to pulling the file across
+        editorCommands.configure(fileExt, filename);
+
+        // get the code
+        this.getResource(codeUrl).then((src: string) => {
+            editorCommands.loadCodeIntoEditor(src, filename, fileExt);
+        }).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
+            console.log("Error loading code: " + e.error_message);
+        });
+    }
+
+    /**
+     * Save the contents of the editor
+     * @return {Promise}
+     */
+    saveCode(): Promise<{}> {
+        return atomicQueryPromise({
+            message: HostInteropType.EDITOR_SAVE_CODE,
+            payload: editorCommands.getSourceText()
+        });
+    }
+
+    /**
+     * Save the contents of a file as filename
+     * @param  {string} filename
+     * @param  {string} fileContents
+     * @return {Promise}
+     */
+    saveFile(filename: string, fileContents: string): Promise<{}> {
+        return atomicQueryPromise({
+            message: HostInteropType.EDITOR_SAVE_FILE,
+            filename: filename,
+            payload: fileContents
+        });
+    }
+
+    /**
+     * Call this function when the client is fully loaded up.  This will notify the host that
+     * it can start loading code
+     */
+    editorLoaded() {
+        if (DEBUG_ALERT) {
+            alert(`Attach chrome dev tools to this instance by navigating to http://localhost:${DEBUG_PORT}`);
+        }
+        atomicQueryPromise(HostInteropType.EDITOR_LOAD_COMPLETE);
+    }
+
+    /**
+     * Queries the host for a particular resource and returns it in a promise
+     * @param  {string} codeUrl
+     * @return {Promise}
+     */
+    getResource(codeUrl: string): Promise<{}> {
+        return new Promise(function(resolve, reject) {
+            const xmlHttp = new XMLHttpRequest();
+            xmlHttp.onreadystatechange = () => {
+                if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
+                    resolve(xmlHttp.responseText);
+                }
+            };
+            xmlHttp.open("GET", codeUrl, true); // true for asynchronous
+            xmlHttp.send(null);
+        });
+    }
+
+    /**
+     * Returns a file resource from the resources directory
+     * @param  {string} filename name and path of file under the project directory or a fully qualified file name
+     * @return {Promise}
+     */
+    getFileResource(filename: string): Promise<{}> {
+        return this.getResource(`atomic://${filename}`);
+    }
+
+    /**
+     * Notify the host that the contents of the editor has changed
+     */
+    notifyEditorChange() {
+        atomicQueryPromise(HostInteropType.EDITOR_CHANGE).catch((e: Editor.ClientExtensions.AtomicErrorMessage) => {
+            console.log("Error on change: " + e.error_message);
+        });
+    }
+
+    /**
+     * Notify that the project has been unloaded
+     */
+    projectUnloaded() {
+        editorCommands.projectUnloaded();
+    }
+
+    /**
+     * Notify that a resource has been renamed
+     * @param  {string} path
+     * @param  {string} newPath
+     */
+    resourceRenamed(path: string, newPath: string) {
+        editorCommands.resourceRenamed(path, newPath);
+    }
+
+    /**
+     * Notify that a resource has been deleted
+     * @param  {string} path
+     */
+    resourceDeleted(path: string) {
+        editorCommands.resourceDeleted(path);
+    }
 }
 
-// Set up the window object so the host can call into it
-window.loadCode = loadCode;
-window.saveCode = saveCode;
+HostInteropType.getInstance().editorLoaded();

+ 2167 - 0
Script/AtomicWebViewEditor/modules/typescript.d.ts

@@ -0,0 +1,2167 @@
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved. 
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0  
+ 
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
+MERCHANTABLITY OR NON-INFRINGEMENT. 
+ 
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+
+declare namespace ts {
+    interface Map<T> {
+        [index: string]: T;
+    }
+    interface FileMap<T> {
+        get(fileName: string): T;
+        set(fileName: string, value: T): void;
+        contains(fileName: string): boolean;
+        remove(fileName: string): void;
+        forEachValue(f: (v: T) => void): void;
+        clear(): void;
+    }
+    interface TextRange {
+        pos: number;
+        end: number;
+    }
+    const enum SyntaxKind {
+        Unknown = 0,
+        EndOfFileToken = 1,
+        SingleLineCommentTrivia = 2,
+        MultiLineCommentTrivia = 3,
+        NewLineTrivia = 4,
+        WhitespaceTrivia = 5,
+        ShebangTrivia = 6,
+        ConflictMarkerTrivia = 7,
+        NumericLiteral = 8,
+        StringLiteral = 9,
+        RegularExpressionLiteral = 10,
+        NoSubstitutionTemplateLiteral = 11,
+        TemplateHead = 12,
+        TemplateMiddle = 13,
+        TemplateTail = 14,
+        OpenBraceToken = 15,
+        CloseBraceToken = 16,
+        OpenParenToken = 17,
+        CloseParenToken = 18,
+        OpenBracketToken = 19,
+        CloseBracketToken = 20,
+        DotToken = 21,
+        DotDotDotToken = 22,
+        SemicolonToken = 23,
+        CommaToken = 24,
+        LessThanToken = 25,
+        LessThanSlashToken = 26,
+        GreaterThanToken = 27,
+        LessThanEqualsToken = 28,
+        GreaterThanEqualsToken = 29,
+        EqualsEqualsToken = 30,
+        ExclamationEqualsToken = 31,
+        EqualsEqualsEqualsToken = 32,
+        ExclamationEqualsEqualsToken = 33,
+        EqualsGreaterThanToken = 34,
+        PlusToken = 35,
+        MinusToken = 36,
+        AsteriskToken = 37,
+        AsteriskAsteriskToken = 38,
+        SlashToken = 39,
+        PercentToken = 40,
+        PlusPlusToken = 41,
+        MinusMinusToken = 42,
+        LessThanLessThanToken = 43,
+        GreaterThanGreaterThanToken = 44,
+        GreaterThanGreaterThanGreaterThanToken = 45,
+        AmpersandToken = 46,
+        BarToken = 47,
+        CaretToken = 48,
+        ExclamationToken = 49,
+        TildeToken = 50,
+        AmpersandAmpersandToken = 51,
+        BarBarToken = 52,
+        QuestionToken = 53,
+        ColonToken = 54,
+        AtToken = 55,
+        EqualsToken = 56,
+        PlusEqualsToken = 57,
+        MinusEqualsToken = 58,
+        AsteriskEqualsToken = 59,
+        AsteriskAsteriskEqualsToken = 60,
+        SlashEqualsToken = 61,
+        PercentEqualsToken = 62,
+        LessThanLessThanEqualsToken = 63,
+        GreaterThanGreaterThanEqualsToken = 64,
+        GreaterThanGreaterThanGreaterThanEqualsToken = 65,
+        AmpersandEqualsToken = 66,
+        BarEqualsToken = 67,
+        CaretEqualsToken = 68,
+        Identifier = 69,
+        BreakKeyword = 70,
+        CaseKeyword = 71,
+        CatchKeyword = 72,
+        ClassKeyword = 73,
+        ConstKeyword = 74,
+        ContinueKeyword = 75,
+        DebuggerKeyword = 76,
+        DefaultKeyword = 77,
+        DeleteKeyword = 78,
+        DoKeyword = 79,
+        ElseKeyword = 80,
+        EnumKeyword = 81,
+        ExportKeyword = 82,
+        ExtendsKeyword = 83,
+        FalseKeyword = 84,
+        FinallyKeyword = 85,
+        ForKeyword = 86,
+        FunctionKeyword = 87,
+        IfKeyword = 88,
+        ImportKeyword = 89,
+        InKeyword = 90,
+        InstanceOfKeyword = 91,
+        NewKeyword = 92,
+        NullKeyword = 93,
+        ReturnKeyword = 94,
+        SuperKeyword = 95,
+        SwitchKeyword = 96,
+        ThisKeyword = 97,
+        ThrowKeyword = 98,
+        TrueKeyword = 99,
+        TryKeyword = 100,
+        TypeOfKeyword = 101,
+        VarKeyword = 102,
+        VoidKeyword = 103,
+        WhileKeyword = 104,
+        WithKeyword = 105,
+        ImplementsKeyword = 106,
+        InterfaceKeyword = 107,
+        LetKeyword = 108,
+        PackageKeyword = 109,
+        PrivateKeyword = 110,
+        ProtectedKeyword = 111,
+        PublicKeyword = 112,
+        StaticKeyword = 113,
+        YieldKeyword = 114,
+        AbstractKeyword = 115,
+        AsKeyword = 116,
+        AnyKeyword = 117,
+        AsyncKeyword = 118,
+        AwaitKeyword = 119,
+        BooleanKeyword = 120,
+        ConstructorKeyword = 121,
+        DeclareKeyword = 122,
+        GetKeyword = 123,
+        IsKeyword = 124,
+        ModuleKeyword = 125,
+        NamespaceKeyword = 126,
+        RequireKeyword = 127,
+        NumberKeyword = 128,
+        SetKeyword = 129,
+        StringKeyword = 130,
+        SymbolKeyword = 131,
+        TypeKeyword = 132,
+        FromKeyword = 133,
+        OfKeyword = 134,
+        QualifiedName = 135,
+        ComputedPropertyName = 136,
+        TypeParameter = 137,
+        Parameter = 138,
+        Decorator = 139,
+        PropertySignature = 140,
+        PropertyDeclaration = 141,
+        MethodSignature = 142,
+        MethodDeclaration = 143,
+        Constructor = 144,
+        GetAccessor = 145,
+        SetAccessor = 146,
+        CallSignature = 147,
+        ConstructSignature = 148,
+        IndexSignature = 149,
+        TypePredicate = 150,
+        TypeReference = 151,
+        FunctionType = 152,
+        ConstructorType = 153,
+        TypeQuery = 154,
+        TypeLiteral = 155,
+        ArrayType = 156,
+        TupleType = 157,
+        UnionType = 158,
+        IntersectionType = 159,
+        ParenthesizedType = 160,
+        ObjectBindingPattern = 161,
+        ArrayBindingPattern = 162,
+        BindingElement = 163,
+        ArrayLiteralExpression = 164,
+        ObjectLiteralExpression = 165,
+        PropertyAccessExpression = 166,
+        ElementAccessExpression = 167,
+        CallExpression = 168,
+        NewExpression = 169,
+        TaggedTemplateExpression = 170,
+        TypeAssertionExpression = 171,
+        ParenthesizedExpression = 172,
+        FunctionExpression = 173,
+        ArrowFunction = 174,
+        DeleteExpression = 175,
+        TypeOfExpression = 176,
+        VoidExpression = 177,
+        AwaitExpression = 178,
+        PrefixUnaryExpression = 179,
+        PostfixUnaryExpression = 180,
+        BinaryExpression = 181,
+        ConditionalExpression = 182,
+        TemplateExpression = 183,
+        YieldExpression = 184,
+        SpreadElementExpression = 185,
+        ClassExpression = 186,
+        OmittedExpression = 187,
+        ExpressionWithTypeArguments = 188,
+        AsExpression = 189,
+        TemplateSpan = 190,
+        SemicolonClassElement = 191,
+        Block = 192,
+        VariableStatement = 193,
+        EmptyStatement = 194,
+        ExpressionStatement = 195,
+        IfStatement = 196,
+        DoStatement = 197,
+        WhileStatement = 198,
+        ForStatement = 199,
+        ForInStatement = 200,
+        ForOfStatement = 201,
+        ContinueStatement = 202,
+        BreakStatement = 203,
+        ReturnStatement = 204,
+        WithStatement = 205,
+        SwitchStatement = 206,
+        LabeledStatement = 207,
+        ThrowStatement = 208,
+        TryStatement = 209,
+        DebuggerStatement = 210,
+        VariableDeclaration = 211,
+        VariableDeclarationList = 212,
+        FunctionDeclaration = 213,
+        ClassDeclaration = 214,
+        InterfaceDeclaration = 215,
+        TypeAliasDeclaration = 216,
+        EnumDeclaration = 217,
+        ModuleDeclaration = 218,
+        ModuleBlock = 219,
+        CaseBlock = 220,
+        ImportEqualsDeclaration = 221,
+        ImportDeclaration = 222,
+        ImportClause = 223,
+        NamespaceImport = 224,
+        NamedImports = 225,
+        ImportSpecifier = 226,
+        ExportAssignment = 227,
+        ExportDeclaration = 228,
+        NamedExports = 229,
+        ExportSpecifier = 230,
+        MissingDeclaration = 231,
+        ExternalModuleReference = 232,
+        JsxElement = 233,
+        JsxSelfClosingElement = 234,
+        JsxOpeningElement = 235,
+        JsxText = 236,
+        JsxClosingElement = 237,
+        JsxAttribute = 238,
+        JsxSpreadAttribute = 239,
+        JsxExpression = 240,
+        CaseClause = 241,
+        DefaultClause = 242,
+        HeritageClause = 243,
+        CatchClause = 244,
+        PropertyAssignment = 245,
+        ShorthandPropertyAssignment = 246,
+        EnumMember = 247,
+        SourceFile = 248,
+        JSDocTypeExpression = 249,
+        JSDocAllType = 250,
+        JSDocUnknownType = 251,
+        JSDocArrayType = 252,
+        JSDocUnionType = 253,
+        JSDocTupleType = 254,
+        JSDocNullableType = 255,
+        JSDocNonNullableType = 256,
+        JSDocRecordType = 257,
+        JSDocRecordMember = 258,
+        JSDocTypeReference = 259,
+        JSDocOptionalType = 260,
+        JSDocFunctionType = 261,
+        JSDocVariadicType = 262,
+        JSDocConstructorType = 263,
+        JSDocThisType = 264,
+        JSDocComment = 265,
+        JSDocTag = 266,
+        JSDocParameterTag = 267,
+        JSDocReturnTag = 268,
+        JSDocTypeTag = 269,
+        JSDocTemplateTag = 270,
+        SyntaxList = 271,
+        Count = 272,
+        FirstAssignment = 56,
+        LastAssignment = 68,
+        FirstReservedWord = 70,
+        LastReservedWord = 105,
+        FirstKeyword = 70,
+        LastKeyword = 134,
+        FirstFutureReservedWord = 106,
+        LastFutureReservedWord = 114,
+        FirstTypeNode = 151,
+        LastTypeNode = 160,
+        FirstPunctuation = 15,
+        LastPunctuation = 68,
+        FirstToken = 0,
+        LastToken = 134,
+        FirstTriviaToken = 2,
+        LastTriviaToken = 7,
+        FirstLiteralToken = 8,
+        LastLiteralToken = 11,
+        FirstTemplateToken = 11,
+        LastTemplateToken = 14,
+        FirstBinaryOperator = 25,
+        LastBinaryOperator = 68,
+        FirstNode = 135,
+    }
+    const enum NodeFlags {
+        Export = 1,
+        Ambient = 2,
+        Public = 16,
+        Private = 32,
+        Protected = 64,
+        Static = 128,
+        Abstract = 256,
+        Async = 512,
+        Default = 1024,
+        MultiLine = 2048,
+        Synthetic = 4096,
+        DeclarationFile = 8192,
+        Let = 16384,
+        Const = 32768,
+        OctalLiteral = 65536,
+        Namespace = 131072,
+        ExportContext = 262144,
+        ContainsThis = 524288,
+        Modifier = 2035,
+        AccessibilityModifier = 112,
+        BlockScoped = 49152,
+    }
+    const enum JsxFlags {
+        None = 0,
+        IntrinsicNamedElement = 1,
+        IntrinsicIndexedElement = 2,
+        ClassElement = 4,
+        UnknownElement = 8,
+        IntrinsicElement = 3,
+    }
+    interface Node extends TextRange {
+        kind: SyntaxKind;
+        flags: NodeFlags;
+        decorators?: NodeArray<Decorator>;
+        modifiers?: ModifiersArray;
+        parent?: Node;
+    }
+    interface NodeArray<T> extends Array<T>, TextRange {
+        hasTrailingComma?: boolean;
+    }
+    interface ModifiersArray extends NodeArray<Node> {
+        flags: number;
+    }
+    interface Identifier extends PrimaryExpression {
+        text: string;
+        originalKeywordKind?: SyntaxKind;
+    }
+    interface QualifiedName extends Node {
+        left: EntityName;
+        right: Identifier;
+    }
+    type EntityName = Identifier | QualifiedName;
+    type DeclarationName = Identifier | LiteralExpression | ComputedPropertyName | BindingPattern;
+    interface Declaration extends Node {
+        _declarationBrand: any;
+        name?: DeclarationName;
+    }
+    interface ComputedPropertyName extends Node {
+        expression: Expression;
+    }
+    interface Decorator extends Node {
+        expression: LeftHandSideExpression;
+    }
+    interface TypeParameterDeclaration extends Declaration {
+        name: Identifier;
+        constraint?: TypeNode;
+        expression?: Expression;
+    }
+    interface SignatureDeclaration extends Declaration {
+        typeParameters?: NodeArray<TypeParameterDeclaration>;
+        parameters: NodeArray<ParameterDeclaration>;
+        type?: TypeNode;
+    }
+    interface VariableDeclaration extends Declaration {
+        parent?: VariableDeclarationList;
+        name: Identifier | BindingPattern;
+        type?: TypeNode;
+        initializer?: Expression;
+    }
+    interface VariableDeclarationList extends Node {
+        declarations: NodeArray<VariableDeclaration>;
+    }
+    interface ParameterDeclaration extends Declaration {
+        dotDotDotToken?: Node;
+        name: Identifier | BindingPattern;
+        questionToken?: Node;
+        type?: TypeNode;
+        initializer?: Expression;
+    }
+    interface BindingElement extends Declaration {
+        propertyName?: Identifier;
+        dotDotDotToken?: Node;
+        name: Identifier | BindingPattern;
+        initializer?: Expression;
+    }
+    interface PropertyDeclaration extends Declaration, ClassElement {
+        name: DeclarationName;
+        questionToken?: Node;
+        type?: TypeNode;
+        initializer?: Expression;
+    }
+    interface ObjectLiteralElement extends Declaration {
+        _objectLiteralBrandBrand: any;
+    }
+    interface PropertyAssignment extends ObjectLiteralElement {
+        _propertyAssignmentBrand: any;
+        name: DeclarationName;
+        questionToken?: Node;
+        initializer: Expression;
+    }
+    interface ShorthandPropertyAssignment extends ObjectLiteralElement {
+        name: Identifier;
+        questionToken?: Node;
+        equalsToken?: Node;
+        objectAssignmentInitializer?: Expression;
+    }
+    interface VariableLikeDeclaration extends Declaration {
+        propertyName?: Identifier;
+        dotDotDotToken?: Node;
+        name: DeclarationName;
+        questionToken?: Node;
+        type?: TypeNode;
+        initializer?: Expression;
+    }
+    interface BindingPattern extends Node {
+        elements: NodeArray<BindingElement>;
+    }
+    /**
+     * Several node kinds share function-like features such as a signature,
+     * a name, and a body. These nodes should extend FunctionLikeDeclaration.
+     * Examples:
+     * - FunctionDeclaration
+     * - MethodDeclaration
+     * - AccessorDeclaration
+     */
+    interface FunctionLikeDeclaration extends SignatureDeclaration {
+        _functionLikeDeclarationBrand: any;
+        asteriskToken?: Node;
+        questionToken?: Node;
+        body?: Block | Expression;
+    }
+    interface FunctionDeclaration extends FunctionLikeDeclaration, Statement {
+        name?: Identifier;
+        body?: Block;
+    }
+    interface MethodDeclaration extends FunctionLikeDeclaration, ClassElement, ObjectLiteralElement {
+        body?: Block;
+    }
+    interface ConstructorDeclaration extends FunctionLikeDeclaration, ClassElement {
+        body?: Block;
+    }
+    interface SemicolonClassElement extends ClassElement {
+        _semicolonClassElementBrand: any;
+    }
+    interface AccessorDeclaration extends FunctionLikeDeclaration, ClassElement, ObjectLiteralElement {
+        _accessorDeclarationBrand: any;
+        body: Block;
+    }
+    interface IndexSignatureDeclaration extends SignatureDeclaration, ClassElement {
+        _indexSignatureDeclarationBrand: any;
+    }
+    interface TypeNode extends Node {
+        _typeNodeBrand: any;
+    }
+    interface FunctionOrConstructorTypeNode extends TypeNode, SignatureDeclaration {
+        _functionOrConstructorTypeNodeBrand: any;
+    }
+    interface TypeReferenceNode extends TypeNode {
+        typeName: EntityName;
+        typeArguments?: NodeArray<TypeNode>;
+    }
+    interface TypePredicateNode extends TypeNode {
+        parameterName: Identifier;
+        type: TypeNode;
+    }
+    interface TypeQueryNode extends TypeNode {
+        exprName: EntityName;
+    }
+    interface TypeLiteralNode extends TypeNode, Declaration {
+        members: NodeArray<Node>;
+    }
+    interface ArrayTypeNode extends TypeNode {
+        elementType: TypeNode;
+    }
+    interface TupleTypeNode extends TypeNode {
+        elementTypes: NodeArray<TypeNode>;
+    }
+    interface UnionOrIntersectionTypeNode extends TypeNode {
+        types: NodeArray<TypeNode>;
+    }
+    interface UnionTypeNode extends UnionOrIntersectionTypeNode {
+    }
+    interface IntersectionTypeNode extends UnionOrIntersectionTypeNode {
+    }
+    interface ParenthesizedTypeNode extends TypeNode {
+        type: TypeNode;
+    }
+    interface StringLiteral extends LiteralExpression, TypeNode {
+        _stringLiteralBrand: any;
+    }
+    interface Expression extends Node {
+        _expressionBrand: any;
+        contextualType?: Type;
+    }
+    interface UnaryExpression extends Expression {
+        _unaryExpressionBrand: any;
+    }
+    interface IncrementExpression extends UnaryExpression {
+        _incrementExpressionBrand: any;
+    }
+    interface PrefixUnaryExpression extends IncrementExpression {
+        operator: SyntaxKind;
+        operand: UnaryExpression;
+    }
+    interface PostfixUnaryExpression extends IncrementExpression {
+        operand: LeftHandSideExpression;
+        operator: SyntaxKind;
+    }
+    interface PostfixExpression extends UnaryExpression {
+        _postfixExpressionBrand: any;
+    }
+    interface LeftHandSideExpression extends IncrementExpression {
+        _leftHandSideExpressionBrand: any;
+    }
+    interface MemberExpression extends LeftHandSideExpression {
+        _memberExpressionBrand: any;
+    }
+    interface PrimaryExpression extends MemberExpression {
+        _primaryExpressionBrand: any;
+    }
+    interface DeleteExpression extends UnaryExpression {
+        expression: UnaryExpression;
+    }
+    interface TypeOfExpression extends UnaryExpression {
+        expression: UnaryExpression;
+    }
+    interface VoidExpression extends UnaryExpression {
+        expression: UnaryExpression;
+    }
+    interface AwaitExpression extends UnaryExpression {
+        expression: UnaryExpression;
+    }
+    interface YieldExpression extends Expression {
+        asteriskToken?: Node;
+        expression?: Expression;
+    }
+    interface BinaryExpression extends Expression {
+        left: Expression;
+        operatorToken: Node;
+        right: Expression;
+    }
+    interface ConditionalExpression extends Expression {
+        condition: Expression;
+        questionToken: Node;
+        whenTrue: Expression;
+        colonToken: Node;
+        whenFalse: Expression;
+    }
+    interface FunctionExpression extends PrimaryExpression, FunctionLikeDeclaration {
+        name?: Identifier;
+        body: Block | Expression;
+    }
+    interface ArrowFunction extends Expression, FunctionLikeDeclaration {
+        equalsGreaterThanToken: Node;
+    }
+    interface LiteralExpression extends PrimaryExpression {
+        text: string;
+        isUnterminated?: boolean;
+        hasExtendedUnicodeEscape?: boolean;
+    }
+    interface TemplateExpression extends PrimaryExpression {
+        head: LiteralExpression;
+        templateSpans: NodeArray<TemplateSpan>;
+    }
+    interface TemplateSpan extends Node {
+        expression: Expression;
+        literal: LiteralExpression;
+    }
+    interface ParenthesizedExpression extends PrimaryExpression {
+        expression: Expression;
+    }
+    interface ArrayLiteralExpression extends PrimaryExpression {
+        elements: NodeArray<Expression>;
+    }
+    interface SpreadElementExpression extends Expression {
+        expression: Expression;
+    }
+    interface ObjectLiteralExpression extends PrimaryExpression, Declaration {
+        properties: NodeArray<ObjectLiteralElement>;
+    }
+    interface PropertyAccessExpression extends MemberExpression {
+        expression: LeftHandSideExpression;
+        dotToken: Node;
+        name: Identifier;
+    }
+    interface ElementAccessExpression extends MemberExpression {
+        expression: LeftHandSideExpression;
+        argumentExpression?: Expression;
+    }
+    interface CallExpression extends LeftHandSideExpression {
+        expression: LeftHandSideExpression;
+        typeArguments?: NodeArray<TypeNode>;
+        arguments: NodeArray<Expression>;
+    }
+    interface ExpressionWithTypeArguments extends TypeNode {
+        expression: LeftHandSideExpression;
+        typeArguments?: NodeArray<TypeNode>;
+    }
+    interface NewExpression extends CallExpression, PrimaryExpression {
+    }
+    interface TaggedTemplateExpression extends MemberExpression {
+        tag: LeftHandSideExpression;
+        template: LiteralExpression | TemplateExpression;
+    }
+    type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator;
+    interface AsExpression extends Expression {
+        expression: Expression;
+        type: TypeNode;
+    }
+    interface TypeAssertion extends UnaryExpression {
+        type: TypeNode;
+        expression: UnaryExpression;
+    }
+    type AssertionExpression = TypeAssertion | AsExpression;
+    interface JsxElement extends PrimaryExpression {
+        openingElement: JsxOpeningElement;
+        children: NodeArray<JsxChild>;
+        closingElement: JsxClosingElement;
+    }
+    interface JsxOpeningElement extends Expression {
+        _openingElementBrand?: any;
+        tagName: EntityName;
+        attributes: NodeArray<JsxAttribute | JsxSpreadAttribute>;
+    }
+    interface JsxSelfClosingElement extends PrimaryExpression, JsxOpeningElement {
+        _selfClosingElementBrand?: any;
+    }
+    type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement;
+    interface JsxAttribute extends Node {
+        name: Identifier;
+        initializer?: Expression;
+    }
+    interface JsxSpreadAttribute extends Node {
+        expression: Expression;
+    }
+    interface JsxClosingElement extends Node {
+        tagName: EntityName;
+    }
+    interface JsxExpression extends Expression {
+        expression?: Expression;
+    }
+    interface JsxText extends Node {
+        _jsxTextExpressionBrand: any;
+    }
+    type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement;
+    interface Statement extends Node {
+        _statementBrand: any;
+    }
+    interface Block extends Statement {
+        statements: NodeArray<Statement>;
+    }
+    interface VariableStatement extends Statement {
+        declarationList: VariableDeclarationList;
+    }
+    interface ExpressionStatement extends Statement {
+        expression: Expression;
+    }
+    interface IfStatement extends Statement {
+        expression: Expression;
+        thenStatement: Statement;
+        elseStatement?: Statement;
+    }
+    interface IterationStatement extends Statement {
+        statement: Statement;
+    }
+    interface DoStatement extends IterationStatement {
+        expression: Expression;
+    }
+    interface WhileStatement extends IterationStatement {
+        expression: Expression;
+    }
+    interface ForStatement extends IterationStatement {
+        initializer?: VariableDeclarationList | Expression;
+        condition?: Expression;
+        incrementor?: Expression;
+    }
+    interface ForInStatement extends IterationStatement {
+        initializer: VariableDeclarationList | Expression;
+        expression: Expression;
+    }
+    interface ForOfStatement extends IterationStatement {
+        initializer: VariableDeclarationList | Expression;
+        expression: Expression;
+    }
+    interface BreakOrContinueStatement extends Statement {
+        label?: Identifier;
+    }
+    interface ReturnStatement extends Statement {
+        expression?: Expression;
+    }
+    interface WithStatement extends Statement {
+        expression: Expression;
+        statement: Statement;
+    }
+    interface SwitchStatement extends Statement {
+        expression: Expression;
+        caseBlock: CaseBlock;
+    }
+    interface CaseBlock extends Node {
+        clauses: NodeArray<CaseOrDefaultClause>;
+    }
+    interface CaseClause extends Node {
+        expression?: Expression;
+        statements: NodeArray<Statement>;
+    }
+    interface DefaultClause extends Node {
+        statements: NodeArray<Statement>;
+    }
+    type CaseOrDefaultClause = CaseClause | DefaultClause;
+    interface LabeledStatement extends Statement {
+        label: Identifier;
+        statement: Statement;
+    }
+    interface ThrowStatement extends Statement {
+        expression: Expression;
+    }
+    interface TryStatement extends Statement {
+        tryBlock: Block;
+        catchClause?: CatchClause;
+        finallyBlock?: Block;
+    }
+    interface CatchClause extends Node {
+        variableDeclaration: VariableDeclaration;
+        block: Block;
+    }
+    interface ClassLikeDeclaration extends Declaration {
+        name?: Identifier;
+        typeParameters?: NodeArray<TypeParameterDeclaration>;
+        heritageClauses?: NodeArray<HeritageClause>;
+        members: NodeArray<ClassElement>;
+    }
+    interface ClassDeclaration extends ClassLikeDeclaration, Statement {
+    }
+    interface ClassExpression extends ClassLikeDeclaration, PrimaryExpression {
+    }
+    interface ClassElement extends Declaration {
+        _classElementBrand: any;
+    }
+    interface InterfaceDeclaration extends Declaration, Statement {
+        name: Identifier;
+        typeParameters?: NodeArray<TypeParameterDeclaration>;
+        heritageClauses?: NodeArray<HeritageClause>;
+        members: NodeArray<Declaration>;
+    }
+    interface HeritageClause extends Node {
+        token: SyntaxKind;
+        types?: NodeArray<ExpressionWithTypeArguments>;
+    }
+    interface TypeAliasDeclaration extends Declaration, Statement {
+        name: Identifier;
+        typeParameters?: NodeArray<TypeParameterDeclaration>;
+        type: TypeNode;
+    }
+    interface EnumMember extends Declaration {
+        name: DeclarationName;
+        initializer?: Expression;
+    }
+    interface EnumDeclaration extends Declaration, Statement {
+        name: Identifier;
+        members: NodeArray<EnumMember>;
+    }
+    interface ModuleDeclaration extends Declaration, Statement {
+        name: Identifier | LiteralExpression;
+        body: ModuleBlock | ModuleDeclaration;
+    }
+    interface ModuleBlock extends Node, Statement {
+        statements: NodeArray<Statement>;
+    }
+    interface ImportEqualsDeclaration extends Declaration, Statement {
+        name: Identifier;
+        moduleReference: EntityName | ExternalModuleReference;
+    }
+    interface ExternalModuleReference extends Node {
+        expression?: Expression;
+    }
+    interface ImportDeclaration extends Statement {
+        importClause?: ImportClause;
+        moduleSpecifier: Expression;
+    }
+    interface ImportClause extends Declaration {
+        name?: Identifier;
+        namedBindings?: NamespaceImport | NamedImports;
+    }
+    interface NamespaceImport extends Declaration {
+        name: Identifier;
+    }
+    interface ExportDeclaration extends Declaration, Statement {
+        exportClause?: NamedExports;
+        moduleSpecifier?: Expression;
+    }
+    interface NamedImportsOrExports extends Node {
+        elements: NodeArray<ImportOrExportSpecifier>;
+    }
+    type NamedImports = NamedImportsOrExports;
+    type NamedExports = NamedImportsOrExports;
+    interface ImportOrExportSpecifier extends Declaration {
+        propertyName?: Identifier;
+        name: Identifier;
+    }
+    type ImportSpecifier = ImportOrExportSpecifier;
+    type ExportSpecifier = ImportOrExportSpecifier;
+    interface ExportAssignment extends Declaration, Statement {
+        isExportEquals?: boolean;
+        expression: Expression;
+    }
+    interface FileReference extends TextRange {
+        fileName: string;
+    }
+    interface CommentRange extends TextRange {
+        hasTrailingNewLine?: boolean;
+        kind: SyntaxKind;
+    }
+    interface JSDocTypeExpression extends Node {
+        type: JSDocType;
+    }
+    interface JSDocType extends TypeNode {
+        _jsDocTypeBrand: any;
+    }
+    interface JSDocAllType extends JSDocType {
+        _JSDocAllTypeBrand: any;
+    }
+    interface JSDocUnknownType extends JSDocType {
+        _JSDocUnknownTypeBrand: any;
+    }
+    interface JSDocArrayType extends JSDocType {
+        elementType: JSDocType;
+    }
+    interface JSDocUnionType extends JSDocType {
+        types: NodeArray<JSDocType>;
+    }
+    interface JSDocTupleType extends JSDocType {
+        types: NodeArray<JSDocType>;
+    }
+    interface JSDocNonNullableType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocNullableType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocRecordType extends JSDocType, TypeLiteralNode {
+        members: NodeArray<JSDocRecordMember>;
+    }
+    interface JSDocTypeReference extends JSDocType {
+        name: EntityName;
+        typeArguments: NodeArray<JSDocType>;
+    }
+    interface JSDocOptionalType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocFunctionType extends JSDocType, SignatureDeclaration {
+        parameters: NodeArray<ParameterDeclaration>;
+        type: JSDocType;
+    }
+    interface JSDocVariadicType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocConstructorType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocThisType extends JSDocType {
+        type: JSDocType;
+    }
+    interface JSDocRecordMember extends PropertyDeclaration {
+        name: Identifier | LiteralExpression;
+        type?: JSDocType;
+    }
+    interface JSDocComment extends Node {
+        tags: NodeArray<JSDocTag>;
+    }
+    interface JSDocTag extends Node {
+        atToken: Node;
+        tagName: Identifier;
+    }
+    interface JSDocTemplateTag extends JSDocTag {
+        typeParameters: NodeArray<TypeParameterDeclaration>;
+    }
+    interface JSDocReturnTag extends JSDocTag {
+        typeExpression: JSDocTypeExpression;
+    }
+    interface JSDocTypeTag extends JSDocTag {
+        typeExpression: JSDocTypeExpression;
+    }
+    interface JSDocParameterTag extends JSDocTag {
+        preParameterName?: Identifier;
+        typeExpression?: JSDocTypeExpression;
+        postParameterName?: Identifier;
+        isBracketed: boolean;
+    }
+    interface SourceFile extends Declaration {
+        statements: NodeArray<Statement>;
+        endOfFileToken: Node;
+        fileName: string;
+        text: string;
+        amdDependencies: {
+            path: string;
+            name: string;
+        }[];
+        moduleName: string;
+        referencedFiles: FileReference[];
+        languageVariant: LanguageVariant;
+        /**
+         * lib.d.ts should have a reference comment like
+         *
+         *  /// <reference no-default-lib="true"/>
+         *
+         * If any other file has this comment, it signals not to include lib.d.ts
+         * because this containing file is intended to act as a default library.
+         */
+        hasNoDefaultLib: boolean;
+        languageVersion: ScriptTarget;
+    }
+    interface ScriptReferenceHost {
+        getCompilerOptions(): CompilerOptions;
+        getSourceFile(fileName: string): SourceFile;
+        getCurrentDirectory(): string;
+    }
+    interface ParseConfigHost extends ModuleResolutionHost {
+        readDirectory(rootDir: string, extension: string, exclude: string[]): string[];
+    }
+    interface WriteFileCallback {
+        (fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void): void;
+    }
+    class OperationCanceledException {
+    }
+    interface CancellationToken {
+        isCancellationRequested(): boolean;
+        /** @throws OperationCanceledException if isCancellationRequested is true */
+        throwIfCancellationRequested(): void;
+    }
+    interface Program extends ScriptReferenceHost {
+        /**
+         * Get a list of root file names that were passed to a 'createProgram'
+         */
+        getRootFileNames(): string[];
+        /**
+         * Get a list of files in the program
+         */
+        getSourceFiles(): SourceFile[];
+        /**
+         * Emits the JavaScript and declaration files.  If targetSourceFile is not specified, then
+         * the JavaScript and declaration files will be produced for all the files in this program.
+         * If targetSourceFile is specified, then only the JavaScript and declaration for that
+         * specific file will be generated.
+         *
+         * If writeFile is not specified then the writeFile callback from the compiler host will be
+         * used for writing the JavaScript and declaration files.  Otherwise, the writeFile parameter
+         * will be invoked when writing the JavaScript and declaration files.
+         */
+        emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken): EmitResult;
+        getOptionsDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
+        getGlobalDiagnostics(cancellationToken?: CancellationToken): Diagnostic[];
+        getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
+        getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
+        getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
+        /**
+         * Gets a type checker that can be used to semantically analyze source fils in the program.
+         */
+        getTypeChecker(): TypeChecker;
+    }
+    interface SourceMapSpan {
+        /** Line number in the .js file. */
+        emittedLine: number;
+        /** Column number in the .js file. */
+        emittedColumn: number;
+        /** Line number in the .ts file. */
+        sourceLine: number;
+        /** Column number in the .ts file. */
+        sourceColumn: number;
+        /** Optional name (index into names array) associated with this span. */
+        nameIndex?: number;
+        /** .ts file (index into sources array) associated with this span */
+        sourceIndex: number;
+    }
+    interface SourceMapData {
+        sourceMapFilePath: string;
+        jsSourceMappingURL: string;
+        sourceMapFile: string;
+        sourceMapSourceRoot: string;
+        sourceMapSources: string[];
+        sourceMapSourcesContent?: string[];
+        inputSourceFileNames: string[];
+        sourceMapNames?: string[];
+        sourceMapMappings: string;
+        sourceMapDecodedMappings: SourceMapSpan[];
+    }
+    /** Return code used by getEmitOutput function to indicate status of the function */
+    enum ExitStatus {
+        Success = 0,
+        DiagnosticsPresent_OutputsSkipped = 1,
+        DiagnosticsPresent_OutputsGenerated = 2,
+    }
+    interface EmitResult {
+        emitSkipped: boolean;
+        diagnostics: Diagnostic[];
+    }
+    interface TypeChecker {
+        getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type;
+        getDeclaredTypeOfSymbol(symbol: Symbol): Type;
+        getPropertiesOfType(type: Type): Symbol[];
+        getPropertyOfType(type: Type, propertyName: string): Symbol;
+        getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
+        getIndexTypeOfType(type: Type, kind: IndexKind): Type;
+        getBaseTypes(type: InterfaceType): ObjectType[];
+        getReturnTypeOfSignature(signature: Signature): Type;
+        getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[];
+        getSymbolAtLocation(node: Node): Symbol;
+        getShorthandAssignmentValueSymbol(location: Node): Symbol;
+        getTypeAtLocation(node: Node): Type;
+        typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
+        symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
+        getSymbolDisplayBuilder(): SymbolDisplayBuilder;
+        getFullyQualifiedName(symbol: Symbol): string;
+        getAugmentedPropertiesOfType(type: Type): Symbol[];
+        getRootSymbols(symbol: Symbol): Symbol[];
+        getContextualType(node: Expression): Type;
+        getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature;
+        getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature;
+        isImplementationOfOverload(node: FunctionLikeDeclaration): boolean;
+        isUndefinedSymbol(symbol: Symbol): boolean;
+        isArgumentsSymbol(symbol: Symbol): boolean;
+        getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
+        isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
+        getAliasedSymbol(symbol: Symbol): Symbol;
+        getExportsOfModule(moduleSymbol: Symbol): Symbol[];
+        getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type;
+        getJsxIntrinsicTagNames(): Symbol[];
+        isOptionalParameter(node: ParameterDeclaration): boolean;
+    }
+    interface SymbolDisplayBuilder {
+        buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void;
+        buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaraiton?: Node, flags?: TypeFormatFlags): void;
+        buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+        buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
+    }
+    interface SymbolWriter {
+        writeKeyword(text: string): void;
+        writeOperator(text: string): void;
+        writePunctuation(text: string): void;
+        writeSpace(text: string): void;
+        writeStringLiteral(text: string): void;
+        writeParameter(text: string): void;
+        writeSymbol(text: string, symbol: Symbol): void;
+        writeLine(): void;
+        increaseIndent(): void;
+        decreaseIndent(): void;
+        clear(): void;
+        trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): void;
+        reportInaccessibleThisError(): void;
+    }
+    const enum TypeFormatFlags {
+        None = 0,
+        WriteArrayAsGenericType = 1,
+        UseTypeOfFunction = 2,
+        NoTruncation = 4,
+        WriteArrowStyleSignature = 8,
+        WriteOwnNameForAnyLike = 16,
+        WriteTypeArgumentsOfSignature = 32,
+        InElementType = 64,
+        UseFullyQualifiedType = 128,
+    }
+    const enum SymbolFormatFlags {
+        None = 0,
+        WriteTypeParametersOrArguments = 1,
+        UseOnlyExternalAliasing = 2,
+    }
+    interface TypePredicate {
+        parameterName: string;
+        parameterIndex: number;
+        type: Type;
+    }
+    const enum SymbolFlags {
+        None = 0,
+        FunctionScopedVariable = 1,
+        BlockScopedVariable = 2,
+        Property = 4,
+        EnumMember = 8,
+        Function = 16,
+        Class = 32,
+        Interface = 64,
+        ConstEnum = 128,
+        RegularEnum = 256,
+        ValueModule = 512,
+        NamespaceModule = 1024,
+        TypeLiteral = 2048,
+        ObjectLiteral = 4096,
+        Method = 8192,
+        Constructor = 16384,
+        GetAccessor = 32768,
+        SetAccessor = 65536,
+        Signature = 131072,
+        TypeParameter = 262144,
+        TypeAlias = 524288,
+        ExportValue = 1048576,
+        ExportType = 2097152,
+        ExportNamespace = 4194304,
+        Alias = 8388608,
+        Instantiated = 16777216,
+        Merged = 33554432,
+        Transient = 67108864,
+        Prototype = 134217728,
+        SyntheticProperty = 268435456,
+        Optional = 536870912,
+        ExportStar = 1073741824,
+        Enum = 384,
+        Variable = 3,
+        Value = 107455,
+        Type = 793056,
+        Namespace = 1536,
+        Module = 1536,
+        Accessor = 98304,
+        FunctionScopedVariableExcludes = 107454,
+        BlockScopedVariableExcludes = 107455,
+        ParameterExcludes = 107455,
+        PropertyExcludes = 107455,
+        EnumMemberExcludes = 107455,
+        FunctionExcludes = 106927,
+        ClassExcludes = 899519,
+        InterfaceExcludes = 792960,
+        RegularEnumExcludes = 899327,
+        ConstEnumExcludes = 899967,
+        ValueModuleExcludes = 106639,
+        NamespaceModuleExcludes = 0,
+        MethodExcludes = 99263,
+        GetAccessorExcludes = 41919,
+        SetAccessorExcludes = 74687,
+        TypeParameterExcludes = 530912,
+        TypeAliasExcludes = 793056,
+        AliasExcludes = 8388608,
+        ModuleMember = 8914931,
+        ExportHasLocal = 944,
+        HasExports = 1952,
+        HasMembers = 6240,
+        BlockScoped = 418,
+        PropertyOrAccessor = 98308,
+        Export = 7340032,
+    }
+    interface Symbol {
+        flags: SymbolFlags;
+        name: string;
+        declarations?: Declaration[];
+        valueDeclaration?: Declaration;
+        members?: SymbolTable;
+        exports?: SymbolTable;
+    }
+    interface SymbolTable {
+        [index: string]: Symbol;
+    }
+    const enum TypeFlags {
+        Any = 1,
+        String = 2,
+        Number = 4,
+        Boolean = 8,
+        Void = 16,
+        Undefined = 32,
+        Null = 64,
+        Enum = 128,
+        StringLiteral = 256,
+        TypeParameter = 512,
+        Class = 1024,
+        Interface = 2048,
+        Reference = 4096,
+        Tuple = 8192,
+        Union = 16384,
+        Intersection = 32768,
+        Anonymous = 65536,
+        Instantiated = 131072,
+        ObjectLiteral = 524288,
+        ESSymbol = 16777216,
+        ThisType = 33554432,
+        StringLike = 258,
+        NumberLike = 132,
+        ObjectType = 80896,
+        UnionOrIntersection = 49152,
+        StructuredType = 130048,
+    }
+    type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
+    interface Type {
+        flags: TypeFlags;
+        symbol?: Symbol;
+        pattern?: DestructuringPattern;
+    }
+    interface StringLiteralType extends Type {
+        text: string;
+    }
+    interface ObjectType extends Type {
+    }
+    interface InterfaceType extends ObjectType {
+        typeParameters: TypeParameter[];
+        outerTypeParameters: TypeParameter[];
+        localTypeParameters: TypeParameter[];
+        thisType: TypeParameter;
+    }
+    interface InterfaceTypeWithDeclaredMembers extends InterfaceType {
+        declaredProperties: Symbol[];
+        declaredCallSignatures: Signature[];
+        declaredConstructSignatures: Signature[];
+        declaredStringIndexType: Type;
+        declaredNumberIndexType: Type;
+    }
+    interface TypeReference extends ObjectType {
+        target: GenericType;
+        typeArguments: Type[];
+    }
+    interface GenericType extends InterfaceType, TypeReference {
+    }
+    interface TupleType extends ObjectType {
+        elementTypes: Type[];
+    }
+    interface UnionOrIntersectionType extends Type {
+        types: Type[];
+    }
+    interface UnionType extends UnionOrIntersectionType {
+    }
+    interface IntersectionType extends UnionOrIntersectionType {
+    }
+    interface TypeParameter extends Type {
+        constraint: Type;
+    }
+    const enum SignatureKind {
+        Call = 0,
+        Construct = 1,
+    }
+    interface Signature {
+        declaration: SignatureDeclaration;
+        typeParameters: TypeParameter[];
+        parameters: Symbol[];
+        typePredicate?: TypePredicate;
+    }
+    const enum IndexKind {
+        String = 0,
+        Number = 1,
+    }
+    interface DiagnosticMessage {
+        key: string;
+        category: DiagnosticCategory;
+        code: number;
+    }
+    /**
+     * A linked list of formatted diagnostic messages to be used as part of a multiline message.
+     * It is built from the bottom up, leaving the head to be the "main" diagnostic.
+     * While it seems that DiagnosticMessageChain is structurally similar to DiagnosticMessage,
+     * the difference is that messages are all preformatted in DMC.
+     */
+    interface DiagnosticMessageChain {
+        messageText: string;
+        category: DiagnosticCategory;
+        code: number;
+        next?: DiagnosticMessageChain;
+    }
+    interface Diagnostic {
+        file: SourceFile;
+        start: number;
+        length: number;
+        messageText: string | DiagnosticMessageChain;
+        category: DiagnosticCategory;
+        code: number;
+    }
+    enum DiagnosticCategory {
+        Warning = 0,
+        Error = 1,
+        Message = 2,
+    }
+    const enum ModuleResolutionKind {
+        Classic = 1,
+        NodeJs = 2,
+    }
+    interface CompilerOptions {
+        allowNonTsExtensions?: boolean;
+        charset?: string;
+        declaration?: boolean;
+        diagnostics?: boolean;
+        emitBOM?: boolean;
+        help?: boolean;
+        init?: boolean;
+        inlineSourceMap?: boolean;
+        inlineSources?: boolean;
+        jsx?: JsxEmit;
+        listFiles?: boolean;
+        locale?: string;
+        mapRoot?: string;
+        module?: ModuleKind;
+        newLine?: NewLineKind;
+        noEmit?: boolean;
+        noEmitHelpers?: boolean;
+        noEmitOnError?: boolean;
+        noErrorTruncation?: boolean;
+        noImplicitAny?: boolean;
+        noLib?: boolean;
+        noResolve?: boolean;
+        out?: string;
+        outFile?: string;
+        outDir?: string;
+        preserveConstEnums?: boolean;
+        project?: string;
+        removeComments?: boolean;
+        rootDir?: string;
+        sourceMap?: boolean;
+        sourceRoot?: string;
+        suppressExcessPropertyErrors?: boolean;
+        suppressImplicitAnyIndexErrors?: boolean;
+        target?: ScriptTarget;
+        version?: boolean;
+        watch?: boolean;
+        isolatedModules?: boolean;
+        experimentalDecorators?: boolean;
+        emitDecoratorMetadata?: boolean;
+        moduleResolution?: ModuleResolutionKind;
+        [option: string]: string | number | boolean;
+    }
+    const enum ModuleKind {
+        None = 0,
+        CommonJS = 1,
+        AMD = 2,
+        UMD = 3,
+        System = 4,
+        ES6 = 5,
+        ES2015 = 5,
+    }
+    const enum JsxEmit {
+        None = 0,
+        Preserve = 1,
+        React = 2,
+    }
+    const enum NewLineKind {
+        CarriageReturnLineFeed = 0,
+        LineFeed = 1,
+    }
+    interface LineAndCharacter {
+        line: number;
+        character: number;
+    }
+    const enum ScriptTarget {
+        ES3 = 0,
+        ES5 = 1,
+        ES6 = 2,
+        ES2015 = 2,
+        Latest = 2,
+    }
+    const enum LanguageVariant {
+        Standard = 0,
+        JSX = 1,
+    }
+    interface ParsedCommandLine {
+        options: CompilerOptions;
+        fileNames: string[];
+        errors: Diagnostic[];
+    }
+    interface ModuleResolutionHost {
+        fileExists(fileName: string): boolean;
+        readFile(fileName: string): string;
+    }
+    interface ResolvedModule {
+        resolvedFileName: string;
+        isExternalLibraryImport?: boolean;
+    }
+    interface ResolvedModuleWithFailedLookupLocations {
+        resolvedModule: ResolvedModule;
+        failedLookupLocations: string[];
+    }
+    interface CompilerHost extends ModuleResolutionHost {
+        getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
+        getCancellationToken?(): CancellationToken;
+        getDefaultLibFileName(options: CompilerOptions): string;
+        writeFile: WriteFileCallback;
+        getCurrentDirectory(): string;
+        getCanonicalFileName(fileName: string): string;
+        useCaseSensitiveFileNames(): boolean;
+        getNewLine(): string;
+        resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
+    }
+    interface TextSpan {
+        start: number;
+        length: number;
+    }
+    interface TextChangeRange {
+        span: TextSpan;
+        newLength: number;
+    }
+}
+declare namespace ts {
+    interface System {
+        args: string[];
+        newLine: string;
+        useCaseSensitiveFileNames: boolean;
+        write(s: string): void;
+        readFile(path: string, encoding?: string): string;
+        writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
+        watchFile?(path: string, callback: (path: string, removed?: boolean) => void): FileWatcher;
+        watchDirectory?(path: string, callback: (path: string) => void, recursive?: boolean): FileWatcher;
+        resolvePath(path: string): string;
+        fileExists(path: string): boolean;
+        directoryExists(path: string): boolean;
+        createDirectory(path: string): void;
+        getExecutingFilePath(): string;
+        getCurrentDirectory(): string;
+        readDirectory(path: string, extension?: string, exclude?: string[]): string[];
+        getMemoryUsage?(): number;
+        exit(exitCode?: number): void;
+    }
+    interface FileWatcher {
+        close(): void;
+    }
+    var sys: System;
+}
+declare namespace ts {
+    interface ErrorCallback {
+        (message: DiagnosticMessage, length: number): void;
+    }
+    interface Scanner {
+        getStartPos(): number;
+        getToken(): SyntaxKind;
+        getTextPos(): number;
+        getTokenPos(): number;
+        getTokenText(): string;
+        getTokenValue(): string;
+        hasExtendedUnicodeEscape(): boolean;
+        hasPrecedingLineBreak(): boolean;
+        isIdentifier(): boolean;
+        isReservedWord(): boolean;
+        isUnterminated(): boolean;
+        reScanGreaterToken(): SyntaxKind;
+        reScanSlashToken(): SyntaxKind;
+        reScanTemplateToken(): SyntaxKind;
+        scanJsxIdentifier(): SyntaxKind;
+        reScanJsxToken(): SyntaxKind;
+        scanJsxToken(): SyntaxKind;
+        scan(): SyntaxKind;
+        setText(text: string, start?: number, length?: number): void;
+        setOnError(onError: ErrorCallback): void;
+        setScriptTarget(scriptTarget: ScriptTarget): void;
+        setLanguageVariant(variant: LanguageVariant): void;
+        setTextPos(textPos: number): void;
+        lookAhead<T>(callback: () => T): T;
+        tryScan<T>(callback: () => T): T;
+    }
+    function tokenToString(t: SyntaxKind): string;
+    function getPositionOfLineAndCharacter(sourceFile: SourceFile, line: number, character: number): number;
+    function getLineAndCharacterOfPosition(sourceFile: SourceFile, position: number): LineAndCharacter;
+    function isWhiteSpace(ch: number): boolean;
+    function isLineBreak(ch: number): boolean;
+    function couldStartTrivia(text: string, pos: number): boolean;
+    function getLeadingCommentRanges(text: string, pos: number): CommentRange[];
+    function getTrailingCommentRanges(text: string, pos: number): CommentRange[];
+    /** Optionally, get the shebang */
+    function getShebang(text: string): string;
+    function isIdentifierStart(ch: number, languageVersion: ScriptTarget): boolean;
+    function isIdentifierPart(ch: number, languageVersion: ScriptTarget): boolean;
+    function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean, languageVariant?: LanguageVariant, text?: string, onError?: ErrorCallback, start?: number, length?: number): Scanner;
+}
+declare namespace ts {
+    function getDefaultLibFileName(options: CompilerOptions): string;
+    function textSpanEnd(span: TextSpan): number;
+    function textSpanIsEmpty(span: TextSpan): boolean;
+    function textSpanContainsPosition(span: TextSpan, position: number): boolean;
+    function textSpanContainsTextSpan(span: TextSpan, other: TextSpan): boolean;
+    function textSpanOverlapsWith(span: TextSpan, other: TextSpan): boolean;
+    function textSpanOverlap(span1: TextSpan, span2: TextSpan): TextSpan;
+    function textSpanIntersectsWithTextSpan(span: TextSpan, other: TextSpan): boolean;
+    function textSpanIntersectsWith(span: TextSpan, start: number, length: number): boolean;
+    function decodedTextSpanIntersectsWith(start1: number, length1: number, start2: number, length2: number): boolean;
+    function textSpanIntersectsWithPosition(span: TextSpan, position: number): boolean;
+    function textSpanIntersection(span1: TextSpan, span2: TextSpan): TextSpan;
+    function createTextSpan(start: number, length: number): TextSpan;
+    function createTextSpanFromBounds(start: number, end: number): TextSpan;
+    function textChangeRangeNewSpan(range: TextChangeRange): TextSpan;
+    function textChangeRangeIsUnchanged(range: TextChangeRange): boolean;
+    function createTextChangeRange(span: TextSpan, newLength: number): TextChangeRange;
+    let unchangedTextChangeRange: TextChangeRange;
+    /**
+     * Called to merge all the changes that occurred across several versions of a script snapshot
+     * into a single change.  i.e. if a user keeps making successive edits to a script we will
+     * have a text change from V1 to V2, V2 to V3, ..., Vn.
+     *
+     * This function will then merge those changes into a single change range valid between V1 and
+     * Vn.
+     */
+    function collapseTextChangeRangesAcrossMultipleVersions(changes: TextChangeRange[]): TextChangeRange;
+    function getTypeParameterOwner(d: Declaration): Declaration;
+    function arrayStructurallyIsEqualTo<T>(array1: Array<T>, array2: Array<T>): boolean;
+}
+declare namespace ts {
+    function getNodeConstructor(kind: SyntaxKind): new () => Node;
+    function createNode(kind: SyntaxKind): Node;
+    function forEachChild<T>(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T;
+    function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes?: boolean): SourceFile;
+    function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile;
+}
+declare namespace ts {
+    const version: string;
+    function findConfigFile(searchPath: string): string;
+    function resolveTripleslashReference(moduleName: string, containingFile: string): string;
+    function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations;
+    function nodeModuleNameResolver(moduleName: string, containingFile: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations;
+    function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations;
+    function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost;
+    function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[];
+    function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string;
+    function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program;
+}
+declare namespace ts {
+    function parseCommandLine(commandLine: string[], readFile?: (path: string) => string): ParsedCommandLine;
+    /**
+      * Read tsconfig.json file
+      * @param fileName The path to the config file
+      */
+    function readConfigFile(fileName: string, readFile: (path: string) => string): {
+        config?: any;
+        error?: Diagnostic;
+    };
+    /**
+      * Parse the text of the tsconfig.json file
+      * @param fileName The path to the config file
+      * @param jsonText The text of the config file
+      */
+    function parseConfigFileTextToJson(fileName: string, jsonText: string): {
+        config?: any;
+        error?: Diagnostic;
+    };
+    /**
+      * Parse the contents of a config file (tsconfig.json).
+      * @param json The contents of the config file to parse
+      * @param basePath A root directory to resolve relative path entries in the config
+      *    file to. e.g. outDir
+      */
+    function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string): ParsedCommandLine;
+}
+declare namespace ts {
+    /** The version of the language service API */
+    let servicesVersion: string;
+    interface Node {
+        getSourceFile(): SourceFile;
+        getChildCount(sourceFile?: SourceFile): number;
+        getChildAt(index: number, sourceFile?: SourceFile): Node;
+        getChildren(sourceFile?: SourceFile): Node[];
+        getStart(sourceFile?: SourceFile): number;
+        getFullStart(): number;
+        getEnd(): number;
+        getWidth(sourceFile?: SourceFile): number;
+        getFullWidth(): number;
+        getLeadingTriviaWidth(sourceFile?: SourceFile): number;
+        getFullText(sourceFile?: SourceFile): string;
+        getText(sourceFile?: SourceFile): string;
+        getFirstToken(sourceFile?: SourceFile): Node;
+        getLastToken(sourceFile?: SourceFile): Node;
+    }
+    interface Symbol {
+        getFlags(): SymbolFlags;
+        getName(): string;
+        getDeclarations(): Declaration[];
+        getDocumentationComment(): SymbolDisplayPart[];
+    }
+    interface Type {
+        getFlags(): TypeFlags;
+        getSymbol(): Symbol;
+        getProperties(): Symbol[];
+        getProperty(propertyName: string): Symbol;
+        getApparentProperties(): Symbol[];
+        getCallSignatures(): Signature[];
+        getConstructSignatures(): Signature[];
+        getStringIndexType(): Type;
+        getNumberIndexType(): Type;
+        getBaseTypes(): ObjectType[];
+    }
+    interface Signature {
+        getDeclaration(): SignatureDeclaration;
+        getTypeParameters(): Type[];
+        getParameters(): Symbol[];
+        getReturnType(): Type;
+        getDocumentationComment(): SymbolDisplayPart[];
+    }
+    interface SourceFile {
+        getLineAndCharacterOfPosition(pos: number): LineAndCharacter;
+        getLineStarts(): number[];
+        getPositionOfLineAndCharacter(line: number, character: number): number;
+        update(newText: string, textChangeRange: TextChangeRange): SourceFile;
+    }
+    /**
+     * Represents an immutable snapshot of a script at a specified time.Once acquired, the
+     * snapshot is observably immutable. i.e. the same calls with the same parameters will return
+     * the same values.
+     */
+    interface IScriptSnapshot {
+        /** Gets a portion of the script snapshot specified by [start, end). */
+        getText(start: number, end: number): string;
+        /** Gets the length of this script snapshot. */
+        getLength(): number;
+        /**
+         * Gets the TextChangeRange that describe how the text changed between this text and
+         * an older version.  This information is used by the incremental parser to determine
+         * what sections of the script need to be re-parsed.  'undefined' can be returned if the
+         * change range cannot be determined.  However, in that case, incremental parsing will
+         * not happen and the entire document will be re - parsed.
+         */
+        getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange;
+        /** Releases all resources held by this script snapshot */
+        dispose?(): void;
+    }
+    module ScriptSnapshot {
+        function fromString(text: string): IScriptSnapshot;
+    }
+    interface PreProcessedFileInfo {
+        referencedFiles: FileReference[];
+        importedFiles: FileReference[];
+        ambientExternalModules: string[];
+        isLibFile: boolean;
+    }
+    interface HostCancellationToken {
+        isCancellationRequested(): boolean;
+    }
+    interface LanguageServiceHost {
+        getCompilationSettings(): CompilerOptions;
+        getNewLine?(): string;
+        getProjectVersion?(): string;
+        getScriptFileNames(): string[];
+        getScriptVersion(fileName: string): string;
+        getScriptSnapshot(fileName: string): IScriptSnapshot;
+        getLocalizedDiagnosticMessages?(): any;
+        getCancellationToken?(): HostCancellationToken;
+        getCurrentDirectory(): string;
+        getDefaultLibFileName(options: CompilerOptions): string;
+        log?(s: string): void;
+        trace?(s: string): void;
+        error?(s: string): void;
+        useCaseSensitiveFileNames?(): boolean;
+        resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
+    }
+    interface LanguageService {
+        cleanupSemanticCache(): void;
+        getSyntacticDiagnostics(fileName: string): Diagnostic[];
+        getSemanticDiagnostics(fileName: string): Diagnostic[];
+        getCompilerOptionsDiagnostics(): Diagnostic[];
+        /**
+         * @deprecated Use getEncodedSyntacticClassifications instead.
+         */
+        getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
+        /**
+         * @deprecated Use getEncodedSemanticClassifications instead.
+         */
+        getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
+        getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications;
+        getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications;
+        getCompletionsAtPosition(fileName: string, position: number): CompletionInfo;
+        getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails;
+        getQuickInfoAtPosition(fileName: string, position: number): QuickInfo;
+        getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan;
+        getBreakpointStatementAtPosition(fileName: string, position: number): TextSpan;
+        getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems;
+        getRenameInfo(fileName: string, position: number): RenameInfo;
+        findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[];
+        getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
+        getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
+        getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
+        findReferences(fileName: string, position: number): ReferencedSymbol[];
+        getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[];
+        /** @deprecated */
+        getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
+        getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
+        getNavigationBarItems(fileName: string): NavigationBarItem[];
+        getOutliningSpans(fileName: string): OutliningSpan[];
+        getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
+        getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[];
+        getIndentationAtPosition(fileName: string, position: number, options: EditorOptions): number;
+        getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[];
+        getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[];
+        getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[];
+        getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion;
+        getEmitOutput(fileName: string): EmitOutput;
+        getProgram(): Program;
+        getSourceFile(fileName: string): SourceFile;
+        dispose(): void;
+    }
+    interface Classifications {
+        spans: number[];
+        endOfLineState: EndOfLineState;
+    }
+    interface ClassifiedSpan {
+        textSpan: TextSpan;
+        classificationType: string;
+    }
+    interface NavigationBarItem {
+        text: string;
+        kind: string;
+        kindModifiers: string;
+        spans: TextSpan[];
+        childItems: NavigationBarItem[];
+        indent: number;
+        bolded: boolean;
+        grayed: boolean;
+    }
+    interface TodoCommentDescriptor {
+        text: string;
+        priority: number;
+    }
+    interface TodoComment {
+        descriptor: TodoCommentDescriptor;
+        message: string;
+        position: number;
+    }
+    class TextChange {
+        span: TextSpan;
+        newText: string;
+    }
+    interface TextInsertion {
+        newText: string;
+        /** The position in newText the caret should point to after the insertion. */
+        caretOffset: number;
+    }
+    interface RenameLocation {
+        textSpan: TextSpan;
+        fileName: string;
+    }
+    interface ReferenceEntry {
+        textSpan: TextSpan;
+        fileName: string;
+        isWriteAccess: boolean;
+    }
+    interface DocumentHighlights {
+        fileName: string;
+        highlightSpans: HighlightSpan[];
+    }
+    module HighlightSpanKind {
+        const none: string;
+        const definition: string;
+        const reference: string;
+        const writtenReference: string;
+    }
+    interface HighlightSpan {
+        fileName?: string;
+        textSpan: TextSpan;
+        kind: string;
+    }
+    interface NavigateToItem {
+        name: string;
+        kind: string;
+        kindModifiers: string;
+        matchKind: string;
+        isCaseSensitive: boolean;
+        fileName: string;
+        textSpan: TextSpan;
+        containerName: string;
+        containerKind: string;
+    }
+    interface EditorOptions {
+        IndentSize: number;
+        TabSize: number;
+        NewLineCharacter: string;
+        ConvertTabsToSpaces: boolean;
+        IndentStyle: IndentStyle;
+    }
+    enum IndentStyle {
+        None = 0,
+        Block = 1,
+        Smart = 2,
+    }
+    interface FormatCodeOptions extends EditorOptions {
+        InsertSpaceAfterCommaDelimiter: boolean;
+        InsertSpaceAfterSemicolonInForStatements: boolean;
+        InsertSpaceBeforeAndAfterBinaryOperators: boolean;
+        InsertSpaceAfterKeywordsInControlFlowStatements: boolean;
+        InsertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean;
+        InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean;
+        InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: boolean;
+        PlaceOpenBraceOnNewLineForFunctions: boolean;
+        PlaceOpenBraceOnNewLineForControlBlocks: boolean;
+        [s: string]: boolean | number | string;
+    }
+    interface DefinitionInfo {
+        fileName: string;
+        textSpan: TextSpan;
+        kind: string;
+        name: string;
+        containerKind: string;
+        containerName: string;
+    }
+    interface ReferencedSymbol {
+        definition: DefinitionInfo;
+        references: ReferenceEntry[];
+    }
+    enum SymbolDisplayPartKind {
+        aliasName = 0,
+        className = 1,
+        enumName = 2,
+        fieldName = 3,
+        interfaceName = 4,
+        keyword = 5,
+        lineBreak = 6,
+        numericLiteral = 7,
+        stringLiteral = 8,
+        localName = 9,
+        methodName = 10,
+        moduleName = 11,
+        operator = 12,
+        parameterName = 13,
+        propertyName = 14,
+        punctuation = 15,
+        space = 16,
+        text = 17,
+        typeParameterName = 18,
+        enumMemberName = 19,
+        functionName = 20,
+        regularExpressionLiteral = 21,
+    }
+    interface SymbolDisplayPart {
+        text: string;
+        kind: string;
+    }
+    interface QuickInfo {
+        kind: string;
+        kindModifiers: string;
+        textSpan: TextSpan;
+        displayParts: SymbolDisplayPart[];
+        documentation: SymbolDisplayPart[];
+    }
+    interface RenameInfo {
+        canRename: boolean;
+        localizedErrorMessage: string;
+        displayName: string;
+        fullDisplayName: string;
+        kind: string;
+        kindModifiers: string;
+        triggerSpan: TextSpan;
+    }
+    interface SignatureHelpParameter {
+        name: string;
+        documentation: SymbolDisplayPart[];
+        displayParts: SymbolDisplayPart[];
+        isOptional: boolean;
+    }
+    /**
+     * Represents a single signature to show in signature help.
+     * The id is used for subsequent calls into the language service to ask questions about the
+     * signature help item in the context of any documents that have been updated.  i.e. after
+     * an edit has happened, while signature help is still active, the host can ask important
+     * questions like 'what parameter is the user currently contained within?'.
+     */
+    interface SignatureHelpItem {
+        isVariadic: boolean;
+        prefixDisplayParts: SymbolDisplayPart[];
+        suffixDisplayParts: SymbolDisplayPart[];
+        separatorDisplayParts: SymbolDisplayPart[];
+        parameters: SignatureHelpParameter[];
+        documentation: SymbolDisplayPart[];
+    }
+    /**
+     * Represents a set of signature help items, and the preferred item that should be selected.
+     */
+    interface SignatureHelpItems {
+        items: SignatureHelpItem[];
+        applicableSpan: TextSpan;
+        selectedItemIndex: number;
+        argumentIndex: number;
+        argumentCount: number;
+    }
+    interface CompletionInfo {
+        isMemberCompletion: boolean;
+        isNewIdentifierLocation: boolean;
+        entries: CompletionEntry[];
+    }
+    interface CompletionEntry {
+        name: string;
+        kind: string;
+        kindModifiers: string;
+        sortText: string;
+    }
+    interface CompletionEntryDetails {
+        name: string;
+        kind: string;
+        kindModifiers: string;
+        displayParts: SymbolDisplayPart[];
+        documentation: SymbolDisplayPart[];
+    }
+    interface OutliningSpan {
+        /** The span of the document to actually collapse. */
+        textSpan: TextSpan;
+        /** The span of the document to display when the user hovers over the collapsed span. */
+        hintSpan: TextSpan;
+        /** The text to display in the editor for the collapsed region. */
+        bannerText: string;
+        /**
+          * Whether or not this region should be automatically collapsed when
+          * the 'Collapse to Definitions' command is invoked.
+          */
+        autoCollapse: boolean;
+    }
+    interface EmitOutput {
+        outputFiles: OutputFile[];
+        emitSkipped: boolean;
+    }
+    const enum OutputFileType {
+        JavaScript = 0,
+        SourceMap = 1,
+        Declaration = 2,
+    }
+    interface OutputFile {
+        name: string;
+        writeByteOrderMark: boolean;
+        text: string;
+    }
+    const enum EndOfLineState {
+        None = 0,
+        InMultiLineCommentTrivia = 1,
+        InSingleQuoteStringLiteral = 2,
+        InDoubleQuoteStringLiteral = 3,
+        InTemplateHeadOrNoSubstitutionTemplate = 4,
+        InTemplateMiddleOrTail = 5,
+        InTemplateSubstitutionPosition = 6,
+    }
+    enum TokenClass {
+        Punctuation = 0,
+        Keyword = 1,
+        Operator = 2,
+        Comment = 3,
+        Whitespace = 4,
+        Identifier = 5,
+        NumberLiteral = 6,
+        StringLiteral = 7,
+        RegExpLiteral = 8,
+    }
+    interface ClassificationResult {
+        finalLexState: EndOfLineState;
+        entries: ClassificationInfo[];
+    }
+    interface ClassificationInfo {
+        length: number;
+        classification: TokenClass;
+    }
+    interface Classifier {
+        /**
+         * Gives lexical classifications of tokens on a line without any syntactic context.
+         * For instance, a token consisting of the text 'string' can be either an identifier
+         * named 'string' or the keyword 'string', however, because this classifier is not aware,
+         * it relies on certain heuristics to give acceptable results. For classifications where
+         * speed trumps accuracy, this function is preferable; however, for true accuracy, the
+         * syntactic classifier is ideal. In fact, in certain editing scenarios, combining the
+         * lexical, syntactic, and semantic classifiers may issue the best user experience.
+         *
+         * @param text                      The text of a line to classify.
+         * @param lexState                  The state of the lexical classifier at the end of the previous line.
+         * @param syntacticClassifierAbsent Whether the client is *not* using a syntactic classifier.
+         *                                  If there is no syntactic classifier (syntacticClassifierAbsent=true),
+         *                                  certain heuristics may be used in its place; however, if there is a
+         *                                  syntactic classifier (syntacticClassifierAbsent=false), certain
+         *                                  classifications which may be incorrectly categorized will be given
+         *                                  back as Identifiers in order to allow the syntactic classifier to
+         *                                  subsume the classification.
+         * @deprecated Use getLexicalClassifications instead.
+         */
+        getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent: boolean): ClassificationResult;
+        getEncodedLexicalClassifications(text: string, endOfLineState: EndOfLineState, syntacticClassifierAbsent: boolean): Classifications;
+    }
+    /**
+      * The document registry represents a store of SourceFile objects that can be shared between
+      * multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
+      * of files in the context.
+      * SourceFile objects account for most of the memory usage by the language service. Sharing
+      * the same DocumentRegistry instance between different instances of LanguageService allow
+      * for more efficient memory utilization since all projects will share at least the library
+      * file (lib.d.ts).
+      *
+      * A more advanced use of the document registry is to serialize sourceFile objects to disk
+      * and re-hydrate them when needed.
+      *
+      * To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
+      * to all subsequent createLanguageService calls.
+      */
+    interface DocumentRegistry {
+        /**
+          * Request a stored SourceFile with a given fileName and compilationSettings.
+          * The first call to acquire will call createLanguageServiceSourceFile to generate
+          * the SourceFile if was not found in the registry.
+          *
+          * @param fileName The name of the file requested
+          * @param compilationSettings Some compilation settings like target affects the
+          * shape of a the resulting SourceFile. This allows the DocumentRegistry to store
+          * multiple copies of the same file for different compilation settings.
+          * @parm scriptSnapshot Text of the file. Only used if the file was not found
+          * in the registry and a new one was created.
+          * @parm version Current version of the file. Only used if the file was not found
+          * in the registry and a new one was created.
+          */
+        acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile;
+        /**
+          * Request an updated version of an already existing SourceFile with a given fileName
+          * and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
+          * to get an updated SourceFile.
+          *
+          * @param fileName The name of the file requested
+          * @param compilationSettings Some compilation settings like target affects the
+          * shape of a the resulting SourceFile. This allows the DocumentRegistry to store
+          * multiple copies of the same file for different compilation settings.
+          * @param scriptSnapshot Text of the file.
+          * @param version Current version of the file.
+          */
+        updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile;
+        /**
+          * Informs the DocumentRegistry that a file is not needed any longer.
+          *
+          * Note: It is not allowed to call release on a SourceFile that was not acquired from
+          * this registry originally.
+          *
+          * @param fileName The name of the file to be released
+          * @param compilationSettings The compilation settings used to acquire the file
+          */
+        releaseDocument(fileName: string, compilationSettings: CompilerOptions): void;
+        reportStats(): string;
+    }
+    module ScriptElementKind {
+        const unknown: string;
+        const warning: string;
+        const keyword: string;
+        const scriptElement: string;
+        const moduleElement: string;
+        const classElement: string;
+        const localClassElement: string;
+        const interfaceElement: string;
+        const typeElement: string;
+        const enumElement: string;
+        const variableElement: string;
+        const localVariableElement: string;
+        const functionElement: string;
+        const localFunctionElement: string;
+        const memberFunctionElement: string;
+        const memberGetAccessorElement: string;
+        const memberSetAccessorElement: string;
+        const memberVariableElement: string;
+        const constructorImplementationElement: string;
+        const callSignatureElement: string;
+        const indexSignatureElement: string;
+        const constructSignatureElement: string;
+        const parameterElement: string;
+        const typeParameterElement: string;
+        const primitiveType: string;
+        const label: string;
+        const alias: string;
+        const constElement: string;
+        const letElement: string;
+    }
+    module ScriptElementKindModifier {
+        const none: string;
+        const publicMemberModifier: string;
+        const privateMemberModifier: string;
+        const protectedMemberModifier: string;
+        const exportedModifier: string;
+        const ambientModifier: string;
+        const staticModifier: string;
+        const abstractModifier: string;
+    }
+    class ClassificationTypeNames {
+        static comment: string;
+        static identifier: string;
+        static keyword: string;
+        static numericLiteral: string;
+        static operator: string;
+        static stringLiteral: string;
+        static whiteSpace: string;
+        static text: string;
+        static punctuation: string;
+        static className: string;
+        static enumName: string;
+        static interfaceName: string;
+        static moduleName: string;
+        static typeParameterName: string;
+        static typeAliasName: string;
+        static parameterName: string;
+        static docCommentTagName: string;
+    }
+    const enum ClassificationType {
+        comment = 1,
+        identifier = 2,
+        keyword = 3,
+        numericLiteral = 4,
+        operator = 5,
+        stringLiteral = 6,
+        regularExpressionLiteral = 7,
+        whiteSpace = 8,
+        text = 9,
+        punctuation = 10,
+        className = 11,
+        enumName = 12,
+        interfaceName = 13,
+        moduleName = 14,
+        typeParameterName = 15,
+        typeAliasName = 16,
+        parameterName = 17,
+        docCommentTagName = 18,
+    }
+    interface DisplayPartsSymbolWriter extends SymbolWriter {
+        displayParts(): SymbolDisplayPart[];
+    }
+    function displayPartsToString(displayParts: SymbolDisplayPart[]): string;
+    function getDefaultCompilerOptions(): CompilerOptions;
+    interface TranspileOptions {
+        compilerOptions?: CompilerOptions;
+        fileName?: string;
+        reportDiagnostics?: boolean;
+        moduleName?: string;
+        renamedDependencies?: Map<string>;
+    }
+    interface TranspileOutput {
+        outputText: string;
+        diagnostics?: Diagnostic[];
+        sourceMapText?: string;
+    }
+    function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput;
+    function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string;
+    function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile;
+    let disableIncrementalParsing: boolean;
+    function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile;
+    function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string;
+    function createDocumentRegistry(useCaseSensitiveFileNames?: boolean): DocumentRegistry;
+    function preProcessFile(sourceText: string, readImportFiles?: boolean): PreProcessedFileInfo;
+    function createLanguageService(host: LanguageServiceHost, documentRegistry?: DocumentRegistry): LanguageService;
+    function createClassifier(): Classifier;
+    /**
+      * Get the path of the default library files (lib.d.ts) as distributed with the typescript
+      * node package.
+      * The functionality is not supported if the ts module is consumed outside of a node module.
+      */
+    function getDefaultLibFilePath(options: CompilerOptions): string;
+}
+
+export = ts;

+ 25 - 4
Script/AtomicWebViewEditor/tsconfig.json

@@ -6,20 +6,41 @@
         "noImplicitAny": false,
         "removeComments": true,
         "noLib": false,
-        "outFile": "../../Data/AtomicEditor/CodeEditor/editor.bundle.js",
+        "outDir": "../../Data/AtomicEditor/CodeEditor/source/editorCore",
         "sourceMap": true
     },
     "filesGlob": [
-        "./**/*.ts"
+        "./**/*.ts",
+        "../TypeScript/*.ts"
     ],
     "atom": {
         "rewriteTsconfig": true
     },
     "files": [
+        "./clientExtensions/ClientExtensionEventNames.ts",
+        "./clientExtensions/ClientExtensionServices.ts",
+        "./clientExtensions/languageExtensions/javascript/JavascriptLanguageExtension.ts",
+        "./clientExtensions/languageExtensions/typescript/TypescriptLanguageExtension.ts",
+        "./clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageService.ts",
+        "./clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageServiceWebWorker.ts",
+        "./clientExtensions/languageExtensions/typescript/workerprocess/workerLoader.ts",
+        "./clientExtensions/languageExtensions/typescript/workerprocess/workerProcessTypes.ts",
+        "./clientExtensions/ServiceLocator.ts",
         "./editor/editor.ts",
-        "./editor/editorConfig.ts",
+        "./editor/editorCommands.ts",
         "./interop.ts",
+        "./modules/typescript.d.ts",
         "./typings/ace.d.ts",
-        "./typings/AtomicQuery.d.ts"
+        "./typings/systemjs.d.ts",
+        "./typings/webworkers.d.ts",
+        "./typings/WindowExt.d.ts",
+        "../TypeScript/Atomic.d.ts",
+        "../TypeScript/AtomicNET.d.ts",
+        "../TypeScript/AtomicPlayer.d.ts",
+        "../TypeScript/AtomicWork.d.ts",
+        "../TypeScript/Editor.d.ts",
+        "../TypeScript/EditorWork.d.ts",
+        "../TypeScript/ToolCore.d.ts",
+        "../TypeScript/WebView.d.ts"
     ]
 }

+ 0 - 5
Script/AtomicWebViewEditor/typings/AtomicQuery.d.ts

@@ -1,5 +0,0 @@
-interface Window {
-    atomicQuery: any;
-    loadCode: (codeUrl) => void;
-    saveCode: () => void;
-}

+ 19 - 0
Script/AtomicWebViewEditor/typings/WindowExt.d.ts

@@ -0,0 +1,19 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+/**
+ * Defines the interface to what is available for the host to call or for the client to call on the window object
+ */
+interface Window {
+    atomicQuery: any;
+    HOST_loadCode: (codeUrl) => void;
+    HOST_saveCode: () => void;
+
+    HOST_projectUnloaded: () => void;
+    HOST_resourceRenamed: (path: string, newPath: string) => void;
+    HOST_resourceDeleted: (path: string) => void;
+}

+ 21 - 0
Script/AtomicWebViewEditor/typings/systemjs.d.ts

@@ -0,0 +1,21 @@
+// Type definitions for System.js 0.18.4
+// Project: https://github.com/systemjs/systemjs
+// Definitions by: Ludovic HENIN <https://github.com/ludohenin/>, Nathan Walker <https://github.com/NathanWalker/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+interface System {
+  import(name: string): any;
+  defined: any;
+  amdDefine: () => void;
+  amdRequire: () => void;
+  baseURL: string;
+  paths: { [key: string]: string };
+  meta: { [key: string]: Object };
+  config: any;
+}
+
+declare var System: System;
+
+declare module "systemjs" {
+  export = System;
+}

+ 29 - 0
Script/AtomicWebViewEditor/typings/webworkers.d.ts

@@ -0,0 +1,29 @@
+// Type definitions for SharedWorker
+// Project: http://www.w3.org/TR/workers/
+// Definitions by: Toshiya Nakakura <https://github.com/nakakura>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+declare module SharedWorker {
+    interface AbstractWorker extends EventTarget {
+        onerror: (ev: ErrorEvent) => any;
+    }
+
+    export interface SharedWorker extends AbstractWorker {
+        /**
+         * the value it was assigned by the object's constructor.
+         * It represents the MessagePort for communicating with the shared worker.
+         * @type {MessagePort}
+         */
+        port: MessagePort;
+    }
+}
+
+declare var SharedWorker: {
+    prototype: SharedWorker.SharedWorker;
+    /***
+     *
+     * @param {string} stringUrl    Pathname to JavaScript file
+     * @param {string} name         Name of the worker to execute
+     */
+    new (stringUrl: string, name?: string): SharedWorker.SharedWorker;
+};

+ 310 - 0
Script/TypeScript/EditorWork.d.ts

@@ -0,0 +1,310 @@
+//
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// LICENSE: Atomic Game Engine Editor and Tools EULA
+// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
+// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
+//
+
+/// <reference path="Editor.d.ts" />
+
+declare module Editor.EditorEvents {
+
+    export interface ModalErrorEvent {
+
+        title: string;
+        message: string;
+
+    }
+
+    export interface PlayerLogEvent {
+
+        message: string;
+        level: number;
+
+    }
+
+    export interface ActiveSceneEditorChangeEvent {
+
+        sceneEditor: Editor.SceneEditor3D;
+
+    }
+
+    export interface SceneClosedEvent {
+
+        scene: Atomic.Scene;
+
+    }
+
+    export interface ContentFolderChangedEvent {
+
+        path: string;
+
+    }
+
+    export interface LoadProjectEvent {
+
+        // The full path to the .atomic file
+        path: string;
+
+    }
+
+    /**
+     * Called once the resource has been saved
+     * @type {String}
+     */
+    export interface SaveResourceEvent {
+
+        // The full path to the resource to save / empty or undefined for current
+        path: string;
+
+    }
+
+    export interface LoadResourceEvent {
+
+        // The full path to the resource to load
+        path: string;
+
+    }
+
+    export interface EditorFileEvent {
+        filename: string;
+        fileExt: string;
+        editor: any;
+    }
+
+    export interface CodeLoadedEvent extends EditorFileEvent {
+        code: string;
+    }
+
+    export interface EditorCloseResourceEvent {
+
+        editor: Editor.ResourceEditor;
+        navigateToAvailableResource: boolean;
+
+    }
+
+    export interface EditResourceEvent {
+
+        // The full path to the resource to edit
+        path: string;
+
+    }
+
+    /**
+     * Called once the resource has been deleted
+     * @type {String}
+     */
+    export interface DeleteResourceEvent {
+
+        // The full path to the resource to edit
+        path: string;
+
+    }
+
+    /**
+     * Called once the resource has been renamed
+     * @type {String}
+     */
+    export interface RenameResourceEvent {
+
+        /**
+         * Original path of the resource
+         * @type {string}
+         */
+        path: string;
+
+        /**
+         * New path of the resource
+         * @type {string}
+         */
+        newPath: string;
+
+        /**
+         * New base name of the resource (no path or extension)
+         * @type {string}
+         */
+        newName?: string;
+
+        // the asset being changed
+        asset?: ToolCore.Asset;
+    }
+
+    export interface SceneEditStateChangeEvent {
+
+        serializable: Atomic.Serializable;
+
+    }
+}
+
+declare module Editor.Extensions {
+
+    /**
+     * Base interface for any editor services.
+     */
+    export interface EditorService {
+        /**
+         * Unique name of this service
+         * @type {string}
+         */
+        name: string;
+
+        /**
+         * Description of this service
+         * @type {string}
+         */
+        description: string;
+
+    }
+
+    interface EventDispatcher {
+        /**
+         * Send a custom event.  This can be used by services to publish custom events
+         * @param  {string} eventType
+         * @param  {any} data
+         */
+        sendEvent(eventType: string, data: any);
+
+        /**
+         * Subscribe to an event and provide a callback.  This can be used by services to subscribe to custom events
+         * @param  {string} eventType
+         * @param  {any} callback
+         */
+        subscribeToEvent(eventType, callback);
+    }
+
+    /**
+     * Generic service locator of editor services that may be injected by either a plugin
+     * or by the editor itself.
+     */
+    export interface ServiceLoader extends EventDispatcher {
+        /**
+         * Loads a service into a service registry
+         * @param  {EditorService} service
+         */
+        loadService(service: EditorService): void;
+    }
+
+    /**
+     * Service registry interface for registering services
+     */
+    export interface ServiceRegistry<T extends EditorService> {
+        registeredServices: T[];
+
+        /**
+         * Adds a service to the registered services list for this type of service
+         * @param  {T}      service the service to register
+         */
+        register(service: T);
+    }
+}
+
+declare module Editor.HostExtensions {
+
+    /**
+     * Generic service locator of editor services that may be injected by either a plugin
+     * or by the editor itself.
+     */
+    export interface HostServiceLocator extends Editor.Extensions.ServiceLoader {
+        resourceServices: Editor.Extensions.ServiceRegistry<ResourceService>;
+        projectServices: Editor.Extensions.ServiceRegistry<ProjectService>;
+    }
+
+    export interface HostEditorService extends Editor.Extensions.EditorService {
+        /**
+         * Called by the service locator at load time
+         */
+        initialize(serviceLocator: Editor.Extensions.ServiceLoader);
+    }
+
+    export interface ResourceService extends Editor.Extensions.EditorService {
+        save?(ev: EditorEvents.SaveResourceEvent);
+        delete?(ev: EditorEvents.DeleteResourceEvent);
+        rename?(ev: EditorEvents.RenameResourceEvent);
+    }
+
+    export interface ProjectService extends Editor.Extensions.EditorService {
+        projectUnloaded?();
+        projectLoaded?(ev: EditorEvents.LoadProjectEvent);
+        playerStarted?();
+    }
+}
+
+/**
+ * Interfaces for client extensions
+ */
+declare module Editor.ClientExtensions {
+
+    /**
+     * Generic service locator of editor services that may be injected by either a plugin
+     * or by the editor itself.
+     */
+    export interface ClientServiceLocator extends Editor.Extensions.ServiceLoader {
+        getHostInterop(): HostInterop;
+    }
+
+    export interface ClientEditorService extends Editor.Extensions.EditorService {
+        /**
+         * Called by the service locator at load time
+         */
+        initialize(serviceLocator: ClientServiceLocator);
+    }
+
+    export interface WebViewService extends Editor.Extensions.EditorService {
+        configureEditor?(ev: EditorEvents.EditorFileEvent);
+        codeLoaded?(ev: EditorEvents.CodeLoadedEvent);
+        save?(ev: EditorEvents.SaveResourceEvent);
+        delete?(ev: EditorEvents.DeleteResourceEvent);
+        rename?(ev: EditorEvents.RenameResourceEvent);
+        projectUnloaded?();
+    }
+
+    export interface AtomicErrorMessage {
+        error_code: number;
+        error_message: string;
+    }
+
+    /**
+     * Interface for functions that are available from the client web view to call on the host
+     */
+    export interface HostInterop {
+
+        /**
+         * Called from the host to notify the client what file to load
+         * @param  {string} codeUrl
+         */
+        loadCode(codeUrl: string);
+
+        /**
+         * Save the contents of the editor
+         * @return {Promise}
+         */
+        saveCode(): PromiseLike<{}>;
+
+        /**
+         * Save the contents of a file as filename
+         * @param  {string} filename
+         * @param  {string} fileContents
+         * @return {Promise}
+         */
+        saveFile(filename: string, fileContents: string): PromiseLike<{}>;
+
+        /**
+         * Queries the host for a particular resource and returns it in a promise
+         * @param  {string} codeUrl
+         * @return {Promise}
+         */
+        getResource(codeUrl: string): PromiseLike<{}>;
+
+        /**
+         * Returns a file resource from the resources directory
+         * @param  {string} filename name and path of file under the project directory or a fully qualified file name
+         * @return {Promise}
+         */
+        getFileResource(filename: string): PromiseLike<{}>;
+
+        /**
+         * Notify the host that the contents of the editor has changed
+         */
+        notifyEditorChange();
+    }
+}

+ 13 - 2
Script/tsconfig.json

@@ -8,7 +8,8 @@
         "noLib": false,
         "outDir": "../Artifacts/Build/Resources/EditorData/AtomicEditor/EditorScripts",
         "moduleResolution": "classic",
-        "sourceMap": true
+        "sourceMap": true,
+        "experimentalDecorators": true
     },
     "filesGlob": [
         "./ToolCore/**/*.ts",
@@ -24,6 +25,9 @@
         "./AtomicEditor/editor/EditorEvents.ts",
         "./AtomicEditor/editor/EditorLicense.ts",
         "./AtomicEditor/editor/Preferences.ts",
+        "./AtomicEditor/hostExtensions/HostExtensionServices.ts",
+        "./AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts",
+        "./AtomicEditor/hostExtensions/ServiceLocator.ts",
         "./AtomicEditor/main.ts",
         "./AtomicEditor/resources/ProjectTemplates.ts",
         "./AtomicEditor/resources/ResourceOps.ts",
@@ -90,6 +94,13 @@
         "./AtomicEditor/ui/ScriptWidget.ts",
         "./AtomicEditor/ui/Shortcuts.ts",
         "./AtomicEditor/ui/UIEvents.ts",
-        "./TypeScript/AtomicWork.d.ts"
+        "./TypeScript/Atomic.d.ts",
+        "./TypeScript/AtomicNET.d.ts",
+        "./TypeScript/AtomicPlayer.d.ts",
+        "./TypeScript/AtomicWork.d.ts",
+        "./TypeScript/Editor.d.ts",
+        "./TypeScript/EditorWork.d.ts",
+        "./TypeScript/ToolCore.d.ts",
+        "./TypeScript/WebView.d.ts"
     ]
 }

+ 0 - 2
Script/tslint.json

@@ -23,9 +23,7 @@
     "no-duplicate-variable": true,
     "no-empty": false,
     "no-eval": true,
-    "no-imports": true,
     "no-string-literal": false,
-    "no-trailing-comma": true,
     "no-trailing-whitespace": true,
     "no-unused-variable": false,
     "no-unreachable": true,

+ 21 - 0
Source/Atomic/Resource/ResourceEvents.h

@@ -74,6 +74,27 @@ EVENT(E_RESOURCEBACKGROUNDLOADED, ResourceBackgroundLoaded)
     PARAM(P_SUCCESS, Success);                      // bool
     PARAM(P_RESOURCE, Resource);                    // Resource pointer
 }
+    
+/// Resource was renamed
+EVENT(E_RENAMERESOURCENOTIFICATION, RenameResourceNotification)
+{
+    PARAM(P_RESOURCEPATH, Path);                    // String
+    PARAM(P_NEWRESOURCEPATH, NewPath);              // String
+    PARAM(P_NEWNAME, NewName);                      // String
+    PARAM(P_RESOURCE, Asset);                       // Resource pointer
+}
+
+/// Resource was deleted
+EVENT(E_DELETERESOURCENOTIFICATION, DeleteResourceNotification)
+{
+    PARAM(P_RESOURCEPATH, Path);                    // String
+}
+
+/// Project was unloaded
+EVENT(E_PROJECTUNLOADEDNOTIFICATION, ProjecUnloadedNotification)
+{
+}
+    
 
 /// Language changed.
 EVENT(E_CHANGELANGUAGE, ChangeLanguage)

+ 2 - 0
Source/AtomicEditor/EditorMode/AEEditorMode.cpp

@@ -77,6 +77,8 @@ void EditorMode::HandleIPCWorkerStarted(StringHash eventType, VariantMap& eventD
 
     startupData["debugHudMode"] = debugHud ? debugHud->GetMode() : (unsigned) 0;
 
+    SendEvent(E_EDITORPLAYREQUEST);
+
     playerBroker_->PostMessage(E_IPCINITIALIZE, startupData);
 
     SendEvent(E_EDITORPLAYERSTARTED);

+ 71 - 6
Source/AtomicEditor/Editors/JSResourceEditor.cpp

@@ -27,6 +27,7 @@
 #include <Atomic/IO/FileSystem.h>
 #include <Atomic/Resource/ResourceCache.h>
 #include <Atomic/Resource/JSONFile.h>
+#include <Atomic/Resource/ResourceEvents.h>
 
 #include <Atomic/Core/CoreEvents.h>
 #include <AtomicJS/Javascript/JSVM.h>
@@ -84,19 +85,60 @@ JSResourceEditor ::JSResourceEditor(Context* context, const String &fullpath, UI
     SubscribeToEvent(webClient_, E_WEBVIEWLOADEND, HANDLER(JSResourceEditor, HandleWebViewLoadEnd));
     SubscribeToEvent(messageHandler_, E_WEBMESSAGE, HANDLER(JSResourceEditor, HandleWebMessage));
 
+    SubscribeToEvent(E_RENAMERESOURCENOTIFICATION, HANDLER(JSResourceEditor, HandleRenameResourceNotification));
+    SubscribeToEvent(E_DELETERESOURCENOTIFICATION, HANDLER(JSResourceEditor, HandleDeleteResourceNotification));
+    SubscribeToEvent(E_PROJECTUNLOADEDNOTIFICATION, HANDLER(JSResourceEditor, HandleProjectUnloadedNotification));
 
     c->AddChild(webView_->GetInternalWidget());
 
 }
-
+    
 JSResourceEditor::~JSResourceEditor()
 {
 
 }
-
+   
+String getNormalizedPath(const String& path)
+{
+    // Full path is the fully qualified path from the root of the filesystem.  In order
+    // to take advantage of the resource caching system, let's trim it down to just the
+    // path inside the resources directory including the Resources directory so that the casing
+    // is correct.
+    const String& RESOURCES_MARKER = "resources/";
+    return path.SubstringUTF8(path.ToLower().Find(RESOURCES_MARKER));
+}
+    
+void JSResourceEditor::HandleRenameResourceNotification(StringHash eventType, VariantMap& eventData)
+{
+    using namespace RenameResourceNotification;
+    const String& newPath = eventData[P_NEWRESOURCEPATH].GetString();
+    const String& path = eventData[P_RESOURCEPATH].GetString();
+    
+    webClient_->ExecuteJavaScript(ToString("HOST_resourceRenamed(\"%s\",\"%s\");", getNormalizedPath(path).CString(), getNormalizedPath(newPath).CString()));
+    
+    if (fullpath_.Compare(path) == 0) {
+        fullpath_ = newPath;
+        SetModified(modified_);
+    }
+}
+    
+void JSResourceEditor::HandleDeleteResourceNotification(StringHash eventType, VariantMap& eventData)
+{
+    using namespace DeleteResourceNotification;
+    const String& path = eventData[P_RESOURCEPATH].GetString();
+    
+    webClient_->ExecuteJavaScript(ToString("HOST_resourceDeleted(\"%s\");", getNormalizedPath(path).CString()));
+}
+    
+void JSResourceEditor::HandleProjectUnloadedNotification(StringHash eventType, VariantMap& eventData)
+{
+    webClient_->ExecuteJavaScript("HOST_projectUnloaded();");
+}
+    
 void JSResourceEditor::HandleWebViewLoadEnd(StringHash eventType, VariantMap& eventData)
 {
-    webClient_->ExecuteJavaScript(ToString("loadCode(\"atomic://resources/%s\");", fullpath_.CString()));
+    // need to wait until we get an editor load complete message since we could
+    // still be streaming things in.
 }
 
 void JSResourceEditor::HandleWebMessage(StringHash eventType, VariantMap& eventData)
@@ -104,25 +146,48 @@ void JSResourceEditor::HandleWebMessage(StringHash eventType, VariantMap& eventD
     using namespace WebMessage;
 
     const String& request = eventData[P_REQUEST].GetString();
+    const String& EDITOR_CHANGE = "editorChange";
+    const String& EDITOR_SAVE_CODE = "editorSaveCode";
+    const String& EDITOR_SAVE_FILE = "editorSaveFile";
+    const String& EDITOR_LOAD_COMPLETE = "editorLoadComplete";
+    
+    String normalizedPath = getNormalizedPath(fullpath_);
+    
     WebMessageHandler* handler = static_cast<WebMessageHandler*>(eventData[P_HANDLER].GetPtr());
 
-    if (request == "change")
+    if (request == EDITOR_CHANGE)
     {
         SetModified(true);
     }
+    else if (request == EDITOR_LOAD_COMPLETE)
+    {
+        // We need to wait until the editor javascript is all required in to call the
+        // method to load the code.  The HandleWebViewLoadEnd event is getting called
+        // too soon.
+        webClient_->ExecuteJavaScript(ToString("HOST_loadCode(\"atomic://%s\");", normalizedPath.CString()));
+    }
     else
     {
         JSONValue jvalue;
         if (JSONFile::ParseJSON(request, jvalue, false))
         {
             String message = jvalue["message"].GetString();
-            if (message == "saveCode")
+            if (message == EDITOR_SAVE_CODE)
             {
                 String code = jvalue["payload"].GetString();
                 File file(context_, fullpath_, FILE_WRITE);
                 file.Write((void*) code.CString(), code.Length());
                 file.Close();
             }
+            else if (message == EDITOR_SAVE_FILE)
+            {
+                String code = jvalue["payload"].GetString();
+                String fn = jvalue["filename"].GetString();
+                // TODO: determine if we are absolute path or partial path
+                File file(context_, fn, FILE_WRITE);
+                file.Write((void*) code.CString(), code.Length());
+                file.Close();
+            }
         }
     }
 
@@ -178,7 +243,7 @@ bool JSResourceEditor::Save()
     if (!modified_)
         return true;
 
-    webClient_->ExecuteJavaScript("saveCode();");
+    webClient_->ExecuteJavaScript("HOST_saveCode();");
 
     SetModified(false);
 

+ 5 - 1
Source/AtomicEditor/Editors/JSResourceEditor.h

@@ -60,7 +60,7 @@ public:
     void SetFocus();
 
     bool Save();
-
+    
 private:
 
     void HandleWebViewLoadEnd(StringHash eventType, VariantMap& eventData);
@@ -68,6 +68,10 @@ private:
 
     bool BeautifyJavascript(const char* source, String& output);
 
+    void HandleRenameResourceNotification(StringHash eventType, VariantMap& eventData);
+    void HandleDeleteResourceNotification(StringHash eventType, VariantMap& eventData);
+    void HandleProjectUnloadedNotification(StringHash eventType, VariantMap& eventData);
+    
     SharedPtr<UIWebView> webView_;
     WeakPtr<WebClient> webClient_;
     WeakPtr<WebMessageHandler> messageHandler_;

+ 17 - 1
Source/AtomicEditor/Editors/ResourceEditor.cpp

@@ -155,6 +155,8 @@ ResourceEditor::ResourceEditor(Context* context, const String& fullpath, UITabCo
     container_->GetContentRoot()->AddChild(rootContentWidget_);
 
     SubscribeToEvent(E_FILECHANGED, HANDLER(ResourceEditor, HandleFileChanged));
+    SubscribeToEvent(E_RENAMERESOURCENOTIFICATION, HANDLER(ResourceEditor, HandleRenameResourceNotification));
+    
 }
 
 ResourceEditor::~ResourceEditor()
@@ -178,6 +180,14 @@ void ResourceEditor::HandleFileChanged(StringHash eventType, VariantMap& eventDa
     */
 }
 
+void ResourceEditor::HandleRenameResourceNotification(StringHash eventType, VariantMap& eventData)
+{
+    using namespace RenameResourceNotification;
+    const String& newPath = eventData[P_NEWRESOURCEPATH].GetString();
+    fullpath_ = newPath;
+    SetModified(modified_);
+}
+    
 void ResourceEditor::RequestClose()
 {
     editorTabLayout_->RequestClose();
@@ -218,5 +228,11 @@ void ResourceEditor::SetModified(bool modified)
         button_->SetText(filename.CString());
     }
 }
-
+    
+    
+void ResourceEditor::ProjectUnloaded() {}
+    
+void ResourceEditor::Delete() {}
+    
+    
 }

+ 4 - 1
Source/AtomicEditor/Editors/ResourceEditor.h

@@ -65,6 +65,9 @@ public:
     virtual void Redo() {}
 
     virtual bool Save() { return true; }
+    
+    virtual void ProjectUnloaded();
+    virtual void Delete();
 
     UIWidget* GetRootContentWidget() { return rootContentWidget_; }
 
@@ -89,7 +92,7 @@ protected:
 private:
 
     void HandleFileChanged(StringHash eventType, VariantMap& eventData);
-
+    void HandleRenameResourceNotification(StringHash eventType, VariantMap& eventData);
 };
 
 }

+ 3 - 0
Source/AtomicWebView/WebBrowserHost.cpp

@@ -129,6 +129,9 @@ WebBrowserHost::WebBrowserHost(Context* context) : Object (context)
 
     CefSettings settings;
     settings.windowless_rendering_enabled = true;
+    
+    // Enable remote debugging on port 3335
+    settings.remote_debugging_port = 3335;
 
     d_ = new WebBrowserHostPrivate(this);
 

+ 4 - 2
Source/AtomicWebView/WebSchemeHandler.cpp

@@ -62,8 +62,8 @@ public:
             return false;
 
         ConvertCEFString(request->GetURL(), url_);
-        // shave off atomic://resources/
-        url_ = url_.SubstringUTF8(19);
+        //shave off the atomic:// part
+        url_ = url_.SubstringUTF8(9);
 
         if (IsAbsolutePath(url_))
         {
@@ -71,6 +71,8 @@ public:
         }
         else
         {
+            //shave off the resources part so we can grab the value from the resource cache
+            url_ = url_.SubstringUTF8(10);
             ResourceCache* cache = webBrowserHost_->GetSubsystem<ResourceCache>();
             file_ = cache->GetFile(url_, false);
         }

Some files were not shown because too many files changed in this diff