mbeditor 0.3.8 → 0.3.9
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 +10 -0
- data/app/assets/javascripts/mbeditor/application_iife_head.js +7 -0
- data/app/assets/javascripts/mbeditor/components/CodeReviewPanel.js +1 -1
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +52 -11
- data/app/assets/javascripts/mbeditor/components/GitPanel.js +14 -4
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +316 -133
- data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +25 -1
- data/app/assets/javascripts/mbeditor/editor_store.js +10 -2
- data/app/assets/javascripts/mbeditor/file_service.js +23 -23
- data/app/assets/javascripts/mbeditor/git_service.js +7 -11
- data/app/assets/javascripts/mbeditor/search_service.js +45 -12
- data/app/assets/javascripts/mbeditor/tab_manager.js +3 -3
- data/app/assets/stylesheets/mbeditor/editor.css +12 -5
- data/app/controllers/mbeditor/editors_controller.rb +134 -128
- data/app/controllers/mbeditor/git_controller.rb +5 -40
- data/app/services/mbeditor/git_blame_service.rb +6 -0
- data/app/services/mbeditor/git_commit_graph_service.rb +2 -0
- data/app/services/mbeditor/git_service.rb +97 -28
- data/app/services/mbeditor/redmine_service.rb +7 -0
- data/app/services/mbeditor/ruby_definition_service.rb +23 -2
- data/lib/mbeditor/configuration.rb +7 -1
- data/lib/mbeditor/engine.rb +27 -0
- data/lib/mbeditor/version.rb +3 -1
- data/lib/mbeditor.rb +2 -0
- metadata +2 -2
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
// The server uses this header to silence editor logs and guard non-GET requests.
|
|
3
3
|
axios.defaults.headers.common['X-Mbeditor-Client'] = '1';
|
|
4
4
|
|
|
5
|
+
// Cap all API calls at 30 s so a hung Rails server never blocks the UI forever.
|
|
6
|
+
// The ping endpoint overrides this with a tighter 4 s timeout per-request.
|
|
7
|
+
axios.defaults.timeout = 30000;
|
|
8
|
+
|
|
5
9
|
// Surface pending-migration errors as a dismissible banner instead of silently failing.
|
|
6
10
|
axios.interceptors.response.use(null, function(error) {
|
|
7
11
|
if (error.response && error.response.data && error.response.data.pending_migration_error) {
|
|
@@ -28,16 +32,12 @@ axios.interceptors.response.use(null, function(error) {
|
|
|
28
32
|
});
|
|
29
33
|
|
|
30
34
|
var FileService = (function () {
|
|
31
|
-
function basePath() {
|
|
32
|
-
return (window.MBEDITOR_BASE_PATH || '/mbeditor').replace(/\/$/, '');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
35
|
function getWorkspace() {
|
|
36
|
-
return axios.get(
|
|
36
|
+
return axios.get(window.mbeditorBasePath() + '/workspace').then(function(res) { return res.data; });
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
function getTree() {
|
|
40
|
-
return axios.get(
|
|
40
|
+
return axios.get(window.mbeditorBasePath() + '/files').then(function(res) { return res.data; });
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function getFile(path, options) {
|
|
@@ -45,72 +45,72 @@ var FileService = (function () {
|
|
|
45
45
|
if (options && options.allowMissing) {
|
|
46
46
|
params.allow_missing = '1';
|
|
47
47
|
}
|
|
48
|
-
return axios.get(
|
|
48
|
+
return axios.get(window.mbeditorBasePath() + '/file', { params: params }).then(function(res) { return res.data; });
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function saveFile(path, code) {
|
|
52
|
-
return axios.post(
|
|
52
|
+
return axios.post(window.mbeditorBasePath() + '/file', { path: path, code: code }).then(function(res) { return res.data; });
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function createFile(path, code) {
|
|
56
|
-
return axios.post(
|
|
56
|
+
return axios.post(window.mbeditorBasePath() + '/create_file', { path: path, code: code || '' }).then(function(res) { return res.data; });
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
function createDir(path) {
|
|
60
|
-
return axios.post(
|
|
60
|
+
return axios.post(window.mbeditorBasePath() + '/create_dir', { path: path }).then(function(res) { return res.data; });
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
function renamePath(path, newPath) {
|
|
64
|
-
return axios.patch(
|
|
64
|
+
return axios.patch(window.mbeditorBasePath() + '/rename', { path: path, new_path: newPath }).then(function(res) { return res.data; });
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function deletePath(path) {
|
|
68
|
-
return axios.delete(
|
|
68
|
+
return axios.delete(window.mbeditorBasePath() + '/delete', { data: { path: path } }).then(function(res) { return res.data; });
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function lintFile(path, code) {
|
|
72
|
-
return axios.post(
|
|
72
|
+
return axios.post(window.mbeditorBasePath() + '/lint', { path: path, code: code }).then(function(res) { return res.data; });
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function quickFixOffense(path, code, copName) {
|
|
76
|
-
return axios.post(
|
|
76
|
+
return axios.post(window.mbeditorBasePath() + '/quick_fix', { path: path, code: code, cop_name: copName }).then(function(res) { return res.data; });
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function formatFile(path, code) {
|
|
80
|
-
return axios.post(
|
|
80
|
+
return axios.post(window.mbeditorBasePath() + '/format', { path: path, code: code }).then(function(res) { return res.data; });
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
function runTests(path) {
|
|
84
|
-
return axios.post(
|
|
84
|
+
return axios.post(window.mbeditorBasePath() + '/test', { path: path }).then(function(res) { return res.data; });
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function ping() {
|
|
88
|
-
return axios.get(
|
|
88
|
+
return axios.get(window.mbeditorBasePath() + '/ping', { timeout: 4000 }).then(function(res) { return res.data; });
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
function getState() {
|
|
92
|
-
return axios.get(
|
|
92
|
+
return axios.get(window.mbeditorBasePath() + '/state').then(function(res) { return res.data; });
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function saveState(state) {
|
|
96
|
-
return axios.post(
|
|
96
|
+
return axios.post(window.mbeditorBasePath() + '/state', { state: state }).then(function(res) { return res.data; });
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
function getBranchState(branch) {
|
|
100
|
-
return axios.get(
|
|
100
|
+
return axios.get(window.mbeditorBasePath() + '/branch_state', { params: { branch: branch } }).then(function(res) { return res.data; });
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
function saveBranchState(branch, state) {
|
|
104
|
-
return axios.post(
|
|
104
|
+
return axios.post(window.mbeditorBasePath() + '/branch_state', { branch: branch, state: state }).then(function(res) { return res.data; });
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function pruneBranchStates() {
|
|
108
|
-
return axios.post(
|
|
108
|
+
return axios.post(window.mbeditorBasePath() + '/prune_branch_states').then(function(res) { return res.data; });
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
function getDefinition(symbol, language, extraOptions) {
|
|
112
112
|
var config = Object.assign({ params: { symbol: symbol, language: language }, timeout: 5000 }, extraOptions || {});
|
|
113
|
-
return axios.get(
|
|
113
|
+
return axios.get(window.mbeditorBasePath() + '/definition', config).then(function(res) { return res.data; });
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
return {
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
var GitService = (function () {
|
|
2
|
-
function basePath() {
|
|
3
|
-
return (window.MBEDITOR_BASE_PATH || '/mbeditor').replace(/\/$/, '');
|
|
4
|
-
}
|
|
5
|
-
|
|
6
2
|
function applyGitInfo(data) {
|
|
7
3
|
var files = data.workingTree || data.files || [];
|
|
8
4
|
EditorStore.setState({
|
|
@@ -14,7 +10,7 @@ var GitService = (function () {
|
|
|
14
10
|
}
|
|
15
11
|
|
|
16
12
|
function fetchInfo() {
|
|
17
|
-
return axios.get(
|
|
13
|
+
return axios.get(window.mbeditorBasePath() + '/git_info')
|
|
18
14
|
.then(function(res) {
|
|
19
15
|
if (res.data && res.data.ok) {
|
|
20
16
|
applyGitInfo(res.data);
|
|
@@ -33,7 +29,7 @@ var GitService = (function () {
|
|
|
33
29
|
return fetchInfo().then(function(data) {
|
|
34
30
|
if (data && data.ok) return data;
|
|
35
31
|
|
|
36
|
-
return axios.get(
|
|
32
|
+
return axios.get(window.mbeditorBasePath() + '/git_status')
|
|
37
33
|
.then(function(res) {
|
|
38
34
|
if (res.data.ok) {
|
|
39
35
|
EditorStore.setState({
|
|
@@ -63,31 +59,31 @@ var GitService = (function () {
|
|
|
63
59
|
if (baseSha) query += '&base=' + encodeURIComponent(baseSha);
|
|
64
60
|
if (headSha) query += '&head=' + encodeURIComponent(headSha);
|
|
65
61
|
|
|
66
|
-
return axios.get(
|
|
62
|
+
return axios.get(window.mbeditorBasePath() + '/git/diff' + query).then(function(res) {
|
|
67
63
|
return res.data;
|
|
68
64
|
});
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
function fetchBlame(path) {
|
|
72
|
-
return axios.get(
|
|
68
|
+
return axios.get(window.mbeditorBasePath() + '/git/blame?file=' + encodeURIComponent(path)).then(function(res) {
|
|
73
69
|
return res.data;
|
|
74
70
|
});
|
|
75
71
|
}
|
|
76
72
|
|
|
77
73
|
function fetchFileHistory(path) {
|
|
78
|
-
return axios.get(
|
|
74
|
+
return axios.get(window.mbeditorBasePath() + '/git/file_history?file=' + encodeURIComponent(path)).then(function(res) {
|
|
79
75
|
return res.data;
|
|
80
76
|
});
|
|
81
77
|
}
|
|
82
78
|
|
|
83
79
|
function fetchCommitGraph() {
|
|
84
|
-
return axios.get(
|
|
80
|
+
return axios.get(window.mbeditorBasePath() + '/git/commit_graph').then(function(res) {
|
|
85
81
|
return res.data;
|
|
86
82
|
});
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
function fetchCommitDetail(sha) {
|
|
90
|
-
return axios.get(
|
|
86
|
+
return axios.get(window.mbeditorBasePath() + '/git/commit_detail?sha=' + encodeURIComponent(sha)).then(function(res) {
|
|
91
87
|
return res.data;
|
|
92
88
|
});
|
|
93
89
|
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
var SearchService = (function () {
|
|
2
2
|
var SEARCH_PAGE_SIZE = 50;
|
|
3
3
|
|
|
4
|
-
function basePath() {
|
|
5
|
-
return (window.MBEDITOR_BASE_PATH || '/mbeditor').replace(/\/$/, '');
|
|
6
|
-
}
|
|
7
|
-
|
|
8
4
|
var _miniSearch = new MiniSearch({
|
|
9
5
|
fields: ['path', 'name'], // indexed fields
|
|
10
6
|
storeFields: ['path', 'name', 'type'] // returned fields (type: 'file'|'dir')
|
|
11
7
|
});
|
|
12
8
|
|
|
9
|
+
// Flat doc list kept in sync with _miniSearch so we can do substring lookups.
|
|
10
|
+
var _allDocs = [];
|
|
11
|
+
|
|
13
12
|
function buildIndex(treeData) {
|
|
14
13
|
// Capture the tree data immediately so a subsequent refresh doesn't
|
|
15
14
|
// clobber us before the idle callback fires.
|
|
@@ -32,31 +31,46 @@ var SearchService = (function () {
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
traverse(snapshot);
|
|
34
|
+
_allDocs = docs.slice();
|
|
35
35
|
_miniSearch.removeAll();
|
|
36
36
|
_miniSearch.addAll(docs);
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// Search files (and optionally folders) in the local MiniSearch index.
|
|
41
|
-
//
|
|
41
|
+
// Also performs a case-insensitive substring scan so that partial-word
|
|
42
|
+
// queries like "project" reliably find "projects_controller.rb".
|
|
43
|
+
// Returns merged results; MiniSearch scored entries come first.
|
|
42
44
|
function searchFiles(query) {
|
|
43
45
|
if (!query) return [];
|
|
44
|
-
|
|
46
|
+
var msResults = _miniSearch.search(query, { prefix: true, fuzzy: 0.2 });
|
|
47
|
+
// Substring fallback — catch anything MiniSearch missed
|
|
48
|
+
var q = query.toLowerCase();
|
|
49
|
+
var msIds = new Set(msResults.map(function(r) { return r.id; }));
|
|
50
|
+
var subResults = _allDocs.filter(function(doc) {
|
|
51
|
+
if (msIds.has(doc.id)) return false;
|
|
52
|
+
return doc.path.toLowerCase().indexOf(q) >= 0 || doc.name.toLowerCase().indexOf(q) >= 0;
|
|
53
|
+
}).map(function(doc) {
|
|
54
|
+
return { id: doc.id, path: doc.path, name: doc.name, type: doc.type, score: 0 };
|
|
55
|
+
});
|
|
56
|
+
return msResults.concat(subResults);
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
// Fetch one page of project-wide full-text search results.
|
|
48
60
|
// offset=0 replaces the EditorStore results list; offset>0 appends.
|
|
61
|
+
// The server includes total_count only on offset=0 (fast rg --count pass).
|
|
49
62
|
function projectSearch(query, offset, limit) {
|
|
50
|
-
if (!query) return Promise.resolve({ results: [], hasMore: false });
|
|
63
|
+
if (!query) return Promise.resolve({ results: [], hasMore: false, totalCount: 0 });
|
|
51
64
|
var off = (typeof offset === 'number') ? offset : 0;
|
|
52
65
|
var lim = (typeof limit === 'number') ? limit : SEARCH_PAGE_SIZE;
|
|
53
66
|
|
|
54
|
-
return axios.get(
|
|
67
|
+
return axios.get(window.mbeditorBasePath() + '/search', {
|
|
55
68
|
params: { q: query, offset: off, limit: lim }
|
|
56
69
|
}).then(function(res) {
|
|
57
70
|
var data = res.data;
|
|
58
|
-
var results
|
|
59
|
-
var hasMore
|
|
71
|
+
var results = Array.isArray(data) ? data : (data && data.results || []);
|
|
72
|
+
var hasMore = !Array.isArray(data) && !!(data && data.has_more);
|
|
73
|
+
var totalCount = (data && data.total_count != null) ? data.total_count : null;
|
|
60
74
|
|
|
61
75
|
if (off === 0) {
|
|
62
76
|
EditorStore.setState({ searchResults: results, searchHasMore: hasMore });
|
|
@@ -64,18 +78,37 @@ var SearchService = (function () {
|
|
|
64
78
|
var prev = EditorStore.getState().searchResults || [];
|
|
65
79
|
EditorStore.setState({ searchResults: prev.concat(results), searchHasMore: hasMore });
|
|
66
80
|
}
|
|
67
|
-
return { results: results, hasMore: hasMore };
|
|
81
|
+
return { results: results, hasMore: hasMore, totalCount: totalCount };
|
|
68
82
|
})
|
|
69
83
|
.catch(function(err) {
|
|
70
84
|
EditorStore.setStatus("Search failed: " + err.message, "error");
|
|
71
|
-
return { results: [], hasMore: false };
|
|
85
|
+
return { results: [], hasMore: false, totalCount: null };
|
|
72
86
|
});
|
|
73
87
|
}
|
|
74
88
|
|
|
89
|
+
// Fetch a specific page by index without touching EditorStore.
|
|
90
|
+
// Used by the random-access virtual scroll loader.
|
|
91
|
+
function fetchPage(query, pageIndex) {
|
|
92
|
+
if (!query) return Promise.resolve({ results: [], hasMore: false });
|
|
93
|
+
var offset = pageIndex * SEARCH_PAGE_SIZE;
|
|
94
|
+
return axios.get(basePath() + '/search', {
|
|
95
|
+
params: { q: query, offset: offset, limit: SEARCH_PAGE_SIZE }
|
|
96
|
+
}).then(function(res) {
|
|
97
|
+
var data = res.data;
|
|
98
|
+
var results = Array.isArray(data) ? data : (data && data.results || []);
|
|
99
|
+
var hasMore = !Array.isArray(data) && !!(data && data.has_more);
|
|
100
|
+
return { results: results, hasMore: hasMore };
|
|
101
|
+
}).catch(function(err) {
|
|
102
|
+
EditorStore.setStatus("Search failed: " + err.message, "error");
|
|
103
|
+
return { results: [], hasMore: false };
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
75
107
|
return {
|
|
76
108
|
buildIndex: buildIndex,
|
|
77
109
|
searchFiles: searchFiles,
|
|
78
110
|
projectSearch: projectSearch,
|
|
111
|
+
fetchPage: fetchPage,
|
|
79
112
|
PAGE_SIZE: SEARCH_PAGE_SIZE
|
|
80
113
|
};
|
|
81
114
|
})();
|
|
@@ -257,11 +257,11 @@ var TabManager = (function () {
|
|
|
257
257
|
});
|
|
258
258
|
EditorStore.setState({ panes: newPanes, focusedPaneId: paneId, activeTabId: tabId });
|
|
259
259
|
|
|
260
|
-
|
|
261
|
-
axios.get(basePath + '/git/combined_diff', { params: { scope: scope || 'local' } })
|
|
260
|
+
axios.get(window.mbeditorBasePath() + '/git/combined_diff', { params: { scope: scope || 'local' } })
|
|
262
261
|
.then(function(res) {
|
|
262
|
+
var data = res.data;
|
|
263
263
|
_updateTab(paneId, tabId, {
|
|
264
|
-
combinedDiffText: typeof
|
|
264
|
+
combinedDiffText: typeof data === 'string' ? data : (data && data.diff) || '',
|
|
265
265
|
combinedDiffLoaded: true
|
|
266
266
|
});
|
|
267
267
|
})
|
|
@@ -475,16 +475,23 @@ html, body, #mbeditor-root {
|
|
|
475
475
|
.welcome-tips li { font-size: 12px; color: var(--ide-text-muted); }
|
|
476
476
|
.welcome-tips li i { color: var(--ide-text-muted); width: 14px; text-align: center; }
|
|
477
477
|
|
|
478
|
-
/* ── Search results
|
|
479
|
-
.search-results-
|
|
480
|
-
|
|
478
|
+
/* ── Search results header (fixed count above scroll area) ── */
|
|
479
|
+
.search-results-header {
|
|
480
|
+
flex-shrink: 0;
|
|
481
|
+
padding: 4px 10px 5px;
|
|
481
482
|
font-size: 11px;
|
|
482
483
|
color: var(--ide-text-muted);
|
|
483
484
|
border-bottom: 1px solid var(--ide-panel-bg-alt);
|
|
484
|
-
|
|
485
|
+
user-select: none;
|
|
485
486
|
}
|
|
486
|
-
.search-results-capped { color: #f0a040; }
|
|
487
487
|
.search-results-empty { padding: 12px 8px; font-size: 12px; color: var(--ide-text-muted); }
|
|
488
|
+
.search-loading-more {
|
|
489
|
+
padding: 10px;
|
|
490
|
+
font-size: 11px;
|
|
491
|
+
color: var(--ide-text-muted);
|
|
492
|
+
text-align: center;
|
|
493
|
+
user-select: none;
|
|
494
|
+
}
|
|
488
495
|
|
|
489
496
|
/* ── Shortcut Help Drawer ────────────────────────────────── */
|
|
490
497
|
.shelp-backdrop {
|