mbeditor 0.2.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 +7 -0
- data/CHANGELOG.md +116 -0
- data/README.md +180 -0
- data/app/assets/javascripts/mbeditor/application.js +21 -0
- data/app/assets/javascripts/mbeditor/components/CodeReviewPanel.js +202 -0
- data/app/assets/javascripts/mbeditor/components/CollapsibleSection.js +71 -0
- data/app/assets/javascripts/mbeditor/components/CombinedDiffViewer.js +139 -0
- data/app/assets/javascripts/mbeditor/components/CommitGraph.js +65 -0
- data/app/assets/javascripts/mbeditor/components/DiffViewer.js +166 -0
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +1139 -0
- data/app/assets/javascripts/mbeditor/components/FileHistoryPanel.js +117 -0
- data/app/assets/javascripts/mbeditor/components/FileTree.js +339 -0
- data/app/assets/javascripts/mbeditor/components/GitPanel.js +501 -0
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +3108 -0
- data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +272 -0
- data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +186 -0
- data/app/assets/javascripts/mbeditor/components/TabBar.js +238 -0
- data/app/assets/javascripts/mbeditor/components/TestResultsPanel.js +150 -0
- data/app/assets/javascripts/mbeditor/editor_plugins.js +758 -0
- data/app/assets/javascripts/mbeditor/editor_store.js +69 -0
- data/app/assets/javascripts/mbeditor/file_icon.js +30 -0
- data/app/assets/javascripts/mbeditor/file_service.js +96 -0
- data/app/assets/javascripts/mbeditor/git_service.js +104 -0
- data/app/assets/javascripts/mbeditor/search_service.js +63 -0
- data/app/assets/javascripts/mbeditor/tab_manager.js +485 -0
- data/app/assets/stylesheets/mbeditor/application.css +848 -0
- data/app/assets/stylesheets/mbeditor/editor.css +2061 -0
- data/app/controllers/mbeditor/application_controller.rb +70 -0
- data/app/controllers/mbeditor/editors_controller.rb +996 -0
- data/app/controllers/mbeditor/git_controller.rb +234 -0
- data/app/services/mbeditor/git_blame_service.rb +98 -0
- data/app/services/mbeditor/git_commit_graph_service.rb +60 -0
- data/app/services/mbeditor/git_diff_service.rb +74 -0
- data/app/services/mbeditor/git_file_history_service.rb +42 -0
- data/app/services/mbeditor/git_service.rb +95 -0
- data/app/services/mbeditor/redmine_service.rb +86 -0
- data/app/services/mbeditor/ruby_definition_service.rb +168 -0
- data/app/services/mbeditor/test_runner_service.rb +286 -0
- data/app/views/layouts/mbeditor/application.html.erb +120 -0
- data/app/views/mbeditor/editors/index.html.erb +1 -0
- data/config/initializers/assets.rb +9 -0
- data/config/routes.rb +44 -0
- data/lib/mbeditor/configuration.rb +22 -0
- data/lib/mbeditor/engine.rb +37 -0
- data/lib/mbeditor/rack/silence_ping_request.rb +56 -0
- data/lib/mbeditor/version.rb +3 -0
- data/lib/mbeditor.rb +19 -0
- data/mbeditor.gemspec +31 -0
- data/public/mbeditor-icon.svg +4 -0
- data/public/min-maps/vs/base/worker/workerMain.js.map +1 -0
- data/public/monaco-editor/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
- data/public/monaco-editor/vs/base/worker/workerMain.js +31 -0
- data/public/monaco-editor/vs/basic-languages/cameligo/cameligo.js +10 -0
- data/public/monaco-editor/vs/basic-languages/css/css.js +12 -0
- data/public/monaco-editor/vs/basic-languages/dart/dart.js +10 -0
- data/public/monaco-editor/vs/basic-languages/flow9/flow9.js +10 -0
- data/public/monaco-editor/vs/basic-languages/go/go.js +10 -0
- data/public/monaco-editor/vs/basic-languages/handlebars/handlebars.js +440 -0
- data/public/monaco-editor/vs/basic-languages/javascript/javascript.js +10 -0
- data/public/monaco-editor/vs/basic-languages/markdown/markdown.js +10 -0
- data/public/monaco-editor/vs/basic-languages/msdax/msdax.js +10 -0
- data/public/monaco-editor/vs/basic-languages/postiats/postiats.js +10 -0
- data/public/monaco-editor/vs/basic-languages/pug/pug.js +412 -0
- data/public/monaco-editor/vs/basic-languages/restructuredtext/restructuredtext.js +10 -0
- data/public/monaco-editor/vs/basic-languages/ruby/ruby.js +10 -0
- data/public/monaco-editor/vs/basic-languages/sb/sb.js +10 -0
- data/public/monaco-editor/vs/basic-languages/shell/shell.js +41 -0
- data/public/monaco-editor/vs/basic-languages/typescript/typescript.js +10 -0
- data/public/monaco-editor/vs/basic-languages/typespec/typespec.js +10 -0
- data/public/monaco-editor/vs/basic-languages/yaml/yaml.js +10 -0
- data/public/monaco-editor/vs/editor/editor.api.js +6 -0
- data/public/monaco-editor/vs/editor/editor.main.css +8 -0
- data/public/monaco-editor/vs/editor/editor.main.js +797 -0
- data/public/monaco-editor/vs/language/typescript/tsMode.js +20 -0
- data/public/monaco-editor/vs/language/typescript/tsWorker.js +51328 -0
- data/public/monaco-editor/vs/loader.js +10 -0
- data/public/monaco-editor/vs/nls.messages.de.js +20 -0
- data/public/monaco-editor/vs/nls.messages.es.js +20 -0
- data/public/monaco-editor/vs/nls.messages.fr.js +18 -0
- data/public/monaco-editor/vs/nls.messages.it.js +18 -0
- data/public/monaco-editor/vs/nls.messages.ja.js +20 -0
- data/public/monaco-editor/vs/nls.messages.ko.js +18 -0
- data/public/monaco-editor/vs/nls.messages.ru.js +20 -0
- data/public/monaco-editor/vs/nls.messages.zh-cn.js +20 -0
- data/public/monaco-editor/vs/nls.messages.zh-tw.js +18 -0
- data/public/monaco_worker.js +5 -0
- data/public/sw.js +8 -0
- data/public/ts_worker.js +5 -0
- data/vendor/assets/javascripts/axios.min.js +5 -0
- data/vendor/assets/javascripts/emmet.js +5452 -0
- data/vendor/assets/javascripts/lodash.min.js +136 -0
- data/vendor/assets/javascripts/marked.min.js +6 -0
- data/vendor/assets/javascripts/minisearch.min.js +2044 -0
- data/vendor/assets/javascripts/monaco-themes-bundle.js +10 -0
- data/vendor/assets/javascripts/monaco-vim.js +9867 -0
- data/vendor/assets/javascripts/prettier-plugin-babel.js +16 -0
- data/vendor/assets/javascripts/prettier-plugin-estree.js +35 -0
- data/vendor/assets/javascripts/prettier-plugin-html.js +19 -0
- data/vendor/assets/javascripts/prettier-plugin-markdown.js +59 -0
- data/vendor/assets/javascripts/prettier-plugin-postcss.js +52 -0
- data/vendor/assets/javascripts/prettier-standalone.js +37 -0
- data/vendor/assets/javascripts/react-dom.min.js +267 -0
- data/vendor/assets/javascripts/react.min.js +31 -0
- data/vendor/assets/stylesheets/fontawesome.min.css.erb +9 -0
- data/vendor/assets/webfonts/fa-brands-400.woff2 +0 -0
- data/vendor/assets/webfonts/fa-regular-400.woff2 +0 -0
- data/vendor/assets/webfonts/fa-solid-900.woff2 +0 -0
- metadata +188 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// EditorStore — central state store for the browser IDE
|
|
2
|
+
// All state lives here; UI subscribes via listeners.
|
|
3
|
+
var EditorStore = (function () {
|
|
4
|
+
var _state = {
|
|
5
|
+
panes: [
|
|
6
|
+
{ id: 1, tabs: [], activeTabId: null },
|
|
7
|
+
{ id: 2, tabs: [], activeTabId: null }
|
|
8
|
+
],
|
|
9
|
+
focusedPaneId: 1, // which pane currently has focus
|
|
10
|
+
activeTabId: null, // alias for backwards compat/easy access to the currently focused tab in the focused pane
|
|
11
|
+
gitFiles: [], // [{ status, path }]
|
|
12
|
+
gitBranch: "",
|
|
13
|
+
gitInfo: null,
|
|
14
|
+
gitInfoError: null,
|
|
15
|
+
searchResults: [],
|
|
16
|
+
searchCapped: false,
|
|
17
|
+
statusMessage: { text: "", kind: "info" },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
var _listeners = [];
|
|
21
|
+
|
|
22
|
+
function getState() { return _state; }
|
|
23
|
+
|
|
24
|
+
function subscribe(fn) {
|
|
25
|
+
_listeners.push(fn);
|
|
26
|
+
return function unsubscribe() {
|
|
27
|
+
_listeners = _listeners.filter(function (l) { return l !== fn; });
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function emit() {
|
|
32
|
+
_listeners.forEach(function (fn) { fn(_state); });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function setState(patch) {
|
|
36
|
+
_state = Object.assign({}, _state, patch);
|
|
37
|
+
emit();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Subscribe to changes in a specific subset of state keys.
|
|
41
|
+
// The listener is only called when at least one of the watched keys changes
|
|
42
|
+
// by reference (===), preventing unnecessary re-renders for unrelated updates.
|
|
43
|
+
function subscribeToSlice(keys, fn) {
|
|
44
|
+
var prev = {};
|
|
45
|
+
keys.forEach(function(k) { prev[k] = _state[k]; });
|
|
46
|
+
return subscribe(function(newState) {
|
|
47
|
+
var changed = keys.some(function(k) { return newState[k] !== prev[k]; });
|
|
48
|
+
if (changed) {
|
|
49
|
+
keys.forEach(function(k) { prev[k] = newState[k]; });
|
|
50
|
+
fn(newState);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function setStatus(text, kind) {
|
|
56
|
+
kind = kind || "info";
|
|
57
|
+
setState({ statusMessage: { text: text, kind: kind } });
|
|
58
|
+
// Auto-clear after 4s for non-error messages
|
|
59
|
+
if (kind !== "error") {
|
|
60
|
+
setTimeout(function () {
|
|
61
|
+
if (_state.statusMessage.text === text) {
|
|
62
|
+
setState({ statusMessage: { text: "", kind: "info" } });
|
|
63
|
+
}
|
|
64
|
+
}, 4000);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { getState: getState, subscribe: subscribe, subscribeToSlice: subscribeToSlice, setState: setState, setStatus: setStatus };
|
|
69
|
+
})();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
var MbeditorFileIcon = (function () {
|
|
2
|
+
function getFileIcon(name) {
|
|
3
|
+
var value = String(name || '');
|
|
4
|
+
var lower = value.toLowerCase();
|
|
5
|
+
var parts = lower.split('.');
|
|
6
|
+
var ext = parts.length > 1 ? parts.pop() : '';
|
|
7
|
+
|
|
8
|
+
if (lower === 'gemfile' || ext === 'gemspec' || ext === 'lock') return 'fas fa-gem ruby-icon';
|
|
9
|
+
if (ext === 'rb' || ext === 'rake' || lower === 'rakefile') return 'far fa-gem ruby-icon';
|
|
10
|
+
if (ext === 'jsx' || lower.endsWith('.js.jsx')) return 'fas fa-atom react-icon';
|
|
11
|
+
if (ext === 'js' || ext === 'mjs' || ext === 'cjs') return 'fa-brands fa-js js-icon';
|
|
12
|
+
if (ext === 'ts' || ext === 'tsx') return 'fas fa-code typescript-icon';
|
|
13
|
+
if (ext === 'html' || ext === 'htm') return 'fa-brands fa-html5 html-icon';
|
|
14
|
+
if (ext === 'erb') return 'fa-brands fa-html5 erb-icon';
|
|
15
|
+
if (ext === 'css' || ext === 'scss' || ext === 'sass') return 'fa-brands fa-css3-alt css-icon';
|
|
16
|
+
if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'ico', 'webp', 'bmp', 'avif'].includes(ext)) return 'far fa-file-image image-icon';
|
|
17
|
+
if (ext === 'json') return 'fas fa-code json-icon';
|
|
18
|
+
if (ext === 'md' || ext === 'txt') return 'fas fa-file-alt md-icon';
|
|
19
|
+
if (ext === 'yml' || ext === 'yaml') return 'fas fa-cogs yml-icon';
|
|
20
|
+
if (ext === 'sh' || ext === 'bash' || ext === 'zsh') return 'fas fa-terminal shell-icon';
|
|
21
|
+
if (ext === 'pdf') return 'far fa-file-pdf pdf-icon';
|
|
22
|
+
|
|
23
|
+
return 'far fa-file-code';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return { getFileIcon: getFileIcon };
|
|
27
|
+
})();
|
|
28
|
+
|
|
29
|
+
window.MbeditorFileIcon = MbeditorFileIcon;
|
|
30
|
+
window.getFileIcon = window.getFileIcon || MbeditorFileIcon.getFileIcon;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Identify all requests as coming from the mbeditor client.
|
|
2
|
+
// The server uses this header to silence editor logs and guard non-GET requests.
|
|
3
|
+
axios.defaults.headers.common['X-Mbeditor-Client'] = '1';
|
|
4
|
+
|
|
5
|
+
var FileService = (function () {
|
|
6
|
+
function basePath() {
|
|
7
|
+
return (window.MBEDITOR_BASE_PATH || '/mbeditor').replace(/\/$/, '');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getWorkspace() {
|
|
11
|
+
return axios.get(basePath() + '/workspace').then(function(res) { return res.data; });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getTree() {
|
|
15
|
+
return axios.get(basePath() + '/files').then(function(res) { return res.data; });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getFile(path, options) {
|
|
19
|
+
var params = { path: path };
|
|
20
|
+
if (options && options.allowMissing) {
|
|
21
|
+
params.allow_missing = '1';
|
|
22
|
+
}
|
|
23
|
+
return axios.get(basePath() + '/file', { params: params }).then(function(res) { return res.data; });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function saveFile(path, code) {
|
|
27
|
+
return axios.post(basePath() + '/file', { path: path, code: code }).then(function(res) { return res.data; });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function createFile(path, code) {
|
|
31
|
+
return axios.post(basePath() + '/create_file', { path: path, code: code || '' }).then(function(res) { return res.data; });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function createDir(path) {
|
|
35
|
+
return axios.post(basePath() + '/create_dir', { path: path }).then(function(res) { return res.data; });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renamePath(path, newPath) {
|
|
39
|
+
return axios.patch(basePath() + '/rename', { path: path, new_path: newPath }).then(function(res) { return res.data; });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function deletePath(path) {
|
|
43
|
+
return axios.delete(basePath() + '/delete', { data: { path: path } }).then(function(res) { return res.data; });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function lintFile(path, code) {
|
|
47
|
+
return axios.post(basePath() + '/lint', { path: path, code: code }).then(function(res) { return res.data; });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function quickFixOffense(path, code, copName) {
|
|
51
|
+
return axios.post(basePath() + '/quick_fix', { path: path, code: code, cop_name: copName }).then(function(res) { return res.data; });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function formatFile(path, code) {
|
|
55
|
+
return axios.post(basePath() + '/format', { path: path, code: code }).then(function(res) { return res.data; });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function runTests(path) {
|
|
59
|
+
return axios.post(basePath() + '/test', { path: path }).then(function(res) { return res.data; });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function ping() {
|
|
63
|
+
return axios.get(basePath() + '/ping', { timeout: 4000 }).then(function(res) { return res.data; });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getState() {
|
|
67
|
+
return axios.get(basePath() + '/state').then(function(res) { return res.data; });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function saveState(state) {
|
|
71
|
+
return axios.post(basePath() + '/state', { state: state }).then(function(res) { return res.data; });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getDefinition(symbol, language) {
|
|
75
|
+
return axios.get(basePath() + '/definition', { params: { symbol: symbol, language: language } }).then(function(res) { return res.data; });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
getWorkspace: getWorkspace,
|
|
80
|
+
getTree: getTree,
|
|
81
|
+
getFile: getFile,
|
|
82
|
+
saveFile: saveFile,
|
|
83
|
+
createFile: createFile,
|
|
84
|
+
createDir: createDir,
|
|
85
|
+
renamePath: renamePath,
|
|
86
|
+
deletePath: deletePath,
|
|
87
|
+
lintFile: lintFile,
|
|
88
|
+
quickFixOffense: quickFixOffense,
|
|
89
|
+
formatFile: formatFile,
|
|
90
|
+
runTests: runTests,
|
|
91
|
+
ping: ping,
|
|
92
|
+
getState: getState,
|
|
93
|
+
saveState: saveState,
|
|
94
|
+
getDefinition: getDefinition
|
|
95
|
+
};
|
|
96
|
+
})();
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
var GitService = (function () {
|
|
2
|
+
function basePath() {
|
|
3
|
+
return (window.MBEDITOR_BASE_PATH || '/mbeditor').replace(/\/$/, '');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function applyGitInfo(data) {
|
|
7
|
+
var files = data.workingTree || data.files || [];
|
|
8
|
+
EditorStore.setState({
|
|
9
|
+
gitFiles: files,
|
|
10
|
+
gitBranch: data.branch || "",
|
|
11
|
+
gitInfo: data,
|
|
12
|
+
gitInfoError: null
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function fetchInfo() {
|
|
17
|
+
return axios.get(basePath() + '/git_info')
|
|
18
|
+
.then(function(res) {
|
|
19
|
+
if (res.data && res.data.ok) {
|
|
20
|
+
applyGitInfo(res.data);
|
|
21
|
+
} else {
|
|
22
|
+
EditorStore.setState({ gitInfoError: (res.data && res.data.error) || 'Failed to load git info' });
|
|
23
|
+
}
|
|
24
|
+
return res.data;
|
|
25
|
+
})
|
|
26
|
+
.catch(function(err) {
|
|
27
|
+
EditorStore.setState({ gitInfoError: err.message || 'Failed to load git info' });
|
|
28
|
+
EditorStore.setStatus("Failed to fetch git info: " + err.message, "error");
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function fetchStatus() {
|
|
33
|
+
return fetchInfo().then(function(data) {
|
|
34
|
+
if (data && data.ok) return data;
|
|
35
|
+
|
|
36
|
+
return axios.get(basePath() + '/git_status')
|
|
37
|
+
.then(function(res) {
|
|
38
|
+
if (res.data.ok) {
|
|
39
|
+
EditorStore.setState({
|
|
40
|
+
gitFiles: res.data.files,
|
|
41
|
+
gitBranch: res.data.branch || "",
|
|
42
|
+
gitInfo: {
|
|
43
|
+
ok: true,
|
|
44
|
+
branch: res.data.branch || "",
|
|
45
|
+
workingTree: res.data.files || [],
|
|
46
|
+
unpushedFiles: [],
|
|
47
|
+
unpushedCommits: [],
|
|
48
|
+
branchCommits: []
|
|
49
|
+
},
|
|
50
|
+
gitInfoError: null
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return res.data;
|
|
54
|
+
})
|
|
55
|
+
.catch(function(err) {
|
|
56
|
+
EditorStore.setStatus("Failed to fetch git status: " + err.message, "error");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function fetchDiff(path, baseSha, headSha) {
|
|
62
|
+
var query = '?file=' + encodeURIComponent(path);
|
|
63
|
+
if (baseSha) query += '&base=' + encodeURIComponent(baseSha);
|
|
64
|
+
if (headSha) query += '&head=' + encodeURIComponent(headSha);
|
|
65
|
+
|
|
66
|
+
return axios.get(basePath() + '/git/diff' + query).then(function(res) {
|
|
67
|
+
return res.data;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function fetchBlame(path) {
|
|
72
|
+
return axios.get(basePath() + '/git/blame?file=' + encodeURIComponent(path)).then(function(res) {
|
|
73
|
+
return res.data;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function fetchFileHistory(path) {
|
|
78
|
+
return axios.get(basePath() + '/git/file_history?file=' + encodeURIComponent(path)).then(function(res) {
|
|
79
|
+
return res.data;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function fetchCommitGraph() {
|
|
84
|
+
return axios.get(basePath() + '/git/commit_graph').then(function(res) {
|
|
85
|
+
return res.data;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function fetchCommitDetail(sha) {
|
|
90
|
+
return axios.get(basePath() + '/git/commit_detail?sha=' + encodeURIComponent(sha)).then(function(res) {
|
|
91
|
+
return res.data;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
fetchStatus: fetchStatus,
|
|
97
|
+
fetchInfo: fetchInfo,
|
|
98
|
+
fetchDiff: fetchDiff,
|
|
99
|
+
fetchBlame: fetchBlame,
|
|
100
|
+
fetchFileHistory: fetchFileHistory,
|
|
101
|
+
fetchCommitGraph: fetchCommitGraph,
|
|
102
|
+
fetchCommitDetail: fetchCommitDetail
|
|
103
|
+
};
|
|
104
|
+
})();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var SearchService = (function () {
|
|
2
|
+
function basePath() {
|
|
3
|
+
return (window.MBEDITOR_BASE_PATH || '/mbeditor').replace(/\/$/, '');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
var _miniSearch = new MiniSearch({
|
|
7
|
+
fields: ['path', 'name'], // indexed fields
|
|
8
|
+
storeFields: ['path', 'name'] // returned fields
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function buildIndex(treeData) {
|
|
12
|
+
// Capture the tree data immediately so a subsequent refresh doesn't
|
|
13
|
+
// clobber us before the idle callback fires.
|
|
14
|
+
var snapshot = treeData;
|
|
15
|
+
var schedule = window.requestIdleCallback || function(cb) { setTimeout(cb, 50); };
|
|
16
|
+
schedule(function() {
|
|
17
|
+
var docs = [];
|
|
18
|
+
var idCounter = 1;
|
|
19
|
+
|
|
20
|
+
function traverse(nodes) {
|
|
21
|
+
nodes.forEach(function(n) {
|
|
22
|
+
if (n.type === 'file') {
|
|
23
|
+
docs.push({ id: idCounter++, path: n.path, name: n.name });
|
|
24
|
+
} else if (n.children) {
|
|
25
|
+
traverse(n.children);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
traverse(snapshot);
|
|
31
|
+
_miniSearch.removeAll();
|
|
32
|
+
_miniSearch.addAll(docs);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function searchFiles(query) {
|
|
37
|
+
if (!query) return [];
|
|
38
|
+
return _miniSearch.search(query, { prefix: true, fuzzy: 0.2 });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function projectSearch(query) {
|
|
42
|
+
if (!query) return Promise.resolve({ results: [], capped: false });
|
|
43
|
+
return axios.get(basePath() + '/search', { params: { q: query } })
|
|
44
|
+
.then(function(res) {
|
|
45
|
+
var data = res.data;
|
|
46
|
+
// Handle both old array response and new {results, capped} shape
|
|
47
|
+
var results = Array.isArray(data) ? data : (data && data.results || []);
|
|
48
|
+
var capped = !Array.isArray(data) && !!(data && data.capped);
|
|
49
|
+
EditorStore.setState({ searchResults: results, searchCapped: capped });
|
|
50
|
+
return { results: results, capped: capped };
|
|
51
|
+
})
|
|
52
|
+
.catch(function(err) {
|
|
53
|
+
EditorStore.setStatus("Search failed: " + err.message, "error");
|
|
54
|
+
return { results: [], capped: false };
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
buildIndex: buildIndex,
|
|
60
|
+
searchFiles: searchFiles,
|
|
61
|
+
projectSearch: projectSearch
|
|
62
|
+
};
|
|
63
|
+
})();
|