mbeditor 0.13.0 → 0.14.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 +157 -1
- data/app/assets/javascripts/mbeditor/application.js +3 -1
- data/app/assets/javascripts/mbeditor/audit_log.js +165 -0
- data/app/assets/javascripts/mbeditor/collaboration_service.js +264 -22
- data/app/assets/javascripts/mbeditor/components/ChangelogView.js +89 -94
- data/app/assets/javascripts/mbeditor/components/CodeReviewPanel.js +6 -9
- data/app/assets/javascripts/mbeditor/components/CollapsibleSection.js +12 -7
- data/app/assets/javascripts/mbeditor/components/CombinedDiffViewer.js +20 -0
- data/app/assets/javascripts/mbeditor/components/DiffViewer.js +1 -1
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +440 -334
- data/app/assets/javascripts/mbeditor/components/FileHistoryPanel.js +6 -9
- data/app/assets/javascripts/mbeditor/components/FileTree.js +26 -12
- data/app/assets/javascripts/mbeditor/components/GitPanel.js +3 -0
- data/app/assets/javascripts/mbeditor/components/Gutter.js +51 -0
- data/app/assets/javascripts/mbeditor/components/ImportDialog.js +9 -1
- data/app/assets/javascripts/mbeditor/components/LogPanel.js +3 -44
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +1322 -1431
- data/app/assets/javascripts/mbeditor/components/ModelGraph.js +94 -37
- data/app/assets/javascripts/mbeditor/components/ProblemsPanel.js +82 -72
- data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +121 -81
- data/app/assets/javascripts/mbeditor/components/SettingsModal.js +342 -0
- data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +2 -1
- data/app/assets/javascripts/mbeditor/components/TabBar.js +67 -98
- data/app/assets/javascripts/mbeditor/editor_plugins.js +694 -306
- data/app/assets/javascripts/mbeditor/file_import.js +13 -15
- data/app/assets/javascripts/mbeditor/file_service.js +46 -57
- data/app/assets/javascripts/mbeditor/git_service.js +15 -1
- data/app/assets/javascripts/mbeditor/history_service.js +5 -13
- data/app/assets/javascripts/mbeditor/search_service.js +29 -2
- data/app/assets/javascripts/mbeditor/tab_manager.js +135 -54
- data/app/assets/javascripts/mbeditor/websocket_service.js +13 -5
- data/app/assets/stylesheets/mbeditor/application.css +6 -1
- data/app/assets/stylesheets/mbeditor/editor.css +762 -297
- data/app/assets/stylesheets/mbeditor/glass.css +163 -0
- data/app/assets/stylesheets/mbeditor/themes.css +90 -30
- data/app/channels/mbeditor/collaboration_channel.rb +25 -6
- data/app/controllers/mbeditor/application_controller.rb +26 -3
- data/app/controllers/mbeditor/editors_controller.rb +125 -465
- data/app/services/mbeditor/archive_service.rb +137 -0
- data/app/services/mbeditor/collaboration_doc_store.rb +180 -12
- data/app/services/mbeditor/duplicate_content_scanner.rb +105 -0
- data/app/services/mbeditor/editor_state_service.rb +14 -51
- data/app/services/mbeditor/file_history_service.rb +222 -0
- data/app/services/mbeditor/git_info_service.rb +6 -0
- data/app/services/mbeditor/js_globals_service.rb +12 -1
- data/app/services/mbeditor/js_syntax_check_service.rb +42 -16
- data/app/services/mbeditor/lint_service.rb +137 -0
- data/app/services/mbeditor/locked_json_file.rb +67 -0
- data/app/services/mbeditor/process_runner.rb +32 -0
- data/app/services/mbeditor/rubocop_run_service.rb +17 -5
- data/app/services/mbeditor/ruby_lsp_result_translator.rb +226 -0
- data/app/services/mbeditor/schema_service.rb +8 -2
- data/app/services/mbeditor/search_replace_service.rb +19 -2
- data/app/services/mbeditor/test_runner_service.rb +3 -56
- data/app/views/layouts/mbeditor/application.html.erb +1 -1
- data/lib/mbeditor/audit_log.rb +203 -0
- data/lib/mbeditor/configuration.rb +6 -9
- data/lib/mbeditor/rack/pending_migration_bypass.rb +15 -9
- data/lib/mbeditor/route_map.rb +4 -1
- data/lib/mbeditor/ruby_lsp_client.rb +82 -14
- data/lib/mbeditor/version.rb +1 -1
- data/lib/mbeditor.rb +1 -0
- data/lib/tasks/mbeditor.rake +23 -0
- metadata +14 -3
- data/app/assets/javascripts/mbeditor/components/TestRunPanel.js +0 -312
|
@@ -13,6 +13,16 @@ var FileImport = (function () {
|
|
|
13
13
|
// otherwise reject the request as a 500 before the server guard can run.
|
|
14
14
|
var MAX_ENTRIES = 100;
|
|
15
15
|
|
|
16
|
+
// Every collector returns this same shape; `truncated` is read off the full
|
|
17
|
+
// list, so it must be computed before the slice.
|
|
18
|
+
function capResult(list, foldersSkipped) {
|
|
19
|
+
return {
|
|
20
|
+
entries: list.slice(0, MAX_ENTRIES),
|
|
21
|
+
truncated: list.length > MAX_ENTRIES,
|
|
22
|
+
foldersSkipped: !!foldersSkipped
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
function hasExternalFiles(dataTransfer) {
|
|
17
27
|
if (!dataTransfer || !dataTransfer.types) return false;
|
|
18
28
|
return Array.prototype.indexOf.call(dataTransfer.types, 'Files') !== -1;
|
|
@@ -41,20 +51,12 @@ var FileImport = (function () {
|
|
|
41
51
|
for (var j = 0; j < files.length; j++) {
|
|
42
52
|
flat.push({ file: files[j], relativePath: files[j].name });
|
|
43
53
|
}
|
|
44
|
-
return Promise.resolve(
|
|
45
|
-
entries: flat.slice(0, MAX_ENTRIES),
|
|
46
|
-
truncated: flat.length > MAX_ENTRIES,
|
|
47
|
-
foldersSkipped: !!(items && items.length > flat.length)
|
|
48
|
-
});
|
|
54
|
+
return Promise.resolve(capResult(flat, items && items.length > flat.length));
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
var collected = [];
|
|
52
58
|
return walkAll(roots, collected).then(function () {
|
|
53
|
-
return
|
|
54
|
-
entries: collected.slice(0, MAX_ENTRIES),
|
|
55
|
-
truncated: collected.length > MAX_ENTRIES,
|
|
56
|
-
foldersSkipped: false
|
|
57
|
-
};
|
|
59
|
+
return capResult(collected, false);
|
|
58
60
|
});
|
|
59
61
|
}
|
|
60
62
|
|
|
@@ -99,11 +101,7 @@ var FileImport = (function () {
|
|
|
99
101
|
var f = fileList[i];
|
|
100
102
|
out.push({ file: f, relativePath: stripLeadingSlash(f.webkitRelativePath || f.name) });
|
|
101
103
|
}
|
|
102
|
-
return
|
|
103
|
-
entries: out.slice(0, MAX_ENTRIES),
|
|
104
|
-
truncated: out.length > MAX_ENTRIES,
|
|
105
|
-
foldersSkipped: false
|
|
106
|
-
};
|
|
104
|
+
return capResult(out, false);
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
// Where in the workspace does this set of files already live?
|
|
@@ -140,56 +140,47 @@ axios.interceptors.response.use(function (response) {
|
|
|
140
140
|
//
|
|
141
141
|
// The editor keeps working while migrations are pending — the server-side
|
|
142
142
|
// bypass (Mbeditor::Rack::PendingMigrationBypass) is what makes that true — so
|
|
143
|
-
// this is purely informational
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
143
|
+
// this is purely informational. It is driven by a response *header* rather than
|
|
144
|
+
// by a failed request, for two reasons: after the bypass there is no failed
|
|
145
|
+
// request left to hang it on, and a header rides every response, so a migration
|
|
146
|
+
// created mid-session raises the warning too, and running the migration clears
|
|
147
|
+
// it without a reload.
|
|
148
148
|
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
'<strong>Pending migrations detected.</strong>' +
|
|
165
|
-
' Run <code style="background:rgba(0,0,0,.15);padding:1px 5px;border-radius:3px">rails db:migrate</code>' +
|
|
166
|
-
' then reload — editing still works in the meantime.' +
|
|
167
|
-
'<button onclick="this.parentNode.remove()" style="margin-left:auto;background:none;border:none;' +
|
|
168
|
-
'cursor:pointer;font-size:16px;line-height:1;padding:0 4px" aria-label="Dismiss">\u00d7</button>';
|
|
169
|
-
document.body.prepend(banner);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function _hideMigrationBanner() {
|
|
173
|
-
var existing = document.getElementById(MIGRATION_BANNER_ID);
|
|
174
|
-
if (existing) existing.remove();
|
|
149
|
+
// Only an explicit "1"/"0" counts. A response with no header at all is no
|
|
150
|
+
// evidence either way: an error page rendered by ActionDispatch::DebugExceptions
|
|
151
|
+
// never carries one (that middleware sits above the bypass), and treating its
|
|
152
|
+
// absence as "cleared" made the warning flicker off and back on.
|
|
153
|
+
//
|
|
154
|
+
// The state lives as a class on <body> so the CSS can recolour the status bar
|
|
155
|
+
// without React; the event is what the status bar's warning listens to.
|
|
156
|
+
var MIGRATION_PENDING_CLASS = 'mbeditor-migration-pending';
|
|
157
|
+
var MIGRATION_PENDING_EVENT = 'mbeditor:pending-migration';
|
|
158
|
+
|
|
159
|
+
function _setPendingMigration(pending) {
|
|
160
|
+
if (!document.body) return;
|
|
161
|
+
if (document.body.classList.contains(MIGRATION_PENDING_CLASS) === pending) return;
|
|
162
|
+
document.body.classList.toggle(MIGRATION_PENDING_CLASS, pending);
|
|
163
|
+
window.dispatchEvent(new CustomEvent(MIGRATION_PENDING_EVENT, { detail: pending }));
|
|
175
164
|
}
|
|
176
165
|
|
|
177
|
-
// Mirrors Mbeditor::Rack::PendingMigrationBypass::PENDING_HEADER.
|
|
178
|
-
// the check passed, so the banner is cleared as soon as the migration is run —
|
|
179
|
-
// no reload needed.
|
|
166
|
+
// Mirrors Mbeditor::Rack::PendingMigrationBypass::PENDING_HEADER.
|
|
180
167
|
function _notePendingMigrationHeader(response) {
|
|
181
168
|
if (!response || !response.headers) return;
|
|
182
|
-
|
|
183
|
-
|
|
169
|
+
var value = response.headers['x-mbeditor-pending-migration'];
|
|
170
|
+
if (value === undefined || value === null || value === '') return;
|
|
171
|
+
_setPendingMigration(value !== '0');
|
|
184
172
|
}
|
|
185
173
|
|
|
186
174
|
axios.interceptors.response.use(function (response) {
|
|
187
175
|
_notePendingMigrationHeader(response);
|
|
188
176
|
return response;
|
|
189
177
|
}, function (error) {
|
|
178
|
+
// A PendingMigrationError raised somewhere the bypass does not cover.
|
|
190
179
|
if (error && error.response && error.response.data &&
|
|
191
180
|
error.response.data.pending_migration_error) {
|
|
192
|
-
|
|
181
|
+
_setPendingMigration(true);
|
|
182
|
+
} else if (error && error.response) {
|
|
183
|
+
_notePendingMigrationHeader(error.response);
|
|
193
184
|
}
|
|
194
185
|
return Promise.reject(error);
|
|
195
186
|
});
|
|
@@ -242,9 +233,17 @@ var FileService = (function () {
|
|
|
242
233
|
// Multipart import of files dragged in from outside the browser. The
|
|
243
234
|
// default 30 s axios timeout is too tight for a large drop on a slow disk,
|
|
244
235
|
// so this one call gets a longer leash.
|
|
245
|
-
|
|
236
|
+
// onProgress(percentOrNull) fires as the body uploads, then once more with
|
|
237
|
+
// null when the bytes are away and the server is still writing. A big drop
|
|
238
|
+
// on a slow link otherwise sits on one static message long enough to look
|
|
239
|
+
// like it has hung.
|
|
240
|
+
function importFiles(formData, onProgress) {
|
|
246
241
|
return axios.post(window.mbeditorBasePath() + '/import', formData, {
|
|
247
|
-
timeout: 120000
|
|
242
|
+
timeout: 120000,
|
|
243
|
+
onUploadProgress: onProgress ? function (e) {
|
|
244
|
+
var done = e.loaded === e.total;
|
|
245
|
+
onProgress(!done && e.total ? Math.round((e.loaded / e.total) * 100) : null);
|
|
246
|
+
} : undefined
|
|
248
247
|
}).then(function (res) { return res.data; });
|
|
249
248
|
}
|
|
250
249
|
|
|
@@ -276,30 +275,21 @@ var FileService = (function () {
|
|
|
276
275
|
|
|
277
276
|
// line (1-based, optional) narrows the run to the single test at that line;
|
|
278
277
|
// the server ignores it unless `path` IS the test file.
|
|
279
|
-
// Server-reported
|
|
280
|
-
// timeout is derived from
|
|
278
|
+
// Server-reported ceiling (seconds), seeded from /workspace. The request
|
|
279
|
+
// timeout is derived from it rather than hard-coded: two independent
|
|
281
280
|
// numbers meant that raising config.test_timeout past the client's own cap
|
|
282
281
|
// made the browser abort a run the server was still executing, and the user
|
|
283
282
|
// saw a generic network error instead of the server's message.
|
|
284
|
-
var
|
|
285
|
-
function
|
|
286
|
-
if (t && t.test > 0) testTimeouts.test = t.test;
|
|
287
|
-
if (t && t.testAll > 0) testTimeouts.testAll = t.testAll;
|
|
288
|
-
}
|
|
289
|
-
// Margin for boot, JSON encoding and the trip back, so the server's own
|
|
290
|
-
// timeout is always the one that fires first and reports why.
|
|
291
|
-
function requestTimeout(seconds) { return (seconds + 30) * 1000; }
|
|
283
|
+
var testTimeout = 180;
|
|
284
|
+
function setTestTimeout(seconds) { if (seconds > 0) testTimeout = seconds; }
|
|
292
285
|
|
|
293
286
|
function runTests(path, line) {
|
|
294
287
|
var payload = { path: path };
|
|
295
288
|
if (line) payload.line = line;
|
|
289
|
+
// Margin for boot, JSON encoding and the trip back, so the server's own
|
|
290
|
+
// timeout is always the one that fires first and reports why.
|
|
296
291
|
return axios.post(window.mbeditorBasePath() + '/test', payload,
|
|
297
|
-
{ timeout:
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function runAllTests() {
|
|
301
|
-
return axios.post(window.mbeditorBasePath() + '/test_all', {},
|
|
302
|
-
{ timeout: requestTimeout(testTimeouts.testAll) }).then(function(res) { return res.data; });
|
|
292
|
+
{ timeout: (testTimeout + 30) * 1000 }).then(function(res) { return res.data; });
|
|
303
293
|
}
|
|
304
294
|
|
|
305
295
|
function ping() {
|
|
@@ -585,8 +575,7 @@ var FileService = (function () {
|
|
|
585
575
|
rubyRename: rubyRename,
|
|
586
576
|
getModelGraph: getModelGraph,
|
|
587
577
|
runRubocop: runRubocop,
|
|
588
|
-
|
|
589
|
-
setTestTimeouts: setTestTimeouts,
|
|
578
|
+
setTestTimeout: setTestTimeout,
|
|
590
579
|
getExceptions: getExceptions,
|
|
591
580
|
clearExceptions: clearExceptions,
|
|
592
581
|
getRelatedFiles: getRelatedFiles,
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// Git's seven unmerged porcelain codes. Defined here because git_service.js
|
|
2
|
+
// loads before both consumers (GitPanel's status badges and the status bar's
|
|
3
|
+
// conflict chip) and one list is the point: testing the first character
|
|
4
|
+
// instead reads AA as "added" and DD as "deleted", so four of the seven
|
|
5
|
+
// conflicts get mislabelled.
|
|
6
|
+
window.MBEDITOR_UNMERGED_STATUSES = { DD: 1, AU: 1, UD: 1, UA: 1, DU: 1, AA: 1, UU: 1 };
|
|
7
|
+
|
|
1
8
|
var GitService = (function () {
|
|
2
9
|
function applyGitInfo(data) {
|
|
3
10
|
var files = data.workingTree || data.files || [];
|
|
@@ -71,6 +78,7 @@ var GitService = (function () {
|
|
|
71
78
|
// unreachable rather than failing over and over in the console.
|
|
72
79
|
function fetchStatusLite(opts) {
|
|
73
80
|
var cfg = (opts && opts.background) ? { mbeditorBackground: true } : {};
|
|
81
|
+
var startedAt = Date.now();
|
|
74
82
|
return axios.get(window.mbeditorBasePath() + '/git_status', cfg)
|
|
75
83
|
.then(function(res) {
|
|
76
84
|
var data = res.data;
|
|
@@ -79,6 +87,8 @@ var GitService = (function () {
|
|
|
79
87
|
var st = EditorStore.getState();
|
|
80
88
|
var prevInfo = st.gitInfo;
|
|
81
89
|
var files = data.files || [];
|
|
90
|
+
var audit = window.MbeditorAudit;
|
|
91
|
+
if (audit) audit.rec(audit.EV.GIT_POLL, Date.now() - startedAt, files.length);
|
|
82
92
|
var gitSig = function(arr) { return arr.map(function(f) { return f.path + '\x00' + f.status; }).join('\x01'); };
|
|
83
93
|
var branchChanged = (data.branch || "") !== (st.gitBranch || "");
|
|
84
94
|
var treeChanged = gitSig(files) !== gitSig(st.gitFiles || []);
|
|
@@ -123,7 +133,11 @@ var GitService = (function () {
|
|
|
123
133
|
|
|
124
134
|
return data;
|
|
125
135
|
})
|
|
126
|
-
.catch(function () {
|
|
136
|
+
.catch(function () {
|
|
137
|
+
// transient poll errors retry on the next tick
|
|
138
|
+
var audit = window.MbeditorAudit;
|
|
139
|
+
if (audit) audit.rec(audit.EV.ERR, audit.EV.GIT_POLL);
|
|
140
|
+
});
|
|
127
141
|
}
|
|
128
142
|
|
|
129
143
|
function fetchStatus() {
|
|
@@ -13,6 +13,10 @@ var HistoryService = (function () {
|
|
|
13
13
|
var _idleTimers = {};
|
|
14
14
|
|
|
15
15
|
var IDLE_MS = 30000;
|
|
16
|
+
// Server-side history format. 2 means tracking starts after the file load, so
|
|
17
|
+
// the load is the base rather than an insert-at-origin op (see #92/#93). The
|
|
18
|
+
// server uses this to tell a legacy payload from a current one.
|
|
19
|
+
var FORMAT_VERSION = 2;
|
|
16
20
|
|
|
17
21
|
function _key(branch, filePath) {
|
|
18
22
|
return branch + ':' + filePath;
|
|
@@ -40,16 +44,6 @@ var HistoryService = (function () {
|
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
function stopTracking(filePath) {
|
|
44
|
-
var rec = _tracking[filePath];
|
|
45
|
-
if (!rec) return;
|
|
46
|
-
var k = _key(rec.branch, filePath);
|
|
47
|
-
clearTimeout(_idleTimers[k]);
|
|
48
|
-
delete _idleTimers[k];
|
|
49
|
-
flush(rec.branch, filePath);
|
|
50
|
-
delete _tracking[filePath];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
47
|
function setReplayInProgress(filePath, inProgress) {
|
|
54
48
|
if (inProgress) {
|
|
55
49
|
_replayingPaths[filePath] = true;
|
|
@@ -86,7 +80,7 @@ var HistoryService = (function () {
|
|
|
86
80
|
clearTimeout(_idleTimers[k]);
|
|
87
81
|
delete _idleTimers[k];
|
|
88
82
|
|
|
89
|
-
var body = { branch: branch, path: filePath, ops: ops };
|
|
83
|
+
var body = { branch: branch, path: filePath, ops: ops, v: FORMAT_VERSION };
|
|
90
84
|
if (_bases.hasOwnProperty(k)) {
|
|
91
85
|
body.base = _bases[k];
|
|
92
86
|
delete _bases[k];
|
|
@@ -161,10 +155,8 @@ var HistoryService = (function () {
|
|
|
161
155
|
return {
|
|
162
156
|
beginTracking: beginTracking,
|
|
163
157
|
resumeTracking: resumeTracking,
|
|
164
|
-
stopTracking: stopTracking,
|
|
165
158
|
setReplayInProgress: setReplayInProgress,
|
|
166
159
|
recordOps: recordOps,
|
|
167
|
-
flush: flush,
|
|
168
160
|
flushAll: flushAll,
|
|
169
161
|
flushForPath: flushForPath,
|
|
170
162
|
fetchHistory: fetchHistory
|
|
@@ -79,13 +79,31 @@ var SearchService = (function () {
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// Fuzziness per term: edit distance is `fuzzy * term.length` inside
|
|
83
|
+
// MiniSearch, so short terms get none (at 3 chars every 3-letter token in the
|
|
84
|
+
// tree is within one edit and the list becomes noise) and longer ones get
|
|
85
|
+
// roughly one typo per 4 characters.
|
|
86
|
+
function _fuzzyFor(term) {
|
|
87
|
+
return term.length > 3 ? 0.25 : false;
|
|
88
|
+
}
|
|
89
|
+
|
|
82
90
|
// Search files (and optionally folders) in the local MiniSearch index.
|
|
91
|
+
// Fuzzy + prefix, so a misspelled or half-typed query still finds the file;
|
|
92
|
+
// ranking back in QuickOpenDialog puts exact matches first, so loosening the
|
|
93
|
+
// match here cannot demote one.
|
|
83
94
|
// Also performs a case-insensitive substring scan so that partial-word
|
|
84
|
-
// queries like "project" reliably find "projects_controller.rb"
|
|
95
|
+
// queries like "project" reliably find "projects_controller.rb" — the
|
|
96
|
+
// tokenizer splits on punctuation, so a query spanning a separator
|
|
97
|
+
// ("models/user") tokenizes into terms no single token can satisfy.
|
|
85
98
|
// Returns merged results; MiniSearch scored entries come first.
|
|
86
99
|
function searchFiles(query) {
|
|
87
100
|
if (!query) return [];
|
|
88
|
-
var msResults = _miniSearch.search(query, {
|
|
101
|
+
var msResults = _miniSearch.search(query, {
|
|
102
|
+
prefix: true,
|
|
103
|
+
fuzzy: _fuzzyFor,
|
|
104
|
+
combineWith: 'AND',
|
|
105
|
+
boost: { name: 2 }
|
|
106
|
+
});
|
|
89
107
|
// Substring fallback — catch anything MiniSearch missed
|
|
90
108
|
var q = query.toLowerCase();
|
|
91
109
|
var msIds = new Set(msResults.map(function(r) { return r.id; }));
|
|
@@ -127,6 +145,7 @@ var SearchService = (function () {
|
|
|
127
145
|
var controller = new AbortController();
|
|
128
146
|
_searchController = controller;
|
|
129
147
|
|
|
148
|
+
var startedAt = Date.now();
|
|
130
149
|
return axios.get(window.mbeditorBasePath() + '/search', {
|
|
131
150
|
params: { q: query, offset: off, limit: lim, regex: useRegex ? 'true' : 'false', match_case: matchCase ? 'true' : 'false', whole_word: wholeWord ? 'true' : 'false' },
|
|
132
151
|
signal: controller.signal
|
|
@@ -137,6 +156,12 @@ var SearchService = (function () {
|
|
|
137
156
|
var hasMore = !Array.isArray(data) && !!(data && data.has_more);
|
|
138
157
|
var totalCount = (data && data.total_count != null) ? data.total_count : null;
|
|
139
158
|
|
|
159
|
+
var audit = window.MbeditorAudit;
|
|
160
|
+
if (audit) {
|
|
161
|
+
audit.rec(audit.EV.SEARCH, audit.code('searchBackend', window.MBEDITOR_SEARCH_BACKEND),
|
|
162
|
+
Date.now() - startedAt, results.length);
|
|
163
|
+
}
|
|
164
|
+
|
|
140
165
|
var payload = { results: results, hasMore: hasMore, totalCount: totalCount };
|
|
141
166
|
_cacheSet(key, payload);
|
|
142
167
|
|
|
@@ -152,6 +177,8 @@ var SearchService = (function () {
|
|
|
152
177
|
if (axios.isCancel(err) || (err && err.name === 'CanceledError')) {
|
|
153
178
|
return { results: [], hasMore: false, totalCount: null };
|
|
154
179
|
}
|
|
180
|
+
var audit = window.MbeditorAudit;
|
|
181
|
+
if (audit) audit.rec(audit.EV.ERR, audit.EV.SEARCH);
|
|
155
182
|
EditorStore.setStatus("Search failed: " + err.message, "error");
|
|
156
183
|
return { results: [], hasMore: false, totalCount: null };
|
|
157
184
|
});
|