Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

386
LINES

< > BotCompany Repo | #1030131 // JavaXHyperlinker - annotates JavaX sources in HTML. Also, COLORS!

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (17527L/108K).

// TODO: fix interaction with line spans made by show-snippet.php

// Transpile & reload-in-eleu #1008705 after changing this 

// TODO: use Map<IntRange, MapSO> attributesForCharRanges
// and generate HTML properly from that

sclass JavaXHyperlinker {
  bool targetBlank;
  
  asclass PeepHole {
    LS tok;
    int cIdx;
    S prevPrev, prev, token, next;
    
    abstract void explain(int i default cIdx, S text);
    abstract void link(int i default cIdx, S url);
  }
  
  // only a single token allowed on LHS
  SS shortFor = litmap(
    "BigInt", "BigInteger",
    "$1", "m.unq(0)",
    "$2", "m.unq(1)",
    "$3", "m.unq(2)",
    "LS", "List<String>",
    "sclass", "static class",
    "asclass", "abstract static class",
    "svoid", "static void",
    "SS", "Map<String, String>",
    "S", "String",
    "ret", "return",
    "L", "List",
    "Cl", "Collection",
    "O", "Object",
    "sO", "static Object",
    "sS", "static String",
    "fO", "final Object",
    "fS", "final String",
    "sS", "static String",
    "sbool", "static boolean",
    "fbool", "final boolean",
    "bool", "boolean",
    "Int", "Integer",
    "cast", "casting to the type required on the left-hand side",
    "°", "()",
    "ItIt", "IterableIterator",
    "ES", "Ext<S> (string plus extended information)",
    "CloseableItIt", "CloseableIterableIterator",
    "LPairS", "List<Pair<String>>",
    "ISegmenter", "IF1<BufferedImage, L<Rect>>",
    "dbl", "double",
  );
  
  S explFunc = "anonymous function declaration (similar to Java 8 lambdas)";
  S explPNoconsole = "Main program including Substance L&F, started in non-AWT thread, hiding the console";
  
  // any number of tokens allowed on LHS
  SS tokenExplanations = litmap(
    "cm", "cm summons the last version of a module in Stefan's OS (cm originally stood for 'compact module')",
    "~.", "short for accessing a field or calling a method by reflection",
    "beaConcept", "a BEA concept is just a concept (database-enabled Java class) derived from BEAObject (the AGI base class)",
    "== null ?:", "short for: == null ? null :",
    "p-exp {", "main program with automatic upgrade",
    "mapLike", "a function that takes a global function name as first argument and can be called like this: <mapLikeFunction> <otherFunction>(...)",
    "mapMethodLike", "a function that takes a method or field name as first argument and can be called like this: <mapMethodLikeFunction> <methodOrFieldName>(...)",
    "func(", explFunc, "func{", explFunc,
    //"f(", explFunc, "f{", explFunc,
    "voidfunc", "anonymous function declaration without return value (similar to Java 8 lambdas)",
    "ctex", "ctex rethrows exceptions as RuntimeExceptions so you don't have to declare them",
    "p {", "short for: public static void main(String[] args)",
    "p-experiment {", "Main program with a nice big console & auto-restart",
    "p-substance {", "Main program including Substance L&F, started in AWT thread",
    "p-subst {", "Main program including Substance L&F, started in non-AWT thread",
    "p-noconsole {", explPNoconsole,
    "pn {", explPNoconsole,
    "answer {", "Answer function - static S answer(S s) { new Matches m; ...; null; }",
    "time {", "Run the code block and print how long it took",
    "cprint {", "A module with default class name derived from DynPrintLog",
    "semiauto {", "In a semiauto block, JavaX automatically adds a ; to (almost) every line",
    "autosemi {", "In an autosemi block, JavaX automatically adds a ; to (almost) every line",
    "Clusters", "Clusters<A> is short for Map<A, Collection<A>>",
    "temp", "temp is like try (...) extending to the end of the current block",
    "pcall {", "protected call - try { ... } catch { print exception }",
    "r {", "r { ... } is short for: new Runnable() { public void run() { ... } }",
    "runnable {", "runnable { ... } is short for: new Runnable() { public void run() { ... } }",
    "pcall-short {", "protected call, shorter output - try { ... } catch { printShortException }",
    "LPair<", "LPair<...> is short for: L<Pair<...>>",
    "visualize {", "Visualisation method for an OS module; returns a Swing component",
    "visualize as", "define a visualization for the module",
    "enhanceFrame {", "Method that changes properties of an OS module's frame",
    "run {", "short for: public void run()",
    "start {", "Method that is run when an OS module is loaded",
    "enter {", "Methods in a module should be marked 'enter' for bookkeeping",
    "vf<", "reference to function as voidfunc",
    "compact module", "try to save memory by reusing code between modules",
    "cmodule", "cmodule = compact module for Stefan's OS. compact = try to save memory by reusing code between modules",
    "cmodule2", "cmodule2 = compact module for Stefan's OS (newer version). compact = try to save memory by reusing code between modules",
    "rThread {", "Runnable in a new thread",
    "shit(", "short for: return with print",
    "shit:", "short for: return with print",
    ":=", [[the := operator translates to a simple comma, but converts an identifier on its left-hand side into a string]],
    "noeq", "don't auto-generate equals+hashCode methods",
    "for single (", "for single is for ping + singletonUnlessNull",
    "selfType", "selfType is replaced with the enclosing class's name",
    "runnable class", "short for: class X implementing Runnable { run { ... }}",
    "aka", "In JavaX, methods can have multiple names",
  );
  
  SS sc, sf;
  StringTree2<S> tokenExplanationsTree;
  new L<IVF1<PeepHole>> explainers;
  NotTooOften ntoClearCaches = nto_mins(5);

  *() {
    maybeClearCaches();
    tokenExplanationsTree = stringTree2_javaTok(tokenExplanations);
  }
  
  void maybeClearCaches {
    ntoClearCaches.do(-> clearCaches());
  }

  S codeToHTML(S code, bool real default true) {
    maybeClearCaches();
    
    LS tok = javaTok(code);
  
    new Map<Int, S> links; // token index -> sf snippet ID
    new Map<Int, S> explanations; // token index -> explanation text
    
    putMultipleKeys(explanations, allPlus(4, jfindAll(tok, "void <id> q {")),
      "Function will execute in module's queue");
      
    // Go over all tokens and colorize
    
    new Map<Int, S> styles;
    tokensToColors(tok, styles);
      
    // Go over code tokens, generate links and explanations
    
    new Matches m;
    for (int i = 1; i < l(tok); i += 2) {
      S t = tok.get(i);
      S prev = get(tok, i-2), prevPrev = get(tok, i-4);
      S next = get(tok, i+2);
      S sfID = sf.get(t);
      
      PeepHole ph = new {
        void explain(int i, S text) { explanations.put(i, text); }
        void link(int i, S url) { links.put(i, url); }
      };
      ph.tok = tok;
      ph.cIdx = i;
      ph.prevPrev = prevPrev;
      ph.prev = prev;
      ph.token = t;
      ph.next = next;
      
      pcallFAll(explainers, ph);
      
      // please include functions
      if (eq(t, "include") && eq(next, "functions")) {
        i += 4;
        S name;
        while (i < l(tok) && isIdentifier(name = get(tok, i))) {
          mapPut(links, i, sf.get(name));
          i += 2;
        }
        continue;
      }
            
      if (startsWith(t, "lambda", m) && isInteger(m.rest()) && isIdentifier(next))
        explanations.put(i, "lambda reference to a \*m.rest()*/-argument function");
        
      if (eq(t, "is") && isIdentifier(next))
        explanations.put(i, "synonym of 'implements'");
        
      if (eq(t, "methodLambda0") && isIdentifier(next))
        explanations.put(i, "short for: x -> x.\*next*/() (with a fresh variable instead of x)");
        
      if (eqOneOf(t, "lambda0", "l0") && isIdentifier(next))
        explanations.put(i, "short for: -> \*next*/()");
        
      if (eq(t, "swappable") && isIdentifier(next))
        explanations.put(i, "swappable functions can be exchanged per object instance");
        
      if (eq(t, "main") && isIdentifier(next))
        explanations.put(i, "reference to the current 'main' class (where all the standard functions are held, not always called 'main')");
        
      if (eq(t, "autoDispose") && isIdentifier(next))
        explanations.put(i, [[autoDispose adds a cleanMeUp method that properly disposes of this variable]]);
        
      if (eq(t, "optional") && isIdentifier(next))
        explanations.put(i, [[optional parameter (null if omitted)]]);
        
      if (eq(t, "macro") && isIdentifier(next))
        explanations.put(i, "define a macro called '" + next + "', visible until the end of the enclosing block (or until redefined)");
        
      if (eq(t, "virtual") && isIdentifier(next))
        explanations.put(i, [["virtual" represents a type that is not visible in this realm, so it is transpiled to just Object]]);
        
      if (eq(t, "concept") && isIdentifier(next))
        explanations.put(i, "A concept is like a Java class, but persistable");
        
      if (eq(t, "flexeq") && isIdentifier(next))
        explanations.put(i, "flexeq is a fix for records inside parameterized classes");
        
      if (eq(t, "switchable") && isIdentifier(next))
        explanations.put(i, "A field that can be changed through the module's popup menu");
        
      if (eq(t, "embedded") && isIdentifier(next))
        explanations.put(i, [["embedded" allows you to put a function where they would not normally be allowed]]);
        
      if (eq(t, "visual") && isIdentifier(next))
        explanations.put(i, "short definition of the visualize() function");
        
      if (eq(t, "dm_q") && isIdentifier(next))
        explanations.put(i, "Function reference delegating to module queue");
        
      if (eq(t, "!") && isIdentifier(prev) && neq(next, "="))
        explanations.put(i, "! is short for .get()");
        
      /*if (eq(t, "!") && containsNewLine(get(tok, i-1)) && isInteger(next))
        explanations.put(i, "Translator invocation");*/
        
      if (eq(t, "#") && isIdentifier(next))
        explanations.put(i, "#<name> makes an identifier local to the scope");
      
      if (eq(t, "f") && isIdentifier(next))
        explanations.put(i, "f <name> references a static function in the main class");
      
      if (eq(t, "r") && isIdentifier(next))
        explanations.put(i, "short for: r { " + next + "() }");
      
      if (eqOneOf(t, "rThread", "rThreadEnter") && isIdentifier(next))
        explanations.put(i, "short for: " + t + " { " + next + "() }");
      
      if (eq(t, "dispose") && isIdentifier(next))
        explanations.put(i, "short for: cleanUp(" + next + "); " + next + " = null;");
      
      if (eq(t, "*") && eq(next, "("))
        explanations.put(i, "Short syntax for a constructor declaration");
    
      if (eq(t, "thread") && eq(next, "{"))
        explanations.put(i, "Start a new thread with the following code");
        
      if (eq(t, "module") && isIdentifier(next))
        explanations.put(i, "A module is a class that can be loaded in Stefan's OS");
        
      if (eq(t, "record") && isIdentifier(next))
        explanations.put(i, "A record is a value-based class");
        
      if (eq(t, "srecord") && isIdentifier(next))
        explanations.put(i, "An srecord is a static value-based class");
        
      if (eqOneOf(t, "cached", "simplyCached") && isIdentifier(next))
        explanations.put(i, "A function that caches its return value");
        
      if (eq(t, "thread") && isQuoted(next))
        explanations.put(i, "Start a new thread with the following name & code");
        
      if (eq(t, "if") && isQuoted(next)) pcall {
        LS tok2 = tok_subListWithoutBorderNTokens(tok, i, i+3);
        tok_expandIfQuoted(tok2);
        explanations.put(i, "short for: " + join(tok2));
      }
        
      if (eq(t, "html") && eq(next, "{"))
        explanations.put(i, "short for: static Object html(String uri, final Map<String, String> params) ctex {");
        
      if (eq(t, 'try) && eq(next, 'answer))
        doublePut(explanations, i, i+2, "\"try answer\" returns the expression if it isn't null or empty");
        
      if (isSingleQuoteIdentifier(t))
        explanations.put(i, "string constant, " + quote(fromSingleQuoteIdentifier(t)));
        
      if (eq(t, "event") && isIdentifier(next))
        explanations.put(i, "declare a function called " + next + "() plus helpers for adding listeners");
        
      if (eqOneOf(t, 'null, 'false, 'true, 'this)
        && eq(next, ";")
        && tok_tokenBeforeLonelyReturnValue(tok, i-2))
        doublePut(explanations, i, i+2, "short for: return " + t + ";");
        
      S e = shortFor.get(t);
      if (e != null)
        mapPut(explanations, i, "short for: " + e);
        
      // link to standard function
      if (!explanations.containsKey(i) && sfID != null) {
        if (eqOneOf(prev, "f", "r", "rThread", "function")
          || startsWith(prev, "lambda")
          || isIdentifier(next) && eqGet(tok, i+4, "(")
          || eqOneOf(next, "(", "°")
            && (neq(prev, ".") || eq(prevPrev, "main") && neq(get(tok, i-6), "."))
          || eq(prev, "{") && eq(next, "}") && eq(prevPrev, "postProcess")
          || eq(prev, ":") && eq(prevPrev, ":"))
          links.put(i, sfID);
      }
      
      L<S> fewTokens = codeTokens(subList(tok, i-1, i+2*5));
      Pair<S, Int> p = stringTreeLeafValue2(tokenExplanationsTree, fewTokens);
      if (p != null) {
        //print(struct(p));
        int lastCodeToken = i+p.b*2-2;
        if (eq(get(tok, lastCodeToken), "{")) lastCodeToken -= 2;
        mapPutInRange(explanations, i, lastCodeToken+1, p.a);
      }
  
      if (isQuoted(t) && eq(prev, "(") && isIdentifier(prevPrev)
        && isMechFunction(prevPrev))
        mapPut(links, i, neatMechListURL(unquote(t)));
      
      //mapPut(explanations, i, tokenExplanations.get(t));
        
      mapPut(links, i, sc.get(t));
    }
    
    // go over tokens, output HTML characters
    
    new StringBuilder out;
    SS titles = getSnippetTitles(filter isSnippetID(values(links)));
    for i over tok: {
      S t = tok.get(i);
      if (empty(t)) continue;
      S id = links.get(i), ex = explanations.get(i);
      S style = styles.get(i);
      if (t.startsWith("[[") && t.endsWith("]]")) {
        S explanation = "[[...]] denotes a multi-line string constant (as in Lua)";
        out.append(dottedSpan("[[", explanation));
        S inner = htmlencode(dropPrefix("[[", dropSuffix("]]", t)));
        out.append(span(inner, style := "background-color: #77FF77"));
        out.append(dottedSpan("]]", explanation));
        continue;
      }
      if (t.startsWith("[=[") && t.endsWith("]=]")) {
        S explanation = "[=[...]=] denotes a multi-line string constant (as in Lua)";
        out.append(dottedSpan("[=[", explanation));
        S inner = htmlencode(dropPrefix("[=[", dropSuffix("]=]", t)));
        out.append(span(inner, style := "background-color: #77FF77"));
        out.append(dottedSpan("]=]", explanation));
        continue;
      }
      S enc = htmlencode(t);
      out.append(id != null
        ? ahref(makeLink(real, id), enc,
          title := isSnippetID(id) ? titles.get(fsI(id)) : ex, style := "text-decoration: none; color: black; border-bottom: dotted 1px", target := targetBlank ? "_blank" : null)
        : ex != null ? dottedSpan(enc, ex)
        : style != null ? span(enc, +style)
        : enc);
    }
    S html = str(out);
    html = dynamize_noEncode(html);
    ret html;
  }

  // id can be a URL
  S makeLink(bool real, S id) {
    if (isRelativeOrAbsoluteURL(id)) ret id;
    if (real)
      ret longSnippetLink(id);
    ret "/" + psI(id);
  }
  
  void clearCaches {
    stdFunctions_clearCache();
    sc = standardClassesMap();
    sf = stdFunctions_cached();
    //sf.putAll(tok_findStandardFunctionDefinitions(javaTokSnippet(#1022367)));
  }
  
  bool isMechFunction(S s) {
    ret startsWithOneOf(s, "mech", "mL");
  }
  
  void tokensToColors(LS tok, Map<Int, S> styles) {
    for (int i = 0; i < l(tok); i += 2)
      if (tok_whitespaceContainsJavaComments(tok.get(i)))
        styles.put(i, "color: #666666");
  }
  
  void addExplainer(IVF1<PeepHole> explainer) {
    explainers.add(explainer);
  }
} // end of JavaXHyperlinker

Author comment

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: 396 / 859
Version history: 62 change(s)
Referenced in: #1034167 - Standard Classes + Interfaces (LIVE, continuation of #1003674)