mbeditor 0.4.0 → 0.4.2
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 +13 -0
- data/app/assets/javascripts/mbeditor/application.js +1 -0
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +161 -0
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +210 -47
- data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +4 -2
- data/app/assets/javascripts/mbeditor/editor_plugins.js +21 -0
- data/app/assets/javascripts/mbeditor/file_service.js +6 -0
- data/app/assets/javascripts/mbeditor/search_service.js +7 -3
- data/app/assets/javascripts/mbeditor/websocket_service.js +126 -0
- data/app/assets/stylesheets/mbeditor/editor.css +148 -9
- data/app/channels/mbeditor/editor_channel.rb +79 -0
- data/app/controllers/mbeditor/editors_controller.rb +42 -12
- data/app/views/layouts/mbeditor/application.html.erb +4 -0
- data/lib/mbeditor/cable_log_filter.rb +28 -0
- data/lib/mbeditor/engine.rb +10 -0
- data/lib/mbeditor/rack/silence_ping_request.rb +4 -1
- data/lib/mbeditor/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d378fdda8fc933a3c3d1f5b1fc5eb1622ca4ebee91db435378dd069827f708e2
|
|
4
|
+
data.tar.gz: 2c58b8db51c1df5e7f746af61550dc2ccf62ba33a515cb374c79931aefed1beb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43753349cc14d328dbb45c4d6413cb0a56b9ac49979e7cd0bafe884de0889ede353decdd8552da38d872c278fa3c70162a1730df5827f45d0f629aa0d44a1f6f
|
|
7
|
+
data.tar.gz: 3547eec20751a3c7279765e79429dda30398dfd6aa64c7b70d7e2f70cda6e8de6d9ae8c30a0c671a9750dcce1422d115d2b42f1a3d35b6679e81d795ba134c3e
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.2] - 2026-04-22
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Updated sidebar search system test selectors to match the current UI loading and clear controls, resolving CI matrix failures in GitHub Actions.
|
|
12
|
+
|
|
13
|
+
## [0.4.1] - 2026-04-22
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Real-time file update integration via Action Cable to improve collaborative and live-refresh editing workflows.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Search functionality enhancements for improved result handling and responsiveness.
|
|
20
|
+
|
|
8
21
|
## [0.4.0] - 2026-04-22
|
|
9
22
|
|
|
10
23
|
### Added
|
|
@@ -68,6 +68,23 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
68
68
|
var editorReady = _useState10[0];
|
|
69
69
|
var setEditorReady = _useState10[1];
|
|
70
70
|
|
|
71
|
+
var _useState11 = useState(false);
|
|
72
|
+
var _useState12 = _slicedToArray(_useState11, 2);
|
|
73
|
+
var methodsOpen = _useState12[0];
|
|
74
|
+
var setMethodsOpen = _useState12[1];
|
|
75
|
+
|
|
76
|
+
var _useState13 = useState([]);
|
|
77
|
+
var _useState14 = _slicedToArray(_useState13, 2);
|
|
78
|
+
var methodsList = _useState14[0];
|
|
79
|
+
var setMethodsList = _useState14[1];
|
|
80
|
+
|
|
81
|
+
var _useState15 = useState(null);
|
|
82
|
+
var _useState16 = _slicedToArray(_useState15, 2);
|
|
83
|
+
var methodsDropdownPos = _useState16[0];
|
|
84
|
+
var setMethodsDropdownPos = _useState16[1];
|
|
85
|
+
|
|
86
|
+
var methodsBtnRef = useRef(null);
|
|
87
|
+
|
|
71
88
|
var onFormatRef = useRef(onFormat);
|
|
72
89
|
onFormatRef.current = onFormat;
|
|
73
90
|
|
|
@@ -514,6 +531,46 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
514
531
|
editor.restoreViewState(tab.viewState);
|
|
515
532
|
}
|
|
516
533
|
|
|
534
|
+
// Restore the find widget state from the previous editor (when persistFindState is on).
|
|
535
|
+
if (editorPrefs.persistFindState !== false && window.__mbeditorFindState && window.__mbeditorFindState.searchString) {
|
|
536
|
+
var _savedFind = window.__mbeditorFindState;
|
|
537
|
+
if (_savedFind.isRevealed) {
|
|
538
|
+
// Open the widget first (setTimeout so layout is ready), then re-apply the
|
|
539
|
+
// saved query — actions.find may seed from the selection and overwrite it.
|
|
540
|
+
setTimeout(function() {
|
|
541
|
+
try {
|
|
542
|
+
editor.trigger('', 'actions.find', null);
|
|
543
|
+
setTimeout(function() {
|
|
544
|
+
try {
|
|
545
|
+
var _fc0 = editor.getContribution('editor.contrib.findController');
|
|
546
|
+
if (_fc0 && _fc0.getState) {
|
|
547
|
+
_fc0.getState().change({
|
|
548
|
+
searchString: _savedFind.searchString,
|
|
549
|
+
isRegex: !!_savedFind.isRegex,
|
|
550
|
+
matchCase: !!_savedFind.matchCase,
|
|
551
|
+
wholeWord: !!_savedFind.wholeWord
|
|
552
|
+
}, false);
|
|
553
|
+
}
|
|
554
|
+
} catch (e) {}
|
|
555
|
+
}, 0);
|
|
556
|
+
} catch (e) {}
|
|
557
|
+
}, 0);
|
|
558
|
+
} else {
|
|
559
|
+
// Widget was closed — just seed the state silently so Ctrl+F pre-fills it.
|
|
560
|
+
try {
|
|
561
|
+
var _fc0 = editor.getContribution('editor.contrib.findController');
|
|
562
|
+
if (_fc0 && _fc0.getState) {
|
|
563
|
+
_fc0.getState().change({
|
|
564
|
+
searchString: _savedFind.searchString,
|
|
565
|
+
isRegex: !!_savedFind.isRegex,
|
|
566
|
+
matchCase: !!_savedFind.matchCase,
|
|
567
|
+
wholeWord: !!_savedFind.wholeWord
|
|
568
|
+
}, false);
|
|
569
|
+
}
|
|
570
|
+
} catch (e) {}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
517
574
|
monacoRef.current = editor;
|
|
518
575
|
window.__mbeditorActiveEditor = editor;
|
|
519
576
|
setEditorReady(true);
|
|
@@ -644,6 +701,26 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
644
701
|
_me.aviBase = aviBaseRef.current;
|
|
645
702
|
_me.aviMax = aviMaxRef.current;
|
|
646
703
|
}
|
|
704
|
+
// Save the current find widget state so it can be restored on next tab switch.
|
|
705
|
+
// Only overwrite the global state when there is an actual search string — this
|
|
706
|
+
// prevents a blank/fresh editor from clobbering the shared query.
|
|
707
|
+
if (editorPrefs.persistFindState !== false) {
|
|
708
|
+
try {
|
|
709
|
+
var _fc = editor.getContribution('editor.contrib.findController');
|
|
710
|
+
if (_fc && _fc.getState) {
|
|
711
|
+
var _fs = _fc.getState();
|
|
712
|
+
if (_fs.searchString) {
|
|
713
|
+
window.__mbeditorFindState = {
|
|
714
|
+
searchString: _fs.searchString,
|
|
715
|
+
isRegex: _fs.isRegex,
|
|
716
|
+
matchCase: _fs.matchCase,
|
|
717
|
+
wholeWord: _fs.wholeWord,
|
|
718
|
+
isRevealed: _fs.isRevealed
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
} catch (e) {}
|
|
723
|
+
}
|
|
647
724
|
if (window.__mbeditorActiveEditor === editor) {
|
|
648
725
|
window.__mbeditorActiveEditor = null;
|
|
649
726
|
}
|
|
@@ -1172,6 +1249,9 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1172
1249
|
var ext = parts.length > 1 ? parts.pop().toLowerCase() : '';
|
|
1173
1250
|
var isImage = tab.isImage || IMAGE_EXTENSIONS.includes(ext);
|
|
1174
1251
|
var isMarkdown = ['md', 'markdown'].includes(ext);
|
|
1252
|
+
var fileBaseName = (tab.path || '').split('/').pop().toLowerCase();
|
|
1253
|
+
var isRubyFile = ext === 'rb' || ext === 'ruby' || ext === 'gemspec' ||
|
|
1254
|
+
fileBaseName === 'gemfile' || fileBaseName === 'gemfile.lock' || fileBaseName === 'rakefile';
|
|
1175
1255
|
|
|
1176
1256
|
useEffect(function () {
|
|
1177
1257
|
if (isMarkdown && window.marked) {
|
|
@@ -1204,6 +1284,36 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1204
1284
|
}
|
|
1205
1285
|
}, [markdownContent, isMarkdown]);
|
|
1206
1286
|
|
|
1287
|
+
// Click-outside handler to close the methods dropdown
|
|
1288
|
+
useEffect(function() {
|
|
1289
|
+
if (!methodsOpen) return;
|
|
1290
|
+
function handleClickOutside(e) {
|
|
1291
|
+
var btn = methodsBtnRef.current;
|
|
1292
|
+
// Close if click is not on the button (the dropdown uses onMouseDown with preventDefault)
|
|
1293
|
+
if (btn && !btn.contains(e.target)) {
|
|
1294
|
+
setMethodsOpen(false);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
1298
|
+
return function() { document.removeEventListener('mousedown', handleClickOutside); };
|
|
1299
|
+
}, [methodsOpen]);
|
|
1300
|
+
|
|
1301
|
+
// Parse all method definitions from the current Monaco model
|
|
1302
|
+
function parseRubyMethods(model) {
|
|
1303
|
+
var methods = [];
|
|
1304
|
+
var lineCount = model.getLineCount();
|
|
1305
|
+
var DEF_RE = /^\s*def\s+(self\.)?([a-zA-Z_][a-zA-Z0-9_?!=]*)/;
|
|
1306
|
+
for (var i = 1; i <= lineCount; i++) {
|
|
1307
|
+
var line = model.getLineContent(i);
|
|
1308
|
+
var m = DEF_RE.exec(line);
|
|
1309
|
+
if (m) {
|
|
1310
|
+
var selfPrefix = m[1] ? 'self.' : '';
|
|
1311
|
+
methods.push({ line: i, name: selfPrefix + m[2] });
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
return methods;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1207
1317
|
if (tab.fileNotFound) {
|
|
1208
1318
|
return React.createElement(
|
|
1209
1319
|
'div',
|
|
@@ -1282,6 +1392,28 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1282
1392
|
React.createElement('i', { className: 'fas fa-history', style: { marginRight: editorPrefs.toolbarIconOnly ? 0 : '5px', flexShrink: 0 } }),
|
|
1283
1393
|
!editorPrefs.toolbarIconOnly && React.createElement('span', { className: 'ide-toolbar-label' }, 'History')
|
|
1284
1394
|
),
|
|
1395
|
+
isRubyFile && React.createElement(
|
|
1396
|
+
'button',
|
|
1397
|
+
{
|
|
1398
|
+
ref: methodsBtnRef,
|
|
1399
|
+
className: 'ide-icon-btn' + (methodsOpen ? ' active' : ''),
|
|
1400
|
+
onClick: function() {
|
|
1401
|
+
var nextOpen = !methodsOpen;
|
|
1402
|
+
if (nextOpen) {
|
|
1403
|
+
var model = monacoRef.current && monacoRef.current.getModel();
|
|
1404
|
+
setMethodsList(model ? parseRubyMethods(model) : []);
|
|
1405
|
+
if (methodsBtnRef.current) {
|
|
1406
|
+
var rect = methodsBtnRef.current.getBoundingClientRect();
|
|
1407
|
+
setMethodsDropdownPos({ top: rect.bottom + 4, right: window.innerWidth - rect.right });
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
setMethodsOpen(nextOpen);
|
|
1411
|
+
},
|
|
1412
|
+
title: 'Jump to Method'
|
|
1413
|
+
},
|
|
1414
|
+
React.createElement('i', { className: 'fas fa-list-ul', style: { marginRight: editorPrefs.toolbarIconOnly ? 0 : '5px', flexShrink: 0 } }),
|
|
1415
|
+
!editorPrefs.toolbarIconOnly && React.createElement('span', { className: 'ide-toolbar-label' }, 'Methods')
|
|
1416
|
+
),
|
|
1285
1417
|
gitAvailable && React.createElement(
|
|
1286
1418
|
'button',
|
|
1287
1419
|
{
|
|
@@ -1306,6 +1438,35 @@ var EditorPanel = function EditorPanel(_ref) {
|
|
|
1306
1438
|
)
|
|
1307
1439
|
),
|
|
1308
1440
|
React.createElement('div', { ref: editorRef, className: 'monaco-container', style: { flex: 1, minHeight: 0 } }),
|
|
1441
|
+
methodsOpen && methodsDropdownPos && React.createElement(
|
|
1442
|
+
'div',
|
|
1443
|
+
{
|
|
1444
|
+
className: 'ide-methods-dropdown',
|
|
1445
|
+
style: { position: 'fixed', top: methodsDropdownPos.top + 'px', right: methodsDropdownPos.right + 'px', left: 'auto', zIndex: 9900 }
|
|
1446
|
+
},
|
|
1447
|
+
methodsList.length === 0
|
|
1448
|
+
? React.createElement('div', { className: 'ide-methods-dropdown-empty' }, 'No methods found')
|
|
1449
|
+
: methodsList.map(function(m) {
|
|
1450
|
+
return React.createElement(
|
|
1451
|
+
'div',
|
|
1452
|
+
{
|
|
1453
|
+
key: m.line,
|
|
1454
|
+
className: 'ide-methods-dropdown-item',
|
|
1455
|
+
onMouseDown: function(e) {
|
|
1456
|
+
e.preventDefault();
|
|
1457
|
+
setMethodsOpen(false);
|
|
1458
|
+
if (monacoRef.current) {
|
|
1459
|
+
monacoRef.current.revealLineInCenter(m.line);
|
|
1460
|
+
monacoRef.current.setPosition({ lineNumber: m.line, column: 1 });
|
|
1461
|
+
monacoRef.current.focus();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
},
|
|
1465
|
+
React.createElement('span', { className: 'ide-methods-dropdown-line' }, m.line),
|
|
1466
|
+
m.name
|
|
1467
|
+
);
|
|
1468
|
+
})
|
|
1469
|
+
),
|
|
1309
1470
|
React.createElement('div', { ref: vimStatusRef, className: 'vim-statusbar', style: { display: editorPrefs.vimMode ? 'flex' : 'none', height: '22px', alignItems: 'center', padding: '0 10px', fontFamily: "'JetBrains Mono', 'Fira Code', Consolas, monospace", fontSize: '12px', background: 'var(--ide-statusbar-bg, #1e1e2e)', color: 'var(--ide-statusbar-fg, #9cdcfe)', borderTop: '1px solid var(--ide-border, #3e3e3e)', flexShrink: 0, userSelect: 'none', letterSpacing: '0.02em' } })
|
|
1310
1471
|
);
|
|
1311
1472
|
};
|
|
@@ -61,7 +61,8 @@ var DEFAULT_EDITOR_PREFS = {
|
|
|
61
61
|
vimMode: false,
|
|
62
62
|
fileTreeTypeahead: true,
|
|
63
63
|
quickOpenShowFolders: false,
|
|
64
|
-
tabDisplayMode: 'scroll'
|
|
64
|
+
tabDisplayMode: 'scroll',
|
|
65
|
+
persistFindState: true
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
var SidebarActionButton = function SidebarActionButton(_ref) {
|
|
@@ -196,7 +197,25 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
196
197
|
var searchOffsetRef = useRef(0);
|
|
197
198
|
var searchLoadingMoreRef = useRef(false);
|
|
198
199
|
|
|
200
|
+
var _useStateRx = useState(false);
|
|
201
|
+
var _useStateRx2 = _slicedToArray(_useStateRx, 2);
|
|
202
|
+
var searchUseRegex = _useStateRx2[0];
|
|
203
|
+
var setSearchUseRegex = _useStateRx2[1];
|
|
204
|
+
|
|
205
|
+
var _useStateMC = useState(false);
|
|
206
|
+
var _useStateMC2 = _slicedToArray(_useStateMC, 2);
|
|
207
|
+
var searchMatchCase = _useStateMC2[0];
|
|
208
|
+
var setSearchMatchCase = _useStateMC2[1];
|
|
209
|
+
|
|
210
|
+
var _useStateWW = useState(false);
|
|
211
|
+
var _useStateWW2 = _slicedToArray(_useStateWW, 2);
|
|
212
|
+
var searchWholeWord = _useStateWW2[0];
|
|
213
|
+
var setSearchWholeWord = _useStateWW2[1];
|
|
214
|
+
|
|
199
215
|
var searchQueryRef = useRef('');
|
|
216
|
+
var searchUseRegexRef = useRef(false);
|
|
217
|
+
var searchMatchCaseRef = useRef(false);
|
|
218
|
+
var searchWholeWordRef = useRef(false);
|
|
200
219
|
var searchResultsContainerRef = useRef(null);
|
|
201
220
|
|
|
202
221
|
var _useState8 = useState("explorer");
|
|
@@ -665,6 +684,9 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
665
684
|
if (workspace && typeof workspace.testAvailable === 'boolean') {
|
|
666
685
|
setTestAvailable(workspace.testAvailable);
|
|
667
686
|
}
|
|
687
|
+
if (workspace && typeof workspace.actionCableEnabled === 'boolean') {
|
|
688
|
+
WebSocketService.connect(workspace.actionCableEnabled);
|
|
689
|
+
}
|
|
668
690
|
});
|
|
669
691
|
|
|
670
692
|
// Helper: load tab content for a set of panes and restore them into EditorStore
|
|
@@ -990,11 +1012,33 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
990
1012
|
if (offers.length > 0) setDraftRestoreOffer(offers);
|
|
991
1013
|
}, [serverOnline]);
|
|
992
1014
|
|
|
1015
|
+
// WebSocket push — when the server broadcasts files_changed, refresh the tree
|
|
1016
|
+
// and git status immediately (same work as the 10s poll below does).
|
|
1017
|
+
useEffect(function () {
|
|
1018
|
+
function handleFilesChanged() {
|
|
1019
|
+
if (document.hidden) return;
|
|
1020
|
+
GitService.fetchStatus()["catch"](function () {});
|
|
1021
|
+
FileService.getTree().then(function (data) {
|
|
1022
|
+
var newData = data || [];
|
|
1023
|
+
setTreeData(function (prevData) {
|
|
1024
|
+
if (JSON.stringify(newData) === JSON.stringify(prevData)) return prevData;
|
|
1025
|
+
SearchService.buildIndex(newData);
|
|
1026
|
+
return newData;
|
|
1027
|
+
});
|
|
1028
|
+
})["catch"](function () {});
|
|
1029
|
+
}
|
|
1030
|
+
WebSocketService.onFilesChanged(handleFilesChanged);
|
|
1031
|
+
return function () { WebSocketService.offFilesChanged(handleFilesChanged); };
|
|
1032
|
+
}, []);
|
|
1033
|
+
|
|
993
1034
|
// Auto-refresh the file tree every 10s to pick up external changes (new files, deletions, etc.)
|
|
1035
|
+
// When an ActionCable WebSocket is connected this acts only as a safety-net fallback —
|
|
1036
|
+
// the WebSocket push above handles immediate invalidation after mbeditor mutations.
|
|
994
1037
|
// Uses functional setTreeData to skip the re-render when nothing has changed.
|
|
995
1038
|
useEffect(function () {
|
|
996
1039
|
var intervalId = setInterval(function () {
|
|
997
1040
|
if (document.hidden) return;
|
|
1041
|
+
if (WebSocketService.isConnected()) return; // WebSocket is handling refreshes
|
|
998
1042
|
// Refresh tree and check for git branch changes (to trigger per-branch tab state swap)
|
|
999
1043
|
GitService.fetchStatus()["catch"](function () {});
|
|
1000
1044
|
FileService.getTree().then(function (data) {
|
|
@@ -1198,6 +1242,12 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1198
1242
|
var activeFileCommit = _useState32[0];
|
|
1199
1243
|
var setActiveFileCommit = _useState32[1];
|
|
1200
1244
|
|
|
1245
|
+
// EOL indicator — tracks current line-ending style of the active file
|
|
1246
|
+
var _useState31e = useState(null);
|
|
1247
|
+
var _useState31e2 = _slicedToArray(_useState31e, 2);
|
|
1248
|
+
var activeEOL = _useState31e2[0];
|
|
1249
|
+
var setActiveEOL = _useState31e2[1];
|
|
1250
|
+
|
|
1201
1251
|
useEffect(function () {
|
|
1202
1252
|
if (!gitAvailable || !activeTab || activeTab.isDiff || activeTab.isCombinedDiff || activeTab.isCommitGraph || !activeTab.path || activeTab.path.indexOf('diff://') === 0 || activeTab.path.indexOf('combined-diff://') === 0) {
|
|
1203
1253
|
setActiveFileCommit(null);
|
|
@@ -1216,6 +1266,22 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1216
1266
|
});
|
|
1217
1267
|
}, [activeTab ? activeTab.id : null, gitAvailable]);
|
|
1218
1268
|
|
|
1269
|
+
// Update EOL indicator whenever active tab or its content changes
|
|
1270
|
+
useEffect(function () {
|
|
1271
|
+
if (!activeTab || typeof activeTab.content !== 'string' ||
|
|
1272
|
+
activeTab.isDiff || activeTab.isCombinedDiff || activeTab.isCommitGraph || activeTab.isPreview) {
|
|
1273
|
+
setActiveEOL(null);
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
if (activeTab.content.indexOf('\r\n') !== -1) {
|
|
1277
|
+
setActiveEOL('CRLF');
|
|
1278
|
+
} else if (activeTab.content.indexOf('\r') !== -1) {
|
|
1279
|
+
setActiveEOL('CR');
|
|
1280
|
+
} else {
|
|
1281
|
+
setActiveEOL('LF');
|
|
1282
|
+
}
|
|
1283
|
+
}, [activeTab ? activeTab.id : null, activeTab ? activeTab.content : null]);
|
|
1284
|
+
|
|
1219
1285
|
useEffect(function () {
|
|
1220
1286
|
if (!activeTab || typeof activeTab.content !== 'string') return;
|
|
1221
1287
|
if (activeTab.isDiff || activeTab.isCombinedDiff || activeTab.isCommitGraph) return;
|
|
@@ -1381,6 +1447,21 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1381
1447
|
clearDragState();
|
|
1382
1448
|
};
|
|
1383
1449
|
|
|
1450
|
+
var handleChangeEOL = function handleChangeEOL(newEOL) {
|
|
1451
|
+
var ed = window.__mbeditorActiveEditor;
|
|
1452
|
+
if (!ed || !window.monaco) return;
|
|
1453
|
+
var model = ed.getModel();
|
|
1454
|
+
if (!model || !activeTab || !focusedPane) return;
|
|
1455
|
+
var seq = newEOL === 'CRLF'
|
|
1456
|
+
? window.monaco.editor.EndOfLineSequence.CRLF
|
|
1457
|
+
: window.monaco.editor.EndOfLineSequence.LF;
|
|
1458
|
+
model.setEOL(seq);
|
|
1459
|
+
var newContent = model.getValue();
|
|
1460
|
+
TabManager.markDirty(focusedPane.id, activeTab.path, newContent);
|
|
1461
|
+
setActiveEOL(newEOL);
|
|
1462
|
+
EditorStore.setStatus('Line endings changed to ' + newEOL, 'info');
|
|
1463
|
+
};
|
|
1464
|
+
|
|
1384
1465
|
var handleFormat = function handleFormat() {
|
|
1385
1466
|
if (!activeTab) return;
|
|
1386
1467
|
|
|
@@ -1578,7 +1659,7 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1578
1659
|
searchQueryRef.current = q;
|
|
1579
1660
|
EditorStore.setState({ searchResults: [], searchHasMore: false });
|
|
1580
1661
|
EditorStore.setStatus("Searching project...", "info");
|
|
1581
|
-
SearchService.projectSearch(q, 0, SearchService.PAGE_SIZE).then(function (res) {
|
|
1662
|
+
SearchService.projectSearch(q, 0, SearchService.PAGE_SIZE, { regex: searchUseRegexRef.current, matchCase: searchMatchCaseRef.current, wholeWord: searchWholeWordRef.current }).then(function (res) {
|
|
1582
1663
|
if (searchRequestIdRef.current !== requestId) return;
|
|
1583
1664
|
var hasMore = !!(res && res.hasMore);
|
|
1584
1665
|
setSearchHasMore(hasMore); searchHasMoreRef.current = hasMore;
|
|
@@ -1596,7 +1677,7 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1596
1677
|
if (!q || searchLoadingMoreRef.current || !searchHasMoreRef.current) return;
|
|
1597
1678
|
searchLoadingMoreRef.current = true;
|
|
1598
1679
|
var offset = searchOffsetRef.current;
|
|
1599
|
-
SearchService.projectSearch(q, offset, SearchService.PAGE_SIZE).then(function(res) {
|
|
1680
|
+
SearchService.projectSearch(q, offset, SearchService.PAGE_SIZE, { regex: searchUseRegexRef.current, matchCase: searchMatchCaseRef.current, wholeWord: searchWholeWordRef.current }).then(function(res) {
|
|
1600
1681
|
if (searchQueryRef.current !== q) { searchLoadingMoreRef.current = false; return; }
|
|
1601
1682
|
var hasMore = !!(res && res.hasMore);
|
|
1602
1683
|
searchHasMoreRef.current = hasMore;
|
|
@@ -1615,6 +1696,33 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
1615
1696
|
_debouncedSearch(val);
|
|
1616
1697
|
};
|
|
1617
1698
|
|
|
1699
|
+
var handleSearchRegexToggle = function handleSearchRegexToggle() {
|
|
1700
|
+
var next = !searchUseRegexRef.current;
|
|
1701
|
+
searchUseRegexRef.current = next;
|
|
1702
|
+
setSearchUseRegex(next);
|
|
1703
|
+
if (searchQueryRef.current) {
|
|
1704
|
+
_debouncedSearch(searchQueryRef.current);
|
|
1705
|
+
}
|
|
1706
|
+
};
|
|
1707
|
+
|
|
1708
|
+
var handleSearchMatchCaseToggle = function handleSearchMatchCaseToggle() {
|
|
1709
|
+
var next = !searchMatchCaseRef.current;
|
|
1710
|
+
searchMatchCaseRef.current = next;
|
|
1711
|
+
setSearchMatchCase(next);
|
|
1712
|
+
if (searchQueryRef.current) {
|
|
1713
|
+
_debouncedSearch(searchQueryRef.current);
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
|
|
1717
|
+
var handleSearchWholeWordToggle = function handleSearchWholeWordToggle() {
|
|
1718
|
+
var next = !searchWholeWordRef.current;
|
|
1719
|
+
searchWholeWordRef.current = next;
|
|
1720
|
+
setSearchWholeWord(next);
|
|
1721
|
+
if (searchQueryRef.current) {
|
|
1722
|
+
_debouncedSearch(searchQueryRef.current);
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
|
|
1618
1726
|
var clearSearch = function clearSearch() {
|
|
1619
1727
|
searchRequestIdRef.current += 1;
|
|
1620
1728
|
if (_debouncedSearch.cancel) _debouncedSearch.cancel();
|
|
@@ -2564,32 +2672,57 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2564
2672
|
{ className: "search-panel" },
|
|
2565
2673
|
React.createElement(
|
|
2566
2674
|
"div",
|
|
2567
|
-
{ className: "search-input-
|
|
2675
|
+
{ className: "search-input-shell" },
|
|
2676
|
+
React.createElement("input", {
|
|
2677
|
+
className: "search-input",
|
|
2678
|
+
placeholder: "Find in files…",
|
|
2679
|
+
value: searchQuery,
|
|
2680
|
+
onChange: handleSearchChange
|
|
2681
|
+
}),
|
|
2568
2682
|
React.createElement(
|
|
2569
2683
|
"div",
|
|
2570
|
-
{ className: "search-input-
|
|
2571
|
-
React.createElement(
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2684
|
+
{ className: "search-input-adornments" },
|
|
2685
|
+
React.createElement(
|
|
2686
|
+
"button",
|
|
2687
|
+
{
|
|
2688
|
+
type: "button",
|
|
2689
|
+
className: "search-adornment-btn" + (searchMatchCase ? " active" : ""),
|
|
2690
|
+
onClick: handleSearchMatchCaseToggle,
|
|
2691
|
+
title: "Match Case"
|
|
2692
|
+
},
|
|
2693
|
+
React.createElement("i", { className: "codicon codicon-case-sensitive" })
|
|
2694
|
+
),
|
|
2695
|
+
React.createElement(
|
|
2696
|
+
"button",
|
|
2697
|
+
{
|
|
2698
|
+
type: "button",
|
|
2699
|
+
className: "search-adornment-btn" + (searchWholeWord ? " active" : ""),
|
|
2700
|
+
onClick: handleSearchWholeWordToggle,
|
|
2701
|
+
title: "Match Whole Word"
|
|
2702
|
+
},
|
|
2703
|
+
React.createElement("i", { className: "codicon codicon-whole-word" })
|
|
2704
|
+
),
|
|
2705
|
+
React.createElement(
|
|
2706
|
+
"button",
|
|
2707
|
+
{
|
|
2708
|
+
type: "button",
|
|
2709
|
+
className: "search-adornment-btn" + (searchUseRegex ? " active" : ""),
|
|
2710
|
+
onClick: handleSearchRegexToggle,
|
|
2711
|
+
title: "Use Regular Expression"
|
|
2712
|
+
},
|
|
2713
|
+
React.createElement("i", { className: "codicon codicon-regex" })
|
|
2714
|
+
),
|
|
2577
2715
|
searchQuery && React.createElement(
|
|
2578
2716
|
"button",
|
|
2579
2717
|
{
|
|
2580
2718
|
type: "button",
|
|
2581
|
-
className: "search-
|
|
2719
|
+
className: "search-adornment-btn search-adornment-clear",
|
|
2582
2720
|
onClick: clearSearch,
|
|
2583
2721
|
title: "Clear search",
|
|
2584
2722
|
"aria-label": "Clear search"
|
|
2585
2723
|
},
|
|
2586
2724
|
React.createElement("i", { className: "fas fa-times" })
|
|
2587
2725
|
)
|
|
2588
|
-
),
|
|
2589
|
-
React.createElement(
|
|
2590
|
-
"button",
|
|
2591
|
-
{ type: "submit", className: "search-btn", disabled: searchLoading, title: searchLoading ? "Searching..." : "Search" },
|
|
2592
|
-
React.createElement("i", { className: searchLoading ? "fas fa-spinner fa-spin" : "fas fa-search" })
|
|
2593
2726
|
)
|
|
2594
2727
|
),
|
|
2595
2728
|
(function() {
|
|
@@ -2608,38 +2741,47 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
2608
2741
|
? (total + (searchHasMore ? '+' : '') + " result" + (total !== 1 ? "s" : ""))
|
|
2609
2742
|
: "No results"
|
|
2610
2743
|
),
|
|
2611
|
-
|
|
2744
|
+
React.createElement(
|
|
2612
2745
|
"div",
|
|
2613
|
-
{
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2746
|
+
{ className: "search-results-area" },
|
|
2747
|
+
hasAny && React.createElement(
|
|
2748
|
+
"div",
|
|
2749
|
+
{
|
|
2750
|
+
className: "search-results" + (searchLoading ? " search-results-blurred" : ""),
|
|
2751
|
+
ref: searchResultsContainerRef,
|
|
2752
|
+
onScroll: handleSearchResultsScroll
|
|
2753
|
+
},
|
|
2754
|
+
allResults.map(function(res, i) {
|
|
2755
|
+
var fileName = res.file.split('/').pop();
|
|
2756
|
+
return React.createElement(
|
|
2757
|
+
"div",
|
|
2758
|
+
{
|
|
2759
|
+
key: i,
|
|
2760
|
+
className: "search-result-item",
|
|
2761
|
+
onClick: (function(r) { return function() { handleSelectFile(r.file, r.file.split('/').pop(), r.line); }; })(res)
|
|
2762
|
+
},
|
|
2763
|
+
React.createElement("i", { className: (window.getFileIcon ? window.getFileIcon(fileName) : 'far fa-file-code') + " search-result-icon" }),
|
|
2630
2764
|
React.createElement(
|
|
2631
|
-
"div", { className: "search-result-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
React.createElement(
|
|
2642
|
-
|
|
2765
|
+
"div", { className: "search-result-body" },
|
|
2766
|
+
React.createElement(
|
|
2767
|
+
"div", { className: "search-result-file" },
|
|
2768
|
+
fileName,
|
|
2769
|
+
React.createElement("span", { className: "search-result-line-num" }, " ", res.file, ":", res.line)
|
|
2770
|
+
),
|
|
2771
|
+
React.createElement("div", { className: "search-result-text" }, res.text)
|
|
2772
|
+
)
|
|
2773
|
+
);
|
|
2774
|
+
}),
|
|
2775
|
+
searchHasMore && React.createElement(
|
|
2776
|
+
"div", { className: "search-loading-more" },
|
|
2777
|
+
React.createElement("i", { className: "fas fa-spinner fa-spin" }),
|
|
2778
|
+
" Loading more\u2026"
|
|
2779
|
+
)
|
|
2780
|
+
),
|
|
2781
|
+
searchLoading && React.createElement(
|
|
2782
|
+
"div",
|
|
2783
|
+
{ className: "search-loading-overlay" },
|
|
2784
|
+
React.createElement("div", { className: "search-loading-spinner" })
|
|
2643
2785
|
)
|
|
2644
2786
|
)
|
|
2645
2787
|
);
|
|
@@ -3228,6 +3370,17 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
3228
3370
|
})
|
|
3229
3371
|
),
|
|
3230
3372
|
|
|
3373
|
+
React.createElement(
|
|
3374
|
+
'label', { className: 'ide-settings-row ide-settings-row-check' },
|
|
3375
|
+
React.createElement('span', { className: 'ide-settings-label' }, 'Persist find state across files'),
|
|
3376
|
+
React.createElement('input', {
|
|
3377
|
+
type: 'checkbox',
|
|
3378
|
+
className: 'ide-settings-checkbox',
|
|
3379
|
+
checked: editorPrefs.persistFindState !== false,
|
|
3380
|
+
onChange: function(e) { var v = e.target.checked; setEditorPrefs(function(p) { return Object.assign({}, p, { persistFindState: v }); }); }
|
|
3381
|
+
})
|
|
3382
|
+
),
|
|
3383
|
+
|
|
3231
3384
|
/* ── RuboCop ─────────────────────────────────── */
|
|
3232
3385
|
React.createElement('div', { className: 'ide-settings-section-header' }, 'RuboCop'),
|
|
3233
3386
|
React.createElement(
|
|
@@ -3538,6 +3691,16 @@ var MbeditorApp = function MbeditorApp() {
|
|
|
3538
3691
|
{ className: "statusbar-msg " + state.statusMessage.kind },
|
|
3539
3692
|
state.statusMessage.text
|
|
3540
3693
|
),
|
|
3694
|
+
activeEOL && React.createElement(
|
|
3695
|
+
"button",
|
|
3696
|
+
{
|
|
3697
|
+
type: "button",
|
|
3698
|
+
className: "statusbar-btn statusbar-eol-btn",
|
|
3699
|
+
title: "Line endings: " + activeEOL + " — click to change",
|
|
3700
|
+
onClick: function() { handleChangeEOL(activeEOL === 'CRLF' ? 'LF' : 'CRLF'); }
|
|
3701
|
+
},
|
|
3702
|
+
activeEOL
|
|
3703
|
+
),
|
|
3541
3704
|
React.createElement(
|
|
3542
3705
|
"div",
|
|
3543
3706
|
{ className: "statusbar-version" },
|
|
@@ -132,10 +132,12 @@ var QuickOpenDialog = function QuickOpenDialog(_ref) {
|
|
|
132
132
|
// JS sort is stable in modern engines so MiniSearch relevance score order is the tiebreaker
|
|
133
133
|
// when match relevance is equal.
|
|
134
134
|
filtered.sort(function(a, b) {
|
|
135
|
+
var aRelevance = getMatchRelevance(a, query);
|
|
136
|
+
var bRelevance = getMatchRelevance(b, query);
|
|
137
|
+
if (aRelevance !== bRelevance) return aRelevance - bRelevance;
|
|
135
138
|
var aPriority = getFilePriority(a.path) + (a.type === 'dir' ? 100 : 0);
|
|
136
139
|
var bPriority = getFilePriority(b.path) + (b.type === 'dir' ? 100 : 0);
|
|
137
|
-
|
|
138
|
-
return getMatchRelevance(a, query) - getMatchRelevance(b, query);
|
|
140
|
+
return aPriority - bPriority;
|
|
139
141
|
});
|
|
140
142
|
setResults(filtered.slice(0, 200));
|
|
141
143
|
setSelectedIndex(0);
|