1 package org.simantics.scl.compiler.markdown.html;
3 import gnu.trove.map.hash.THashMap;
4 import gnu.trove.procedure.TObjectProcedure;
5 import gnu.trove.set.hash.THashSet;
7 import java.util.ArrayList;
8 import java.util.Collections;
10 import org.simantics.scl.compiler.common.datatypes.Constructor;
11 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
12 import org.simantics.scl.compiler.elaboration.modules.TypeClass;
13 import org.simantics.scl.compiler.elaboration.modules.TypeConstructor;
14 import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter;
15 import org.simantics.scl.compiler.errors.Failable;
16 import org.simantics.scl.compiler.markdown.internal.ExtensionNodeHandler;
17 import org.simantics.scl.compiler.markdown.internal.HtmlEscape;
18 import org.simantics.scl.compiler.markdown.internal.MarkdownParser;
19 import org.simantics.scl.compiler.markdown.nodes.CodeBlockNode;
20 import org.simantics.scl.compiler.markdown.nodes.DocumentNode;
21 import org.simantics.scl.compiler.markdown.nodes.HtmlNode;
22 import org.simantics.scl.compiler.markdown.nodes.Node;
23 import org.simantics.scl.compiler.module.Module;
24 import org.simantics.scl.compiler.module.repository.ModuleRepository;
25 import org.simantics.scl.compiler.types.TPred;
26 import org.simantics.scl.compiler.types.TVar;
27 import org.simantics.scl.compiler.types.Types;
28 import org.simantics.scl.compiler.types.util.TypeUnparsingContext;
30 public class SCLDocumentationExtensionNodeHandler implements ExtensionNodeHandler {
32 final ModuleRepository moduleRepository;
33 final String documentationName;
35 Failable<Module> relatedModule;
36 THashMap<String,Failable<Module>> moduleCache = new THashMap<String,Failable<Module>>();
38 THashSet<String> documentedValues = new THashSet<String>();
39 THashSet<String> documentedClasses = new THashSet<String>();
40 THashSet<String> documentedTypeConstructors = new THashSet<String>();
42 public SCLDocumentationExtensionNodeHandler(ModuleRepository moduleRepository,
43 String documentationName) {
44 this.moduleRepository = moduleRepository;
45 this.documentationName = documentationName;
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) {
57 if(!documentedValues.add(name))
58 System.err.println("Value '" + name + "' has already been documented in " + documentationName + ".");
59 generateValueDocumentation(container, name);
63 else if(extension.equals("class")) {
64 String[] names = content.split(",");
65 DocumentNode container = new DocumentNode();
66 for(String name : names) {
70 if(!documentedClasses.add(name))
71 System.err.println("Class '" + name + "' has already been documented in " + documentationName + ".");
72 generateClassDocumentation(container, name);
76 else if(extension.equals("data")) {
77 String[] names = content.split(",");
78 DocumentNode container = new DocumentNode();
79 for(String name : names) {
83 if(!documentedTypeConstructors.add(name))
84 System.err.println("Type constructor '" + name + "' has already been documented in " + documentationName + ".");
85 generateDataDocumentation(container, name);
89 else if(extension.equals("undocumented")) {
90 Module module = getRelatedModule();
93 final ArrayList<String> undocumentedValues = new ArrayList<String>();
94 module.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure<SCLValue>() {
96 public boolean execute(SCLValue value) {
99 String name = value.getName().name;
100 if(documentedValues.contains(name) ||
101 name.charAt(0) == '_' ||
102 (name.contains("$") && Character.isAlphabetic(name.charAt(0))))
104 undocumentedValues.add(name);
108 Collections.sort(undocumentedValues);
110 DocumentNode container = new DocumentNode();
111 for(String name : undocumentedValues) {
112 generateValueDocumentation(container, name);
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);
126 if(relatedModule.didSucceed())
127 return relatedModule.getResult();
132 private Module getModule(String moduleName) {
133 Failable<Module> fm = moduleCache.get(moduleName);
135 fm = moduleRepository.getModule(moduleName);
136 moduleCache.put(moduleName, fm);
138 System.err.println("Couldn't load the module " + moduleName);
141 return fm.getResult();
146 private void generateValueDocumentation(Node container, String name) {
147 int p = name.lastIndexOf('/');
150 while(p > 0 && name.charAt(p-1) == '/')
152 module = getModule(name.substring(0, p));
153 name = name.substring(p+1);
156 module = getRelatedModule();
159 generateValueDocumentation(container, module, name);
162 private void generateValueDocumentation(Node container, Module module, String name, TypeUnparsingContext tuc) {
163 SCLValue value = module.getValue(name);
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));
171 if(value.isPrivate())
172 System.err.println("Documentation " + documentationName + " refers to a private value " + name + ".");
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>");
188 signature.append("</div><div class=\"doc\">");
189 container.addChild(new HtmlNode(signature));
191 if(value.documentation != null) {
192 MarkdownParser parser = new MarkdownParser();
193 container.addChild(parser.parseDocument(value.documentation));
196 System.out.println(name);
197 container.addChild(new HtmlNode("</div></div>"));
200 private void generateValueDocumentation(Node container, Module module, String name) {
201 generateValueDocumentation(container, module, name, new TypeUnparsingContext());
204 private void generateClassDocumentation(Node container, String name) {
205 int p = name.lastIndexOf('/');
208 module = getModule(name.substring(0, p));
209 name = name.substring(p+1);
212 module = getRelatedModule();
215 generateClassDocumentation(container, module, name);
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));
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) {
239 signature.append(", ");
240 cx.toString(tuc, signature);
242 signature.append(") => ");
244 signature.append(name);
245 for(TVar p : typeClass.parameters) {
246 signature.append(' ');
247 p.toName(tuc, signature);
249 signature.append("</div><div class=\"doc\">");
250 container.addChild(new HtmlNode(signature));
252 if(typeClass.documentation != null) {
253 MarkdownParser parser = new MarkdownParser();
254 container.addChild(parser.parseDocument(typeClass.documentation));
257 System.out.println(name);
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));
264 container.addChild(new HtmlNode("</div></div>"));
267 private void generateDataDocumentation(Node container, String name) {
268 int p = name.lastIndexOf('/');
271 module = getModule(name.substring(0, p));
272 name = name.substring(p+1);
275 module = getRelatedModule();
278 generateDataDocumentation(container, module, name);
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));
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);
300 String moduleName = module.getName();
301 if(!moduleName.equals(documentationName)) {
302 signature.append(" <span class=\"greyed\">(").append(moduleName).append(")</span>");
304 signature.append("</div><div class=\"doc\">");
305 container.addChild(new HtmlNode(signature));
307 if(typeConstructor.documentation != null) {
308 MarkdownParser parser = new MarkdownParser();
309 container.addChild(parser.parseDocument(typeConstructor.documentation));
312 System.out.println(name);
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));
319 container.addChild(new HtmlNode("</div></div>"));