Libraryless. Click here for Pure Java version (17527L/108K).
1 | // TODO: fix interaction with line spans made by show-snippet.php |
2 | |
3 | // Transpile & reload-in-eleu #1008705 after changing this |
4 | |
5 | // TODO: use Map<IntRange, MapSO> attributesForCharRanges |
6 | // and generate HTML properly from that |
7 | |
8 | sclass JavaXHyperlinker { |
9 | bool targetBlank; |
10 | |
11 | asclass PeepHole { |
12 | LS tok; |
13 | int cIdx; |
14 | S prevPrev, prev, token, next; |
15 | |
16 | abstract void explain(int i default cIdx, S text); |
17 | abstract void link(int i default cIdx, S url); |
18 | } |
19 | |
20 | // only a single token allowed on LHS |
21 | SS shortFor = litmap( |
22 | "BigInt", "BigInteger", |
23 | "$1", "m.unq(0)", |
24 | "$2", "m.unq(1)", |
25 | "$3", "m.unq(2)", |
26 | "LS", "List<String>", |
27 | "sclass", "static class", |
28 | "asclass", "abstract static class", |
29 | "svoid", "static void", |
30 | "SS", "Map<String, String>", |
31 | "S", "String", |
32 | "ret", "return", |
33 | "L", "List", |
34 | "Cl", "Collection", |
35 | "O", "Object", |
36 | "sO", "static Object", |
37 | "sS", "static String", |
38 | "fO", "final Object", |
39 | "fS", "final String", |
40 | "sS", "static String", |
41 | "sbool", "static boolean", |
42 | "fbool", "final boolean", |
43 | "bool", "boolean", |
44 | "Int", "Integer", |
45 | "cast", "casting to the type required on the left-hand side", |
46 | "°", "()", |
47 | "ItIt", "IterableIterator", |
48 | "ES", "Ext<S> (string plus extended information)", |
49 | "CloseableItIt", "CloseableIterableIterator", |
50 | "LPairS", "List<Pair<String>>", |
51 | "ISegmenter", "IF1<BufferedImage, L<Rect>>", |
52 | "dbl", "double", |
53 | ); |
54 | |
55 | S explFunc = "anonymous function declaration (similar to Java 8 lambdas)"; |
56 | S explPNoconsole = "Main program including Substance L&F, started in non-AWT thread, hiding the console"; |
57 | |
58 | // any number of tokens allowed on LHS |
59 | SS tokenExplanations = litmap( |
60 | "cm", "cm summons the last version of a module in Stefan's OS (cm originally stood for 'compact module')", |
61 | "~.", "short for accessing a field or calling a method by reflection", |
62 | "beaConcept", "a BEA concept is just a concept (database-enabled Java class) derived from BEAObject (the AGI base class)", |
63 | "== null ?:", "short for: == null ? null :", |
64 | "p-exp {", "main program with automatic upgrade", |
65 | "mapLike", "a function that takes a global function name as first argument and can be called like this: <mapLikeFunction> <otherFunction>(...)", |
66 | "mapMethodLike", "a function that takes a method or field name as first argument and can be called like this: <mapMethodLikeFunction> <methodOrFieldName>(...)", |
67 | "func(", explFunc, "func{", explFunc, |
68 | //"f(", explFunc, "f{", explFunc, |
69 | "voidfunc", "anonymous function declaration without return value (similar to Java 8 lambdas)", |
70 | "ctex", "ctex rethrows exceptions as RuntimeExceptions so you don't have to declare them", |
71 | "p {", "short for: public static void main(String[] args)", |
72 | "p-experiment {", "Main program with a nice big console & auto-restart", |
73 | "p-substance {", "Main program including Substance L&F, started in AWT thread", |
74 | "p-subst {", "Main program including Substance L&F, started in non-AWT thread", |
75 | "p-noconsole {", explPNoconsole, |
76 | "pn {", explPNoconsole, |
77 | "answer {", "Answer function - static S answer(S s) { new Matches m; ...; null; }", |
78 | "time {", "Run the code block and print how long it took", |
79 | "cprint {", "A module with default class name derived from DynPrintLog", |
80 | "semiauto {", "In a semiauto block, JavaX automatically adds a ; to (almost) every line", |
81 | "autosemi {", "In an autosemi block, JavaX automatically adds a ; to (almost) every line", |
82 | "Clusters", "Clusters<A> is short for Map<A, Collection<A>>", |
83 | "temp", "temp is like try (...) extending to the end of the current block", |
84 | "pcall {", "protected call - try { ... } catch { print exception }", |
85 | "r {", "r { ... } is short for: new Runnable() { public void run() { ... } }", |
86 | "runnable {", "runnable { ... } is short for: new Runnable() { public void run() { ... } }", |
87 | "pcall-short {", "protected call, shorter output - try { ... } catch { printShortException }", |
88 | "LPair<", "LPair<...> is short for: L<Pair<...>>", |
89 | "visualize {", "Visualisation method for an OS module; returns a Swing component", |
90 | "visualize as", "define a visualization for the module", |
91 | "enhanceFrame {", "Method that changes properties of an OS module's frame", |
92 | "run {", "short for: public void run()", |
93 | "start {", "Method that is run when an OS module is loaded", |
94 | "enter {", "Methods in a module should be marked 'enter' for bookkeeping", |
95 | "vf<", "reference to function as voidfunc", |
96 | "compact module", "try to save memory by reusing code between modules", |
97 | "cmodule", "cmodule = compact module for Stefan's OS. compact = try to save memory by reusing code between modules", |
98 | "cmodule2", "cmodule2 = compact module for Stefan's OS (newer version). compact = try to save memory by reusing code between modules", |
99 | "rThread {", "Runnable in a new thread", |
100 | "shit(", "short for: return with print", |
101 | "shit:", "short for: return with print", |
102 | ":=", [[the := operator translates to a simple comma, but converts an identifier on its left-hand side into a string]], |
103 | "noeq", "don't auto-generate equals+hashCode methods", |
104 | "for single (", "for single is for ping + singletonUnlessNull", |
105 | "selfType", "selfType is replaced with the enclosing class's name", |
106 | "runnable class", "short for: class X implementing Runnable { run { ... }}", |
107 | "aka", "In JavaX, methods can have multiple names", |
108 | ); |
109 | |
110 | SS sc, sf; |
111 | StringTree2<S> tokenExplanationsTree; |
112 | new L<IVF1<PeepHole>> explainers; |
113 | NotTooOften ntoClearCaches = nto_mins(5); |
114 | |
115 | *() { |
116 | maybeClearCaches(); |
117 | tokenExplanationsTree = stringTree2_javaTok(tokenExplanations); |
118 | } |
119 | |
120 | void maybeClearCaches { |
121 | ntoClearCaches.do(-> clearCaches()); |
122 | } |
123 | |
124 | S codeToHTML(S code, bool real default true) { |
125 | maybeClearCaches(); |
126 | |
127 | LS tok = javaTok(code); |
128 | |
129 | new Map<Int, S> links; // token index -> sf snippet ID |
130 | new Map<Int, S> explanations; // token index -> explanation text |
131 | |
132 | putMultipleKeys(explanations, allPlus(4, jfindAll(tok, "void <id> q {")), |
133 | "Function will execute in module's queue"); |
134 | |
135 | // Go over all tokens and colorize |
136 | |
137 | new Map<Int, S> styles; |
138 | tokensToColors(tok, styles); |
139 | |
140 | // Go over code tokens, generate links and explanations |
141 | |
142 | new Matches m; |
143 | for (int i = 1; i < l(tok); i += 2) { |
144 | S t = tok.get(i); |
145 | S prev = get(tok, i-2), prevPrev = get(tok, i-4); |
146 | S next = get(tok, i+2); |
147 | S sfID = sf.get(t); |
148 | |
149 | PeepHole ph = new { |
150 | void explain(int i, S text) { explanations.put(i, text); } |
151 | void link(int i, S url) { links.put(i, url); } |
152 | }; |
153 | ph.tok = tok; |
154 | ph.cIdx = i; |
155 | ph.prevPrev = prevPrev; |
156 | ph.prev = prev; |
157 | ph.token = t; |
158 | ph.next = next; |
159 | |
160 | pcallFAll(explainers, ph); |
161 | |
162 | // please include functions |
163 | if (eq(t, "include") && eq(next, "functions")) { |
164 | i += 4; |
165 | S name; |
166 | while (i < l(tok) && isIdentifier(name = get(tok, i))) { |
167 | mapPut(links, i, sf.get(name)); |
168 | i += 2; |
169 | } |
170 | continue; |
171 | } |
172 | |
173 | if (startsWith(t, "lambda", m) && isInteger(m.rest()) && isIdentifier(next)) |
174 | explanations.put(i, "lambda reference to a \*m.rest()*/-argument function"); |
175 | |
176 | if (eq(t, "is") && isIdentifier(next)) |
177 | explanations.put(i, "synonym of 'implements'"); |
178 | |
179 | if (eq(t, "methodLambda0") && isIdentifier(next)) |
180 | explanations.put(i, "short for: x -> x.\*next*/() (with a fresh variable instead of x)"); |
181 | |
182 | if (eqOneOf(t, "lambda0", "l0") && isIdentifier(next)) |
183 | explanations.put(i, "short for: -> \*next*/()"); |
184 | |
185 | if (eq(t, "swappable") && isIdentifier(next)) |
186 | explanations.put(i, "swappable functions can be exchanged per object instance"); |
187 | |
188 | if (eq(t, "main") && isIdentifier(next)) |
189 | explanations.put(i, "reference to the current 'main' class (where all the standard functions are held, not always called 'main')"); |
190 | |
191 | if (eq(t, "autoDispose") && isIdentifier(next)) |
192 | explanations.put(i, [[autoDispose adds a cleanMeUp method that properly disposes of this variable]]); |
193 | |
194 | if (eq(t, "optional") && isIdentifier(next)) |
195 | explanations.put(i, [[optional parameter (null if omitted)]]); |
196 | |
197 | if (eq(t, "macro") && isIdentifier(next)) |
198 | explanations.put(i, "define a macro called '" + next + "', visible until the end of the enclosing block (or until redefined)"); |
199 | |
200 | if (eq(t, "virtual") && isIdentifier(next)) |
201 | explanations.put(i, [["virtual" represents a type that is not visible in this realm, so it is transpiled to just Object]]); |
202 | |
203 | if (eq(t, "concept") && isIdentifier(next)) |
204 | explanations.put(i, "A concept is like a Java class, but persistable"); |
205 | |
206 | if (eq(t, "flexeq") && isIdentifier(next)) |
207 | explanations.put(i, "flexeq is a fix for records inside parameterized classes"); |
208 | |
209 | if (eq(t, "switchable") && isIdentifier(next)) |
210 | explanations.put(i, "A field that can be changed through the module's popup menu"); |
211 | |
212 | if (eq(t, "embedded") && isIdentifier(next)) |
213 | explanations.put(i, [["embedded" allows you to put a function where they would not normally be allowed]]); |
214 | |
215 | if (eq(t, "visual") && isIdentifier(next)) |
216 | explanations.put(i, "short definition of the visualize() function"); |
217 | |
218 | if (eq(t, "dm_q") && isIdentifier(next)) |
219 | explanations.put(i, "Function reference delegating to module queue"); |
220 | |
221 | if (eq(t, "!") && isIdentifier(prev) && neq(next, "=")) |
222 | explanations.put(i, "! is short for .get()"); |
223 | |
224 | /*if (eq(t, "!") && containsNewLine(get(tok, i-1)) && isInteger(next)) |
225 | explanations.put(i, "Translator invocation");*/ |
226 | |
227 | if (eq(t, "#") && isIdentifier(next)) |
228 | explanations.put(i, "#<name> makes an identifier local to the scope"); |
229 | |
230 | if (eq(t, "f") && isIdentifier(next)) |
231 | explanations.put(i, "f <name> references a static function in the main class"); |
232 | |
233 | if (eq(t, "r") && isIdentifier(next)) |
234 | explanations.put(i, "short for: r { " + next + "() }"); |
235 | |
236 | if (eqOneOf(t, "rThread", "rThreadEnter") && isIdentifier(next)) |
237 | explanations.put(i, "short for: " + t + " { " + next + "() }"); |
238 | |
239 | if (eq(t, "dispose") && isIdentifier(next)) |
240 | explanations.put(i, "short for: cleanUp(" + next + "); " + next + " = null;"); |
241 | |
242 | if (eq(t, "*") && eq(next, "(")) |
243 | explanations.put(i, "Short syntax for a constructor declaration"); |
244 | |
245 | if (eq(t, "thread") && eq(next, "{")) |
246 | explanations.put(i, "Start a new thread with the following code"); |
247 | |
248 | if (eq(t, "module") && isIdentifier(next)) |
249 | explanations.put(i, "A module is a class that can be loaded in Stefan's OS"); |
250 | |
251 | if (eq(t, "record") && isIdentifier(next)) |
252 | explanations.put(i, "A record is a value-based class"); |
253 | |
254 | if (eq(t, "srecord") && isIdentifier(next)) |
255 | explanations.put(i, "An srecord is a static value-based class"); |
256 | |
257 | if (eqOneOf(t, "cached", "simplyCached") && isIdentifier(next)) |
258 | explanations.put(i, "A function that caches its return value"); |
259 | |
260 | if (eq(t, "thread") && isQuoted(next)) |
261 | explanations.put(i, "Start a new thread with the following name & code"); |
262 | |
263 | if (eq(t, "if") && isQuoted(next)) pcall { |
264 | LS tok2 = tok_subListWithoutBorderNTokens(tok, i, i+3); |
265 | tok_expandIfQuoted(tok2); |
266 | explanations.put(i, "short for: " + join(tok2)); |
267 | } |
268 | |
269 | if (eq(t, "html") && eq(next, "{")) |
270 | explanations.put(i, "short for: static Object html(String uri, final Map<String, String> params) ctex {"); |
271 | |
272 | if (eq(t, 'try) && eq(next, 'answer)) |
273 | doublePut(explanations, i, i+2, "\"try answer\" returns the expression if it isn't null or empty"); |
274 | |
275 | if (isSingleQuoteIdentifier(t)) |
276 | explanations.put(i, "string constant, " + quote(fromSingleQuoteIdentifier(t))); |
277 | |
278 | if (eq(t, "event") && isIdentifier(next)) |
279 | explanations.put(i, "declare a function called " + next + "() plus helpers for adding listeners"); |
280 | |
281 | if (eqOneOf(t, 'null, 'false, 'true, 'this) |
282 | && eq(next, ";") |
283 | && tok_tokenBeforeLonelyReturnValue(tok, i-2)) |
284 | doublePut(explanations, i, i+2, "short for: return " + t + ";"); |
285 | |
286 | S e = shortFor.get(t); |
287 | if (e != null) |
288 | mapPut(explanations, i, "short for: " + e); |
289 | |
290 | // link to standard function |
291 | if (!explanations.containsKey(i) && sfID != null) { |
292 | if (eqOneOf(prev, "f", "r", "rThread", "function") |
293 | || startsWith(prev, "lambda") |
294 | || isIdentifier(next) && eqGet(tok, i+4, "(") |
295 | || eqOneOf(next, "(", "°") |
296 | && (neq(prev, ".") || eq(prevPrev, "main") && neq(get(tok, i-6), ".")) |
297 | || eq(prev, "{") && eq(next, "}") && eq(prevPrev, "postProcess") |
298 | || eq(prev, ":") && eq(prevPrev, ":")) |
299 | links.put(i, sfID); |
300 | } |
301 | |
302 | L<S> fewTokens = codeTokens(subList(tok, i-1, i+2*5)); |
303 | Pair<S, Int> p = stringTreeLeafValue2(tokenExplanationsTree, fewTokens); |
304 | if (p != null) { |
305 | //print(struct(p)); |
306 | int lastCodeToken = i+p.b*2-2; |
307 | if (eq(get(tok, lastCodeToken), "{")) lastCodeToken -= 2; |
308 | mapPutInRange(explanations, i, lastCodeToken+1, p.a); |
309 | } |
310 | |
311 | if (isQuoted(t) && eq(prev, "(") && isIdentifier(prevPrev) |
312 | && isMechFunction(prevPrev)) |
313 | mapPut(links, i, neatMechListURL(unquote(t))); |
314 | |
315 | //mapPut(explanations, i, tokenExplanations.get(t)); |
316 | |
317 | mapPut(links, i, sc.get(t)); |
318 | } |
319 | |
320 | // go over tokens, output HTML characters |
321 | |
322 | new StringBuilder out; |
323 | SS titles = getSnippetTitles(filter isSnippetID(values(links))); |
324 | for i over tok: { |
325 | S t = tok.get(i); |
326 | if (empty(t)) continue; |
327 | S id = links.get(i), ex = explanations.get(i); |
328 | S style = styles.get(i); |
329 | if (t.startsWith("[[") && t.endsWith("]]")) { |
330 | S explanation = "[[...]] denotes a multi-line string constant (as in Lua)"; |
331 | out.append(dottedSpan("[[", explanation)); |
332 | S inner = htmlencode(dropPrefix("[[", dropSuffix("]]", t))); |
333 | out.append(span(inner, style := "background-color: #77FF77")); |
334 | out.append(dottedSpan("]]", explanation)); |
335 | continue; |
336 | } |
337 | if (t.startsWith("[=[") && t.endsWith("]=]")) { |
338 | S explanation = "[=[...]=] denotes a multi-line string constant (as in Lua)"; |
339 | out.append(dottedSpan("[=[", explanation)); |
340 | S inner = htmlencode(dropPrefix("[=[", dropSuffix("]=]", t))); |
341 | out.append(span(inner, style := "background-color: #77FF77")); |
342 | out.append(dottedSpan("]=]", explanation)); |
343 | continue; |
344 | } |
345 | S enc = htmlencode(t); |
346 | out.append(id != null |
347 | ? ahref(makeLink(real, id), enc, |
348 | title := isSnippetID(id) ? titles.get(fsI(id)) : ex, style := "text-decoration: none; color: black; border-bottom: dotted 1px", target := targetBlank ? "_blank" : null) |
349 | : ex != null ? dottedSpan(enc, ex) |
350 | : style != null ? span(enc, +style) |
351 | : enc); |
352 | } |
353 | S html = str(out); |
354 | html = dynamize_noEncode(html); |
355 | ret html; |
356 | } |
357 | |
358 | // id can be a URL |
359 | S makeLink(bool real, S id) { |
360 | if (isRelativeOrAbsoluteURL(id)) ret id; |
361 | if (real) |
362 | ret longSnippetLink(id); |
363 | ret "/" + psI(id); |
364 | } |
365 | |
366 | void clearCaches { |
367 | stdFunctions_clearCache(); |
368 | sc = standardClassesMap(); |
369 | sf = stdFunctions_cached(); |
370 | //sf.putAll(tok_findStandardFunctionDefinitions(javaTokSnippet(#1022367))); |
371 | } |
372 | |
373 | bool isMechFunction(S s) { |
374 | ret startsWithOneOf(s, "mech", "mL"); |
375 | } |
376 | |
377 | void tokensToColors(LS tok, Map<Int, S> styles) { |
378 | for (int i = 0; i < l(tok); i += 2) |
379 | if (tok_whitespaceContainsJavaComments(tok.get(i))) |
380 | styles.put(i, "color: #666666"); |
381 | } |
382 | |
383 | void addExplainer(IVF1<PeepHole> explainer) { |
384 | explainers.add(explainer); |
385 | } |
386 | } // end of JavaXHyperlinker |
Began life as a copy of #1008705
download show line numbers debug dex old transpilations
Travelled to 5 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt
No comments. add comment
Snippet ID: | #1030131 |
Snippet name: | JavaXHyperlinker - annotates JavaX sources in HTML. Also, COLORS! |
Eternal ID of this version: | #1030131/63 |
Text MD5: | e2b2c559f3ac8d71094faabc7943623a |
Transpilation MD5: | 72a2574a8bb882b4c960398890fd2379 |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-07-30 02:19:45 |
Source code size: | 16641 bytes / 386 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 395 / 858 |
Version history: | 62 change(s) |
Referenced in: | [show references] |