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;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
29 import gnu.trove.map.hash.THashMap;
30 import gnu.trove.procedure.TObjectProcedure;
31 import gnu.trove.set.hash.THashSet;
33 public class SCLDocumentationExtensionNodeHandler implements ExtensionNodeHandler {
35 private static final Logger LOGGER = LoggerFactory.getLogger(SCLDocumentationExtensionNodeHandler.class);
36 final ModuleRepository moduleRepository;
37 final String documentationName;
39 Failable<Module> relatedModule;
40 THashMap<String,Failable<Module>> moduleCache = new THashMap<String,Failable<Module>>();
42 THashSet<String> documentedValues = new THashSet<String>();
43 THashSet<String> documentedClasses = new THashSet<String>();
44 THashSet<String> documentedTypeConstructors = new THashSet<String>();
46 public SCLDocumentationExtensionNodeHandler(ModuleRepository moduleRepository,
47 String documentationName) {
48 this.moduleRepository = moduleRepository;
49 this.documentationName = documentationName;
53 public DocumentNode expandBlock(String extension, String content) {
54 if(extension.equals("value")) {
55 String[] names = content.split(",");
56 DocumentNode container = new DocumentNode();
57 for(String name : names) {
61 if(!documentedValues.add(name))
62 LOGGER.warn("Value '" + name + "' has already been documented in " + documentationName + ".");
63 generateValueDocumentation(container, name);
67 else if(extension.equals("class")) {
68 String[] names = content.split(",");
69 DocumentNode container = new DocumentNode();
70 for(String name : names) {
74 if(!documentedClasses.add(name))
75 LOGGER.warn("Class '" + name + "' has already been documented in " + documentationName + ".");
76 generateClassDocumentation(container, name);
80 else if(extension.equals("data")) {
81 String[] names = content.split(",");
82 DocumentNode container = new DocumentNode();
83 for(String name : names) {
87 if(!documentedTypeConstructors.add(name))
88 LOGGER.warn("Type constructor '" + name + "' has already been documented in " + documentationName + ".");
89 generateDataDocumentation(container, name);
93 else if(extension.equals("undocumented")) {
94 Module module = getRelatedModule();
97 final ArrayList<String> undocumentedValues = new ArrayList<String>();
98 module.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure<SCLValue>() {
100 public boolean execute(SCLValue value) {
101 if(value.isPrivate())
103 String name = value.getName().name;
104 if(documentedValues.contains(name) ||
105 name.charAt(0) == '_' ||
106 (name.contains("$") && Character.isAlphabetic(name.charAt(0))))
108 undocumentedValues.add(name);
112 Collections.sort(undocumentedValues);
114 DocumentNode container = new DocumentNode();
115 for(String name : undocumentedValues) {
116 generateValueDocumentation(container, name);
124 private Module getRelatedModule() {
125 if(relatedModule == null) {
126 relatedModule = moduleRepository.getModule(documentationName);
127 if(!relatedModule.didSucceed())
128 LOGGER.warn("Couldn't load the module " + documentationName);
130 if(relatedModule.didSucceed())
131 return relatedModule.getResult();
136 private Module getModule(String moduleName) {
137 Failable<Module> fm = moduleCache.get(moduleName);
139 fm = moduleRepository.getModule(moduleName);
140 moduleCache.put(moduleName, fm);
142 LOGGER.warn("Couldn't load the module " + moduleName);
145 return fm.getResult();
150 private void generateValueDocumentation(Node container, String name) {
151 int p = name.lastIndexOf('/');
154 while(p > 0 && name.charAt(p-1) == '/')
156 module = getModule(name.substring(0, p));
157 name = name.substring(p+1);
160 module = getRelatedModule();
163 generateValueDocumentation(container, module, name);
166 private void generateValueDocumentation(Node container, Module module, String name, TypeUnparsingContext tuc) {
167 SCLValue value = module.getValue(name);
169 StringBuilder error = new StringBuilder();
170 error.append("Didn't find the value '" + name + "'.");
171 LOGGER.error(error.toString());
172 container.addChild(new CodeBlockNode(error));
175 if(value.isPrivate())
176 LOGGER.warn("Documentation " + documentationName + " refers to a private value " + name + ".");
178 StringBuilder signature = new StringBuilder();
179 signature.append("<div id=\"")
180 .append(HtmlEscape.escape(name))
181 .append("\" class=\"code-doc-box\"><div class=\"code\">");
182 char firstChar = name.charAt(0);
183 if(!Character.isAlphabetic(firstChar) && firstChar != '_')
184 name = "(" + name + ")";
185 signature.append(HtmlEscape.escape(name)).append(" :: ");
186 String typeStr = Types.removeForAll(value.getType(), new ArrayList<TVar>()).toString(tuc);
187 signature.append(HtmlEscape.escape(typeStr));
188 String moduleName = module.getName();
189 if(!moduleName.equals(documentationName)) {
190 signature.append(" <span class=\"greyed\">(").append(moduleName).append(")</span>");
192 signature.append("</div><div class=\"doc\">");
193 container.addChild(new HtmlNode(signature));
195 if(value.documentation != null) {
196 MarkdownParser parser = new MarkdownParser();
197 container.addChild(parser.parseDocument(value.documentation));
201 container.addChild(new HtmlNode("</div></div>"));
204 private void generateValueDocumentation(Node container, Module module, String name) {
205 generateValueDocumentation(container, module, name, new TypeUnparsingContext());
208 private void generateClassDocumentation(Node container, String name) {
209 int p = name.lastIndexOf('/');
212 module = getModule(name.substring(0, p));
213 name = name.substring(p+1);
216 module = getRelatedModule();
219 generateClassDocumentation(container, module, name);
222 private void generateClassDocumentation(Node container, Module module, String name) {
223 TypeClass typeClass = module.getTypeClass(name);
224 if(typeClass == null) {
225 StringBuilder error = new StringBuilder();
226 error.append("Didn't find the type class '" + name + "'.");
227 LOGGER.error(error.toString());
228 container.addChild(new CodeBlockNode(error));
232 TypeUnparsingContext tuc = new TypeUnparsingContext();
233 StringBuilder signature = new StringBuilder();
234 signature.append("<div class=\"code-doc-box\"><div class=\"code\">");
235 signature.append("class ");
236 if(typeClass.context.length > 0) {
237 signature.append('(');
238 boolean first = true;
239 for(TPred cx : typeClass.context) {
243 signature.append(", ");
244 cx.toString(tuc, signature);
246 signature.append(") => ");
248 signature.append(name);
249 for(TVar p : typeClass.parameters) {
250 signature.append(' ');
251 p.toName(tuc, signature);
253 signature.append("</div><div class=\"doc\">");
254 container.addChild(new HtmlNode(signature));
256 if(typeClass.documentation != null) {
257 MarkdownParser parser = new MarkdownParser();
258 container.addChild(parser.parseDocument(typeClass.documentation));
263 for(String methodName : typeClass.methodNames) {
264 if(!documentedValues.add(methodName))
265 LOGGER.warn("Method '" + methodName + "' has already been documented in " + documentationName + ".");
266 generateValueDocumentation(container, module, methodName, new TypeUnparsingContext(tuc));
268 container.addChild(new HtmlNode("</div></div>"));
271 private void generateDataDocumentation(Node container, String name) {
272 int p = name.lastIndexOf('/');
275 module = getModule(name.substring(0, p));
276 name = name.substring(p+1);
279 module = getRelatedModule();
282 generateDataDocumentation(container, module, name);
285 private void generateDataDocumentation(Node container, Module module, String name) {
286 TypeDescriptor typeDescriptor = module.getTypeDescriptor(name);
287 if(typeDescriptor == null) {
288 StringBuilder error = new StringBuilder();
289 error.append("Didn't find the type " + name + ".");
290 container.addChild(new CodeBlockNode(error));
294 TypeUnparsingContext tuc = new TypeUnparsingContext();
295 StringBuilder signature = new StringBuilder();
296 signature.append("<div class=\"code-doc-box\"><div class=\"code\">");
297 signature.append("data ");
298 signature.append(typeDescriptor.name.name);
299 if(typeDescriptor instanceof TypeConstructor) {
300 for(TVar p : ((TypeConstructor)typeDescriptor).parameters) {
301 signature.append(' ');
302 p.toName(tuc, signature);
305 String moduleName = module.getName();
306 if(!moduleName.equals(documentationName)) {
307 signature.append(" <span class=\"greyed\">(").append(moduleName).append(")</span>");
309 signature.append("</div><div class=\"doc\">");
310 container.addChild(new HtmlNode(signature));
312 if(typeDescriptor.getDocumentation() != null) {
313 MarkdownParser parser = new MarkdownParser();
314 container.addChild(parser.parseDocument(typeDescriptor.getDocumentation()));
319 if(typeDescriptor instanceof TypeConstructor) {
320 for(Constructor constructor : ((TypeConstructor)typeDescriptor).constructors) {
321 if(!documentedValues.add(constructor.name.name))
322 LOGGER.warn("Method '" + constructor.name.name + "' has already been documented in " + documentationName + ".");
323 generateValueDocumentation(container, module, constructor.name.name, new TypeUnparsingContext(tuc));
326 container.addChild(new HtmlNode("</div></div>"));