]> gerrit.simantics Code Review - simantics/platform.git/blob
d971f7d0faf8735fe1bb8377432e1de0fa34ff1b
[simantics/platform.git] /
1 package org.simantics.scl.compiler.markdown.html;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5
6 import org.simantics.scl.compiler.common.datatypes.Constructor;
7 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
8 import org.simantics.scl.compiler.elaboration.modules.TypeClass;
9 import org.simantics.scl.compiler.elaboration.modules.TypeConstructor;
10 import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter;
11 import org.simantics.scl.compiler.errors.Failable;
12 import org.simantics.scl.compiler.markdown.internal.ExtensionNodeHandler;
13 import org.simantics.scl.compiler.markdown.internal.HtmlEscape;
14 import org.simantics.scl.compiler.markdown.internal.MarkdownParser;
15 import org.simantics.scl.compiler.markdown.nodes.CodeBlockNode;
16 import org.simantics.scl.compiler.markdown.nodes.DocumentNode;
17 import org.simantics.scl.compiler.markdown.nodes.HtmlNode;
18 import org.simantics.scl.compiler.markdown.nodes.Node;
19 import org.simantics.scl.compiler.module.Module;
20 import org.simantics.scl.compiler.module.repository.ModuleRepository;
21 import org.simantics.scl.compiler.types.TPred;
22 import org.simantics.scl.compiler.types.TVar;
23 import org.simantics.scl.compiler.types.Types;
24 import org.simantics.scl.compiler.types.util.TypeUnparsingContext;
25
26 import gnu.trove.map.hash.THashMap;
27 import gnu.trove.procedure.TObjectProcedure;
28 import gnu.trove.set.hash.THashSet;
29
30 public class SCLDocumentationExtensionNodeHandler implements ExtensionNodeHandler {
31
32     final ModuleRepository moduleRepository;
33     final String documentationName;
34     
35     Failable<Module> relatedModule;
36     THashMap<String,Failable<Module>> moduleCache = new THashMap<String,Failable<Module>>(); 
37     
38     THashSet<String> documentedValues = new THashSet<String>();
39     THashSet<String> documentedClasses = new THashSet<String>();
40     THashSet<String> documentedTypeConstructors = new THashSet<String>();
41     
42     public SCLDocumentationExtensionNodeHandler(ModuleRepository moduleRepository,
43             String documentationName) {
44         this.moduleRepository = moduleRepository;
45         this.documentationName = documentationName;
46     }
47
48     @Override
49     public DocumentNode expandBlock(String extension, String content) {
50         if(extension.equals("value")) {
51             String[] names = content.split(",");
52             DocumentNode container = new DocumentNode();
53             for(String name : names) {
54                 name = name.trim();
55                 if(name.isEmpty())
56                     continue;
57                 if(!documentedValues.add(name))
58                     System.err.println("Value '" + name + "' has already been documented in " + documentationName + ".");
59                 generateValueDocumentation(container, name);
60             }
61             return container;
62         }
63         else if(extension.equals("class")) {
64             String[] names = content.split(",");
65             DocumentNode container = new DocumentNode();
66             for(String name : names) {
67                 name = name.trim();
68                 if(name.isEmpty())
69                     continue;
70                 if(!documentedClasses.add(name))
71                     System.err.println("Class '" + name + "' has already been documented in " + documentationName + ".");
72                 generateClassDocumentation(container, name);
73             }
74             return container;
75         }
76         else if(extension.equals("data")) {
77             String[] names = content.split(",");
78             DocumentNode container = new DocumentNode();
79             for(String name : names) {
80                 name = name.trim();
81                 if(name.isEmpty())
82                     continue;
83                 if(!documentedTypeConstructors.add(name))
84                     System.err.println("Type constructor '" + name + "' has already been documented in " + documentationName + ".");
85                 generateDataDocumentation(container, name);
86             }
87             return container;
88         }
89         else if(extension.equals("undocumented")) {
90             Module module = getRelatedModule();
91             if(module == null)
92                 return null;
93             final ArrayList<String> undocumentedValues = new ArrayList<String>(); 
94             module.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure<SCLValue>() {
95                 @Override
96                 public boolean execute(SCLValue value) {
97                     if(value.isPrivate())
98                         return true;
99                     String name = value.getName().name;
100                     if(documentedValues.contains(name) ||
101                             name.charAt(0) == '_' ||
102                             (name.contains("$") && Character.isAlphabetic(name.charAt(0))))
103                         return true;
104                     undocumentedValues.add(name);
105                     return true;
106                 }
107             });
108             Collections.sort(undocumentedValues);
109
110             DocumentNode container = new DocumentNode();
111             for(String name : undocumentedValues) {
112                 generateValueDocumentation(container, name);
113             }
114             return container;
115         }
116         else
117             return null;
118     }
119     
120     private Module getRelatedModule() {
121         if(relatedModule == null) {
122             relatedModule = moduleRepository.getModule(documentationName);
123             if(!relatedModule.didSucceed())
124                 System.err.println("Couldn't load the module " + documentationName);
125         }
126         if(relatedModule.didSucceed())
127             return relatedModule.getResult();
128         else
129             return null;
130     }
131     
132     private Module getModule(String moduleName) {
133         Failable<Module> fm = moduleCache.get(moduleName);
134         if(fm == null) {
135             fm = moduleRepository.getModule(moduleName);
136             moduleCache.put(moduleName, fm);
137             if(!fm.didSucceed())
138                 System.err.println("Couldn't load the module " + moduleName);
139         }
140         if(fm.didSucceed())
141             return fm.getResult();
142         else
143             return null;
144     }
145     
146     private void generateValueDocumentation(Node container, String name) {
147         int p = name.lastIndexOf('/');
148         Module module;
149         if(p >= 0) {
150             while(p > 0 && name.charAt(p-1) == '/')
151                 --p;
152             module = getModule(name.substring(0, p));
153             name = name.substring(p+1);
154         }
155         else
156             module = getRelatedModule();
157         
158         if(module != null)
159             generateValueDocumentation(container, module, name);
160     }
161     
162     private void generateValueDocumentation(Node container, Module module, String name, TypeUnparsingContext tuc) {
163         SCLValue value = module.getValue(name);
164         if(value == null) {
165             StringBuilder error = new StringBuilder();
166             error.append("Didn't find the value '" + name + "'.");
167             System.err.println(error);
168             container.addChild(new CodeBlockNode(error));
169             return;
170         }
171         if(value.isPrivate())
172             System.err.println("Documentation " + documentationName + " refers to a private value " + name + ".");
173         
174         StringBuilder signature = new StringBuilder();
175         signature.append("<div id=\"")
176         .append(HtmlEscape.escape(name))
177         .append("\" class=\"code-doc-box\"><div class=\"code\">");
178         char firstChar = name.charAt(0);
179         if(!Character.isAlphabetic(firstChar) && firstChar != '_')
180             name = "(" + name + ")";
181         signature.append(HtmlEscape.escape(name)).append(" :: ");
182         String typeStr = Types.removeForAll(value.getType(), new ArrayList<TVar>()).toString(tuc);
183         signature.append(HtmlEscape.escape(typeStr));
184         String moduleName = module.getName();
185         if(!moduleName.equals(documentationName)) {
186             signature.append(" <span class=\"greyed\">(").append(moduleName).append(")</span>");
187         }
188         signature.append("</div><div class=\"doc\">");
189         container.addChild(new HtmlNode(signature));
190         
191         if(value.documentation != null) {
192             MarkdownParser parser = new MarkdownParser();
193             container.addChild(parser.parseDocument(value.documentation));
194         }
195         else
196             System.out.println(name);
197         container.addChild(new HtmlNode("</div></div>"));
198     }
199     
200     private void generateValueDocumentation(Node container, Module module, String name) {
201         generateValueDocumentation(container, module, name, new TypeUnparsingContext());
202     }
203     
204     private void generateClassDocumentation(Node container, String name) {
205         int p = name.lastIndexOf('/');
206         Module module;
207         if(p >= 0) {
208             module = getModule(name.substring(0, p));
209             name = name.substring(p+1);
210         }
211         else
212             module = getRelatedModule();
213         
214         if(module != null)
215             generateClassDocumentation(container, module, name);
216     }
217     
218     private void generateClassDocumentation(Node container, Module module, String name) {
219         TypeClass typeClass = module.getTypeClass(name);
220         if(typeClass == null) {
221             StringBuilder error = new StringBuilder();
222             error.append("Didn't find the type class '" + name + "'.");
223             System.err.println(error);
224             container.addChild(new CodeBlockNode(error));
225             return;
226         }
227         
228         TypeUnparsingContext tuc = new TypeUnparsingContext();
229         StringBuilder signature = new StringBuilder();
230         signature.append("<div class=\"code-doc-box\"><div class=\"code\">");
231         signature.append("class ");
232         if(typeClass.context.length > 0) {
233             signature.append('(');
234             boolean first = true;
235             for(TPred cx : typeClass.context) {
236                 if(first)
237                     first = false;
238                 else
239                     signature.append(", ");
240                 cx.toString(tuc, signature);
241             }
242             signature.append(") => ");
243         }
244         signature.append(name);
245         for(TVar p : typeClass.parameters) {
246             signature.append(' ');
247             p.toName(tuc, signature);
248         } 
249         signature.append("</div><div class=\"doc\">");
250         container.addChild(new HtmlNode(signature));
251         
252         if(typeClass.documentation != null) {
253             MarkdownParser parser = new MarkdownParser();
254             container.addChild(parser.parseDocument(typeClass.documentation));
255         }
256         else
257             System.out.println(name);
258         
259         for(String methodName : typeClass.methodNames) {
260             if(!documentedValues.add(methodName))
261                 System.err.println("Method '" + methodName + "' has already been documented in " + documentationName + ".");
262             generateValueDocumentation(container, module, methodName, new TypeUnparsingContext(tuc));
263         }
264         container.addChild(new HtmlNode("</div></div>"));
265     }
266
267     private void generateDataDocumentation(Node container, String name) {
268         int p = name.lastIndexOf('/');
269         Module module;
270         if(p >= 0) {
271             module = getModule(name.substring(0, p));
272             name = name.substring(p+1);
273         }
274         else
275             module = getRelatedModule();
276         
277         if(module != null)
278             generateDataDocumentation(container, module, name);
279     }
280     
281     private void generateDataDocumentation(Node container, Module module, String name) {
282         TypeConstructor typeConstructor = module.getTypeConstructor(name);
283         if(typeConstructor == null) {
284             StringBuilder error = new StringBuilder();
285             error.append("Didn't find the type constructor '" + name + "'.");
286             System.err.println(error);
287             container.addChild(new CodeBlockNode(error));
288             return;
289         }
290         
291         TypeUnparsingContext tuc = new TypeUnparsingContext();
292         StringBuilder signature = new StringBuilder();
293         signature.append("<div class=\"code-doc-box\"><div class=\"code\">");
294         signature.append("data ");
295         signature.append(typeConstructor.name.name);
296         for(TVar p : typeConstructor.parameters) {
297             signature.append(' ');
298             p.toName(tuc, signature);
299         }
300         String moduleName = module.getName();
301         if(!moduleName.equals(documentationName)) {
302             signature.append(" <span class=\"greyed\">(").append(moduleName).append(")</span>");
303         }
304         signature.append("</div><div class=\"doc\">");
305         container.addChild(new HtmlNode(signature));
306         
307         if(typeConstructor.documentation != null) {
308             MarkdownParser parser = new MarkdownParser();
309             container.addChild(parser.parseDocument(typeConstructor.documentation));
310         }
311         else
312             System.out.println(name);
313         
314         for(Constructor constructor : typeConstructor.constructors) {
315             if(!documentedValues.add(constructor.name.name))
316                 System.err.println("Method '" + constructor.name.name + "' has already been documented in " + documentationName + ".");
317             generateValueDocumentation(container, module, constructor.name.name, new TypeUnparsingContext(tuc));
318         }
319         container.addChild(new HtmlNode("</div></div>"));
320     }
321 }