set flag Matches_fsi. sbool assisting; sclass RandomMain { S generatorsID; JFrame frame; JTable table, chatTable; //JTextArea chat; JTextField input; L log = synchroList(); L recommendations; L thinkThreads = synchroList(); JLabel status; S lastInput; Map matchingSuggestion; Map> allLogs = synchroMap(); JFrame analyzersFrame; S dialogImageID; JFrame dialogImageFrame; int listDelay = 2000; int maxLineLength = 1000; int maxLongStringLength = 100*1000; Bool thinking; new Thinker thinker; bool showCPU = true; S systemPrefix = "[system]"; S dialog = "new"; void randomMain() { regularGC(); //substanceLAF("EmeraldDusk"); // Too dark! //substanceLAF("ChallengerDeep"); // So purple! //substance("MistAqua"); substance("Moderate"); table = tableWithTooltips(); //chat = autoScroll(wordWrapTextArea()); chatTable = tableWithTooltips(); input = new JTextField; status = jlabel(""); S title = assisting ? "Assistance - " + autoFrameTitle() : autoFrameTitle(); frame = showFrame(title, vgrid(centerAndSouth( //jtabs(1, "Chat", chat, "Details", chatTable), chatTable, input), centerAndSouth(table, jline(jMemoryInfo(), status)))); if (!assisting) hideConsole(); //setFrameIconLater(frame, "#1003593"); addMenu(frame, "Random", "Pop up last line in window (!pop)", r { pop(); }, "Delete last line (!delete)", r { post("!delete"); }, "Reload generators (!gen)", r { post("!gen"); }, "Restart app (!restart)", r { post("!restart"); }, "Restart Java engine (!fresh)", r { post("!fresh"); }, "Execute Java code (!j ...)", r { setInput("!j 1+2"); }, "Switch dialog (!dialog ...)", r { setInput("!dialog bla"); }, "Restore last input", r { if (nempty(lastInput)) setInput(lastInput); }, "Show raw dialog", r { showText("Raw Dialog", rawDialog()); }, ); makeDialogsMenu(); addMenu(frame, "Tools", "Show Logic List", r { nohupJavax("#1004419"); } ); onEnter(input, r { post(); }); onDoubleClick(table, voidfunc(int row) { chooseSuggestionForEditing(row); }); for (int i = 1; i <= 12; i++) { final int _i = i; registerFunctionKey(frame, i, r { chooseSuggestionForEditing(_i-1); }); registerShiftFunctionKey(frame, i, r { chooseSuggestion(_i-1); }); registerCtrlFunctionKey(frame, i, r { // post user input and then choose suggestion for editing Map map = getTableLineAsMap(table, _i-1); if (map == null) ret; rankToTop(map); S s = trim(unnull(map.get("Suggestion"))); logEvent("Suggestion chosen for editing with pre-post", mapPlus(map, "Row", _i, "Input", getInput(), "Top Suggesters", topSuggesters())); post(); setInput(s); }); } onDoubleClickOrEnter(chatTable, voidfunc(int row) { Map map = getTableLineAsMap(chatTable, row); if (map != null) setInput(getString(map, "Text")); }); onUpdate(input, r { updateOnce(); }); loadDialog(); logEvent("Starting"); updateOnce(); input.requestFocus(); makeBot("Chat.", this); veryQuickJava_optimize(); S last = last(log); if (isAction(last) && confirmYesNo(input, "Run action? " + last(log))) action(last); } S getInput() { ret joinLines(" # ", input.getText().trim()); } void post() { postAsUser(getInput(), null); } void postAsUser(S i, Map infos) { if (inputAllowedByUser(i)) post(i, infos); } void chooseSuggestionForEditing(int row) { Map map = getTableLineAsMap(table, row); if (map == null) ret; rankToTop(map); S s = trim(unnull(map.get("Suggestion"))); logEvent("Suggestion chosen for editing", mapPlus(map, "Row", row+1, "Input", getInput(), "Top Suggesters", topSuggesters())); setInput(s); } void setInput(final S s) { awtIfNecessary { lastInput = input.getText(); input.setText(s); input.selectAll(); input.requestFocus(); } } void rankToTop(Map map) { rankToTop(map, false); } void rankToTop(Map map, bool removeISuggesters) { if (map == null) ret; O s = map.get("Suggesters"); // Table cells have been structure'd by dataToTable L sugg = s instanceof L ? (L) s : (L) unstructure((S) s); // These are cheaters! if (removeISuggesters) sugg = rejectWhere(func(S s) { s.endsWith("/i") }, sugg); thinker.rankToTop(first(sugg)); } void chooseSuggestion(int row) { Map map = getTableLineAsMap(table, row); if (map == null) ret; rankToTop(map); S s = trim(unnull(map.get("Suggestion"))); if (empty(s)) ret; //logEvent("Suggestion chosen", mapPlus(map, "Row", row+1, "Input", getInput(), "Top Suggesters", topSuggesters)); setInput(s); postAsUser(s, mapPlus(map, "Index", row+1)); } L topSuggesters() { int n = 20; n = min(n, tableRows(table)); new L topSuggesters; for (int i = 0; i < n; i++) topSuggesters.add(getTableLineAsMap(table, i)); //if (empty(topSuggesters)) topSuggesters = null; ret topSuggesters; } void logEvent(S type) { logEvent(type, litmap()); } void logEvent(S type, Map map) { logStructure(new File(dialogDir(), "event.log"), ll(type, chatTime(), map)); } bool inputAllowedByUser(S i) { ret !swic(i, systemPrefix); } // may be called from other thread void postSystemMessage(final S msg) { if (empty(msg)) ret; awtIfNecessary { post(systemPrefix + " " + msg, litmap("By", "System")); } } S post(S i) { ret post(i, null); } S post(S i, Map infos) { S chatTime = null; try { i = trim(i); if (empty(i)) null; //i = escapeNewLines(i); if (infos == null) { infos = matchingSuggestion; if (infos != null) print("Ranking to top: " + struct(infos)); rankToTop(infos, true); } infos = mapPlus(infos, "Top Suggesters", topSuggesters()); bool tooLong = l(i) > maxLongStringLength; if (l(i) > maxLineLength) { S id = saveLongString(i); i = substring(i, 0, maxLineLength) + "... [" + (tooLong ? "too " : "") + "long text " + id + "]"; } } catch e { printStackTrace(e); i = systemPrefix + " " + exceptionToStringShort(e); } S s = i + "\n"; //chat.append(escapeNewLines(i) + "\n"); chatTime = chatTime(); appendToFile(logFile(), "[" + chatTime + "] " + s); logEvent("Posting", litmap("Text", i, "Infos", infos)); log.add(i); updateChatTable(); input.selectAll(); updateOnce(); try { action(i); } catch e { printStackTrace(e); postSystemMessage(exceptionToStringShort(e)); } ret chatTime; } S dropActionPrefix(S s) { if (s == null) null; s = dropBracketPrefix(s); // e.g. "[bot]" if (!s.startsWith("!")) null; ret s.substring(1); } bool isAction(S s) { ret dropActionPrefix(s) != null; } void action(S s) { s = dropActionPrefix(s); if (s == null) ret; final S _s = s; thread "Action" { JWindow _loading_window = showLoadingAnimation(); try { genLog_set(getLog()); // 'case user needs it gOtherLogs_set(getOtherLogs()); // ditto randomsOwnCmds(_s); systemCommands(_s, RandomMain.this); } catch e { printStackTrace(e); postSystemMessage("Error - " + exceptionToStringShort(e)); } finally { genLog_clear(); gOtherLogs_clear(); disposeWindow(_loading_window); } } } volatile bool again; // This logic is bad... void fillList(bool force) { bool t = force || shouldUpdateList(); if (neq(t, thinking)) { thinking = t; setFrameIcon(table, t ? "#1003603" : "#1003593"); } if (!t) { if (!force) againl8r(); } else { if (nempty(thinkThreads)) { again = true; ret; } fillListImpl(); } } void addKeys(L data) { for (int i = 0; i < l(data); i++) { int k = i+1; S key = k <= 12 ? "F" + k : null; Map m = litorderedmap("Key", key); m.putAll(data.get(i)); data.set(i, m); } } // data should be in the un-stringified form void removeMatchingLine(L data) { S input = getInput(); matchingSuggestion = null; for (int i = 0; i < l(data); i++) if (eq(input, data.get(i).get("Suggestion"))) { Map m = data.get(i); matchingSuggestion = mapPlus(m, "Index", i+1); pcall { L suggesters = (L) m.get("Suggesters"); if (allEndWith(suggesters, "/i")) { // << These cheat too much //print("Dropping matching suggestion from: " + struct(suggesters)); matchingSuggestion = null; } } data.remove(i); ret; } } void fillListImpl() { thread "Fill List" { try { thinkThreads.add(currentThread()); final new L data; thinker.makeListData(cloneList(log), getInput(), getOtherLogs(), data); awt { pcall { removeMatchingLine(data); addKeys(data); dataToTable_uneditable(table, data); tableColumnMaxWidth(table, 0, 30); // "Key" column } againl8r(); } } finally { thinkThreads.remove(currentThread()); if (again) { again = false; fillListImpl(); } } } } void updateOnce() { fillList(true); } void againl8r() { swingAfter(table, listDelay, r { fillList(false); }); } bool shouldUpdateList() { bool result = false; S text = " "; if (getFrame(table).isFocused()) { result = !mouseInComponent(table); text = result ? " Thinking..." + (showCPU /*&& thinker.load != 0*/ ? " (" + thinker.load + "% CPU)" : "") : "Not thinking cause you got the mouse in there"; } status.setText(text); ret result; } // also called from outside L loadLog() { log.clear(); log.addAll(scanEventLogForText(dialogDir())); ret log; } void loadAllLogs() { allLogs.clear(); for (File f : findAllFiles(getProgramDir())) if (f.getName().equals("event.log")) allLogs.put(f.getParentFile().getName(), new ImmL(scanEventLogForText(f))); } synchronized L getLastFromLog(int n) { ret cloneList(getLast(log, n)); } synchronized L getLog() { ret cloneList(log); } File dialogDir() { ret prepareProgramFile(dialog); } File logFile() { ret new File(dialogDir(), "log.txt"); } void switchDialog(final S name) { swingAndWait(r { dialog = name; touchFile(new File(dialogDir(), "event.log")); loadDialog(); loadAllLogs(); makeDialogsMenu(); setFrameTitle(dialog + " - " + getProgramTitle()); // show special stuff for dialog, like an image dialogImageID = trim(readTextFile(new File(dialogDir(), "image-id.txt"))); showDialogImage(); }); } void loadDialog() { loadLog(); thinker = new Thinker; thinker.startUp(this, log); //chat.setText(joinLines(log)); updateChatTable(); } void randomsOwnCmds(S s) { new Matches m; if "dialog *" { switchDialog(m.unq(0)); // TODO: show current dialog somewhere else //postSystemMessage("OK, dialog switched to " + quote(dialog)); } if "gen" generators = null; if "delete" updateChatTable(); if "analyzers" showAnalyzers(); if (matchOneOf(s, m, "img *", "image *")) { S imageID = m.fsi(0); saveTextFile(new File(dialogDir(), "image-id.txt"), imageID); dialogImageID = imageID; showDialogImage(); } if (matchOneOf(s, m, "img", "image")) { if (empty(dialogImageID)) postSystemMessage("No image set for this dialog"); else postSystemMessage("Image for this dialog: " + fsI(dialogImageID)); } } void showAnalyzers() { if (analyzersFrame == null) analyzersFrame = showFrame("Analyzers"); } void updateChatTable() { awtIfNecessary { new L data; L l = scanLog_safeUnstructure(new File(dialogDir(), "event.log")); for (int i = 0; i < l(l); i++) pcall { L a = l.get(i), prev = get(l, i-1); if (firstIs(a, "Posting")) { Map map = cast get(a, 2); S text = getString(map, "Text").trim(); if (eq(text, "!delete")) { removeLast(data); continue; } S idx = ""; Map infos = cast map.get("Infos"); if (infos != null && infos.containsKey("Index")) idx = str(infos.get("Index")); else pcall { //printStruct("prev: ", prev); if (prev != null && firstIs(prev, "Suggestion chosen for editing")) { Map m = getMap(prev, 2); S suggestion = getString(m, "Suggestion"); //print("Suggestion: " + structure(suggestion)); idx = str(get(m, "Row")); if (neq(suggestion, text)) idx += "?"; } } data.add(litorderedmap( "#", "", // not known yet cause of !delete statements "Text", escapeNewLines(text), "Sugg." /* Suggestion Index */, idx)); } } addNumbering(data); dataToTable_uneditable(chatTable, data); tableColumnMaxWidth(chatTable, 0, 30); // enough for 3 digits? tableColumnMaxWidth(chatTable, 2, 40); // enough for 2 digits and a "?"? scrollTableDownIn(chatTable, 50); } } void addNumbering(L data) { int n = 0; for (int i = l(data)-1; i >= 0; i--) data.get(i).put("#", ++n); } synchronized S saveLongString(S s) { s = substring(s, 0, maxLongStringLength); S id; File f; do { id = randomID(10); f = getProgramFile("long-strings/" + id); } while (f.exists()); saveTextFile(f, s); ret id; } O generators; void makeGenerators(L l) { synchronized(main.class) { if (!isSnippetID(generatorsID)) fail("No generators ID set"); if (generators == null) generators = hotwire(generatorsID); } new L l2; callOpt(generators, "makeGenerators", l2); callOpt(generators, "deterministicGenerators", l2); l.addAll((L) quickImport(l2)); } S rawDialog() { ret fromLines(log); } void makeDialogsMenu() { new L items; for (File dir : listDirs(getProgramDir())) if (containsFile(dir, "event.log")) { fS dialog = dir.getName(); items.add(dialog); items.add(r { switchDialog(dialog); }); } addMenu(frame, "Dialogs", items); } L> getOtherLogs() { // TODO: sort by latest or smth? ret valuesList(mapMinus(allLogs, dialog)); } void hideDialogImage() { if (dialogImageFrame != null) { disposeFrame(dialogImageFrame); dialogImageFrame = null; } } void showDialogImage() { hideDialogImage(); if (nempty(dialogImageID)) { S title = getSnippetTitle(dialogImageID) + " [" + fsI(dialogImageID) + "/" + dialog + "]"; dialogImageFrame = getFrame(showImage(dialogImageID, title)); } } S answer(S s) { print("answer: " + s); new Matches m; if "please post * to chat file *" { final S line = m.unq(0); File file = new File(m.unq(1)); File myFile = new File(dialogDir(), "event.log"); print("Comparing files: " + file + " / " + myFile); if (sameFile(file, myFile)) { print("Foreign-posting: " + quote(line)); ret ok(swingAndWait(func { post(line) })); } } null; } void pop() { S text = last(log); if (nempty(text)) showText(text); } }