Browse Source

Work on real-time syntax checking

Shaddock Heath 9 years ago
parent
commit
c68c964ae6

+ 18 - 1
Script/AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts

@@ -29,6 +29,19 @@ import * as EditorEvents from "../../editor/EditorEvents";
  * @type {ts.CompilerOptions}
  */
 const defaultCompilerOptions = {
+    noEmitOnError: true,
+    noImplicitAny: false,
+    target: "es5",
+    module: "commonjs",
+    declaration: false,
+    inlineSourceMap: false,
+    removeComments: false,
+    noLib: true,
+    allowNonTsExtensions: false,
+    allowJs: false
+};
+
+const defaultCompilerOptionsJs = {
     noEmitOnError: true,
     noImplicitAny: false,
     target: "es5",
@@ -79,7 +92,6 @@ export default class TypescriptLanguageExtension implements Editor.HostExtension
         // only build out a tsconfig.atomic if we actually have typescript files in the project
         let projectFiles: Array<string> = [];
 
-        let compilerOptions = defaultCompilerOptions;
 
         //scan all the files in the project for any typescript files and add them to the project
         Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.ts", Atomic.SCAN_FILES, true).forEach(filename => {
@@ -89,6 +101,11 @@ export default class TypescriptLanguageExtension implements Editor.HostExtension
             }
         });
 
+        let compilerOptions = defaultCompilerOptions;
+        if (!this.isTypescriptProject)  {
+            compilerOptions = defaultCompilerOptionsJs;
+        }
+
         Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.js", Atomic.SCAN_FILES, true).forEach(filename => {
             let fn = Atomic.addTrailingSlash(ToolCore.toolSystem.project.resourcePath) + filename;
 

+ 47 - 18
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/TypescriptLanguageExtension.ts

@@ -157,8 +157,11 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
                     noSyntaxValidation: true
                 });
 
-                // Register editor feature providers
-                monaco.languages.registerCompletionItemProvider("javascript", new CustomCompletionProvider(this));
+                if (!this.isTranspiledJsFile(ev.filename, this.getTsConfig())) {
+                    // Register editor feature providers
+                    monaco.languages.registerCompletionItemProvider("javascript", new CustomCompletionProvider(this));
+                }
+
             } else {
                 monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
                     noEmit: true,
@@ -171,7 +174,7 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
                     noSemanticValidation: true,
                     noSyntaxValidation: true
                 });
-                
+
                 // Register editor feature providers
                 monaco.languages.registerCompletionItemProvider("typescript", new CustomCompletionProvider(this));
             }
@@ -196,18 +199,26 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
 
             let tsConfig = this.getTsConfig();
             if (!this.isTranspiledJsFile(ev.filename, tsConfig)) {
+                let model = this.editor.getModel();
+                let handle: number;
+                model.onDidChangeContent(() => {
+                    clearTimeout(handle);
+                    handle = setTimeout(() => this.getAnnotations(), 500);
+                });
+
                 // post a message to the shared web worker
                 this.worker.port.postMessage({
                     command: WorkerProcessTypes.Connect,
                     sender: "Typescript Language Extension",
                     filename: ev.filename,
-                    tsConfig: tsConfig
+                    tsConfig: tsConfig,
+                    code: ev.code
                 });
             } else {
                 // This is a transpiled file..make it readonly
                 const generatedHeader = [
                     "// ********************** MACHINE GENERATED FILE *********************************",
-                    `// This file was generated from the TypeScript source file: ${ev.filename.replace(/\.js$/,".ts")}`,
+                    `// This file was generated from the TypeScript source file: ${ev.filename.replace(/\.js$/, ".ts")}`,
                     "// Any edits made to this file will be overwritten.",
                     "// *******************************************************************************",
                     "",
@@ -217,7 +228,6 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
                 let model = this.editor.getModel();
                 model.setValue(generatedHeader + model.getValue());
             }
-
         }
     }
 
@@ -255,18 +265,43 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
         }
     }
 
+    /**
+     * Request the markers to display
+     */
+    getAnnotations() {
+        const message: WorkerProcessTypes.GetAnnotationsMessageData = {
+            command: WorkerProcessTypes.GetAnnotations,
+            code: this.editor.getModel().getValue(),
+            filename: this.filename,
+            fileExt: null,
+            editor: null // cannot send editor across the boundary
+        };
+
+        this.worker.port.postMessage(message);
+    }
+
     /**
      * Set annotations based upon issues reported by the typescript language service
      * @param  {WorkerProcessTypes.GetAnnotationsResponseMessageData} event
      */
     setAnnotations(event: WorkerProcessTypes.GetAnnotationsResponseMessageData) {
-        // grab the existing annotations and filter out any TS annotations
-        //let oldAnnotations = this.editor.session.getAnnotations().filter(ann => !ann.tsAnnotation);
-        //this.editor.session.clearAnnotations();
+        let model = this.editor.getModel();
+        let markers = event.annotations
+        .filter(ann => ann.start != undefined)
+        .map(ann => {
+            return {
+                code: ann.code,
+                severity: monaco.Severity.Error,
+                message: ann.message,
+                //source?: string;
+                startLineNumber: model.getPositionAt(ann.start).lineNumber,
+                startColumn: model.getPositionAt(ann.start).column,
+                endLineNumber: model.getPositionAt(ann.start + ann.length).lineNumber,
+                endColumn: model.getPositionAt(ann.start + ann.length).column
+            };
+        });
 
-        // Mark these annotations as special
-        //event.annotations.forEach(ann => ann.tsAnnotation = true);
-        //this.editor.session.setAnnotations(oldAnnotations.concat(event.annotations));
+        monaco.editor.setModelMarkers(this.editor.getModel(), "Atomic", markers);
     }
 
     /**
@@ -293,8 +328,6 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
      */
     save(ev: Editor.EditorEvents.CodeSavedEvent) {
         if (this.active && this.isValidFiletype(ev.filename)) {
-            //console.log(`${this.name}: received a save resource event for ${ev.filename}`);
-
             const message: WorkerProcessTypes.SaveMessageData = {
                 command: ClientExtensionEventNames.CodeSavedEvent,
                 filename: ev.filename,
@@ -313,8 +346,6 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
      */
     delete(ev: Editor.EditorEvents.DeleteResourceEvent) {
         if (this.active && 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,
@@ -331,8 +362,6 @@ export default class TypescriptLanguageExtension implements Editor.ClientExtensi
      */
     rename(ev: Editor.EditorEvents.RenameResourceEvent) {
         if (this.active && 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,

+ 43 - 23
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageService.ts

@@ -126,7 +126,7 @@ export class TypescriptLanguageService {
      * @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) {
+    addProjectFile(filename: string, fileContents?: string): ts.SourceFile {
         if (this.projectFiles.indexOf(filename) == -1) {
             console.log("Added project file: " + filename);
             this.versionMap[filename] = {
@@ -134,11 +134,13 @@ export class TypescriptLanguageService {
                 snapshot: ts.ScriptSnapshot.fromString(fileContents || this.fs.getFile(filename))
             };
             this.projectFiles.push(filename);
-            this.documentRegistry.acquireDocument(
+            return this.documentRegistry.acquireDocument(
                 filename,
                 this.compilerOptions,
                 this.versionMap[filename].snapshot,
                 "0");
+        } else {
+            return null;
         }
     }
 
@@ -149,14 +151,17 @@ export class TypescriptLanguageService {
      * @return {ts.SourceFile}
      */
     updateProjectFile(filename: string, fileContents: string): ts.SourceFile {
-        this.versionMap[filename].version++;
-        this.versionMap[filename].snapshot = ts.ScriptSnapshot.fromString(fileContents);
-
-        return this.documentRegistry.updateDocument(
-            filename,
-            this.compilerOptions,
-            this.versionMap[filename].snapshot,
-            this.versionMap[filename].version.toString());
+        if (this.projectFiles.indexOf(filename) == -1) {
+            return this.addProjectFile(filename, fileContents);
+        } else {
+            this.versionMap[filename].version++;
+            this.versionMap[filename].snapshot = ts.ScriptSnapshot.fromString(fileContents);
+            return this.documentRegistry.updateDocument(
+                filename,
+                this.compilerOptions,
+                this.versionMap[filename].snapshot,
+                this.versionMap[filename].version.toString());
+        }
     }
 
     /**
@@ -181,25 +186,35 @@ export class TypescriptLanguageService {
         return this.projectFiles;
     }
 
+    getDiagnostics(filename: string) {
+        let allDiagnostics = this.languageService.getSyntacticDiagnostics(filename);
+        if (filename.endsWith(".ts")) {
+            allDiagnostics = allDiagnostics.concat(this.languageService.getSemanticDiagnostics(filename));
+        }
+
+        return allDiagnostics.map(diagnostic => {
+            return {
+                message: ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"),
+                type: diagnostic.category == 1 ? "error" : "warning",
+                start: diagnostic.start,
+                length: diagnostic.length,
+                code: diagnostic.code,
+                source: filename
+            };
+        });
+    }
     getPreEmitWarnings(filename: string) {
         let allDiagnostics = this.compileFile(filename);
         let results = [];
 
         allDiagnostics.forEach(diagnostic => {
-            let row = 0;
-            let char = 0;
-            if (diagnostic.file) {
-                let lineChar = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
-                row = lineChar.line;
-                char = lineChar.character;
-            }
-
-            let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
             results.push({
-                row: row,
-                column: char,
-                text: message,
-                type: diagnostic.category == 1 ? "error" : "warning"
+                message: ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"),
+                type: diagnostic.category == 1 ? "error" : "warning",
+                start: diagnostic.start,
+                length: diagnostic.length,
+                code: diagnostic.code,
+                source: filename
             });
         });
         return results;
@@ -277,6 +292,11 @@ export class TypescriptLanguageService {
         }
 
         files.forEach(filename => {
+            // Don't compile .js files
+            if (!filename.endsWith(".js")) {
+                return;
+            }
+
             let currentErrors = this.compileFile(filename);
             errors = errors.concat(currentErrors);
             if (progress) {

+ 5 - 3
Script/AtomicWebViewEditor/clientExtensions/languageExtensions/typescript/workerprocess/TypescriptLanguageServiceWebWorker.ts

@@ -276,7 +276,8 @@ export default class TypescriptLanguageServiceWebWorker {
     handleHELO(port: MessagePort, eventData: any | {
         sender: string,
         filename: string,
-        tsConfig: any
+        tsConfig: any,
+        code: string
     }) {
         //port.postMessage({ command: WorkerProcessTypes.Message, message: "Hello " + eventData.sender + " (port #" + this.connections + ")" });
         this.tsConfig = eventData.tsConfig;
@@ -290,7 +291,7 @@ export default class TypescriptLanguageServiceWebWorker {
         const fn = this.resolvePartialFilename(eventData.filename);
 
         this.loadProjectFiles().then(() => {
-            let diagnostics = this.languageService.compile([fn]);
+            //let diagnostics = this.languageService.compile([fn]);
             this.handleGetAnnotations(port, eventData);
         });
     }
@@ -382,9 +383,10 @@ export default class TypescriptLanguageServiceWebWorker {
 
     handleGetAnnotations(port: MessagePort, eventData: WorkerProcessTypes.GetAnnotationsMessageData) {
         let filename = this.resolvePartialFilename(eventData.filename);
+        this.languageService.updateProjectFile(filename, eventData.code);
         let message: WorkerProcessTypes.GetAnnotationsResponseMessageData = {
             command: WorkerProcessTypes.AnnotationsUpdated,
-            annotations: this.languageService.getPreEmitWarnings(filename)
+            annotations: this.languageService.getDiagnostics(filename)
         };
 
         port.postMessage(message);

+ 2 - 4
Script/AtomicWebViewEditor/editor/editor.ts

@@ -33,12 +33,10 @@ editor.setOptions({
     theme: "ace/theme/monokai"
 });
 */
-export function getInternalEditor() : monaco.editor.IStandaloneCodeEditor {
+export function getInternalEditor(): monaco.editor.IStandaloneCodeEditor {
     return editor;
 }
 
-export function setInternalEditor(editorInstance: any) {
+export function setInternalEditor(editorInstance: monaco.editor.IStandaloneCodeEditor) {
     editor = editorInstance;
-
-    let monacoEditor = <monaco.editor.IStandaloneCodeEditor>editorInstance;
 }