]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/markdown/html/SCLDocumentationExtensionNodeHandler.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / markdown / html / SCLDocumentationExtensionNodeHandler.java
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/markdown/html/SCLDocumentationExtensionNodeHandler.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/markdown/html/SCLDocumentationExtensionNodeHandler.java
new file mode 100644 (file)
index 0000000..6a505fa
--- /dev/null
@@ -0,0 +1,321 @@
+package org.simantics.scl.compiler.markdown.html;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.procedure.TObjectProcedure;
+import gnu.trove.set.hash.THashSet;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.simantics.scl.compiler.common.datatypes.Constructor;
+import org.simantics.scl.compiler.elaboration.modules.SCLValue;
+import org.simantics.scl.compiler.elaboration.modules.TypeClass;
+import org.simantics.scl.compiler.elaboration.modules.TypeConstructor;
+import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter;
+import org.simantics.scl.compiler.errors.Failable;
+import org.simantics.scl.compiler.markdown.internal.ExtensionNodeHandler;
+import org.simantics.scl.compiler.markdown.internal.HtmlEscape;
+import org.simantics.scl.compiler.markdown.internal.MarkdownParser;
+import org.simantics.scl.compiler.markdown.nodes.CodeBlockNode;
+import org.simantics.scl.compiler.markdown.nodes.DocumentNode;
+import org.simantics.scl.compiler.markdown.nodes.HtmlNode;
+import org.simantics.scl.compiler.markdown.nodes.Node;
+import org.simantics.scl.compiler.module.Module;
+import org.simantics.scl.compiler.module.repository.ModuleRepository;
+import org.simantics.scl.compiler.types.TPred;
+import org.simantics.scl.compiler.types.TVar;
+import org.simantics.scl.compiler.types.Types;
+import org.simantics.scl.compiler.types.util.TypeUnparsingContext;
+
+public class SCLDocumentationExtensionNodeHandler implements ExtensionNodeHandler {
+
+    final ModuleRepository moduleRepository;
+    final String documentationName;
+    
+    Failable<Module> relatedModule;
+    THashMap<String,Failable<Module>> moduleCache = new THashMap<String,Failable<Module>>(); 
+    
+    THashSet<String> documentedValues = new THashSet<String>();
+    THashSet<String> documentedClasses = new THashSet<String>();
+    THashSet<String> documentedTypeConstructors = new THashSet<String>();
+    
+    public SCLDocumentationExtensionNodeHandler(ModuleRepository moduleRepository,
+            String documentationName) {
+        this.moduleRepository = moduleRepository;
+        this.documentationName = documentationName;
+    }
+
+    @Override
+    public DocumentNode expandBlock(String extension, String content) {
+        if(extension.equals("value")) {
+            String[] names = content.split(",");
+            DocumentNode container = new DocumentNode();
+            for(String name : names) {
+                name = name.trim();
+                if(name.isEmpty())
+                    continue;
+                if(!documentedValues.add(name))
+                    System.err.println("Value '" + name + "' has already been documented in " + documentationName + ".");
+                generateValueDocumentation(container, name);
+            }
+            return container;
+        }
+        else if(extension.equals("class")) {
+            String[] names = content.split(",");
+            DocumentNode container = new DocumentNode();
+            for(String name : names) {
+                name = name.trim();
+                if(name.isEmpty())
+                    continue;
+                if(!documentedClasses.add(name))
+                    System.err.println("Class '" + name + "' has already been documented in " + documentationName + ".");
+                generateClassDocumentation(container, name);
+            }
+            return container;
+        }
+        else if(extension.equals("data")) {
+            String[] names = content.split(",");
+            DocumentNode container = new DocumentNode();
+            for(String name : names) {
+                name = name.trim();
+                if(name.isEmpty())
+                    continue;
+                if(!documentedTypeConstructors.add(name))
+                    System.err.println("Type constructor '" + name + "' has already been documented in " + documentationName + ".");
+                generateDataDocumentation(container, name);
+            }
+            return container;
+        }
+        else if(extension.equals("undocumented")) {
+            Module module = getRelatedModule();
+            if(module == null)
+                return null;
+            final ArrayList<String> undocumentedValues = new ArrayList<String>(); 
+            module.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure<SCLValue>() {
+                @Override
+                public boolean execute(SCLValue value) {
+                    if(value.isPrivate())
+                        return true;
+                    String name = value.getName().name;
+                    if(documentedValues.contains(name) ||
+                            name.charAt(0) == '_' ||
+                            (name.contains("$") && Character.isAlphabetic(name.charAt(0))))
+                        return true;
+                    undocumentedValues.add(name);
+                    return true;
+                }
+            });
+            Collections.sort(undocumentedValues);
+
+            DocumentNode container = new DocumentNode();
+            for(String name : undocumentedValues) {
+                generateValueDocumentation(container, name);
+            }
+            return container;
+        }
+        else
+            return null;
+    }
+    
+    private Module getRelatedModule() {
+        if(relatedModule == null) {
+            relatedModule = moduleRepository.getModule(documentationName);
+            if(!relatedModule.didSucceed())
+                System.err.println("Couldn't load the module " + documentationName);
+        }
+        if(relatedModule.didSucceed())
+            return relatedModule.getResult();
+        else
+            return null;
+    }
+    
+    private Module getModule(String moduleName) {
+        Failable<Module> fm = moduleCache.get(moduleName);
+        if(fm == null) {
+            fm = moduleRepository.getModule(moduleName);
+            moduleCache.put(moduleName, fm);
+            if(!fm.didSucceed())
+                System.err.println("Couldn't load the module " + moduleName);
+        }
+        if(fm.didSucceed())
+            return fm.getResult();
+        else
+            return null;
+    }
+    
+    private void generateValueDocumentation(Node container, String name) {
+        int p = name.lastIndexOf('/');
+        Module module;
+        if(p >= 0) {
+            while(p > 0 && name.charAt(p-1) == '/')
+                --p;
+            module = getModule(name.substring(0, p));
+            name = name.substring(p+1);
+        }
+        else
+            module = getRelatedModule();
+        
+        if(module != null)
+            generateValueDocumentation(container, module, name);
+    }
+    
+    private void generateValueDocumentation(Node container, Module module, String name, TypeUnparsingContext tuc) {
+        SCLValue value = module.getValue(name);
+        if(value == null) {
+            StringBuilder error = new StringBuilder();
+            error.append("Didn't find the value '" + name + "'.");
+            System.err.println(error);
+            container.addChild(new CodeBlockNode(error));
+            return;
+        }
+        if(value.isPrivate())
+            System.err.println("Documentation " + documentationName + " refers to a private value " + name + ".");
+        
+        StringBuilder signature = new StringBuilder();
+        signature.append("<div id=\"")
+        .append(HtmlEscape.escape(name))
+        .append("\" class=\"code-doc-box\"><div class=\"code\">");
+        char firstChar = name.charAt(0);
+        if(!Character.isAlphabetic(firstChar) && firstChar != '_')
+            name = "(" + name + ")";
+        signature.append(HtmlEscape.escape(name)).append(" :: ");
+        String typeStr = Types.removeForAll(value.getType(), new ArrayList<TVar>()).toString(tuc);
+        signature.append(HtmlEscape.escape(typeStr));
+        String moduleName = module.getName();
+        if(!moduleName.equals(documentationName)) {
+            signature.append(" <span class=\"greyed\">(").append(moduleName).append(")</span>");
+        }
+        signature.append("</div><div class=\"doc\">");
+        container.addChild(new HtmlNode(signature));
+        
+        if(value.documentation != null) {
+            MarkdownParser parser = new MarkdownParser();
+            container.addChild(parser.parseDocument(value.documentation));
+        }
+        else
+            System.out.println(name);
+        container.addChild(new HtmlNode("</div></div>"));
+    }
+    
+    private void generateValueDocumentation(Node container, Module module, String name) {
+        generateValueDocumentation(container, module, name, new TypeUnparsingContext());
+    }
+    
+    private void generateClassDocumentation(Node container, String name) {
+        int p = name.lastIndexOf('/');
+        Module module;
+        if(p >= 0) {
+            module = getModule(name.substring(0, p));
+            name = name.substring(p+1);
+        }
+        else
+            module = getRelatedModule();
+        
+        if(module != null)
+            generateClassDocumentation(container, module, name);
+    }
+    
+    private void generateClassDocumentation(Node container, Module module, String name) {
+        TypeClass typeClass = module.getTypeClass(name);
+        if(typeClass == null) {
+            StringBuilder error = new StringBuilder();
+            error.append("Didn't find the type class '" + name + "'.");
+            System.err.println(error);
+            container.addChild(new CodeBlockNode(error));
+            return;
+        }
+        
+        TypeUnparsingContext tuc = new TypeUnparsingContext();
+        StringBuilder signature = new StringBuilder();
+        signature.append("<div class=\"code-doc-box\"><div class=\"code\">");
+        signature.append("class ");
+        if(typeClass.context.length > 0) {
+            signature.append('(');
+            boolean first = true;
+            for(TPred cx : typeClass.context) {
+                if(first)
+                    first = false;
+                else
+                    signature.append(", ");
+                cx.toString(tuc, signature);
+            }
+            signature.append(") => ");
+        }
+        signature.append(name);
+        for(TVar p : typeClass.parameters) {
+            signature.append(' ');
+            p.toName(tuc, signature);
+        } 
+        signature.append("</div><div class=\"doc\">");
+        container.addChild(new HtmlNode(signature));
+        
+        if(typeClass.documentation != null) {
+            MarkdownParser parser = new MarkdownParser();
+            container.addChild(parser.parseDocument(typeClass.documentation));
+        }
+        else
+            System.out.println(name);
+        
+        for(String methodName : typeClass.methodNames) {
+            if(!documentedValues.add(methodName))
+                System.err.println("Method '" + methodName + "' has already been documented in " + documentationName + ".");
+            generateValueDocumentation(container, module, methodName, new TypeUnparsingContext(tuc));
+        }
+        container.addChild(new HtmlNode("</div></div>"));
+    }
+
+    private void generateDataDocumentation(Node container, String name) {
+        int p = name.lastIndexOf('/');
+        Module module;
+        if(p >= 0) {
+            module = getModule(name.substring(0, p));
+            name = name.substring(p+1);
+        }
+        else
+            module = getRelatedModule();
+        
+        if(module != null)
+            generateDataDocumentation(container, module, name);
+    }
+    
+    private void generateDataDocumentation(Node container, Module module, String name) {
+        TypeConstructor typeConstructor = module.getTypeConstructor(name);
+        if(typeConstructor == null) {
+            StringBuilder error = new StringBuilder();
+            error.append("Didn't find the type constructor '" + name + "'.");
+            System.err.println(error);
+            container.addChild(new CodeBlockNode(error));
+            return;
+        }
+        
+        TypeUnparsingContext tuc = new TypeUnparsingContext();
+        StringBuilder signature = new StringBuilder();
+        signature.append("<div class=\"code-doc-box\"><div class=\"code\">");
+        signature.append("data ");
+        signature.append(typeConstructor.name.name);
+        for(TVar p : typeConstructor.parameters) {
+            signature.append(' ');
+            p.toName(tuc, signature);
+        }
+        String moduleName = module.getName();
+        if(!moduleName.equals(documentationName)) {
+            signature.append(" <span class=\"greyed\">(").append(moduleName).append(")</span>");
+        }
+        signature.append("</div><div class=\"doc\">");
+        container.addChild(new HtmlNode(signature));
+        
+        if(typeConstructor.documentation != null) {
+            MarkdownParser parser = new MarkdownParser();
+            container.addChild(parser.parseDocument(typeConstructor.documentation));
+        }
+        else
+            System.out.println(name);
+        
+        for(Constructor constructor : typeConstructor.constructors) {
+            if(!documentedValues.add(constructor.name.name))
+                System.err.println("Method '" + constructor.name.name + "' has already been documented in " + documentationName + ".");
+            generateValueDocumentation(container, module, constructor.name.name, new TypeUnparsingContext(tuc));
+        }
+        container.addChild(new HtmlNode("</div></div>"));
+    }
+}