1 package org.simantics.scl.compiler.markdown.html;
3 import java.util.ArrayList;
4 import java.util.Collections;
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;
27 import gnu.trove.map.hash.THashMap;
28 import gnu.trove.procedure.TObjectProcedure;
29 import gnu.trove.set.hash.THashSet;
31 public class SCLDocumentationExtensionNodeHandler implements ExtensionNodeHandler {
33 final ModuleRepository moduleRepository;
34 final String documentationName;
36 Failable<Module> relatedModule;
37 THashMap<String,Failable<Module>> moduleCache = new THashMap<String,Failable<Module>>();
39 THashSet<String> documentedValues = new THashSet<String>();
40 THashSet<String> documentedClasses = new THashSet<String>();
41 THashSet<String> documentedTypeConstructors = new THashSet<String>();
43 public SCLDocumentationExtensionNodeHandler(ModuleRepository moduleRepository,
44 String documentationName) {
45 this.moduleRepository = moduleRepository;
46 this.documentationName = documentationName;
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) {
58 if(!documentedValues.add(name))
59 System.err.println("Value '" + name + "' has already been documented in " + documentationName + ".");
60 generateValueDocumentation(container, name);
64 else if(extension.equals("class")) {
65 String[] names = content.split(",");
66 DocumentNode container = new DocumentNode();
67 for(String name : names) {
71 if(!documentedClasses.add(name))
72 System.err.println("Class '" + name + "' has already been documented in " + documentationName + ".");
73 generateClassDocumentation(container, name);
77 else if(extension.equals("data")) {
78 String[] names = content.split(",");
79 DocumentNode container = new DocumentNode();
80 for(String name : names) {
84 if(!documentedTypeConstructors.add(name))
85 System.err.println("Type constructor '" + name + "' has already been documented in " + documentationName + ".");
86 generateDataDocumentation(container, name);
90 else if(extension.equals("undocumented")) {
91 Module module = getRelatedModule();
94 final ArrayList<String> undocumentedValues = new ArrayList<String>();
95 module.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure<SCLValue>() {
97 public boolean execute(SCLValue value) {
100 String name = value.getName().name;
101 if(documentedValues.contains(name) ||
102 name.charAt(0) == '_' ||
103 (name.contains("$") && Character.isAlphabetic(name.charAt(0))))
105 undocumentedValues.add(name);
109 Collections.sort(undocumentedValues);
111 DocumentNode container = new DocumentNode();
112 for(String name : undocumentedValues) {
113 generateValueDocumentation(container, name);
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);
127 if(relatedModule.didSucceed())
128 return relatedModule.getResult();
133 private Module getModule(String moduleName) {
134 Failable<Module> fm = moduleCache.get(moduleName);
136 fm = moduleRepository.getModule(moduleName);
137 moduleCache.put(moduleName, fm);
139 System.err.println("Couldn't load the module " + moduleName);
142 return fm.getResult();
147 private void generateValueDocumentation(Node container, String name) {
148 int p = name.lastIndexOf('/');
151 while(p > 0 && name.charAt(p-1) == '/')
153 module = getModule(name.substring(0, p));
154 name = name.substring(p+1);
157 module = getRelatedModule();
160 generateValueDocumentation(container, module, name);
163 private void generateValueDocumentation(Node container, Module module, String name, TypeUnparsingContext tuc) {
164 SCLValue value = module.getValue(name);
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));
172 if(value.isPrivate())
173 System.err.println("Documentation " + documentationName + " refers to a private value " + name + ".");
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>");
189 signature.append("</div><div class=\"doc\">");
190 container.addChild(new HtmlNode(signature));
192 if(value.documentation != null) {
193 MarkdownParser parser = new MarkdownParser();
194 container.addChild(parser.parseDocument(value.documentation));
197 System.out.println(name);
198 container.addChild(new HtmlNode("</div></div>"));
201 private void generateValueDocumentation(Node container, Module module, String name) {
202 generateValueDocumentation(container, module, name, new TypeUnparsingContext());
205 private void generateClassDocumentation(Node container, String name) {
206 int p = name.lastIndexOf('/');
209 module = getModule(name.substring(0, p));
210 name = name.substring(p+1);
213 module = getRelatedModule();
216 generateClassDocumentation(container, module, name);
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));
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) {
240 signature.append(", ");
241 cx.toString(tuc, signature);
243 signature.append(") => ");
245 signature.append(name);
246 for(TVar p : typeClass.parameters) {
247 signature.append(' ');
248 p.toName(tuc, signature);
250 signature.append("</div><div class=\"doc\">");
251 container.addChild(new HtmlNode(signature));
253 if(typeClass.documentation != null) {
254 MarkdownParser parser = new MarkdownParser();
255 container.addChild(parser.parseDocument(typeClass.documentation));
258 System.out.println(name);
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));
265 container.addChild(new HtmlNode("</div></div>"));
268 private void generateDataDocumentation(Node container, String name) {
269 int p = name.lastIndexOf('/');
272 module = getModule(name.substring(0, p));
273 name = name.substring(p+1);
276 module = getRelatedModule();
279 generateDataDocumentation(container, module, name);
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));
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);
302 String moduleName = module.getName();
303 if(!moduleName.equals(documentationName)) {
304 signature.append(" <span class=\"greyed\">(").append(moduleName).append(")</span>");
306 signature.append("</div><div class=\"doc\">");
307 container.addChild(new HtmlNode(signature));
309 if(typeDescriptor.getDocumentation() != null) {
310 MarkdownParser parser = new MarkdownParser();
311 container.addChild(parser.parseDocument(typeDescriptor.getDocumentation()));
314 System.out.println(name);
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));
323 container.addChild(new HtmlNode("</div></div>"));