Browse Source

Merge pull request #7199 from sailro/deprecated-support

Support for deprecated members in the Playground
David Catuhe 5 years ago
parent
commit
deba3adf1e

+ 98 - 0
Playground/js/definitionWorker.js

@@ -0,0 +1,98 @@
+// monaco is using 'define' for module dependencies and service lookup.
+// hopefully typescript is self-contained
+var ts = null;
+var define = (id, dependencies, callback) => ts = callback();
+
+importScripts("../node_modules/monaco-editor/dev/vs/language/typescript/lib/typescriptServices.js");
+
+// store deprecated names
+var deprecatedCandidates = [];
+
+function canHaveJsDoc(node) {
+    const kind = node.kind;
+    switch (kind) {
+        case ts.SyntaxKind.Parameter:
+        case ts.SyntaxKind.CallSignature:
+        case ts.SyntaxKind.ConstructSignature:
+        case ts.SyntaxKind.MethodSignature:
+        case ts.SyntaxKind.PropertySignature:
+        case ts.SyntaxKind.ArrowFunction:
+        case ts.SyntaxKind.ParenthesizedExpression:
+        case ts.SyntaxKind.SpreadAssignment:
+        case ts.SyntaxKind.ShorthandPropertyAssignment:
+        case ts.SyntaxKind.PropertyAssignment:
+        case ts.SyntaxKind.FunctionExpression:
+        case ts.SyntaxKind.FunctionDeclaration:
+        case ts.SyntaxKind.LabeledStatement:
+        case ts.SyntaxKind.ExpressionStatement:
+        case ts.SyntaxKind.VariableStatement:
+        case ts.SyntaxKind.Constructor:
+        case ts.SyntaxKind.MethodDeclaration:
+        case ts.SyntaxKind.PropertyDeclaration:
+        case ts.SyntaxKind.GetAccessor:
+        case ts.SyntaxKind.SetAccessor:
+        case ts.SyntaxKind.ClassDeclaration:
+        case ts.SyntaxKind.ClassExpression:
+        case ts.SyntaxKind.InterfaceDeclaration:
+        case ts.SyntaxKind.TypeAliasDeclaration:
+        case ts.SyntaxKind.EnumMember:
+        case ts.SyntaxKind.EnumDeclaration:
+        case ts.SyntaxKind.ModuleDeclaration:
+        case ts.SyntaxKind.ImportEqualsDeclaration:
+        case ts.SyntaxKind.IndexSignature:
+        case ts.SyntaxKind.FunctionType:
+        case ts.SyntaxKind.ConstructorType:
+        case ts.SyntaxKind.JSDocFunctionType:
+        case ts.SyntaxKind.EndOfFileToken:
+        case ts.SyntaxKind.ExportDeclaration:
+            return true;
+        default:
+            return false;
+    }
+}
+
+function onFindDeprecatedCandidate(node) {
+    const name = relatedName(node);
+    if (name)
+        deprecatedCandidates.push(name);
+}
+
+function relatedName(node) {
+    if (canHaveJsDoc(node) && node.name)
+        return node.name.escapedText;
+
+    if (node.parent)
+        return relatedName(parent);
+
+    return undefined;
+}
+
+function visit(node) {
+
+    if (node.jsDoc) {
+        for (const jsDocEntry of node.jsDoc) {
+            if (jsDocEntry.tags) {
+                for (const tag of jsDocEntry.tags) {
+                    if (tag.tagName && tag.tagName.escapedText == 'deprecated')
+                        onFindDeprecatedCandidate(node);
+                }
+            }
+        }
+    }
+
+    ts.forEachChild(node, visit);
+}
+
+function processDefinition(code) {
+    if (deprecatedCandidates.length == 0) {
+        const sourceFile = ts.createSourceFile('babylon.js', code, ts.ScriptTarget.ESNext, true);
+        ts.forEachChild(sourceFile, visit);
+    }
+
+    self.postMessage({ result: deprecatedCandidates });
+}
+
+self.addEventListener('message', event => {
+    const { code } = event.data;
+    processDefinition(code);
+});

+ 123 - 30
Playground/js/monacoCreator.js

@@ -4,12 +4,14 @@
 class MonacoCreator {
     constructor(parent) {
         this.parent = parent;
-        
+
         this.jsEditor = null;
         this.diffEditor = null;
         this.diffNavigator = null;
         this.monacoMode = "javascript";
         this.blockEditorChange = false;
+        this.definitionWorker = null;
+        this.deprecatedCandidates = [];
 
         this.compilerTriggerTimeoutID = null;
     }
@@ -21,7 +23,7 @@ class MonacoCreator {
     };
 
     getCode() {
-        if(this.jsEditor) return this.jsEditor.getValue();
+        if (this.jsEditor) return this.jsEditor.getValue();
         else return "";
     };
     setCode(value) {
@@ -56,6 +58,8 @@ class MonacoCreator {
             return;
 
         const libContent = await response.text();
+        this.setupDefinitionWorker(libContent);
+
         require.config({ paths: { 'vs': 'node_modules/monaco-editor/dev/vs' } });
 
         require(['vs/editor/editor.main'], () => {
@@ -70,24 +74,112 @@ class MonacoCreator {
         });
     };
 
-    hookMonacoCompletionProvider(provider) {
-        const hooked = provider.prototype.provideCompletionItems;
+    setupDefinitionWorker(libContent) {
+        this.definitionWorker = new Worker('js/definitionWorker.js');
+        this.definitionWorker.addEventListener('message', ({ data }) => {
+            this.deprecatedCandidates = data.result;
+            this.analyzeCode();
+        });
+        this.definitionWorker.postMessage({ code: libContent });
+    }
+
+    isDeprecatedEntry(details) {
+        return details
+            && details.tags
+            && details.tags.find(this.isDeprecatedTag);
+    }
+
+    isDeprecatedTag(tag) {
+        return tag
+            && tag.name == "deprecated";
+    }
+
+    async analyzeCode() {
+        // if the definition worker is very fast, this can be called out of context
+        if (!this.jsEditor)
+            return;
+
+        const model = this.jsEditor.getModel();
+        if (!model)
+            return;
+
+        const uri = model.uri;
+
+        let worker = null;
+        if (this.parent.settingsPG.ScriptLanguage == "JS")
+            worker = await monaco.languages.typescript.getJavaScriptWorker();
+        else
+            worker = await monaco.languages.typescript.getTypeScriptWorker();
 
-        const suggestionFilter = function(suggestion) {
-            return !suggestion.label.startsWith("_");
+        const languageService = await worker(uri);
+        const source = '[deprecated members]';
+
+        monaco.editor.setModelMarkers(model, source, []);
+        const markers = [];
+
+        for (const candidate of this.deprecatedCandidates) {
+            const matches = model.findMatches(candidate, null, false, true, null, false);
+            for (const match of matches) {
+                const position = { lineNumber: match.range.startLineNumber, column: match.range.startColumn };
+                const wordInfo = model.getWordAtPosition(position);
+                const offset = model.getOffsetAt(position);
+
+                // continue if we already found an issue here
+                if (markers.find(m => m.startLineNumber == position.lineNumber && m.startColumn == position.column))
+                    continue;
+
+                // the following is time consuming on all suggestions, that's why we precompute deprecated candidate names in the definition worker to filter calls
+                const details = await languageService.getCompletionEntryDetails(uri.toString(), offset, wordInfo.word);
+                if (this.isDeprecatedEntry(details)) {
+                    const deprecatedInfo = details.tags.find(this.isDeprecatedTag);
+                    markers.push({
+                        startLineNumber: match.range.startLineNumber,
+                        endLineNumber: match.range.endLineNumber,
+                        startColumn: wordInfo.startColumn,
+                        endColumn: wordInfo.endColumn,
+                        message: deprecatedInfo.text,
+                        severity: monaco.MarkerSeverity.Warning,
+                        source: source,
+                    });
+                }
+            }
         }
 
-        provider.prototype.provideCompletionItems = async function(model, position, context, token) {
+        monaco.editor.setModelMarkers(model, source, markers);
+    }
+
+    hookMonacoCompletionProvider(provider) {
+        const provideCompletionItems = provider.prototype.provideCompletionItems;
+        const owner = this;
+
+        provider.prototype.provideCompletionItems = async function (model, position, context, token) {
             // reuse 'this' to preserve context through call (using apply)
-            var result = await hooked.apply(this, [model, position, context, token]);
-            
+            const result = await provideCompletionItems.apply(this, [model, position, context, token]);
+
             if (!result || !result.suggestions)
                 return result;
 
-            const suggestions = result.suggestions.filter(suggestionFilter);
-            const incomplete = result.incomplete && result.incomplete == true;
+            const suggestions = result.suggestions.filter(item => !item.label.startsWith("_"));
+
+            for (const suggestion of suggestions) {
+                if (owner.deprecatedCandidates.includes(suggestion.label)) {
+
+                    // the following is time consuming on all suggestions, that's why we precompute deprecated candidate names in the definition worker to filter calls
+                    const uri = suggestion.uri;
+                    const worker = await this._worker(uri);
+                    const model = monaco.editor.getModel(uri);
+                    const details = await worker.getCompletionEntryDetails(uri.toString(), model.getOffsetAt(position), suggestion.label)
+
+                    if (owner.isDeprecatedEntry(details)) {
+                        suggestion.tags = [monaco.languages.CompletionItemTag.Deprecated];
+                    }
+                }
+            }
 
-            return { 
+            // preserve incomplete flag or force it when the definition is not yet analyzed
+            const incomplete = (result.incomplete && result.incomplete == true) || owner.deprecatedCandidates.length == 0;
+
+            return {
                 suggestions: suggestions,
                 incomplete: incomplete
             };
@@ -127,18 +219,18 @@ class MonacoCreator {
         monaco.languages.registerColorProvider(this.monacoMode, {
             provideColorPresentations: (model, colorInfo) => {
                 const color = colorInfo.color;
-                
+
                 const precision = 100.0;
                 const converter = (n) => Math.round(n * precision) / precision;
-                
+
                 let label;
                 if (color.alpha === undefined || color.alpha === 1.0) {
                     label = `(${converter(color.red)}, ${converter(color.green)}, ${converter(color.blue)})`;
                 } else {
                     label = `(${converter(color.red)}, ${converter(color.green)}, ${converter(color.blue)}, ${converter(color.alpha)})`;
                 }
-        
-                return [ { label: label } ];
+
+                return [{ label: label }];
             },
 
             provideDocumentColors: (model) => {
@@ -150,13 +242,13 @@ class MonacoCreator {
                 const converter = (g) => g === undefined ? undefined : Number(g);
 
                 return matches.map(match => ({
-                    color: { 
-                        red: converter(match.matches[1]), 
-                        green: converter(match.matches[2]), 
+                    color: {
+                        red: converter(match.matches[1]),
+                        green: converter(match.matches[2]),
                         blue: converter(match.matches[3]),
                         alpha: converter(match.matches[4])
                     },
-                    range:{
+                    range: {
                         startLineNumber: match.range.startLineNumber,
                         startColumn: match.range.startColumn + match.matches[0].indexOf("("),
                         endLineNumber: match.range.startLineNumber,
@@ -200,8 +292,9 @@ class MonacoCreator {
         this.jsEditor = monaco.editor.create(document.getElementById('jsEditor'), editorOptions);
 
         this.jsEditor.setValue(oldCode);
-        this.jsEditor.onKeyUp(function () {
+        this.jsEditor.onDidChangeModelContent(function () {
             this.parent.utils.markDirty();
+            this.analyzeCode();
         }.bind(this));
     };
 
@@ -232,15 +325,15 @@ class MonacoCreator {
             followsCaret: true,
             ignoreCharChanges: true
         });
-        
+
         const menuPG = this.parent.menuPG;
         const main = this.parent.main;
         const monacoCreator = this;
 
-        this.diffEditor.addCommand(monaco.KeyCode.Escape, function() { main.toggleDiffEditor(monacoCreator, menuPG); });
+        this.diffEditor.addCommand(monaco.KeyCode.Escape, function () { main.toggleDiffEditor(monacoCreator, menuPG); });
         // Adding default VSCode bindinds for previous/next difference
-        this.diffEditor.addCommand(monaco.KeyMod.Alt | monaco.KeyCode.F5, function() { main.navigateToNext(); });
-        this.diffEditor.addCommand(monaco.KeyMod.Shift | monaco.KeyMod.Alt | monaco.KeyCode.F5, function() { main.navigateToPrevious(); });
+        this.diffEditor.addCommand(monaco.KeyMod.Alt | monaco.KeyCode.F5, function () { main.navigateToNext(); });
+        this.diffEditor.addCommand(monaco.KeyMod.Shift | monaco.KeyMod.Alt | monaco.KeyCode.F5, function () { main.navigateToPrevious(); });
 
         this.diffEditor.focus();
     }
@@ -253,7 +346,7 @@ class MonacoCreator {
         let model = this.diffEditor.getModel();
         let leftModel = model.original;
         let rightModel = model.modified;
-        
+
         leftModel.dispose();
         rightModel.dispose();
 
@@ -267,14 +360,14 @@ class MonacoCreator {
     /**
      * Format the code in the editor
      */
-    formatCode () {
+    formatCode() {
         this.jsEditor.getAction('editor.action.formatDocument').run();
     };
 
     /**
      * Toggle the minimap
      */
-    toggleMinimap () {
+    toggleMinimap() {
         var minimapToggle = document.getElementById("minimapToggle1280");
         if (minimapToggle.classList.contains('checked')) {
             this.jsEditor.updateOptions({ minimap: { enabled: false } });
@@ -306,11 +399,11 @@ class MonacoCreator {
             const result = await languageService.getEmitOutput(uriStr);
             const diagnostics = await Promise.all([languageService.getSyntacticDiagnostics(uriStr), languageService.getSemanticDiagnostics(uriStr)]);
 
-            diagnostics.forEach(function(diagset) {
+            diagnostics.forEach(function (diagset) {
                 if (diagset.length) {
                     const diagnostic = diagset[0];
                     const position = model.getPositionAt(diagnostic.start);
-                    
+
                     const error = new EvalError(diagnostic.messageText);
                     error.lineNumber = position.lineNumber;
                     error.columnNumber = position.column;

+ 2 - 2
Viewer/src/configuration/configuration.ts

@@ -97,10 +97,10 @@ export interface ViewerConfiguration {
             specular?: { r: number, g: number, b: number };
         }
         hideLoadingDelay?: number;
-        /** Deprecated */
+        /** @deprecated */
         assetsRootURL?: string;
         environmentMainColor?: { r: number, g: number, b: number };
-        /** Deprecated */
+        /** @deprecated */
         environmentMap?: {
             /**
              * Environment map texture path in relative to the asset folder.

+ 1 - 1
Viewer/src/configuration/interfaces/groundConfiguration.ts

@@ -2,7 +2,7 @@ export interface IGroundConfiguration {
     size?: number;
     receiveShadows?: boolean;
     shadowLevel?: number;
-    shadowOnly?: boolean; // deprecated
+    /** @deprecated */ shadowOnly?: boolean; 
     mirror?: boolean | {
         sizeRatio?: number;
         blurKernel?: number;

+ 2 - 4
Viewer/src/configuration/interfaces/sceneConfiguration.ts

@@ -5,15 +5,13 @@ import { IGlowLayerOptions } from "babylonjs";
 export interface ISceneConfiguration {
     debug?: boolean;
     clearColor?: { r: number, g: number, b: number, a: number };
-    /** Deprecated, use environmentMap.mainColor instead. */
+    /** @deprecated Please use environmentMap.mainColor instead. */
     mainColor?: { r?: number, g?: number, b?: number };
     imageProcessingConfiguration?: IImageProcessingConfiguration;
     environmentTexture?: string;
     colorGrading?: IColorGradingConfiguration;
     environmentRotationY?: number;
-    /**
-     * Deprecated, please use default rendering pipeline
-     */
+    /** @deprecated Please use default rendering pipeline. */
     glow?: boolean | IGlowLayerOptions;
     disableHdr?: boolean;
     renderInBackground?: boolean;

+ 2 - 2
Viewer/src/configuration/interfaces/skyboxConfiguration.ts

@@ -7,9 +7,9 @@ export interface ISkyboxConfiguration {
         url?: string | Array<string>;
     };
     color?: { r: number, g: number, b: number };
-    pbr?: boolean; // deprecated
+    /** @deprecated */ pbr?: boolean;
     scale?: number;
-    blur?: number; // deprecated
+    /** @deprecated */ blur?: number;
     material?: {
         imageProcessingConfiguration?: IImageProcessingConfiguration;
         [propName: string]: any;

+ 1 - 0
dist/preview release/what's new.md

@@ -88,6 +88,7 @@
 - Added support for diffing snippets in the playground ([sailro](http://www.github.com/sailro))
 - Added diff navigator in the playground ([sailro](http://www.github.com/sailro))
 - Added custom filter to remove internals from the completion in the playground ([sailro](http://www.github.com/sailro))
+- Added support for tagging deprecated members (both in editor and for completion) in the playground ([sailro](http://www.github.com/sailro))
 
 ### Meshes
 

+ 1 - 1
src/Materials/Node/Blocks/colorMergerBlock.ts

@@ -85,7 +85,7 @@ export class ColorMergerBlock extends NodeMaterialBlock {
 
     /**
      * Gets the rgb component (output)
-     * @deprecated Please use rgbOut instead
+     * @deprecated Please use rgbOut instead.
      */
     public get rgb(): NodeMaterialConnectionPoint {
         return this.rgbOut;

+ 2 - 2
src/Materials/Node/Blocks/vectorMergerBlock.ts

@@ -101,7 +101,7 @@ export class VectorMergerBlock extends NodeMaterialBlock {
 
     /**
      * Gets the xy component (output)
-     * @deprecated Please use xyOut instead
+     * @deprecated Please use xyOut instead.
      */
     public get xy(): NodeMaterialConnectionPoint {
         return this.xyOut;
@@ -109,7 +109,7 @@ export class VectorMergerBlock extends NodeMaterialBlock {
 
     /**
      * Gets the xyz component (output)
-     * @deprecated Please use xyzOut instead
+     * @deprecated Please use xyzOut instead.
      */
     public get xyz(): NodeMaterialConnectionPoint {
         return this.xyzOut;

+ 2 - 2
src/Maths/math.vector.ts

@@ -1308,10 +1308,10 @@ export class Vector3 {
 
     /**
      * Returns a new Vector3 set from the index "offset" of the given Float32Array
-     * This function is deprecated. Use FromArray instead
      * @param array defines the source array
      * @param offset defines the offset in the source array
      * @returns the new Vector3
+     * @deprecated Please use FromArray instead.
      */
     public static FromFloatArray(array: DeepImmutable<Float32Array>, offset?: number): Vector3 {
         return Vector3.FromArray(array, offset);
@@ -1331,10 +1331,10 @@ export class Vector3 {
 
     /**
      * Sets the given vector "result" with the element values from the index "offset" of the given Float32Array
-     * This function is deprecated.  Use FromArrayToRef instead.
      * @param array defines the source array
      * @param offset defines the offset in the source array
      * @param result defines the Vector3 where to store the result
+     * @deprecated Please use FromArrayToRef instead.
      */
     public static FromFloatArrayToRef(array: DeepImmutable<Float32Array>, offset: number, result: Vector3): void {
         return Vector3.FromArrayToRef(array, offset, result);

+ 3 - 3
src/Meshes/buffer.ts

@@ -99,8 +99,8 @@ export class Buffer {
     /**
      * Gets the stride in float32 units (i.e. byte stride / 4).
      * May not be an integer if the byte stride is not divisible by 4.
-     * DEPRECATED. Use byteStride instead.
      * @returns the stride in float32 units
+     * @deprecated Please use byteStride instead.
      */
     public getStrideSize(): number {
         return this.byteStride / Float32Array.BYTES_PER_ELEMENT;
@@ -371,8 +371,8 @@ export class VertexBuffer {
     /**
      * Gets the stride in float32 units (i.e. byte stride / 4).
      * May not be an integer if the byte stride is not divisible by 4.
-     * DEPRECATED. Use byteStride instead.
      * @returns the stride in float32 units
+     * @deprecated Please use byteStride instead.
      */
     public getStrideSize(): number {
         return this.byteStride / VertexBuffer.GetTypeByteLength(this.type);
@@ -380,8 +380,8 @@ export class VertexBuffer {
 
     /**
      * Returns the offset as a multiple of the type byte length.
-     * DEPRECATED. Use byteOffset instead.
      * @returns the offset in bytes
+     * @deprecated Please use byteOffset instead.
      */
     public getOffset(): number {
         return this.byteOffset / VertexBuffer.GetTypeByteLength(this.type);