mbeditor 0.4.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/app/assets/javascripts/mbeditor/application_iife_tail.js +1 -0
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +35 -16
- data/app/assets/javascripts/mbeditor/components/FileTree.js +23 -1
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +324 -48
- data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +2 -0
- data/app/assets/javascripts/mbeditor/editor_plugins.js +173 -0
- data/app/assets/javascripts/mbeditor/file_service.js +84 -1
- data/app/assets/javascripts/mbeditor/git_service.js +7 -3
- data/app/assets/javascripts/mbeditor/search_service.js +91 -2
- data/app/assets/javascripts/mbeditor/tab_manager.js +55 -2
- data/app/assets/stylesheets/mbeditor/editor.css +29 -0
- data/app/controllers/mbeditor/editors_controller.rb +295 -41
- data/app/services/mbeditor/ruby_definition_service.rb +163 -21
- data/app/services/mbeditor/unused_methods_service.rb +139 -0
- data/app/views/layouts/mbeditor/application.html.erb +86 -56
- data/config/routes.rb +4 -0
- data/lib/mbeditor/cable_log_filter.rb +6 -1
- data/lib/mbeditor/version.rb +1 -1
- metadata +3 -2
|
@@ -62,7 +62,8 @@ var DEFAULT_EDITOR_PREFS = {
|
|
|
62
62
|
fileTreeTypeahead: true,
|
|
63
63
|
quickOpenShowFolders: false,
|
|
64
64
|
tabDisplayMode: 'scroll',
|
|
65
|
-
persistFindState: true
|
|
65
|
+
persistFindState: true,
|
|
66
|
+
showDotFiles: false
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
var SidebarActionButton = function SidebarActionButton(_ref) {
|
|
@@ -218,6 +219,21 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
218
219
|
var searchWholeWordRef = useRef(false);
|
|
219
220
|
var searchResultsContainerRef = useRef(null);
|
|
220
221
|
|
|
222
|
+
var _useStateRM = useState(false);
|
|
223
|
+
var _useStateRM2 = _slicedToArray(_useStateRM, 2);
|
|
224
|
+
var replaceMode = _useStateRM2[0];
|
|
225
|
+
var setReplaceMode = _useStateRM2[1];
|
|
226
|
+
|
|
227
|
+
var _useStateRQ = useState('');
|
|
228
|
+
var _useStateRQ2 = _slicedToArray(_useStateRQ, 2);
|
|
229
|
+
var replaceQuery = _useStateRQ2[0];
|
|
230
|
+
var setReplaceQuery = _useStateRQ2[1];
|
|
231
|
+
|
|
232
|
+
var _useStateRL = useState(false);
|
|
233
|
+
var _useStateRL2 = _slicedToArray(_useStateRL, 2);
|
|
234
|
+
var replaceLoading = _useStateRL2[0];
|
|
235
|
+
var setReplaceLoading = _useStateRL2[1];
|
|
236
|
+
|
|
221
237
|
var _useState8 = useState("explorer");
|
|
222
238
|
|
|
223
239
|
var _useState82 = _slicedToArray(_useState8, 2);
|
|
@@ -432,10 +448,16 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
432
448
|
var contextMenu = _useState232[0];
|
|
433
449
|
var setContextMenu = _useState232[1];
|
|
434
450
|
|
|
451
|
+
var _useState24 = useState(140);
|
|
452
|
+
var _useState242 = _slicedToArray(_useState24, 2);
|
|
453
|
+
var openEditorsHeight = _useState242[0];
|
|
454
|
+
var setOpenEditorsHeight = _useState242[1];
|
|
455
|
+
|
|
435
456
|
var resizeSessionRef = useRef(null);
|
|
436
457
|
var resizeRafRef = useRef(null);
|
|
437
458
|
var prevGitBranchRef = useRef(null);
|
|
438
459
|
var isSwitchingBranchRef = useRef(false);
|
|
460
|
+
var stateRestoredRef = useRef(false);
|
|
439
461
|
|
|
440
462
|
// ── Draft backup helpers ─────────────────────────────────────────────────
|
|
441
463
|
var draftWriteTimerRef = useRef({});
|
|
@@ -467,10 +489,26 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
467
489
|
var draftRestoreOffer = _useState_dro2[0];
|
|
468
490
|
var setDraftRestoreOffer = _useState_dro2[1];
|
|
469
491
|
|
|
492
|
+
var _useStateMR = useState(false);
|
|
493
|
+
var _useStateMR2 = _slicedToArray(_useStateMR, 2);
|
|
494
|
+
var monacoReady = _useStateMR2[0];
|
|
495
|
+
var setMonacoReady = _useStateMR2[1];
|
|
496
|
+
|
|
497
|
+
var _useStateZen = useState(false);
|
|
498
|
+
var _useStateZen2 = _slicedToArray(_useStateZen, 2);
|
|
499
|
+
var zenMode = _useStateZen2[0];
|
|
500
|
+
var setZenMode = _useStateZen2[1];
|
|
501
|
+
|
|
470
502
|
var clamp = function clamp(value, min, max) {
|
|
471
503
|
return Math.min(max, Math.max(min, value));
|
|
472
504
|
};
|
|
473
505
|
|
|
506
|
+
var filterDotFiles = function filterDotFiles(nodes) {
|
|
507
|
+
return nodes.filter(function(n) { return n.name[0] !== '.'; }).map(function(n) {
|
|
508
|
+
return n.children ? Object.assign({}, n, { children: filterDotFiles(n.children) }) : n;
|
|
509
|
+
});
|
|
510
|
+
};
|
|
511
|
+
|
|
474
512
|
var normalizeRelativePath = function normalizeRelativePath(input) {
|
|
475
513
|
return (input || "").replace(/\\/g, "/").trim().replace(/^\/+/, "").replace(/\/+$/, "").replace(/\/+/g, "/");
|
|
476
514
|
};
|
|
@@ -654,6 +692,17 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
654
692
|
// Subscribe to EditorStore
|
|
655
693
|
var unsubscribe = EditorStore.subscribe(setState);
|
|
656
694
|
|
|
695
|
+
// Resolve monacoReady when the __monacoReady promise settles.
|
|
696
|
+
// This lets EditorPanel defer monaco.editor.create() until Monaco is loaded
|
|
697
|
+
// while the rest of the UI (file tree, tabs, sidebar) renders immediately.
|
|
698
|
+
var _mrMounted = true;
|
|
699
|
+
if (window.__monacoReady && typeof window.__monacoReady.then === 'function') {
|
|
700
|
+
window.__monacoReady.then(function() { if (_mrMounted) setMonacoReady(true); });
|
|
701
|
+
} else {
|
|
702
|
+
// Fallback: Monaco was already loaded synchronously (e.g. tests / old path).
|
|
703
|
+
setMonacoReady(true);
|
|
704
|
+
}
|
|
705
|
+
|
|
657
706
|
// Initial load
|
|
658
707
|
Promise.all([FileService.getWorkspace()["catch"](function () {
|
|
659
708
|
return null;
|
|
@@ -765,6 +814,10 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
765
814
|
if (typeof savedState.gitPanelWidth === 'number') {
|
|
766
815
|
setGitPanelWidth(savedState.gitPanelWidth);
|
|
767
816
|
}
|
|
817
|
+
if (typeof savedState.openEditorsHeight === 'number') {
|
|
818
|
+
setOpenEditorsHeight(savedState.openEditorsHeight);
|
|
819
|
+
}
|
|
820
|
+
stateRestoredRef.current = true;
|
|
768
821
|
|
|
769
822
|
// Load pane state for current branch; fall back to legacy global state panes
|
|
770
823
|
var branchStatePromise = branch
|
|
@@ -873,6 +926,7 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
873
926
|
e.preventDefault();
|
|
874
927
|
toggleGitPanel();
|
|
875
928
|
}
|
|
929
|
+
// Ctrl+Shift+Z is handled in capture phase below so Monaco cannot swallow it.
|
|
876
930
|
if (e.key === 'Escape') {
|
|
877
931
|
setContextMenu(null);
|
|
878
932
|
setShowHelp(false);
|
|
@@ -886,6 +940,7 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
886
940
|
// Throttle via rAF — skip if a frame is already queued to avoid paint thrashing
|
|
887
941
|
if (resizeRafRef.current) return;
|
|
888
942
|
var clientX = e.clientX;
|
|
943
|
+
var clientY = e.clientY;
|
|
889
944
|
resizeRafRef.current = requestAnimationFrame(function () {
|
|
890
945
|
resizeRafRef.current = null;
|
|
891
946
|
var s = resizeSessionRef.current;
|
|
@@ -919,6 +974,12 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
919
974
|
var nextWidth = rect.right - clientX;
|
|
920
975
|
setGitPanelWidth(clamp(nextWidth, GIT_PANEL_MIN_WIDTH, 600));
|
|
921
976
|
}
|
|
977
|
+
|
|
978
|
+
if (s.mode === 'openeditors') {
|
|
979
|
+
var delta = clientY - s.startY;
|
|
980
|
+
var nextHeight = Math.max(60, Math.min(400, s.startHeight + delta));
|
|
981
|
+
setOpenEditorsHeight(nextHeight);
|
|
982
|
+
}
|
|
922
983
|
});
|
|
923
984
|
};
|
|
924
985
|
|
|
@@ -935,10 +996,22 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
935
996
|
document.body.style.userSelect = '';
|
|
936
997
|
};
|
|
937
998
|
|
|
999
|
+
// Capture-phase listener for Ctrl+Shift+Z so it fires before Monaco's
|
|
1000
|
+
// own keybinding handler (which intercepts in the bubble phase).
|
|
1001
|
+
var onZenCapture = function(e) {
|
|
1002
|
+
if (e.ctrlKey && !e.metaKey && e.shiftKey && e.key.toLowerCase() === 'z') {
|
|
1003
|
+
e.preventDefault();
|
|
1004
|
+
e.stopPropagation();
|
|
1005
|
+
toggleZenMode();
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
|
|
938
1009
|
window.addEventListener('keydown', onKeyDown);
|
|
1010
|
+
document.addEventListener('keydown', onZenCapture, true);
|
|
939
1011
|
window.addEventListener('mousemove', handleMouseMove);
|
|
940
1012
|
window.addEventListener('mouseup', handleMouseUp);
|
|
941
1013
|
return function () {
|
|
1014
|
+
_mrMounted = false;
|
|
942
1015
|
unsubscribe();
|
|
943
1016
|
unsubBranch();
|
|
944
1017
|
if (resizeRafRef.current) {
|
|
@@ -946,6 +1019,7 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
946
1019
|
resizeRafRef.current = null;
|
|
947
1020
|
}
|
|
948
1021
|
window.removeEventListener('keydown', onKeyDown);
|
|
1022
|
+
document.removeEventListener('keydown', onZenCapture, true);
|
|
949
1023
|
window.removeEventListener('mousemove', handleMouseMove);
|
|
950
1024
|
window.removeEventListener('mouseup', handleMouseUp);
|
|
951
1025
|
document.body.style.cursor = '';
|
|
@@ -1117,7 +1191,13 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1117
1191
|
EditorStore.setStatus("Saving " + tab.name + "...", "info");
|
|
1118
1192
|
FileService.saveFile(tab.path, tab.content).then(function () {
|
|
1119
1193
|
EditorStore.setStatus("Saved", "success");
|
|
1194
|
+
SearchService.invalidate();
|
|
1120
1195
|
GitService.fetchStatus();
|
|
1196
|
+
// Reset the AVI clean baseline so undo past this save point shows dirty correctly.
|
|
1197
|
+
var _closeEntry = window.__mbeditorModels && window.__mbeditorModels[tab.path];
|
|
1198
|
+
if (_closeEntry && _closeEntry.model && !_closeEntry.model.isDisposed()) {
|
|
1199
|
+
_closeEntry.cleanVersionId = _closeEntry.model.getAlternativeVersionId();
|
|
1200
|
+
}
|
|
1121
1201
|
TabManager.closeTab(closingPaneId, tab.id);
|
|
1122
1202
|
})["catch"](function (err) {
|
|
1123
1203
|
EditorStore.setStatus("Save failed: " + err.message, "error");
|
|
@@ -1174,6 +1254,8 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1174
1254
|
|
|
1175
1255
|
// Persist state when panes, focusedPaneId, or collapsedSections changes
|
|
1176
1256
|
useEffect(function () {
|
|
1257
|
+
// Don't overwrite server state with React defaults before the initial load completes.
|
|
1258
|
+
if (!stateRestoredRef.current) return;
|
|
1177
1259
|
// debounce explicitly using setTimeout to avoid spamming the backend
|
|
1178
1260
|
var timeoutId = setTimeout(function () {
|
|
1179
1261
|
var st = EditorStore.getState();
|
|
@@ -1205,12 +1287,12 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1205
1287
|
FileService.saveBranchState(currentBranch, { panes: lightweightPanes, focusedPaneId: st.focusedPaneId })["catch"](function () {});
|
|
1206
1288
|
}
|
|
1207
1289
|
// Also persist to global state (prefs + panes as legacy fallback)
|
|
1208
|
-
FileService.saveState({ panes: lightweightPanes, focusedPaneId: st.focusedPaneId, collapsedSections: collapsedSections, expandedDirs: expandedDirs, showGitPanel: showGitPanel, gitPanelWidth: gitPanelWidth, editorPrefs: editorPrefs, activeSidebarTab: activeSidebarTab, sidebarCollapsed: sidebarCollapsed });
|
|
1290
|
+
FileService.saveState({ panes: lightweightPanes, focusedPaneId: st.focusedPaneId, collapsedSections: collapsedSections, expandedDirs: expandedDirs, showGitPanel: showGitPanel, gitPanelWidth: gitPanelWidth, editorPrefs: editorPrefs, activeSidebarTab: activeSidebarTab, sidebarCollapsed: sidebarCollapsed, openEditorsHeight: openEditorsHeight });
|
|
1209
1291
|
}, 1000);
|
|
1210
1292
|
return function () {
|
|
1211
1293
|
return clearTimeout(timeoutId);
|
|
1212
1294
|
};
|
|
1213
|
-
}, [state.panes, state.focusedPaneId, collapsedSections, expandedDirs, showGitPanel, gitPanelWidth, editorPrefs, activeSidebarTab, sidebarCollapsed]);
|
|
1295
|
+
}, [state.panes, state.focusedPaneId, collapsedSections, expandedDirs, showGitPanel, gitPanelWidth, editorPrefs, activeSidebarTab, sidebarCollapsed, openEditorsHeight]);
|
|
1214
1296
|
|
|
1215
1297
|
useEffect(function() {
|
|
1216
1298
|
document.documentElement.setAttribute('data-theme', editorPrefs.theme || 'vs-dark');
|
|
@@ -1366,14 +1448,20 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1366
1448
|
return p;
|
|
1367
1449
|
});
|
|
1368
1450
|
EditorStore.setState({ panes: newPanes });
|
|
1451
|
+
// Reset the AVI clean baseline so undo past this save point shows dirty correctly.
|
|
1452
|
+
var _modelEntry = window.__mbeditorModels && window.__mbeditorModels[tab.path];
|
|
1453
|
+
if (_modelEntry && _modelEntry.model && !_modelEntry.model.isDisposed()) {
|
|
1454
|
+
_modelEntry.cleanVersionId = _modelEntry.model.getAlternativeVersionId();
|
|
1455
|
+
}
|
|
1369
1456
|
EditorStore.setStatus("Saved", "success");
|
|
1370
1457
|
_clearDraft(tab.path);
|
|
1371
|
-
|
|
1458
|
+
SearchService.invalidate();
|
|
1459
|
+
|
|
1372
1460
|
// Hot reload for Markdown: sync preview tab after save
|
|
1373
1461
|
if (/\.(md|markdown)$/i.test(tab.path)) {
|
|
1374
1462
|
TabManager.syncMarkdownPreview(tab.path, tab.content);
|
|
1375
1463
|
}
|
|
1376
|
-
|
|
1464
|
+
|
|
1377
1465
|
GitService.fetchStatus();
|
|
1378
1466
|
})["catch"](function (err) {
|
|
1379
1467
|
EditorStore.setStatus("Save failed: " + err.message, "error");
|
|
@@ -1407,7 +1495,15 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1407
1495
|
});
|
|
1408
1496
|
});
|
|
1409
1497
|
EditorStore.setState({ panes: newPanes });
|
|
1498
|
+
// Reset AVI clean baselines for all saved files so undo past save shows dirty correctly.
|
|
1499
|
+
dirtyTabs.forEach(function(tab) {
|
|
1500
|
+
var _me = window.__mbeditorModels && window.__mbeditorModels[tab.path];
|
|
1501
|
+
if (_me && _me.model && !_me.model.isDisposed()) {
|
|
1502
|
+
_me.cleanVersionId = _me.model.getAlternativeVersionId();
|
|
1503
|
+
}
|
|
1504
|
+
});
|
|
1410
1505
|
EditorStore.setStatus("All files saved", "success");
|
|
1506
|
+
SearchService.invalidate();
|
|
1411
1507
|
GitService.fetchStatus();
|
|
1412
1508
|
})["catch"](function (err) {
|
|
1413
1509
|
EditorStore.setStatus("Failed to save some files", "error");
|
|
@@ -1511,38 +1607,51 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1511
1607
|
};
|
|
1512
1608
|
var parserName = formatMap[ext];
|
|
1513
1609
|
|
|
1514
|
-
if (parserName
|
|
1610
|
+
if (parserName) {
|
|
1515
1611
|
setLoading(function (prev) {
|
|
1516
1612
|
return _extends({}, prev, { format: true });
|
|
1517
1613
|
});
|
|
1518
1614
|
EditorStore.setStatus("Formatting with Prettier...", "info");
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1615
|
+
var doFormat = function doFormat() {
|
|
1616
|
+
return window.prettier.format(activeTab.content, {
|
|
1617
|
+
parser: parserName,
|
|
1618
|
+
plugins: Object.values(window.prettierPlugins),
|
|
1619
|
+
printWidth: editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80,
|
|
1620
|
+
tabWidth: editorPrefs.prettierTabWidth != null ? editorPrefs.prettierTabWidth : 2,
|
|
1621
|
+
useTabs: !!editorPrefs.prettierUseTabs,
|
|
1622
|
+
semi: editorPrefs.prettierSemi !== false,
|
|
1623
|
+
singleQuote: !!editorPrefs.prettierSingleQuote,
|
|
1624
|
+
trailingComma: editorPrefs.prettierTrailingComma || 'all',
|
|
1625
|
+
bracketSpacing: editorPrefs.prettierBracketSpacing !== false
|
|
1626
|
+
}).then(function (formatted) {
|
|
1627
|
+
var newPanes = EditorStore.getState().panes.map(function (p) {
|
|
1628
|
+
if (p.id === focusedPane.id) return _extends({}, p, { tabs: p.tabs.map(function (t) {
|
|
1629
|
+
return t.id === activeTab.id ? _extends({}, t, { content: formatted, dirty: true, externalContentVersion: (t.externalContentVersion || 0) + 1 }) : t;
|
|
1630
|
+
}) });
|
|
1631
|
+
return p;
|
|
1632
|
+
});
|
|
1633
|
+
EditorStore.setState({ panes: newPanes });
|
|
1634
|
+
EditorStore.setStatus("Formatted (Unsaved)", "success");
|
|
1635
|
+
GitService.fetchStatus();
|
|
1636
|
+
})["catch"](function (err) {
|
|
1637
|
+
EditorStore.setStatus("Prettier Formatter failed: " + err.message, "error");
|
|
1638
|
+
})["finally"](function () {
|
|
1639
|
+
setLoading(function (prev) {
|
|
1640
|
+
return _extends({}, prev, { format: false });
|
|
1641
|
+
});
|
|
1535
1642
|
});
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
return _extends({}, prev, { format: false });
|
|
1643
|
+
};
|
|
1644
|
+
if (window.prettier && window.prettierPlugins) {
|
|
1645
|
+
doFormat();
|
|
1646
|
+
} else if (window.loadPrettierPlugins) {
|
|
1647
|
+
window.loadPrettierPlugins().then(doFormat)["catch"](function (err) {
|
|
1648
|
+
EditorStore.setStatus("Failed to load Prettier: " + err.message, "error");
|
|
1649
|
+
setLoading(function (prev) { return _extends({}, prev, { format: false }); });
|
|
1544
1650
|
});
|
|
1545
|
-
}
|
|
1651
|
+
} else {
|
|
1652
|
+
EditorStore.setStatus("Prettier is not available.", "warning");
|
|
1653
|
+
setLoading(function (prev) { return _extends({}, prev, { format: false }); });
|
|
1654
|
+
}
|
|
1546
1655
|
return;
|
|
1547
1656
|
}
|
|
1548
1657
|
|
|
@@ -1741,6 +1850,59 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1741
1850
|
_debouncedSearch(searchQuery);
|
|
1742
1851
|
};
|
|
1743
1852
|
|
|
1853
|
+
var handleReplaceAll = function handleReplaceAll() {
|
|
1854
|
+
if (!searchQuery.trim()) {
|
|
1855
|
+
EditorStore.setStatus("Enter a search query first", "error");
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
var matchCount = (state.searchResults || []).length;
|
|
1859
|
+
var confirmMsg = "Replace all occurrences of \"" + searchQuery + "\" with \"" + replaceQuery + "\"?";
|
|
1860
|
+
if (matchCount > 0) {
|
|
1861
|
+
confirmMsg += " (" + matchCount + (searchHasMore ? "+" : "") + " match" + (matchCount !== 1 ? "es" : "") + " across files)";
|
|
1862
|
+
}
|
|
1863
|
+
if (!window.confirm(confirmMsg)) return;
|
|
1864
|
+
|
|
1865
|
+
setReplaceLoading(true);
|
|
1866
|
+
EditorStore.setStatus("Replacing…", "info");
|
|
1867
|
+
SearchService.replaceInFiles(searchQuery, replaceQuery, {
|
|
1868
|
+
regex: searchUseRegexRef.current,
|
|
1869
|
+
matchCase: searchMatchCaseRef.current,
|
|
1870
|
+
wholeWord: searchWholeWordRef.current
|
|
1871
|
+
}).then(function(data) {
|
|
1872
|
+
var count = data.replaced_count || 0;
|
|
1873
|
+
var files = data.files_affected || [];
|
|
1874
|
+
var errors = data.errors || [];
|
|
1875
|
+
var msg = "Replaced " + count + " occurrence" + (count !== 1 ? "s" : "") + " in " + files.length + " file" + (files.length !== 1 ? "s" : "");
|
|
1876
|
+
if (errors.length) msg += " (" + errors.length + " error" + (errors.length !== 1 ? "s" : "") + ")";
|
|
1877
|
+
EditorStore.setStatus(msg, errors.length ? "warning" : "success");
|
|
1878
|
+
|
|
1879
|
+
// Invalidate search cache so next search reflects the new content.
|
|
1880
|
+
SearchService.invalidate();
|
|
1881
|
+
|
|
1882
|
+
// Update any open Monaco models whose content changed.
|
|
1883
|
+
if (files.length && window.__mbeditorModels) {
|
|
1884
|
+
files.forEach(function(relPath) {
|
|
1885
|
+
var model = window.__mbeditorModels[relPath];
|
|
1886
|
+
if (!model) return;
|
|
1887
|
+
FileService.getFile(relPath).then(function(res) {
|
|
1888
|
+
if (res && res.content != null && !model.isDisposed()) {
|
|
1889
|
+
model.setValue(res.content);
|
|
1890
|
+
}
|
|
1891
|
+
}).catch(function() {});
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
// Refresh search results to reflect replacements.
|
|
1896
|
+
if (searchQueryRef.current) {
|
|
1897
|
+
_debouncedSearch(searchQueryRef.current);
|
|
1898
|
+
}
|
|
1899
|
+
}).catch(function(err) {
|
|
1900
|
+
EditorStore.setStatus("Replace failed: " + (err.message || String(err)), "error");
|
|
1901
|
+
}).finally(function() {
|
|
1902
|
+
setReplaceLoading(false);
|
|
1903
|
+
});
|
|
1904
|
+
};
|
|
1905
|
+
|
|
1744
1906
|
// Load more results when the user scrolls near the bottom of the list.
|
|
1745
1907
|
var handleSearchResultsScroll = function handleSearchResultsScroll(e) {
|
|
1746
1908
|
var el = e.currentTarget;
|
|
@@ -1756,6 +1918,22 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1756
1918
|
});
|
|
1757
1919
|
};
|
|
1758
1920
|
|
|
1921
|
+
var toggleZenMode = function toggleZenMode() {
|
|
1922
|
+
setZenMode(function (prev) {
|
|
1923
|
+
var next = !prev;
|
|
1924
|
+
// After React re-renders the new layout, call layout() on all visible Monaco editors
|
|
1925
|
+
// so they fill the reclaimed space correctly.
|
|
1926
|
+
setTimeout(function () {
|
|
1927
|
+
if (window.monaco && window.monaco.editor) {
|
|
1928
|
+
window.monaco.editor.getEditors().forEach(function(ed) {
|
|
1929
|
+
if (typeof ed.layout === 'function') ed.layout();
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
}, 50);
|
|
1933
|
+
return next;
|
|
1934
|
+
});
|
|
1935
|
+
};
|
|
1936
|
+
|
|
1759
1937
|
var startGitPanelResize = function startGitPanelResize(e) {
|
|
1760
1938
|
e.preventDefault();
|
|
1761
1939
|
resizeSessionRef.current = { mode: 'gitpanel' };
|
|
@@ -1874,6 +2052,15 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1874
2052
|
document.body.style.userSelect = 'none';
|
|
1875
2053
|
};
|
|
1876
2054
|
|
|
2055
|
+
var startOpenEditorsResize = function startOpenEditorsResize(e) {
|
|
2056
|
+
if (collapsedSections.openEditors) return;
|
|
2057
|
+
e.preventDefault();
|
|
2058
|
+
resizeSessionRef.current = { mode: 'openeditors', startY: e.clientY, startHeight: openEditorsHeight };
|
|
2059
|
+
setActiveResizeMode('openeditors');
|
|
2060
|
+
document.body.style.cursor = 'row-resize';
|
|
2061
|
+
document.body.style.userSelect = 'none';
|
|
2062
|
+
};
|
|
2063
|
+
|
|
1877
2064
|
var toggleSidebarCollapsed = function toggleSidebarCollapsed() {
|
|
1878
2065
|
setSidebarCollapsed(function (prev) { return !prev; });
|
|
1879
2066
|
};
|
|
@@ -2404,7 +2591,7 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2404
2591
|
{ className: "ide-body", id: "ide-body-container" },
|
|
2405
2592
|
React.createElement(
|
|
2406
2593
|
"div",
|
|
2407
|
-
{ className: "ide-sidebar" + (sidebarCollapsed ? " ide-sidebar-collapsed" : ""), style: { width: (sidebarCollapsed ? SIDEBAR_COLLAPSED_WIDTH : sidebarWidth) + "px" } },
|
|
2594
|
+
{ className: "ide-sidebar" + (sidebarCollapsed ? " ide-sidebar-collapsed" : ""), style: { width: (sidebarCollapsed ? SIDEBAR_COLLAPSED_WIDTH : sidebarWidth) + "px", display: zenMode ? 'none' : undefined } },
|
|
2408
2595
|
sidebarCollapsed
|
|
2409
2596
|
? React.createElement(
|
|
2410
2597
|
"div",
|
|
@@ -2474,7 +2661,7 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2474
2661
|
{ className: "ide-sidebar-content" },
|
|
2475
2662
|
React.createElement(
|
|
2476
2663
|
"div",
|
|
2477
|
-
{ className: "ide-sidebar-fixed" },
|
|
2664
|
+
{ className: "ide-sidebar-fixed", style: { '--open-editors-height': openEditorsHeight + 'px' } },
|
|
2478
2665
|
state.panes.flatMap(function (p) {
|
|
2479
2666
|
return p.tabs;
|
|
2480
2667
|
}).length > 0 && React.createElement(
|
|
@@ -2584,6 +2771,10 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2584
2771
|
)
|
|
2585
2772
|
)
|
|
2586
2773
|
),
|
|
2774
|
+
state.panes.flatMap(function (p) { return p.tabs; }).length > 0 && React.createElement(
|
|
2775
|
+
"div",
|
|
2776
|
+
{ className: "open-editors-resize-handle", onMouseDown: startOpenEditorsResize }
|
|
2777
|
+
),
|
|
2587
2778
|
React.createElement(
|
|
2588
2779
|
"div",
|
|
2589
2780
|
{ className: "ide-sidebar-scrollable" },
|
|
@@ -2643,7 +2834,7 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2643
2834
|
)
|
|
2644
2835
|
},
|
|
2645
2836
|
React.createElement(FileTree, {
|
|
2646
|
-
items: treeData,
|
|
2837
|
+
items: editorPrefs.showDotFiles ? treeData : filterDotFiles(treeData || []),
|
|
2647
2838
|
onSelect: handleSoftOpenFile,
|
|
2648
2839
|
activePath: editorPrefs.autoRevealInExplorer !== false ? (activeTab && activeTab.path) : null,
|
|
2649
2840
|
selectedPaths: selectedPaths,
|
|
@@ -2712,6 +2903,16 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2712
2903
|
},
|
|
2713
2904
|
React.createElement("i", { className: "codicon codicon-regex" })
|
|
2714
2905
|
),
|
|
2906
|
+
React.createElement(
|
|
2907
|
+
"button",
|
|
2908
|
+
{
|
|
2909
|
+
type: "button",
|
|
2910
|
+
className: "search-adornment-btn" + (replaceMode ? " active" : ""),
|
|
2911
|
+
onClick: function() { setReplaceMode(function(p) { return !p; }); },
|
|
2912
|
+
title: "Toggle Replace"
|
|
2913
|
+
},
|
|
2914
|
+
React.createElement("i", { className: "codicon codicon-replace" })
|
|
2915
|
+
),
|
|
2715
2916
|
searchQuery && React.createElement(
|
|
2716
2917
|
"button",
|
|
2717
2918
|
{
|
|
@@ -2725,6 +2926,33 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2725
2926
|
)
|
|
2726
2927
|
)
|
|
2727
2928
|
),
|
|
2929
|
+
replaceMode && React.createElement(
|
|
2930
|
+
"div",
|
|
2931
|
+
{ className: "search-replace-row" },
|
|
2932
|
+
React.createElement("input", {
|
|
2933
|
+
className: "search-input search-replace-input",
|
|
2934
|
+
placeholder: "Replace with…",
|
|
2935
|
+
value: replaceQuery,
|
|
2936
|
+
onChange: function(e) { setReplaceQuery(e.target.value); },
|
|
2937
|
+
disabled: replaceLoading
|
|
2938
|
+
}),
|
|
2939
|
+
React.createElement(
|
|
2940
|
+
"button",
|
|
2941
|
+
{
|
|
2942
|
+
type: "button",
|
|
2943
|
+
className: "search-replace-all-btn",
|
|
2944
|
+
onClick: handleReplaceAll,
|
|
2945
|
+
disabled: !searchQuery.trim() || replaceLoading,
|
|
2946
|
+
title: "Replace All"
|
|
2947
|
+
},
|
|
2948
|
+
replaceLoading
|
|
2949
|
+
? React.createElement("i", { className: "fas fa-spinner fa-spin" })
|
|
2950
|
+
: React.createElement(React.Fragment, null,
|
|
2951
|
+
React.createElement("i", { className: "codicon codicon-replace-all" }),
|
|
2952
|
+
" Replace All"
|
|
2953
|
+
)
|
|
2954
|
+
)
|
|
2955
|
+
),
|
|
2728
2956
|
(function() {
|
|
2729
2957
|
var allResults = state.searchResults || [];
|
|
2730
2958
|
var loadedCount = allResults.length;
|
|
@@ -2923,12 +3151,17 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2923
3151
|
'label', { className: 'ide-settings-row ide-settings-row-half' },
|
|
2924
3152
|
React.createElement('span', { className: 'ide-settings-label' }, 'Font size'),
|
|
2925
3153
|
React.createElement('input', {
|
|
3154
|
+
key: String(editorPrefs.fontSize || 13),
|
|
2926
3155
|
type: 'number', min: '8', max: '32', step: '1',
|
|
2927
3156
|
className: 'ide-settings-input',
|
|
2928
|
-
|
|
3157
|
+
defaultValue: editorPrefs.fontSize || 13,
|
|
2929
3158
|
onChange: function(e) {
|
|
2930
3159
|
var v = parseInt(e.target.value, 10);
|
|
2931
3160
|
if (v >= 8 && v <= 32) setEditorPrefs(function(p) { return Object.assign({}, p, { fontSize: v }); });
|
|
3161
|
+
},
|
|
3162
|
+
onBlur: function(e) {
|
|
3163
|
+
var v = parseInt(e.target.value, 10);
|
|
3164
|
+
if (isNaN(v) || v < 8 || v > 32) e.target.value = String(editorPrefs.fontSize || 13);
|
|
2932
3165
|
}
|
|
2933
3166
|
})
|
|
2934
3167
|
),
|
|
@@ -2946,12 +3179,17 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2946
3179
|
'label', { className: 'ide-settings-row ide-settings-row-half', title: 'Row height in pixels. 0 = auto (roughly font size × 1.5)' },
|
|
2947
3180
|
React.createElement('span', { className: 'ide-settings-label' }, 'Line height (0=auto)'),
|
|
2948
3181
|
React.createElement('input', {
|
|
3182
|
+
key: String(editorPrefs.lineHeight != null ? editorPrefs.lineHeight : 0),
|
|
2949
3183
|
type: 'number', min: '0', max: '100', step: '1',
|
|
2950
3184
|
className: 'ide-settings-input',
|
|
2951
|
-
|
|
3185
|
+
defaultValue: editorPrefs.lineHeight != null ? editorPrefs.lineHeight : 0,
|
|
2952
3186
|
onChange: function(e) {
|
|
2953
3187
|
var v = parseInt(e.target.value, 10);
|
|
2954
3188
|
if (!isNaN(v) && v >= 0 && v <= 100) setEditorPrefs(function(p) { return Object.assign({}, p, { lineHeight: v }); });
|
|
3189
|
+
},
|
|
3190
|
+
onBlur: function(e) {
|
|
3191
|
+
var v = parseInt(e.target.value, 10);
|
|
3192
|
+
if (isNaN(v) || v < 0 || v > 100) e.target.value = String(editorPrefs.lineHeight != null ? editorPrefs.lineHeight : 0);
|
|
2955
3193
|
}
|
|
2956
3194
|
})
|
|
2957
3195
|
),
|
|
@@ -2959,12 +3197,17 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2959
3197
|
'label', { className: 'ide-settings-row ide-settings-row-half', title: 'Extra space between characters in pixels. 0 = default' },
|
|
2960
3198
|
React.createElement('span', { className: 'ide-settings-label' }, 'Letter spacing (px)'),
|
|
2961
3199
|
React.createElement('input', {
|
|
3200
|
+
key: String(editorPrefs.letterSpacing != null ? editorPrefs.letterSpacing : 0),
|
|
2962
3201
|
type: 'number', min: '-5', max: '20', step: '0.5',
|
|
2963
3202
|
className: 'ide-settings-input',
|
|
2964
|
-
|
|
3203
|
+
defaultValue: editorPrefs.letterSpacing != null ? editorPrefs.letterSpacing : 0,
|
|
2965
3204
|
onChange: function(e) {
|
|
2966
3205
|
var v = parseFloat(e.target.value);
|
|
2967
3206
|
if (!isNaN(v) && v >= -5 && v <= 20) setEditorPrefs(function(p) { return Object.assign({}, p, { letterSpacing: v }); });
|
|
3207
|
+
},
|
|
3208
|
+
onBlur: function(e) {
|
|
3209
|
+
var v = parseFloat(e.target.value);
|
|
3210
|
+
if (isNaN(v) || v < -5 || v > 20) e.target.value = String(editorPrefs.letterSpacing != null ? editorPrefs.letterSpacing : 0);
|
|
2968
3211
|
}
|
|
2969
3212
|
})
|
|
2970
3213
|
),
|
|
@@ -2975,12 +3218,17 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2975
3218
|
'label', { className: 'ide-settings-row ide-settings-row-half' },
|
|
2976
3219
|
React.createElement('span', { className: 'ide-settings-label' }, 'Tab size'),
|
|
2977
3220
|
React.createElement('input', {
|
|
3221
|
+
key: String(editorPrefs.tabSize || 4),
|
|
2978
3222
|
type: 'number', min: '1', max: '8', step: '1',
|
|
2979
3223
|
className: 'ide-settings-input',
|
|
2980
|
-
|
|
3224
|
+
defaultValue: editorPrefs.tabSize || 4,
|
|
2981
3225
|
onChange: function(e) {
|
|
2982
3226
|
var v = parseInt(e.target.value, 10);
|
|
2983
3227
|
if (v >= 1 && v <= 8) setEditorPrefs(function(p) { return Object.assign({}, p, { tabSize: v }); });
|
|
3228
|
+
},
|
|
3229
|
+
onBlur: function(e) {
|
|
3230
|
+
var v = parseInt(e.target.value, 10);
|
|
3231
|
+
if (isNaN(v) || v < 1 || v > 8) e.target.value = String(editorPrefs.tabSize || 4);
|
|
2984
3232
|
}
|
|
2985
3233
|
})
|
|
2986
3234
|
),
|
|
@@ -3262,12 +3510,17 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
3262
3510
|
'label', { className: 'ide-settings-row ide-settings-row-half' },
|
|
3263
3511
|
React.createElement('span', { className: 'ide-settings-label' }, 'Print width'),
|
|
3264
3512
|
React.createElement('input', {
|
|
3513
|
+
key: String(editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80),
|
|
3265
3514
|
type: 'number', min: '40', max: '200', step: '1',
|
|
3266
3515
|
className: 'ide-settings-input',
|
|
3267
|
-
|
|
3516
|
+
defaultValue: editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80,
|
|
3268
3517
|
onChange: function(e) {
|
|
3269
3518
|
var v = parseInt(e.target.value, 10);
|
|
3270
3519
|
if (v >= 40 && v <= 200) setEditorPrefs(function(p) { return Object.assign({}, p, { prettierPrintWidth: v }); });
|
|
3520
|
+
},
|
|
3521
|
+
onBlur: function(e) {
|
|
3522
|
+
var v = parseInt(e.target.value, 10);
|
|
3523
|
+
if (isNaN(v) || v < 40 || v > 200) e.target.value = String(editorPrefs.prettierPrintWidth != null ? editorPrefs.prettierPrintWidth : 80);
|
|
3271
3524
|
}
|
|
3272
3525
|
})
|
|
3273
3526
|
),
|
|
@@ -3337,6 +3590,16 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
3337
3590
|
onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { fileTreeTypeahead: v }); }); }
|
|
3338
3591
|
})
|
|
3339
3592
|
),
|
|
3593
|
+
React.createElement(
|
|
3594
|
+
'label', { className: 'ide-settings-row ide-settings-row-check' },
|
|
3595
|
+
React.createElement('span', { className: 'ide-settings-label' }, 'Show dotfiles'),
|
|
3596
|
+
React.createElement('input', {
|
|
3597
|
+
type: 'checkbox',
|
|
3598
|
+
className: 'ide-settings-checkbox',
|
|
3599
|
+
checked: !!(editorPrefs.showDotFiles),
|
|
3600
|
+
onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { showDotFiles: v }); }); }
|
|
3601
|
+
})
|
|
3602
|
+
),
|
|
3340
3603
|
React.createElement(
|
|
3341
3604
|
'label', { className: 'ide-settings-row ide-settings-row-half' },
|
|
3342
3605
|
React.createElement('span', { className: 'ide-settings-label' }, 'Tab bar layout'),
|
|
@@ -3445,22 +3708,21 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
3445
3708
|
testLoading: testLoading,
|
|
3446
3709
|
testInlineVisible: testInlineVisible,
|
|
3447
3710
|
editorPrefs: editorPrefs,
|
|
3711
|
+
monacoReady: monacoReady,
|
|
3448
3712
|
onFormat: function() { onFormatRef.current(); },
|
|
3449
3713
|
onSave: function() { handleSave(pane.id, pActiveTab); },
|
|
3450
3714
|
onRunTest: handleRunTest,
|
|
3451
3715
|
onShowHistory: function(path) { setHistoryPanelPath(path); },
|
|
3452
3716
|
onContentChange: function onContentChange(val) {
|
|
3717
|
+
// Dirty/clean state is now set in EditorPanel via AVI comparison.
|
|
3718
|
+
// onContentChange only needs to handle draft persistence.
|
|
3453
3719
|
var st = EditorStore.getState();
|
|
3454
3720
|
var cp = st.panes.find(function(p) { return p.id === pane.id; });
|
|
3455
3721
|
var ct = cp && cp.tabs.find(function(t) { return t.id === pActiveTab.id; });
|
|
3456
|
-
|
|
3457
|
-
var valNorm = val.replace(/\r\n/g, '\n');
|
|
3458
|
-
if (valNorm === cleanNorm) {
|
|
3459
|
-
TabManager.markClean(pane.id, pActiveTab.id, val);
|
|
3460
|
-
_clearDraft(pActiveTab.path);
|
|
3461
|
-
} else {
|
|
3462
|
-
TabManager.markDirty(pane.id, pActiveTab.id, val);
|
|
3722
|
+
if (ct && ct.dirty) {
|
|
3463
3723
|
_scheduleDraftWrite(pActiveTab.path, val);
|
|
3724
|
+
} else {
|
|
3725
|
+
_clearDraft(pActiveTab.path);
|
|
3464
3726
|
}
|
|
3465
3727
|
}
|
|
3466
3728
|
});
|
|
@@ -3576,6 +3838,10 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
3576
3838
|
React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Shift+G")),
|
|
3577
3839
|
React.createElement("td", null, "Toggle git panel")
|
|
3578
3840
|
),
|
|
3841
|
+
React.createElement("tr", null,
|
|
3842
|
+
React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Shift+Z")),
|
|
3843
|
+
React.createElement("td", null, "Toggle zen / focus mode")
|
|
3844
|
+
),
|
|
3579
3845
|
React.createElement("tr", null,
|
|
3580
3846
|
React.createElement("td", null, React.createElement("kbd", null, "Ctrl+Z\u00a0/\u00a0Ctrl+Y")),
|
|
3581
3847
|
React.createElement("td", null, "Undo / Redo")
|
|
@@ -3617,14 +3883,14 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
3617
3883
|
),
|
|
3618
3884
|
|
|
3619
3885
|
// Right-side Git panel (children of ide-body, alongside sidebar and ide-main)
|
|
3620
|
-
showGitPanel && React.createElement("div", {
|
|
3886
|
+
showGitPanel && !zenMode && React.createElement("div", {
|
|
3621
3887
|
className: "panel-divider gitpanel-divider " + (activeResizeMode === 'gitpanel' ? 'active' : ''),
|
|
3622
3888
|
onMouseDown: startGitPanelResize,
|
|
3623
3889
|
role: "separator",
|
|
3624
3890
|
"aria-orientation": "vertical",
|
|
3625
3891
|
"aria-label": "Resize git panel"
|
|
3626
3892
|
}),
|
|
3627
|
-
showGitPanel && React.createElement(
|
|
3893
|
+
showGitPanel && !zenMode && React.createElement(
|
|
3628
3894
|
"div",
|
|
3629
3895
|
{ className: "ide-git-right-panel", style: { width: gitPanelWidth + "px" } },
|
|
3630
3896
|
React.createElement(window.GitPanel || GitPanel, {
|
|
@@ -3701,6 +3967,16 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
3701
3967
|
},
|
|
3702
3968
|
activeEOL
|
|
3703
3969
|
),
|
|
3970
|
+
zenMode && React.createElement(
|
|
3971
|
+
"button",
|
|
3972
|
+
{
|
|
3973
|
+
type: "button",
|
|
3974
|
+
className: "statusbar-btn statusbar-zen-btn",
|
|
3975
|
+
title: "Zen mode active — click or press Ctrl+Shift+Z to exit",
|
|
3976
|
+
onClick: toggleZenMode
|
|
3977
|
+
},
|
|
3978
|
+
"ZEN"
|
|
3979
|
+
),
|
|
3704
3980
|
React.createElement(
|
|
3705
3981
|
"div",
|
|
3706
3982
|
{ className: "statusbar-version" },
|