concept SFEditor { // What are we editing? O editing; // File or snippet ID S remoteText; S savedText; int caretPos; int fontSize = 16; S fontID = /*#1400030*/#1400031; S snippetTitle; transient EditorFrame e; transient SimpleLiveValue lvEditing; transient AutoCompletion autoCompletion; transient ReliableSingleThread rstAutoComplete = new(r installAutoComplete); transient bool installAutoComplete_first; transient Set autoComplete_lastSet; transient DynamicHStack smartArea; transient int autoCompleteRefreshDelay = 1000; transient JTextArea transpilationErrorBox; !include #1015622 // setAndSave etc. void reset { cleanMeUp(); autoCompletion = null; autoComplete_lastSet = null; start(); } void start { swing { installAutoComplete_first = true; lvEditing = new SimpleLiveValue(O); e = new EditorFrame; e._setFont(deriveFont(loadFont(fontID), e._getFont().getSize())); e._setFontSize(fontSize); rstAutoComplete.trigger(); onChange(e.textArea, rTrigger(rstAutoComplete)); addToWindowWithMargin(e, jHigherScrollPane(jfullcenter(smartArea = dynamicHStack()))); e.setText(savedText); load('caretPos); setCaretPosition(e.textArea, caretPos); showFrame(e); frameIcon(e, #1101185 /*#1004655*/); addFontChangersToTitleMenu(e, r reset); onFrameClosing(e, r { if (!cleaningUp()) deleteConcept(SFEditor.this); }); addMenu(e, "&New", "New text", r newText, "New window", r newWindow); addMenu(e, "&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(); setText(e.textArea, remoteText); } }); 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(e, "&Save", lvSaveCmd, r { saveThen(null) }, "Save as snippet...", r saveAsSnippet); addMenu(e, "&Transpile", "Save & Quick Transpile", r { transpile(false) }, "Save & Medium Transpile", r { transpile(true) }); addMenu(e, "&Activate", "Run", r runIt, "Butter-Reload", r _butterReload); addMenu(e, "&View", "Bigger font", r { e._setFontSize(setAndSave(fontSize := e._getFontSize()+1)) }, "Smaller font", r { e._setFontSize(setAndSave(fontSize := max(1, e._getFontSize()-1))) }); reload(); awtCalcEvery(e, 1000, r update); onFrameDeactivated(e, r fullUpdate); } } void reload { if (editing instanceof S) _loadSnippet_noSet((S) editing); else if (editing instanceof File) _loadFile_noSet((File) editing); } void update { update(windowActive(e)); } void fullUpdate { update(true); } void update(bool full) { S text = e.getText(); setAndSaveIfChanged(savedText := text); setAndSaveIfChanged(caretPos := getCaretPosition(e.textArea)); 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(final Runnable next) { fS text = getText(); if (editing instanceof S) { status("Saving snippet..."); thread { fS page = editSnippet((S) editing, text); remoteText = text; // TODO: check save result status("Saved snippet: " + page); awtCallF(next); } } else if (editing instanceof File) { status("Saving file..."); thread { saveTextFile((File) editing, text); remoteText = text; status("Saved file"); awtCallF(next); } } } void _butterReload() { time { butterReload((S) editing); } infoBox("Reloaded in " + lastTiming_format100ms()); } void transpile(final bool medium) { saveThen(r { _transpile(medium) }); } void _transpile(bool medium) { status("Transpiling..."); //jdisable(btnEleuReload); thread "Transpiling" { try { transpileOnServerWithErrorWindow((S) editing, medium, r { status("Transpiled OK!") }, onError := voidfunc(S error) { status("Transpilation error. " + error); }); } catch print e { status("Transpilation problem. " + e); } //jenable(btnEleuReload); } } void _loadSnippet(S snippetID) { _loadSnippet_noSet(snippetID); e.textArea.setText(remoteText); } void _loadSnippet_noSet(S snippetID) { remoteText = loadSnippet(snippetID); setAndSave('editing, snippetID); lvEditing.set(snippetID); frameTitle(e, snippetID + " - " + (snippetTitle = snippetTitle(snippetID))); } void _loadFile(File f) { _loadFile_noSet(f); e.textArea.setText(remoteText); } void _loadFile_noSet(File f) { remoteText = loadTextFile(f); setAndSave('editing, f); lvEditing.set(f); frameTitle(e, f2s(f)); } void newText { setAndSave(editing := null); lvEditing.set(null); remoteText = null; S lastText = e.getText(); if (nempty(lastText)) logQuotedWithDate("saved-texts.log", lastText); e.setText(""); frameTitle(e, programTitle()); } void cleanMeUp { update(false); disposeWindow(e); } 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(s -> 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 { final JTextField tfTitle = jtextfield(); final JComboBox cbType = jComboBox_javaxTypes(); showFormTitled("Save as new snippet", "Title:", tfTitle, "Type:", cbType, r-thread { loading { int type = parseFirstInt(getSelectedItem(cbType)); S title = getTextTrim(tfTitle); S snippetID = createSnippet(getText(), title, type); _loadSnippet_noSet(snippetID); } }); } S getText() { ret e.getText(); } }