!7 // TODO: use central transpiler set flag bindLiveValueListenerToComponent_debug. //set flag addMenu_debug. cmodule2 SFEditor { // What are we editing? O editing; // File or snippet ID S remoteText; Int snippetType; S savedText; int caretPos; int fontSize = 16; S fontID = /*#1400030*/#1400031; S snippetTitle; switchable bool alwaysTranspileHere = true; transient EditorFrame2 e; transient SimpleLiveValue lvEditing; transient AutoCompletion autoCompletion; transient ReliableSingleThread rstAutoComplete = dm_rst(this, r installAutoComplete); transient bool installAutoComplete_first; transient Set autoComplete_lastSet; transient int autoCompleteRefreshDelay = 1000; transient JFastLogView_noWrap transpilationErrorBox; transient ReliableSingleThread persistLater = rstWithDelay(10.0, r persistMe); transient JButton saveAndTranspileButton; !include #1016117 // setAndSave etc. bool warnOnDelete() { ret neq(e.getText(), unnull(remoteText)); } void reset { cleanMeUp(); autoCompletion = null; autoComplete_lastSet = null; start(); } void start() swing { temp enter(); installAutoComplete_first = true; lvEditing = dm_fieldLiveValue('editing); e = new EditorFrame2; e._setFont(deriveFont(loadFont(fontID), e._getFont().getSize())); e._setFontSize(fontSize); main.onChangeAndNow(e.textArea, rstAutoComplete); // Register Ctrl+S e.textArea.getInputMap().put(ctrlKeyStroke(KeyEvent.VK_S), abstractAction("Save", rThread { saveThen(null) })); e.setText(savedText); load('caretPos); setCaretPosition(e.textArea, caretPos); } visual e.panel; enhanceFrame { e.prepareFrame(f/JInternalFrame); minimalFrameSize(f/JInternalFrame, 400, 400); reload(); makeMenus(f); awtCalcEvery(f/JInternalFrame.getRootPane(), 1000, r update); onPossiblyInternalFrameDeactivated(f/JInternalFrame.getRootPane(), r fullUpdate); } void makeMenus(Container f) { if (f == null) ret; bool wasEmpty = countComponents(getMenuBar(f)) < 2; // Search menu is added somewhere else //print("Components in menu: " + getComponents(getMenuBar(f))); addMenu(f, "&View", "Bigger font", r { e._setFontSize(setAndSave(fontSize := e._getFontSize()+1)) }, "Smaller font", r { e._setFontSize(setAndSave(fontSize := max(1, e._getFontSize()-1))) }, "---", "Show snippet in browser", rThread showInBrowser); addMenu(f, "&New", "New text", r newText, "New window", rThread { dm_showNewModule(dm_moduleLibID()) }); addMenu(f, "&Load", "Load snippet...", r { selectSnippetID(voidfunc(S snippetID) { _loadSnippet(snippetID) }); }, "Load file...", r { selectFile("File to load", voidfunc(File f) { _loadFile(f) }); }, "Reload", r-thread { // TODO: single thread if (editing != null) { reload(); main setText(e.textArea, remoteText); } }); // There is a weird bug that breaks the bindToComponent magic LiveValue lvSaveCmd = mapLiveValue(func(O editing) -> S { editing instanceof S ? "Save snippet " + editing : editing instanceof File ? "Save file " + f2s((File) editing) : "{Save}" }, S.class, lvEditing); addMenu(f, "&Save", lvSaveCmd, rThread { saveThen(null) }, "Save as snippet...", r saveAsSnippet, "Save as file...", r saveAsFile, "Rename snippet...", rThread renameTheSnippet); addMenu(f, "&Transpile", "Save & Quick Transpile", r { transpile(false) }, "Save & Medium Transpile", r { transpile(true) }, "---", "Show transpilation", r showTranspilation); //print("Editing: " + editing); addMenu(f, "&Activate", eq(snippetType, snippetType_JavaXDesktop()) ? "Run" : null, rThread runIt, isString(editing) ? "Load Module" : null, rThread _loadModule, isString(editing) ? "Reload Module" : null, rThread _reloadModule, haveMuricaPassword() ? "Reload on server" : null, rThread _butterReload, haveMuricaPassword() ? "Transpile quick & reload on server" : null, rThread { transpileAndButterReload(false) }, haveMuricaPassword() ? "Transpile medium & reload on server" : null, rThread { transpileAndButterReload(true) }); if (wasEmpty) { // add horizontal spacer so icons end up on the right addToContainer(getMenuBar(f), Box.createHorizontalGlue()); // Save & medium transpile icon addToContainer(getMenuBar(f), jbutton("Save", rThread { saveThen(null) }), setToolTip("Save & medium-transpile", setBorderPainted(false, saveAndTranspileButton = jimagebutton(#1102952, rThread { transpile(true) }))) ); } } void reload { if (editing instanceof S) _loadSnippet_noSet((S) editing); else if (editing instanceof File) _loadFile_noSet((File) editing); } void update { update(internalFrameActive(e.panel)); } void fullUpdate { update(true); } void update(bool full) { if (e == null) ret; // e.g. if this instance was made by dm_originalFieldValues S text = e.getText(); int cp = getCaretPosition(e.textArea); if (neq(savedText, text)) { setAndSave(savedText := text); setAndSave(caretPos := cp); } else if (cp != caretPos) { caretPos = cp; persistLater.trigger(); } if (!full) ret; S status = remoteText != null && neq(remoteText, text) ? "Changes. " : ""; Pair p = testBracketHygieneExt(text); status += p == null ? "Hygienic" : p.b; silentStatus(trim(status)); } void status(S s) { silentStatus(infoBoxAndReturn(s)); } void silentStatus(S s) { e.setStatus(s); } void saveThen(Runnable next) { fS text = dropCarriageReturn(getText()); if (editing cast S) { status("Saving snippet..."); thread { fS page = editSnippet(editing, text); remoteText = text; // TODO: check save result status("Saved snippet: " + page); callF(next); } } else if (editing cast File) { status("Saving file..."); thread { saveTextFile(editing, text); remoteText = text; status("Saved file"); callF(next); } } } void renameTheSnippet enter { if (!editing instanceof S) ret with infoBox("Not editing a snippet"); S snippetID = cast editing; inputText("Rename snippet " + snippetID, snippetTitle(snippetID), voidfunc(S name) { renameSnippet(snippetID, snippetTitle = name); setModuleName("Editing: " + snippetID + " - " + quote(snippetTitle)); }); } void _butterReload() { time { butterReload((S) editing); } infoBox("Reloaded in " + lastTiming_format100ms()); } void transpileAndButterReload(bool medium) { transpile(medium, onSuccess := r _butterReload); } void transpile(bool medium, O... _) { saveThen(r { _transpile(medium, _) }); } void _transpile(bool medium, O... _) { optPar Runnable onSuccess; status("Transpiling..."); //jdisable(btnEleuReload); S toTranspile = or(mainTranspilationSnippetFromSrc(getText()), (S) editing); S msg = "Transpiling " + toTranspile + "..."; infoBox(msg); thread msg { try { Pair p; { temp tempDisableButton(saveAndTranspileButton); p = transpileHereOrOnServer(toTranspile, medium, alwaysHere := alwaysTranspileHere); } awt { if (p.a) { status("Transpiled OK!"); hideWindow(transpilationErrorBox); transpilationErrorBox = null; callFInNewThread(onSuccess); } else { S text = htmlDecode(htmlDecode(p.b)); /// XXX status("Transpilation error. " + text); bool first = transpilationErrorBox == null; transpilationErrorBox = maximizeFrame(scrollAllTheWayDown(showText_fast_noWrap(transpilationErrorBox, "Transpilation Error", text))); if (first) { setFrameIcon(#1101268, transpilationErrorBox); addButtonsToWindow(transpilationErrorBox, "Medium transpile", r { _transpile(true) }); } } } } catch print e { status("Transpilation problem. " + e); } //jenable(btnEleuReload); } } void _loadSnippet(S snippetID) enter { _loadSnippet_noSet(snippetID); e.textArea.setText(remoteText); } void _loadSnippet_noSet(S snippetID) { remoteText = loadSnippet(snippetID); setField(snippetType := getSnippetType(snippetID)); setAndSave('editing, snippetID); setModuleName("Editing: " + snippetID + " - " + quote(snippetTitle = snippetTitle(snippetID))); print("_loadSnippet_noSet: Making menus for " + dm_frame() + " of " + className(dm_vis())); makeMenus(dm_frame()); } void _loadFile(File f) { _loadFile_noSet(f); e.textArea.setText(remoteText); } void _loadFile_noSet(File f) { remoteText = loadTextFile(f); setField(snippetType := null); setAndSave('editing, f); setModuleName("Editing: " + f2s(f)); makeMenus(dm_frame()); } void newText { setAndSave(editing := null); remoteText = null; setField(snippetType := null); S lastText = e.getText(); if (nempty(lastText)) logQuotedWithDate("saved-texts.log", lastText); e.setText(""); setModuleName(programTitle()); } void cleanMeUp { update(false); } void installAutoComplete { if (installAutoComplete_first) installAutoComplete_first = false; else sleep(autoCompleteRefreshDelay); // don't update too often //temp tempMiniLoadingAnim(); Set set = concatListsToSet(allJavaKeywords(), standardFunctionNames(), deepWords(getText()), deepWords(snippetTitle)); if (eq(set, autoComplete_lastSet)) ret; autoComplete_lastSet = set; final new DefaultCompletionProvider provider; provider.addCompletions(map(func(S s) -> Completion { new BasicCompletion(provider, s) }, set)); swing { if (autoCompletion == null) { (autoCompletion = new AutoCompletion(provider)).install(e.textArea); call(autoCompletion, 'setHideOnCompletionProviderChange, false); } else autoCompletion.setCompletionProvider(provider); } } void newWindow { copyFields(this, new SFEditor(), 'fontSize).start(); } void runIt { if (editing instanceof S) nohupJavax((S) editing); } void saveAsSnippet { JTextField tfTitle = jtextfield(snippetTitle); JComboBox cbType = jComboBox_javaxTypes(); JCheckBox cbPublic = jCheckBox("Public", true); renameSubmitButton("Create snippet", showFormTitled("Save as new snippet", "Title:", tfTitle, "Type:", cbType, "", cbPublic, r-thread { loading { int type = parseFirstInt(getSelectedItem(cbType)); S title = getTextTrim(tfTitle); S snippetID = createSnippet(getText(), title, type, isChecked(cbPublic)); _loadSnippet_noSet(snippetID); } })); } void saveAsFile { inputFilePath("Save as...", optCast(File, editing), voidfunc(File f) { saveTextFile(f, getText()); _loadFile_noSet(f); }); } S getText() { ret e.getText(); } void _reloadModule enter { dm_reloadAllModulesWithSnippetID((S) editing); } void _loadModule enter { dm_addDynamicModuleDialog2((S) editing); } S snippetID() { ret optCast S(editing); } void showTranspilation enter { if (snippetID() == null) ret; showText("Transpilation of " + snippetID(), getServerTranspiledWithoutLibs(snippetID())); } void showInBrowser { if (snippetID() == null) ret; openInBrowser(snippetLink(snippetID())); } // API void setText(S text) enter { e.textArea.setText(text); } }