001/* 002 * Copyright (c) 2012-2021 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package org.eclipse.golo.doc; 012 013import gololang.IO; 014 015import java.nio.file.Path; 016import java.util.*; 017 018public class CtagsProcessor extends AbstractProcessor { 019 020 private final LinkedList<String> ctags = new LinkedList<>(); 021 private String file = "file"; 022 023 private void ctagsLine(String name, String address, String field) { 024 ctags.add(String.format("%s\t%s\t%s;\"\t%s\tlanguage:golo%n", name, file, address, field)); 025 } 026 027 private void ctagsModule(ModuleDocumentation module) { 028 ctagsLine(module.moduleName(), 029 "/^module[:blank:]+" + module.moduleName().replace(".", "\\.") + "$/", 030 "p\tline:" + module.moduleDefLine()); 031 } 032 033 private void ctagsFunction(FunctionDocumentation funct) { 034 ctagsFunction(funct, "", false); 035 } 036 037 private void ctagsMacro(FunctionDocumentation macro) { 038 ctagsFunction(macro, "", false, "macro", "d"); 039 } 040 041 private void ctagsFunction(FunctionDocumentation funct, String parent, boolean named) { 042 ctagsFunction(funct, parent, named, "function", "f"); 043 } 044 045 private void ctagsFunction(FunctionDocumentation funct, String parent, boolean named, String keyword, String tag) { 046 String address = String.format("/%s[:blank:]+%s[:blank:]+=/", keyword, funct.name()); 047 048 StringBuilder signature = new StringBuilder("\tsignature:("); 049 if (funct.arity() > 0) { 050 signature.append(funct.argument(0)); 051 for (int i = 1; i < funct.arity(); i++) { 052 signature.append(", ").append(funct.argument(i)); 053 } 054 if (funct.varargs()) { signature.append("..."); } 055 } 056 signature.append(")"); 057 058 StringBuilder fields = new StringBuilder(tag); 059 fields.append("\tline:").append(funct.line()); 060 if (funct.local()) { 061 fields.append("\taccess:private\tfile:"); 062 } else { 063 fields.append("\taccess:public"); 064 } 065 fields.append(signature); 066 if (!parent.isEmpty()) { 067 if (named) { 068 fields.append("\taugmentation:").append(parent); 069 } else { 070 fields.append("\taugment:").append(parent); 071 } 072 } 073 ctagsLine(funct.name(), address, fields.toString()); 074 } 075 076 private void ctagsAugment(String name, int line) { 077 ctagsLine(name, 078 String.format("/^augment[:blank:]+%s/", name.replace(".", "\\.")), 079 String.format("a\tline:%s", line)); 080 } 081 082 private void ctagsAugmentation(String name, int line) { 083 ctagsLine(name, 084 String.format("/^augmentation[:blank:]+%s[:blank:]+=[:blank:]+{/", name), 085 String.format("na\tline:%s", line)); 086 } 087 088 private void ctagsStruct(String name, int line) { 089 ctagsLine(name, 090 String.format("/^struct[:blank:]+%s[:blank:]+=/", name), 091 String.format("s\tline:%s", line)); 092 } 093 094 private void ctagsUnion(UnionDocumentation unionDoc) { 095 ctagsLine(unionDoc.name(), 096 String.format("/^union[:blank:]+%s[:blank:]+=[:blank:]+{/", unionDoc.name()), 097 String.format("g\tline:%s", unionDoc.line())); 098 099 for (UnionDocumentation.UnionValueDocumentation valueDoc : unionDoc.values()) { 100 ctagsUnionValue(unionDoc.name(), valueDoc); 101 } 102 } 103 104 private void ctagsUnionValue(String unionName, UnionDocumentation.UnionValueDocumentation valueDoc) { 105 ctagsLine(valueDoc.name(), 106 String.format("/[:blank:]+%s[:blank:]+%s", valueDoc.name(), 107 valueDoc.hasMembers() ? "[:blank:]*=[:blank:]+{" : ""), 108 String.format("e\tline:%s\tunion:%s", valueDoc.line(), unionName)); 109 110 for (MemberDocumentation member : valueDoc.members()) { 111 ctagsLine(member.name(), 112 String.format("/[:blank:]+%s[:blank:]+=/", valueDoc.name()), 113 String.format("m\tline:%s\taccess:public\tvalue:%s", 114 member.line(), 115 valueDoc.name())); 116 } 117 } 118 119 private void ctagsImport(String name, int line) { 120 ctagsLine(name, 121 String.format("/^import[:blank:]+%s/", name.replace(".", "\\.")), 122 String.format("i\tline:%s", line)); 123 } 124 125 private void ctagsModState(String name, int line) { 126 ctagsLine(name, 127 String.format("(let|var)[:blank:]+%s[:blank:]+=/", name), 128 String.format("v\taccess:private\tfile:\tline:%s", line)); 129 } 130 131 private void ctagsStructMember(String struct, String member, int line) { 132 ctagsLine(member, 133 String.format("/struct[:blank:]+%s[:blank:]+=/", struct), 134 String.format("m\tline:%s\taccess:%s\tstruct:%s", 135 line, 136 (member.charAt(0) == '_') ? "private" : "public", 137 struct)); 138 } 139 140 private String ctagsAsString() { 141 java.util.Collections.sort(ctags); 142 StringBuilder buffer = new StringBuilder(); 143 for (String line : ctags) { 144 buffer.append(line); 145 } 146 return buffer.toString(); 147 } 148 149 @Override 150 public String render(ModuleDocumentation documentation) throws Throwable { 151 ctagsModule(documentation); 152 for (Map.Entry<String, Integer> imp : documentation.imports().entrySet()) { 153 ctagsImport(imp.getKey(), imp.getValue()); 154 } 155 for (StructDocumentation struct : documentation.structs()) { 156 ctagsStruct(struct.name(), struct.line()); 157 for (MemberDocumentation member : struct.members()) { 158 ctagsStructMember(struct.name(), member.name(), member.line()); 159 } 160 } 161 for (UnionDocumentation unionDoc : documentation.unions()) { 162 ctagsUnion(unionDoc); 163 } 164 for (NamedAugmentationDocumentation augment : documentation.namedAugmentations()) { 165 ctagsAugmentation(augment.name(), augment.line()); 166 for (FunctionDocumentation funct : augment.functions()) { 167 ctagsFunction(funct, augment.name(), true); 168 } 169 } 170 for (AugmentationDocumentation augment : documentation.augmentations()) { 171 ctagsAugment(augment.target(), augment.line()); 172 for (FunctionDocumentation funct : augment.functions()) { 173 ctagsFunction(funct, augment.target(), false); 174 } 175 } 176 for (Map.Entry<String, Integer> state : documentation.moduleStates().entrySet()) { 177 ctagsModState(state.getKey(), state.getValue()); 178 } 179 for (FunctionDocumentation funct : documentation.functions(true)) { 180 ctagsFunction(funct); 181 } 182 for (FunctionDocumentation funct : documentation.macros()) { 183 ctagsMacro(funct); 184 } 185 return ctagsAsString(); 186 } 187 188 @Override 189 public void process(Collection<ModuleDocumentation> modules, Path targetFolder) throws Throwable { 190 Path targetFile = null; 191 if ("-".equals(targetFolder.toString())) { 192 targetFile = targetFolder; 193 } else { 194 targetFile = targetFolder.resolve("tags"); 195 } 196 ctags.clear(); 197 for (ModuleDocumentation doc : modules) { 198 file = doc.sourceFile(); 199 render(doc); 200 } 201 IO.textToFile(ctagsAsString(), targetFile); 202 } 203}