Uses 2597K of libraries. Click here for Pure Java version (28892L/158K).
1 | !7 |
2 | |
3 | // TODO: use central transpiler |
4 | |
5 | set flag bindLiveValueListenerToComponent_debug. |
6 | //set flag addMenu_debug. |
7 | |
8 | cmodule2 SFEditor { |
9 | // What are we editing? |
10 | O editing; // File or snippet ID |
11 | S remoteText; |
12 | Int snippetType; |
13 | |
14 | S savedText; |
15 | int caretPos; |
16 | int fontSize = 16; |
17 | S fontID = /*#1400030*/#1400031; |
18 | S snippetTitle; |
19 | switchable bool alwaysTranspileHere = true; |
20 | |
21 | transient EditorFrame2 e; |
22 | transient SimpleLiveValue<O> lvEditing; |
23 | transient AutoCompletion autoCompletion; |
24 | transient ReliableSingleThread rstAutoComplete = dm_rst(this, r installAutoComplete); |
25 | transient bool installAutoComplete_first; |
26 | transient Set<S> autoComplete_lastSet; |
27 | transient int autoCompleteRefreshDelay = 1000; |
28 | transient JFastLogView_noWrap transpilationErrorBox; |
29 | transient ReliableSingleThread persistLater = rstWithDelay(10.0, r persistMe); |
30 | transient JButton saveAndTranspileButton; |
31 | |
32 | !include #1016117 // setAndSave etc. |
33 | |
34 | bool warnOnDelete() { ret neq(e.getText(), unnull(remoteText)); } |
35 | |
36 | void reset { |
37 | cleanMeUp(); |
38 | autoCompletion = null; |
39 | autoComplete_lastSet = null; |
40 | start(); |
41 | } |
42 | |
43 | void start() swing { |
44 | temp enter(); |
45 | installAutoComplete_first = true; |
46 | lvEditing = dm_fieldLiveValue('editing); |
47 | |
48 | e = new EditorFrame2; |
49 | e._setFont(deriveFont(loadFont(fontID), e._getFont().getSize())); |
50 | e._setFontSize(fontSize); |
51 | main.onChangeAndNow(e.textArea, rstAutoComplete); |
52 | |
53 | // Register Ctrl+S |
54 | e.textArea.getInputMap().put(ctrlKeyStroke(KeyEvent.VK_S), |
55 | abstractAction("Save", rThread { saveThen(null) })); |
56 | |
57 | e.setText(savedText); |
58 | load('caretPos); |
59 | setCaretPosition(e.textArea, caretPos); |
60 | } |
61 | |
62 | visual e.panel; |
63 | |
64 | enhanceFrame { |
65 | e.prepareFrame(f/JInternalFrame); |
66 | minimalFrameSize(f/JInternalFrame, 400, 400); |
67 | reload(); |
68 | makeMenus(f); |
69 | awtCalcEvery(f/JInternalFrame.getRootPane(), 1000, r update); |
70 | onPossiblyInternalFrameDeactivated(f/JInternalFrame.getRootPane(), r fullUpdate); |
71 | } |
72 | |
73 | void makeMenus(Container f) { |
74 | if (f == null) ret; |
75 | bool wasEmpty = countComponents(getMenuBar(f)) < 2; // Search menu is added somewhere else |
76 | //print("Components in menu: " + getComponents(getMenuBar(f))); |
77 | |
78 | addMenu(f, "&View", |
79 | "Bigger font", r { e._setFontSize(setAndSave(fontSize := e._getFontSize()+1)) }, |
80 | "Smaller font", r { e._setFontSize(setAndSave(fontSize := max(1, e._getFontSize()-1))) }, |
81 | "---", |
82 | "Show snippet in browser", rThread showInBrowser); |
83 | |
84 | addMenu(f, "&New", |
85 | "New text", r newText, |
86 | "New window", rThread { dm_showNewModule(dm_moduleLibID()) }); |
87 | |
88 | addMenu(f, "&Load", "Load snippet...", r { |
89 | selectSnippetID(voidfunc(S snippetID) { _loadSnippet(snippetID) }); |
90 | }, "Load file...", r { |
91 | selectFile("File to load", voidfunc(File f) { _loadFile(f) }); |
92 | }, "Reload", r-thread { // TODO: single thread |
93 | if (editing != null) { |
94 | reload(); |
95 | main setText(e.textArea, remoteText); |
96 | } |
97 | }); |
98 | |
99 | // There is a weird bug that breaks the bindToComponent magic |
100 | LiveValue<S> lvSaveCmd = mapLiveValue(func(O editing) -> S { |
101 | editing instanceof S ? "Save snippet " + editing |
102 | : editing instanceof File ? "Save file " + f2s((File) editing) |
103 | : "{Save}" }, S.class, lvEditing); |
104 | |
105 | addMenu(f, "&Save", |
106 | lvSaveCmd, rThread { saveThen(null) }, |
107 | "Save as snippet...", r saveAsSnippet, |
108 | "Save as file...", r saveAsFile, |
109 | "Rename snippet...", rThread renameTheSnippet); |
110 | |
111 | addMenu(f, "&Transpile", |
112 | "Save & Quick Transpile", r { transpile(false) }, |
113 | "Save & Medium Transpile", r { transpile(true) }, |
114 | "---", |
115 | "Show transpilation", r showTranspilation); |
116 | |
117 | //print("Editing: " + editing); |
118 | |
119 | addMenu(f, "&Activate", |
120 | eq(snippetType, snippetType_JavaXDesktop()) ? "Run" : null, rThread runIt, |
121 | isString(editing) ? "Load Module" : null, rThread _loadModule, |
122 | isString(editing) ? "Reload Module" : null, rThread _reloadModule, |
123 | haveMuricaPassword() ? "Reload on server" : null, rThread _butterReload, |
124 | haveMuricaPassword() ? "Transpile quick & reload on server" : null, rThread { transpileAndButterReload(false) }, |
125 | haveMuricaPassword() ? "Transpile medium & reload on server" : null, rThread { transpileAndButterReload(true) }); |
126 | |
127 | if (wasEmpty) { |
128 | // add horizontal spacer so icons end up on the right |
129 | addToContainer(getMenuBar(f), Box.createHorizontalGlue()); |
130 | |
131 | // Save & medium transpile icon |
132 | addToContainer(getMenuBar(f), |
133 | jbutton("Save", rThread { saveThen(null) }), |
134 | setToolTip("Save & medium-transpile", |
135 | setBorderPainted(false, |
136 | saveAndTranspileButton = jimagebutton(#1102952, rThread { transpile(true) }))) |
137 | ); |
138 | } |
139 | } |
140 | |
141 | void reload { |
142 | if (editing instanceof S) _loadSnippet_noSet((S) editing); |
143 | else if (editing instanceof File) _loadFile_noSet((File) editing); |
144 | } |
145 | |
146 | void update { update(internalFrameActive(e.panel)); } |
147 | void fullUpdate { update(true); } |
148 | void update(bool full) { |
149 | if (e == null) ret; // e.g. if this instance was made by dm_originalFieldValues |
150 | |
151 | S text = e.getText(); |
152 | int cp = getCaretPosition(e.textArea); |
153 | if (neq(savedText, text)) { |
154 | setAndSave(savedText := text); |
155 | setAndSave(caretPos := cp); |
156 | } else if (cp != caretPos) { |
157 | caretPos = cp; |
158 | persistLater.trigger(); |
159 | } |
160 | |
161 | if (!full) ret; |
162 | S status = remoteText != null && neq(remoteText, text) ? "Changes. " : ""; |
163 | Pair<Int, S> p = testBracketHygieneExt(text); |
164 | status += p == null ? "Hygienic" : p.b; |
165 | silentStatus(trim(status)); |
166 | } |
167 | |
168 | void status(S s) { silentStatus(infoBoxAndReturn(s)); } |
169 | void silentStatus(S s) { e.setStatus(s); } |
170 | |
171 | void saveThen(Runnable next) { |
172 | fS text = dropCarriageReturn(getText()); |
173 | if (editing cast S) { |
174 | status("Saving snippet..."); |
175 | thread { |
176 | fS page = editSnippet(editing, text); |
177 | remoteText = text; // TODO: check save result |
178 | status("Saved snippet: " + page); |
179 | callF(next); |
180 | } |
181 | } else if (editing cast File) { |
182 | status("Saving file..."); |
183 | thread { |
184 | saveTextFile(editing, text); |
185 | remoteText = text; |
186 | status("Saved file"); |
187 | callF(next); |
188 | } |
189 | } |
190 | } |
191 | |
192 | void renameTheSnippet enter { |
193 | if (!editing instanceof S) |
194 | ret with infoBox("Not editing a snippet"); |
195 | S snippetID = cast editing; |
196 | inputText("Rename snippet " + snippetID, snippetTitle(snippetID), voidfunc(S name) { |
197 | renameSnippet(snippetID, snippetTitle = name); |
198 | setModuleName("Editing: " + snippetID + " - " + quote(snippetTitle)); |
199 | }); |
200 | } |
201 | |
202 | void _butterReload() { |
203 | time { butterReload((S) editing); } |
204 | infoBox("Reloaded in " + lastTiming_format100ms()); |
205 | } |
206 | |
207 | void transpileAndButterReload(bool medium) { |
208 | transpile(medium, onSuccess := r _butterReload); |
209 | } |
210 | |
211 | void transpile(bool medium, O... _) { saveThen(r { _transpile(medium, _) }); } |
212 | |
213 | void _transpile(bool medium, O... _) { |
214 | optPar Runnable onSuccess; |
215 | status("Transpiling..."); |
216 | //jdisable(btnEleuReload); |
217 | |
218 | S toTranspile = or(mainTranspilationSnippetFromSrc(getText()), (S) editing); |
219 | S msg = "Transpiling " + toTranspile + "..."; |
220 | infoBox(msg); |
221 | thread msg { |
222 | try { |
223 | Pair<Bool, S> p; |
224 | { |
225 | temp tempDisableButton(saveAndTranspileButton); |
226 | p = transpileHereOrOnServer(toTranspile, medium, alwaysHere := alwaysTranspileHere); |
227 | } |
228 | |
229 | awt { |
230 | if (p.a) { |
231 | status("Transpiled OK!"); |
232 | hideWindow(transpilationErrorBox); |
233 | transpilationErrorBox = null; |
234 | callFInNewThread(onSuccess); |
235 | } else { |
236 | S text = htmlDecode(htmlDecode(p.b)); /// XXX |
237 | status("Transpilation error. " + text); |
238 | bool first = transpilationErrorBox == null; |
239 | transpilationErrorBox = maximizeFrame(scrollAllTheWayDown(showText_fast_noWrap(transpilationErrorBox, "Transpilation Error", text))); |
240 | if (first) { |
241 | setFrameIcon(#1101268, transpilationErrorBox); |
242 | addButtonsToWindow(transpilationErrorBox, "Medium transpile", r { _transpile(true) }); |
243 | } |
244 | } |
245 | } |
246 | } catch print e { |
247 | status("Transpilation problem. " + e); |
248 | } |
249 | //jenable(btnEleuReload); |
250 | } |
251 | } |
252 | |
253 | void _loadSnippet(S snippetID) enter { |
254 | _loadSnippet_noSet(snippetID); |
255 | e.textArea.setText(remoteText); |
256 | } |
257 | |
258 | void _loadSnippet_noSet(S snippetID) { |
259 | remoteText = loadSnippet(snippetID); |
260 | setField(snippetType := getSnippetType(snippetID)); |
261 | setAndSave('editing, snippetID); |
262 | setModuleName("Editing: " + snippetID + " - " + quote(snippetTitle = snippetTitle(snippetID))); |
263 | print("_loadSnippet_noSet: Making menus for " + dm_frame() + " of " + className(dm_vis())); |
264 | makeMenus(dm_frame()); |
265 | } |
266 | |
267 | void _loadFile(File f) { |
268 | _loadFile_noSet(f); |
269 | e.textArea.setText(remoteText); |
270 | } |
271 | |
272 | void _loadFile_noSet(File f) { |
273 | remoteText = loadTextFile(f); |
274 | setField(snippetType := null); |
275 | setAndSave('editing, f); |
276 | setModuleName("Editing: " + f2s(f)); |
277 | makeMenus(dm_frame()); |
278 | } |
279 | |
280 | void newText { |
281 | setAndSave(editing := null); |
282 | remoteText = null; |
283 | setField(snippetType := null); |
284 | S lastText = e.getText(); |
285 | if (nempty(lastText)) logQuotedWithDate("saved-texts.log", lastText); |
286 | e.setText(""); |
287 | setModuleName(programTitle()); |
288 | } |
289 | |
290 | void cleanMeUp { |
291 | update(false); |
292 | } |
293 | |
294 | void installAutoComplete { |
295 | if (installAutoComplete_first) installAutoComplete_first = false; |
296 | else sleep(autoCompleteRefreshDelay); // don't update too often |
297 | |
298 | //temp tempMiniLoadingAnim(); |
299 | |
300 | Set<S> set = concatListsToSet(allJavaKeywords(), standardFunctionNames(), deepWords(getText()), deepWords(snippetTitle)); |
301 | |
302 | if (eq(set, autoComplete_lastSet)) ret; |
303 | autoComplete_lastSet = set; |
304 | |
305 | final new DefaultCompletionProvider provider; |
306 | provider.addCompletions(map(func(S s) -> Completion { new BasicCompletion(provider, s) }, set)); |
307 | |
308 | swing { |
309 | if (autoCompletion == null) { |
310 | (autoCompletion = new AutoCompletion(provider)).install(e.textArea); |
311 | call(autoCompletion, 'setHideOnCompletionProviderChange, false); |
312 | } else |
313 | autoCompletion.setCompletionProvider(provider); |
314 | } |
315 | } |
316 | |
317 | void newWindow { |
318 | copyFields(this, new SFEditor(), 'fontSize).start(); |
319 | } |
320 | |
321 | void runIt { |
322 | if (editing instanceof S) |
323 | nohupJavax((S) editing); |
324 | } |
325 | |
326 | void saveAsSnippet { |
327 | JTextField tfTitle = jtextfield(snippetTitle); |
328 | JComboBox cbType = jComboBox_javaxTypes(); |
329 | JCheckBox cbPublic = jCheckBox("Public", true); |
330 | |
331 | renameSubmitButton("Create snippet", showFormTitled("Save as new snippet", |
332 | "Title:", tfTitle, |
333 | "Type:", cbType, |
334 | "", cbPublic, |
335 | r-thread { |
336 | loading { |
337 | int type = parseFirstInt(getSelectedItem(cbType)); |
338 | S title = getTextTrim(tfTitle); |
339 | S snippetID = createSnippet(getText(), title, type, isChecked(cbPublic)); |
340 | _loadSnippet_noSet(snippetID); |
341 | } |
342 | })); |
343 | } |
344 | |
345 | void saveAsFile { |
346 | inputFilePath("Save as...", optCast(File, editing), voidfunc(File f) { |
347 | saveTextFile(f, getText()); |
348 | _loadFile_noSet(f); |
349 | }); |
350 | } |
351 | |
352 | S getText() { ret e.getText(); } |
353 | |
354 | void _reloadModule enter { |
355 | dm_reloadAllModulesWithSnippetID((S) editing); |
356 | } |
357 | |
358 | void _loadModule enter { |
359 | dm_addDynamicModuleDialog2((S) editing); |
360 | } |
361 | |
362 | S snippetID() { ret optCast S(editing); } |
363 | |
364 | void showTranspilation enter { |
365 | if (snippetID() == null) ret; |
366 | showText("Transpilation of " + snippetID(), |
367 | getServerTranspiledWithoutLibs(snippetID())); |
368 | } |
369 | |
370 | void showInBrowser { |
371 | if (snippetID() == null) ret; |
372 | openInBrowser(snippetLink(snippetID())); |
373 | } |
374 | |
375 | // API |
376 | |
377 | void setText(S text) enter { |
378 | e.textArea.setText(text); |
379 | } |
380 | } |
Began life as a copy of #1015563
download show line numbers debug dex old transpilations
Travelled to 17 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, hpgrupgrauku, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1016118 |
Snippet name: | Code Editor [edits snippets and files] |
Eternal ID of this version: | #1016118/114 |
Text MD5: | 4b2f9396554283499d5676e9678f5e6e |
Transpilation MD5: | df51796755f586a85e1e8106f4ed2e5e |
Author: | stefan |
Category: | javax / gui |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-02-26 17:15:17 |
Source code size: | 12366 bytes / 380 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 713 / 13725 |
Version history: | 113 change(s) |
Referenced in: | [show references] |