mbeditor 0.1.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.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +127 -0
  3. data/app/assets/javascripts/mbeditor/application.js +19 -0
  4. data/app/assets/javascripts/mbeditor/components/CodeReviewPanel.js +202 -0
  5. data/app/assets/javascripts/mbeditor/components/CollapsibleSection.js +71 -0
  6. data/app/assets/javascripts/mbeditor/components/CombinedDiffViewer.js +139 -0
  7. data/app/assets/javascripts/mbeditor/components/CommitGraph.js +65 -0
  8. data/app/assets/javascripts/mbeditor/components/DiffViewer.js +142 -0
  9. data/app/assets/javascripts/mbeditor/components/EditorPanel.js +363 -0
  10. data/app/assets/javascripts/mbeditor/components/FileHistoryPanel.js +112 -0
  11. data/app/assets/javascripts/mbeditor/components/FileTree.js +304 -0
  12. data/app/assets/javascripts/mbeditor/components/GitPanel.js +416 -0
  13. data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +2335 -0
  14. data/app/assets/javascripts/mbeditor/components/QuickOpenDialog.js +118 -0
  15. data/app/assets/javascripts/mbeditor/components/ShortcutHelp.js +186 -0
  16. data/app/assets/javascripts/mbeditor/components/TabBar.js +123 -0
  17. data/app/assets/javascripts/mbeditor/editor_plugins.js +282 -0
  18. data/app/assets/javascripts/mbeditor/editor_store.js +53 -0
  19. data/app/assets/javascripts/mbeditor/file_service.js +77 -0
  20. data/app/assets/javascripts/mbeditor/git_service.js +104 -0
  21. data/app/assets/javascripts/mbeditor/search_service.js +53 -0
  22. data/app/assets/javascripts/mbeditor/tab_manager.js +461 -0
  23. data/app/assets/stylesheets/mbeditor/application.css +705 -0
  24. data/app/assets/stylesheets/mbeditor/editor.css +1264 -0
  25. data/app/controllers/mbeditor/application_controller.rb +10 -0
  26. data/app/controllers/mbeditor/editors_controller.rb +695 -0
  27. data/app/controllers/mbeditor/git_controller.rb +188 -0
  28. data/app/services/mbeditor/git_blame_service.rb +98 -0
  29. data/app/services/mbeditor/git_commit_graph_service.rb +60 -0
  30. data/app/services/mbeditor/git_diff_service.rb +71 -0
  31. data/app/services/mbeditor/git_file_history_service.rb +42 -0
  32. data/app/services/mbeditor/git_service.rb +82 -0
  33. data/app/services/mbeditor/redmine_service.rb +86 -0
  34. data/app/views/layouts/mbeditor/application.html.erb +71 -0
  35. data/app/views/mbeditor/editors/index.html.erb +1 -0
  36. data/config/environments/development.rb +53 -0
  37. data/config/initializers/assets.rb +9 -0
  38. data/config/routes.rb +37 -0
  39. data/lib/mbeditor/configuration.rb +16 -0
  40. data/lib/mbeditor/engine.rb +28 -0
  41. data/lib/mbeditor/version.rb +3 -0
  42. data/lib/mbeditor.rb +19 -0
  43. data/mbeditor.gemspec +30 -0
  44. data/public/min-maps/vs/base/worker/workerMain.js.map +1 -0
  45. data/public/monaco-editor/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
  46. data/public/monaco-editor/vs/base/worker/workerMain.js +31 -0
  47. data/public/monaco-editor/vs/basic-languages/cameligo/cameligo.js +10 -0
  48. data/public/monaco-editor/vs/basic-languages/css/css.js +12 -0
  49. data/public/monaco-editor/vs/basic-languages/dart/dart.js +10 -0
  50. data/public/monaco-editor/vs/basic-languages/flow9/flow9.js +10 -0
  51. data/public/monaco-editor/vs/basic-languages/go/go.js +10 -0
  52. data/public/monaco-editor/vs/basic-languages/handlebars/handlebars.js +440 -0
  53. data/public/monaco-editor/vs/basic-languages/javascript/javascript.js +10 -0
  54. data/public/monaco-editor/vs/basic-languages/markdown/markdown.js +10 -0
  55. data/public/monaco-editor/vs/basic-languages/msdax/msdax.js +10 -0
  56. data/public/monaco-editor/vs/basic-languages/postiats/postiats.js +10 -0
  57. data/public/monaco-editor/vs/basic-languages/pug/pug.js +412 -0
  58. data/public/monaco-editor/vs/basic-languages/restructuredtext/restructuredtext.js +10 -0
  59. data/public/monaco-editor/vs/basic-languages/ruby/ruby.js +10 -0
  60. data/public/monaco-editor/vs/basic-languages/sb/sb.js +10 -0
  61. data/public/monaco-editor/vs/basic-languages/typespec/typespec.js +10 -0
  62. data/public/monaco-editor/vs/basic-languages/yaml/yaml.js +10 -0
  63. data/public/monaco-editor/vs/editor/editor.main.css +8 -0
  64. data/public/monaco-editor/vs/editor/editor.main.js +797 -0
  65. data/public/monaco-editor/vs/language/typescript/tsMode.js +20 -0
  66. data/public/monaco-editor/vs/language/typescript/tsWorker.js +51328 -0
  67. data/public/monaco-editor/vs/loader.js +10 -0
  68. data/public/monaco-editor/vs/nls.messages.de.js +20 -0
  69. data/public/monaco-editor/vs/nls.messages.es.js +20 -0
  70. data/public/monaco-editor/vs/nls.messages.fr.js +18 -0
  71. data/public/monaco-editor/vs/nls.messages.it.js +18 -0
  72. data/public/monaco-editor/vs/nls.messages.ja.js +20 -0
  73. data/public/monaco-editor/vs/nls.messages.ko.js +18 -0
  74. data/public/monaco-editor/vs/nls.messages.ru.js +20 -0
  75. data/public/monaco-editor/vs/nls.messages.zh-cn.js +20 -0
  76. data/public/monaco-editor/vs/nls.messages.zh-tw.js +18 -0
  77. data/public/monaco_worker.js +5 -0
  78. data/vendor/assets/javascripts/axios.min.js +2 -0
  79. data/vendor/assets/javascripts/lodash.min.js +140 -0
  80. data/vendor/assets/javascripts/marked.min.js +6 -0
  81. data/vendor/assets/javascripts/minisearch.min.js +2044 -0
  82. data/vendor/assets/javascripts/prettier-plugin-babel.js +16 -0
  83. data/vendor/assets/javascripts/prettier-plugin-estree.js +35 -0
  84. data/vendor/assets/javascripts/prettier-plugin-html.js +19 -0
  85. data/vendor/assets/javascripts/prettier-plugin-markdown.js +59 -0
  86. data/vendor/assets/javascripts/prettier-plugin-postcss.js +52 -0
  87. data/vendor/assets/javascripts/prettier-standalone.js +37 -0
  88. data/vendor/assets/javascripts/react-dom.min.js +267 -0
  89. data/vendor/assets/javascripts/react.min.js +31 -0
  90. data/vendor/assets/stylesheets/fontawesome.min.css +9 -0
  91. data/vendor/assets/webfonts/fa-brands-400.woff2 +0 -0
  92. data/vendor/assets/webfonts/fa-regular-400.woff2 +0 -0
  93. data/vendor/assets/webfonts/fa-solid-900.woff2 +0 -0
  94. metadata +173 -0
@@ -0,0 +1,282 @@
1
+ 'use strict';
2
+
3
+ (function () {
4
+ var RUBY_BLOCK_START = /^\s*(def|class|module|if|unless|case|while|until|for|begin)\b.*$/;
5
+ var RUBY_DO_BLOCK_START = /\bdo(\s*\|.*\|)?\s*$/;
6
+ var RUBY_END_LINE = /^end\b/;
7
+ var VOID_HTML_ELEMENTS = {
8
+ area: true,
9
+ base: true,
10
+ br: true,
11
+ col: true,
12
+ embed: true,
13
+ hr: true,
14
+ img: true,
15
+ input: true,
16
+ link: true,
17
+ meta: true,
18
+ param: true,
19
+ source: true,
20
+ track: true,
21
+ wbr: true
22
+ };
23
+
24
+ var globalsRegistered = false;
25
+
26
+ function leadingWhitespace(line) {
27
+ var match = line.match(/^\s*/);
28
+ return match ? match[0] : '';
29
+ }
30
+
31
+ function escapeRegExp(value) {
32
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
33
+ }
34
+
35
+ function rubyIndentUnit(model) {
36
+ var options = model.getOptions ? model.getOptions() : null;
37
+ var tabSize = options && options.tabSize ? options.tabSize : 4;
38
+ return new Array(tabSize + 1).join(' ');
39
+ }
40
+
41
+ function rubyClosingIndent(model, cursorLineNumber, openerLine) {
42
+ var cursorIndent = leadingWhitespace(model.getLineContent(cursorLineNumber));
43
+ var indentUnit = rubyIndentUnit(model);
44
+
45
+ if (cursorIndent.length >= indentUnit.length) {
46
+ return cursorIndent.slice(0, cursorIndent.length - indentUnit.length);
47
+ }
48
+
49
+ return leadingWhitespace(openerLine);
50
+ }
51
+
52
+ function isRubyBlockStart(line) {
53
+ var trimmed = line.trim();
54
+ if (!trimmed || trimmed[0] === '#') return false;
55
+ return RUBY_BLOCK_START.test(line) || RUBY_DO_BLOCK_START.test(trimmed);
56
+ }
57
+
58
+ function hasMatchingRubyEnd(model, openerLineNumber, openerIndent) {
59
+ var lineCount = model.getLineCount();
60
+ var openerIndentLength = openerIndent.length;
61
+
62
+ for (var lineNumber = openerLineNumber + 1; lineNumber <= lineCount; lineNumber += 1) {
63
+ var line = model.getLineContent(lineNumber);
64
+ var trimmed = line.trim();
65
+ if (!trimmed || trimmed[0] === '#') continue;
66
+
67
+ var lineIndent = leadingWhitespace(line);
68
+ var lineIndentLength = lineIndent.length;
69
+
70
+ if (RUBY_END_LINE.test(trimmed)) {
71
+ if (lineIndentLength === openerIndentLength) return true;
72
+ if (lineIndentLength < openerIndentLength) return false;
73
+ }
74
+ }
75
+
76
+ return false;
77
+ }
78
+
79
+ function rubyEnterContext(editor, model) {
80
+ var selection = editor.getSelection();
81
+ if (!selection || !selection.isEmpty()) return null;
82
+
83
+ var cursorPosition = editor.getPosition();
84
+ if (!cursorPosition) return null;
85
+
86
+ var openerLine = model.getLineContent(cursorPosition.lineNumber);
87
+ var beforeCursor = openerLine.substring(0, cursorPosition.column - 1);
88
+ var afterCursor = openerLine.substring(cursorPosition.column - 1);
89
+
90
+ if (afterCursor.trim() !== '') return null;
91
+ if (!isRubyBlockStart(beforeCursor)) return null;
92
+
93
+ return {
94
+ cursorPosition: cursorPosition,
95
+ openerIndent: leadingWhitespace(openerLine),
96
+ hasExistingEnd: hasMatchingRubyEnd(model, cursorPosition.lineNumber, leadingWhitespace(openerLine))
97
+ };
98
+ }
99
+
100
+ function handleRubyEnter(editor, model) {
101
+ var context = rubyEnterContext(editor, model);
102
+ if (!context) return false;
103
+
104
+ var innerIndent = context.openerIndent + rubyIndentUnit(model);
105
+ var insertedText = '\n' + innerIndent;
106
+ if (!context.hasExistingEnd) {
107
+ insertedText += '\n' + context.openerIndent + 'end';
108
+ }
109
+
110
+ editor.executeEdits('ruby-auto-end', [{
111
+ range: new window.monaco.Range(context.cursorPosition.lineNumber, context.cursorPosition.column, context.cursorPosition.lineNumber, context.cursorPosition.column),
112
+ text: insertedText
113
+ }]);
114
+
115
+ editor.setPosition({
116
+ lineNumber: context.cursorPosition.lineNumber + 1,
117
+ column: innerIndent.length + 1
118
+ });
119
+ editor.focus();
120
+ return true;
121
+ }
122
+
123
+ function handleMarkupAutoClose(editor, model, change) {
124
+ if (change.rangeLength !== 0 || change.text !== '>') return false;
125
+
126
+ var lineNumber = change.range.startLineNumber;
127
+ var columnBeforeInsert = change.range.startColumn;
128
+ var lineContent = model.getLineContent(lineNumber);
129
+ var textBefore = lineContent.substring(0, columnBeforeInsert - 1);
130
+
131
+ if (/\/$/.test(textBefore)) return false;
132
+
133
+ var tagMatch = textBefore.match(/<([a-zA-Z][a-zA-Z0-9:\-_]*)(?:\s+[^>]*?)?$/);
134
+ if (!tagMatch) return false;
135
+
136
+ var tagName = tagMatch[1];
137
+ if (VOID_HTML_ELEMENTS[tagName.toLowerCase()]) return false;
138
+
139
+ var closingTag = '</' + tagName + '>';
140
+ var afterCursor = lineContent.substring(columnBeforeInsert);
141
+ var existingClosePattern = new RegExp('^\\s*' + escapeRegExp(closingTag));
142
+ if (existingClosePattern.test(afterCursor)) return false;
143
+
144
+ window.setTimeout(function () {
145
+ var activeModel = editor.getModel();
146
+ if (!activeModel || activeModel !== model) return;
147
+
148
+ var latestLineContent = model.getLineContent(lineNumber);
149
+ var latestAfterCursor = latestLineContent.substring(columnBeforeInsert);
150
+ if (existingClosePattern.test(latestAfterCursor)) return;
151
+
152
+ editor.executeEdits('html-auto-close', [{
153
+ range: new window.monaco.Range(lineNumber, columnBeforeInsert + 1, lineNumber, columnBeforeInsert + 1),
154
+ text: closingTag
155
+ }]);
156
+
157
+ window.setTimeout(function () {
158
+ editor.setPosition({ lineNumber: lineNumber, column: columnBeforeInsert + 1 });
159
+ editor.focus();
160
+ }, 0);
161
+ }, 0);
162
+
163
+ return true;
164
+ }
165
+
166
+ function attachEditorFeatures(editor, language) {
167
+ var model = editor && editor.getModel ? editor.getModel() : null;
168
+ if (!model) {
169
+ return { dispose: function dispose() {} };
170
+ }
171
+
172
+ var suppressInternalEdit = false;
173
+ var keydownDisposable = null;
174
+
175
+ if (language === 'ruby') {
176
+ keydownDisposable = editor.onKeyDown(function (event) {
177
+ if (event.keyCode !== window.monaco.KeyCode.Enter) return;
178
+ if (!handleRubyEnter(editor, model)) return;
179
+
180
+ event.preventDefault();
181
+ event.stopPropagation();
182
+ });
183
+ }
184
+
185
+ var contentDisposable = model.onDidChangeContent(function (event) {
186
+ if (suppressInternalEdit) return;
187
+ if (event.isUndoing || event.isRedoing) return;
188
+ if (!event.changes || event.changes.length !== 1) return;
189
+
190
+ var change = event.changes[0];
191
+ var handled = false;
192
+
193
+ suppressInternalEdit = true;
194
+ try {
195
+ if (language === 'html') {
196
+ handled = handleMarkupAutoClose(editor, model, change) || handled;
197
+ }
198
+
199
+ if (language === 'javascript' || language === 'typescript') {
200
+ handled = handleMarkupAutoClose(editor, model, change) || handled;
201
+ }
202
+ } finally {
203
+ suppressInternalEdit = false;
204
+ }
205
+
206
+ return handled;
207
+ });
208
+
209
+ return {
210
+ dispose: function dispose() {
211
+ if (keydownDisposable) keydownDisposable.dispose();
212
+ contentDisposable.dispose();
213
+ }
214
+ };
215
+ }
216
+
217
+ function registerGlobalExtensions(monaco) {
218
+ if (globalsRegistered) return;
219
+ if (!monaco || !monaco.languages) return;
220
+
221
+ globalsRegistered = true;
222
+
223
+ monaco.languages.setLanguageConfiguration('ruby', {
224
+ indentationRules: {
225
+ increaseIndentPattern: /^\s*(def|class|module|if|unless|case|while|until|for|begin|elsif|else|rescue|ensure|when)\b/,
226
+ decreaseIndentPattern: /^\s*(end|elsif|else|rescue|ensure|when)\b/
227
+ }
228
+ });
229
+
230
+ var genericLinkedProvider = {
231
+ provideLinkedEditingRanges: function provideLinkedEditingRanges(model, position) {
232
+ var line = model.getLineContent(position.lineNumber);
233
+ var wordInfo = model.getWordAtPosition(position);
234
+ if (!wordInfo) return null;
235
+
236
+ var word = wordInfo.word;
237
+ var startCol = wordInfo.startColumn;
238
+ var endCol = wordInfo.endColumn;
239
+
240
+ if (line[startCol - 2] === '<') {
241
+ var closeTagStr = '</' + word + '>';
242
+ var closeIdx = line.indexOf(closeTagStr, endCol - 1);
243
+ if (closeIdx !== -1) {
244
+ return {
245
+ ranges: [new monaco.Range(position.lineNumber, startCol, position.lineNumber, endCol), new monaco.Range(position.lineNumber, closeIdx + 3, position.lineNumber, closeIdx + 3 + word.length)],
246
+ wordPattern: /[a-zA-Z0-9:\-_]+/
247
+ };
248
+ }
249
+ }
250
+
251
+ if (line[startCol - 3] === '<' && line[startCol - 2] === '/') {
252
+ var openTagRegex = new RegExp('<' + word + '(?:\\s|>)');
253
+ var match = line.match(openTagRegex);
254
+ if (match) {
255
+ var openStart = match.index + 2;
256
+ if (openStart < startCol) {
257
+ return {
258
+ ranges: [new monaco.Range(position.lineNumber, openStart, position.lineNumber, openStart + word.length), new monaco.Range(position.lineNumber, startCol, position.lineNumber, endCol)],
259
+ wordPattern: /[a-zA-Z0-9:\-_]+/
260
+ };
261
+ }
262
+ }
263
+ }
264
+
265
+ return null;
266
+ }
267
+ };
268
+
269
+ monaco.languages.registerLinkedEditingRangeProvider('javascript', genericLinkedProvider);
270
+ monaco.languages.registerLinkedEditingRangeProvider('typescript', genericLinkedProvider);
271
+ monaco.languages.registerLinkedEditingRangeProvider('ruby', genericLinkedProvider);
272
+ }
273
+
274
+ window.MbeditorEditorPlugins = {
275
+ registerGlobalExtensions: registerGlobalExtensions,
276
+ attachEditorFeatures: attachEditorFeatures,
277
+ runRubyEnter: function runRubyEnter(editor) {
278
+ if (!editor || !editor.getModel) return false;
279
+ return handleRubyEnter(editor, editor.getModel());
280
+ }
281
+ };
282
+ })();
@@ -0,0 +1,53 @@
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
+ statusMessage: { text: "", kind: "info" },
17
+ };
18
+
19
+ var _listeners = [];
20
+
21
+ function getState() { return _state; }
22
+
23
+ function subscribe(fn) {
24
+ _listeners.push(fn);
25
+ return function unsubscribe() {
26
+ _listeners = _listeners.filter(function (l) { return l !== fn; });
27
+ };
28
+ }
29
+
30
+ function emit() {
31
+ _listeners.forEach(function (fn) { fn(_state); });
32
+ }
33
+
34
+ function setState(patch) {
35
+ _state = Object.assign({}, _state, patch);
36
+ emit();
37
+ }
38
+
39
+ function setStatus(text, kind) {
40
+ kind = kind || "info";
41
+ setState({ statusMessage: { text: text, kind: kind } });
42
+ // Auto-clear after 4s for non-error messages
43
+ if (kind !== "error") {
44
+ setTimeout(function () {
45
+ if (_state.statusMessage.text === text) {
46
+ setState({ statusMessage: { text: "", kind: "info" } });
47
+ }
48
+ }, 4000);
49
+ }
50
+ }
51
+
52
+ return { getState: getState, subscribe: subscribe, setState: setState, setStatus: setStatus };
53
+ })();
@@ -0,0 +1,77 @@
1
+ // Identify all requests as coming from the mbeditor client.
2
+ // The server checks this header on every non-GET request as a CSRF guard.
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) {
19
+ return axios.get(basePath() + '/file', { params: { path: path } }).then(function(res) { return res.data; });
20
+ }
21
+
22
+ function saveFile(path, code) {
23
+ return axios.post(basePath() + '/file', { path: path, code: code }).then(function(res) { return res.data; });
24
+ }
25
+
26
+ function createFile(path, code) {
27
+ return axios.post(basePath() + '/create_file', { path: path, code: code || '' }).then(function(res) { return res.data; });
28
+ }
29
+
30
+ function createDir(path) {
31
+ return axios.post(basePath() + '/create_dir', { path: path }).then(function(res) { return res.data; });
32
+ }
33
+
34
+ function renamePath(path, newPath) {
35
+ return axios.patch(basePath() + '/rename', { path: path, new_path: newPath }).then(function(res) { return res.data; });
36
+ }
37
+
38
+ function deletePath(path) {
39
+ return axios.delete(basePath() + '/delete', { data: { path: path } }).then(function(res) { return res.data; });
40
+ }
41
+
42
+ function lintFile(path, code) {
43
+ return axios.post(basePath() + '/lint', { path: path, code: code }).then(function(res) { return res.data; });
44
+ }
45
+
46
+ function formatFile(path) {
47
+ return axios.post(basePath() + '/format', { path: path }).then(function(res) { return res.data; });
48
+ }
49
+
50
+ function ping() {
51
+ return axios.get(basePath() + '/ping', { timeout: 4000 }).then(function(res) { return res.data; });
52
+ }
53
+
54
+ function getState() {
55
+ return axios.get(basePath() + '/state').then(function(res) { return res.data; });
56
+ }
57
+
58
+ function saveState(state) {
59
+ return axios.post(basePath() + '/state', { state: state }).then(function(res) { return res.data; });
60
+ }
61
+
62
+ return {
63
+ getWorkspace: getWorkspace,
64
+ getTree: getTree,
65
+ getFile: getFile,
66
+ saveFile: saveFile,
67
+ createFile: createFile,
68
+ createDir: createDir,
69
+ renamePath: renamePath,
70
+ deletePath: deletePath,
71
+ lintFile: lintFile,
72
+ formatFile: formatFile,
73
+ ping: ping,
74
+ getState: getState,
75
+ saveState: saveState
76
+ };
77
+ })();
@@ -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,53 @@
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
+ var docs = [];
13
+ var idCounter = 1;
14
+
15
+ function traverse(nodes) {
16
+ nodes.forEach(function(n) {
17
+ if (n.type === 'file') {
18
+ docs.push({ id: idCounter++, path: n.path, name: n.name });
19
+ } else if (n.children) {
20
+ traverse(n.children);
21
+ }
22
+ });
23
+ }
24
+
25
+ traverse(treeData);
26
+ _miniSearch.removeAll();
27
+ _miniSearch.addAll(docs);
28
+ }
29
+
30
+ function searchFiles(query) {
31
+ if (!query) return [];
32
+ return _miniSearch.search(query, { prefix: true, fuzzy: 0.2 });
33
+ }
34
+
35
+ function projectSearch(query) {
36
+ if (!query) return Promise.resolve([]);
37
+ return axios.get(basePath() + '/search', { params: { q: query } })
38
+ .then(function(res) {
39
+ EditorStore.setState({ searchResults: res.data });
40
+ return res.data;
41
+ })
42
+ .catch(function(err) {
43
+ EditorStore.setStatus("Search failed: " + err.message, "error");
44
+ return [];
45
+ });
46
+ }
47
+
48
+ return {
49
+ buildIndex: buildIndex,
50
+ searchFiles: searchFiles,
51
+ projectSearch: projectSearch
52
+ };
53
+ })();