srecord noeq JTextFileEditor(File file) is Swingable { settable bool autoSave; gettable JTextArea textArea; JButton btnReload, btnSave; JLabel lblChanged; volatile S fileContents, savedText; FileWatchService fileWatcher; Q q = startQ(); RSTOverQ rstLoad = rstWithPreDelay(0.5, q, r _load); RSTOverQ rstSave; cachedVisualize { assertNotNull(file); rstSave = rstWithPreDelay(autoSave ? 0.5 : 0.0, q, r _save); savedText = fileContents = loadTextFile(file); textArea = makeTextArea(); setText(savedText); onChange(textArea, -> { updateButtons(); if (autoSave) rstSave!; }); bindToComponent(textArea, -> { fileWatcher = new FileWatchService; fileWatcher.addNonRecursiveListener(dirOfFile(file), f -> { if (sameFile(f, file)) rstLoad!; }); rstLoad!; }, -> { dispose fileWatcher; }); ret northAndCenter( withSideMargin(centerAndEastWithMargin( new JFilePathLabel(file).iconOnTheLeft(true).visualize(), jline( autoSave ? (lblChanged = jlabel()) : (btnSave = disableButton(jbutton("Save", rstSave))), btnReload = jbutton("Reload", r reload) ))), withMargin(textArea) ); } void updateButtons { bool changed = !eq(savedText, getText(textArea)); setEnabled(btnSave, changed); setEnabled(btnReload, !eq(fileContents, savedText)); main setText(lblChanged, changed ? "*" : ""); } void _save { saveTextFileVerbose(file, savedText = getText(textArea)); } void reload { savedText = fileContents; setText(textArea(), savedText); updateButtons(); } void _load { print("Loading " + file); fileContents = loadTextFile(file); updateButtons(); } swappable JTextArea makeTextArea() { ret wordWrapTypeWriterTextArea(); } void setText(S text) { main setText(textArea(), text); } }