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