Skip to content

Commit 7910253

Browse files
sbraconniertonygermano
authored andcommitted
Add the ability to manually format Javascript code
This change allows the user to format code within the code editor areas. The user can format the currently selected lines or the entire document. A default hotkey of ctrl+shift+F has been added to run this action, but it may be changed by the user. Additionally, this action can be performed in the right-click context menu by selecting "Code->Format" Signed-off-by: Tony Germano <tony@germano.name> Original-sign-off: 132de74 Issue: #87
1 parent 7ecf54b commit 7910253

8 files changed

Lines changed: 4180 additions & 2484 deletions

File tree

‎client/src/com/mirth/connect/client/ui/components/rsta/MirthRSyntaxTextArea.java‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
importcom.mirth.connect.client.ui.components.rsta.actions.ExpandFoldAction;
6363
importcom.mirth.connect.client.ui.components.rsta.actions.FindNextAction;
6464
importcom.mirth.connect.client.ui.components.rsta.actions.FindReplaceAction;
65+
importcom.mirth.connect.client.ui.components.rsta.actions.FormatCodeAction;
6566
importcom.mirth.connect.client.ui.components.rsta.actions.GoToMatchingBracketAction;
6667
importcom.mirth.connect.client.ui.components.rsta.actions.HorizontalPageAction;
6768
importcom.mirth.connect.client.ui.components.rsta.actions.InsertBreakAction;
@@ -127,6 +128,8 @@ public class MirthRSyntaxTextArea extends RSyntaxTextArea implements MirthTextIn
127128
privateCustomJCheckBoxMenuItemshowWhitespaceMenuItem;
128129
privateCustomJCheckBoxMenuItemshowLineEndingsMenuItem;
129130
privateCustomJCheckBoxMenuItemwrapLinesMenuItem;
131+
privateJMenucodeMenu;
132+
privateCustomMenuItemformatCodeMenuItem;
130133
privateJMenumacroMenu;
131134
privateCustomMenuItembeginMacroMenuItem;
132135
privateCustomMenuItemendMacroMenuItem;
@@ -228,6 +231,8 @@ public void keyPressed(KeyEvent e) {
228231
showWhitespaceMenuItem = newCustomJCheckBoxMenuItem(this, newShowWhitespaceAction(this), ActionInfo.DISPLAY_SHOW_WHITESPACE);
229232
showLineEndingsMenuItem = newCustomJCheckBoxMenuItem(this, newShowLineEndingsAction(this), ActionInfo.DISPLAY_SHOW_LINE_ENDINGS);
230233
wrapLinesMenuItem = newCustomJCheckBoxMenuItem(this, newWrapLinesAction(this), ActionInfo.DISPLAY_WRAP_LINES);
234+
codeMenu = newJMenu("Code");
235+
formatCodeMenuItem = newCustomMenuItem(this, newFormatCodeAction(this), ActionInfo.FORMAT_CODE);
231236
macroMenu = newJMenu("Macro");
232237
beginMacroMenuItem = newCustomMenuItem(this, newBeginMacroAction(), ActionInfo.MACRO_BEGIN);
233238
endMacroMenuItem = newCustomMenuItem(this, newEndMacroAction(), ActionInfo.MACRO_END);
@@ -239,6 +244,7 @@ public void keyPressed(KeyEvent e) {
239244
getActionMap().put(ActionInfo.DELETE_LINE.getActionMapKey(), newDeleteLineAction());
240245
getActionMap().put(ActionInfo.JOIN_LINE.getActionMapKey(), newJoinLineAction());
241246
getActionMap().put(ActionInfo.GO_TO_MATCHING_BRACKET.getActionMapKey(), newGoToMatchingBracketAction());
247+
getActionMap().put(ActionInfo.FORMAT_CODE.getActionMapKey(), newFormatCodeAction(this));
242248
getActionMap().put(ActionInfo.TOGGLE_COMMENT.getActionMapKey(), newToggleCommentAction());
243249
getActionMap().put(ActionInfo.DOCUMENT_START.getActionMapKey(), newDocumentStartAction(false));
244250
getActionMap().put(ActionInfo.DOCUMENT_SELECT_START.getActionMapKey(), newDocumentStartAction(true));
@@ -489,6 +495,10 @@ protected JPopupMenu createPopupMenu() {
489495
menu.add(displayMenu);
490496
menu.addSeparator();
491497

498+
codeMenu.add(formatCodeMenuItem);
499+
menu.add(codeMenu);
500+
menu.addSeparator();
501+
492502
macroMenu.add(beginMacroMenuItem);
493503
macroMenu.add(endMacroMenuItem);
494504
macroMenu.add(playbackMacroMenuItem);
@@ -514,6 +524,7 @@ protected void configurePopupMenu(JPopupMenu popupMenu) {
514524
findNextMenuItem.setEnabled(findNextMenuItem.getAction().isEnabled() && CollectionUtils.isNotEmpty(rstaPreferences.getFindReplaceProperties().getFindHistory()));
515525
clearMarkedOccurrencesMenuItem.setEnabled(clearMarkedOccurrencesMenuItem.getAction().isEnabled() && canType && ((RSyntaxTextAreaHighlighter) getHighlighter()).getMarkAllHighlightCount() > 0);
516526
foldingMenu.setEnabled(getFoldManager().isCodeFoldingSupportedAndEnabled());
527+
formatCodeMenuItem.setEnabled(formatCodeMenuItem.getAction().isEnabled());
517528
beginMacroMenuItem.setEnabled(!isRecordingMacro());
518529
endMacroMenuItem.setEnabled(isRecordingMacro());
519530
playbackMacroMenuItem.setEnabled(!isRecordingMacro() && getCurrentMacro() != null);
@@ -533,6 +544,7 @@ protected void configurePopupMenu(JPopupMenu popupMenu) {
533544
collapseAllFoldsMenuItem.updateAccelerator();
534545
collapseAllCommentFoldsMenuItem.updateAccelerator();
535546
expandAllFoldsMenuItem.updateAccelerator();
547+
formatCodeMenuItem.updateAccelerator();
536548
beginMacroMenuItem.updateAccelerator();
537549
endMacroMenuItem.updateAccelerator();
538550
playbackMacroMenuItem.updateAccelerator();
@@ -550,4 +562,4 @@ public void setSyntaxEditingStyle(String styleKey) {
550562
publicAction[] getActions() {
551563
returnactions != null ? actions : getUI().getEditorKit(this).getActions();
552564
}
553-
}
565+
}

‎client/src/com/mirth/connect/client/ui/components/rsta/MirthRSyntaxTextArea.properties‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ TOGGLE_COMMENT.Name=Toggle Comment
118118
TOGGLE_COMMENT.Desc=Comments / uncomments the current selected line(s).
119119
TOGGLE_COMMENT.Toggle=false
120120

121+
FORMAT_CODE.Mnemonic=F
122+
FORMAT_CODE.Name=Format
123+
FORMAT_CODE.Desc=Format the current script / selected line(s).
124+
FORMAT_CODE.Toggle=false
125+
121126
VIEW_USER_API.Mnemonic=V
122127
VIEW_USER_API.Name=View User API
123128
VIEW_USER_API.Desc=Opens up the User API Javadoc in a browser.

‎client/src/com/mirth/connect/client/ui/components/rsta/RSTAPreferences.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ private void setDefaultKeyStrokeMap() {
234234
putKeyStroke(ActionInfo.FOLD_EXPAND_ALL, KeyEvent.VK_MULTIPLY, defaultModifier);
235235
putKeyStroke(ActionInfo.GO_TO_MATCHING_BRACKET, KeyEvent.VK_OPEN_BRACKET, defaultModifier);
236236
putKeyStroke(ActionInfo.TOGGLE_COMMENT, KeyEvent.VK_SLASH, defaultModifier);
237+
putKeyStroke(ActionInfo.FORMAT_CODE, KeyEvent.VK_F, ctrl + shift);
237238
putKeyStroke(ActionInfo.AUTO_COMPLETE, KeyEvent.VK_SPACE, ctrl);
238239

239240
if (isOSX) {

‎client/src/com/mirth/connect/client/ui/components/rsta/actions/ActionInfo.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public enum ActionInfo {
4040
DISPLAY_WRAP_LINES ("mirth-wrap-lines"),
4141
GO_TO_MATCHING_BRACKET (RSyntaxTextAreaEditorKit.rstaGoToMatchingBracketAction),
4242
TOGGLE_COMMENT (RSyntaxTextAreaEditorKit.rstaToggleCommentAction),
43+
FORMAT_CODE ("mirth-format-code"),
4344
VIEW_USER_API ("mirth-view-user-api"),
4445
AUTO_COMPLETE ("AutoComplete"),
4546
DOCUMENT_START (DefaultEditorKit.beginAction),
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// SPDX-FileCopyrightText: 2025 Simon Braconnier <simonbraconnier@gmail.com>
3+
4+
packagecom.mirth.connect.client.ui.components.rsta.actions;
5+
6+
importjava.awt.event.ActionEvent;
7+
8+
importjavax.swing.UIManager;
9+
importjavax.swing.text.BadLocationException;
10+
importjavax.swing.text.Caret;
11+
importjavax.swing.text.Element;
12+
13+
importorg.fife.ui.rsyntaxtextarea.RSyntaxDocument;
14+
15+
importcom.mirth.connect.client.ui.components.rsta.MirthRSyntaxTextArea;
16+
importcom.mirth.connect.util.JavaScriptSharedUtil;
17+
18+
publicclassFormatCodeActionextendsMirthRecordableTextAction {
19+
20+
publicFormatCodeAction(MirthRSyntaxTextAreatextArea) {
21+
super(textArea, ActionInfo.FORMAT_CODE);
22+
}
23+
24+
@Override
25+
publicvoidactionPerformedImpl(ActionEventevt) {
26+
27+
if (!textArea.isEditable() || !textArea.isEnabled()) {
28+
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
29+
return;
30+
}
31+
32+
Caretc = textArea.getCaret();
33+
intstart = Math.min(c.getDot(), c.getMark());
34+
intend = Math.max(c.getDot(), c.getMark());
35+
if (start == end) {
36+
formatAll();
37+
} else {
38+
formatRange(start, end);
39+
}
40+
41+
}
42+
43+
@Override
44+
publicbooleanisEnabled() {
45+
returntextArea.isEditable() && textArea.isEnabled();
46+
}
47+
48+
privatevoidformatAll() {
49+
50+
Stringcode = textArea.getText();
51+
formatAndReplace(code, 0, code.length());
52+
}
53+
54+
privatevoidformatRange(intstart, intend) {
55+
56+
// We want to format all the lines of the selection (not only the selected text)
57+
58+
try {
59+
RSyntaxDocumentdoc = (RSyntaxDocument) textArea.getDocument();
60+
Elementmap = doc.getDefaultRootElement();
61+
62+
// Get the lines indexes from the selected text indexes.
63+
intstartLine = map.getElementIndex(start);
64+
intendLine = map.getElementIndex(end);
65+
66+
intreplaceRangeStart = 0;
67+
intreplaceRangeEnd = 0;
68+
69+
// Build a string from the selected lines.
70+
StringBuildersb = newStringBuilder();
71+
for (intline = startLine; line <= endLine; line++) {
72+
Elementelem = map.getElement(line);
73+
74+
intstartOffset = elem.getStartOffset();
75+
intendOffset = elem.getEndOffset() - 1;
76+
77+
// Save the start index of the first line for the final replacement.
78+
if (line == startLine) {
79+
replaceRangeStart = startOffset;
80+
}
81+
82+
// Save the end index of the last line for the final replacement.
83+
if (line == endLine) {
84+
replaceRangeEnd = endOffset;
85+
}
86+
87+
sb.append(doc.getText(startOffset, endOffset - startOffset + 1));
88+
}
89+
90+
// Format the code and replace the selected lines.
91+
formatAndReplace(sb.toString(), replaceRangeStart, replaceRangeEnd);
92+
93+
} catch (BadLocationExceptionble) {
94+
ble.printStackTrace();
95+
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
96+
}
97+
}
98+
99+
privatevoidformatAndReplace(Stringcode, intstart, intend) {
100+
101+
StringformattedCode = JavaScriptSharedUtil.prettyPrint(code);
102+
textArea.replaceRange(formattedCode, start, end);
103+
textArea.setCaretPosition(start);
104+
}
105+
}

‎server/src/com/mirth/connect/util/JavaScriptSharedUtil.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,12 @@ private static ScriptableObject getFormatterScope() {
198198
InputStreamis = null;
199199

200200
try {
201-
is = JavaScriptSharedUtil.class.getResourceAsStream("beautify-1.6.8.js");
201+
is = JavaScriptSharedUtil.class.getResourceAsStream("beautify-1.15.3.js");
202202
Stringscript = IOUtils.toString(is);
203203
ScriptableObjectscope = context.initStandardObjects();
204204
context.evaluateString(scope, "var global = {};", UUID.randomUUID().toString(), 1, null);
205205
context.evaluateString(scope, script, UUID.randomUUID().toString(), 1, null);
206-
context.evaluateString(scope, "var opts = { 'e4x': true };", UUID.randomUUID().toString(), 1, null);
206+
context.evaluateString(scope, "var opts = { 'e4x': true, 'indent_with_tabs': true };", UUID.randomUUID().toString(), 1, null);
207207
returnscope;
208208
} catch (Exceptione) {
209209
logger.error("Failed to load beautify library.");

0 commit comments

Comments
 (0)