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
|
@@ -7,100 +7,80 @@
|
|
|
7
7
|
(function() {
|
|
8
8
|
var _React = React;
|
|
9
9
|
var useEffect = _React.useEffect;
|
|
10
|
+
var useState = _React.useState;
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
var parts = text.split(/\*\*([^*]+)\*\*/g);
|
|
18
|
-
return parts.map(function(part, i) {
|
|
19
|
-
return i % 2 === 1
|
|
20
|
-
? React.createElement('strong', { key: i }, part)
|
|
21
|
-
: part;
|
|
22
|
-
});
|
|
23
|
-
}
|
|
12
|
+
var SECTION_ICONS = { Added: 'fas fa-plus-circle', Fixed: 'fas fa-wrench', Changed: 'fas fa-exchange-alt', Removed: 'fas fa-minus-circle', Performance: 'fas fa-bolt', Tests: 'fas fa-vial', Security: 'fas fa-shield-alt' };
|
|
13
|
+
// Shared with EditorPanel.js's markdown preview, which renders untrusted
|
|
14
|
+
// workspace files through the same marked renderer. One definition on
|
|
15
|
+
// purpose: this is the allow-list that keeps `javascript:` out of an href,
|
|
16
|
+
// and a second copy is how one of them silently stops matching the other.
|
|
17
|
+
var SAFE_HREF_SCHEME = /^(https?:|mailto:|#|\/)/i;
|
|
24
18
|
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
var listBuffer = [];
|
|
29
|
-
var key = 0;
|
|
30
|
-
|
|
31
|
-
function flushList() {
|
|
32
|
-
if (!listBuffer.length) return;
|
|
33
|
-
elements.push(React.createElement(
|
|
34
|
-
'ul', { key: 'ul' + key++, className: 'changelog-list' },
|
|
35
|
-
listBuffer.slice()
|
|
36
|
-
));
|
|
37
|
-
listBuffer = [];
|
|
38
|
-
}
|
|
19
|
+
window.mbeditorEscapeHtml = function escapeHtml(str) {
|
|
20
|
+
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
21
|
+
};
|
|
39
22
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
// H3 — section name, e.g. "### Added"
|
|
67
|
-
if (/^### /.test(line)) {
|
|
68
|
-
flushList();
|
|
69
|
-
var section = line.slice(4).trim();
|
|
70
|
-
var iconMap = { Added: 'fas fa-plus-circle', Fixed: 'fas fa-wrench', Changed: 'fas fa-exchange-alt', Removed: 'fas fa-minus-circle', Performance: 'fas fa-bolt', Tests: 'fas fa-vial', Security: 'fas fa-shield-alt' };
|
|
71
|
-
var iconClass = iconMap[section] || 'fas fa-tag';
|
|
72
|
-
elements.push(React.createElement(
|
|
73
|
-
'div', { key: key++, className: 'changelog-section changelog-section-' + section.toLowerCase() },
|
|
74
|
-
React.createElement('i', { className: iconClass }),
|
|
75
|
-
' ',
|
|
76
|
-
section
|
|
77
|
-
));
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
// Horizontal rule
|
|
81
|
-
if (/^---+$/.test(line.trim())) {
|
|
82
|
-
flushList();
|
|
83
|
-
elements.push(React.createElement('hr', { key: key++, className: 'changelog-rule' }));
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
// Bullet list item
|
|
87
|
-
if (/^- /.test(line)) {
|
|
88
|
-
listBuffer.push(React.createElement('li', { key: key++ }, parseInline(line.slice(2).trim())));
|
|
89
|
-
return;
|
|
23
|
+
window.mbeditorSafeHref = function safeHref(href) {
|
|
24
|
+
if (!href) return href;
|
|
25
|
+
return SAFE_HREF_SCHEME.test(href.trim()) ? href : '#';
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
var escapeHtml = window.mbeditorEscapeHtml;
|
|
29
|
+
var safeHref = window.mbeditorSafeHref;
|
|
30
|
+
|
|
31
|
+
// Custom marked.Renderer() that layers the changelog's bespoke presentation
|
|
32
|
+
// (version badges on H2, section icons on H3) over marked's own HTML, plus
|
|
33
|
+
// the same link/image href scheme allow-list EditorPanel.js uses. marked's
|
|
34
|
+
// Renderer methods here take positional args (href, title, text) — not the
|
|
35
|
+
// token objects EditorPanel.js's override checks for — so that override is
|
|
36
|
+
// effectively a no-op against the vendored marked build; this one uses the
|
|
37
|
+
// signature marked actually calls.
|
|
38
|
+
function buildRenderer() {
|
|
39
|
+
var renderer = new window.marked.Renderer();
|
|
40
|
+
|
|
41
|
+
renderer.heading = function(text, level) {
|
|
42
|
+
if (level === 2) {
|
|
43
|
+
var vMatch = text.match(/^\[([^\]]+)\](?:\s*-\s*(.+))?/);
|
|
44
|
+
var badge = vMatch ? vMatch[1] : text;
|
|
45
|
+
var date = vMatch && vMatch[2] ? vMatch[2].trim() : '';
|
|
46
|
+
return '<div class="changelog-version-row"><span class="changelog-version-badge">' + badge + '</span>' +
|
|
47
|
+
(date ? '<span class="changelog-version-date">' + date + '</span>' : '') + '</div>\n';
|
|
90
48
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return;
|
|
49
|
+
if (level === 3) {
|
|
50
|
+
var iconClass = SECTION_ICONS[text] || 'fas fa-tag';
|
|
51
|
+
return '<div class="changelog-section changelog-section-' + text.toLowerCase() + '"><i class="' + iconClass + '"></i> ' + text + '</div>\n';
|
|
95
52
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
53
|
+
return '<h1 class="changelog-h1">' + text + '</h1>\n';
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
renderer.list = function(body, ordered, start) {
|
|
57
|
+
var tag = ordered ? 'ol' : 'ul';
|
|
58
|
+
var cls = ordered ? '' : ' class="changelog-list"';
|
|
59
|
+
return '<' + tag + cls + '>\n' + body + '</' + tag + '>\n';
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
renderer.paragraph = function(text) {
|
|
63
|
+
return '<p class="changelog-p">' + text + '</p>\n';
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
renderer.hr = function() {
|
|
67
|
+
return '<hr class="changelog-rule">\n';
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
renderer.html = function(html) {
|
|
71
|
+
return '<pre>' + escapeHtml(html) + '</pre>';
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
var _origLink = renderer.link.bind(renderer);
|
|
75
|
+
var _origImage = renderer.image.bind(renderer);
|
|
76
|
+
renderer.link = function(href, title, text) {
|
|
77
|
+
return _origLink(safeHref(href), title, text);
|
|
78
|
+
};
|
|
79
|
+
renderer.image = function(href, title, text) {
|
|
80
|
+
return _origImage(safeHref(href), title, text);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return renderer;
|
|
104
84
|
}
|
|
105
85
|
|
|
106
86
|
// ---------------------------------------------------------------------------
|
|
@@ -109,11 +89,24 @@
|
|
|
109
89
|
var ChangelogView = function ChangelogView(props) {
|
|
110
90
|
var changelogState = props.changelogState;
|
|
111
91
|
var onLoad = props.onLoad;
|
|
92
|
+
var content = (changelogState && changelogState.content) || '';
|
|
93
|
+
|
|
94
|
+
var _markupState = useState('');
|
|
95
|
+
var markup = _markupState[0];
|
|
96
|
+
var setMarkup = _markupState[1];
|
|
112
97
|
|
|
113
98
|
useEffect(function() {
|
|
114
99
|
if (onLoad) onLoad();
|
|
115
100
|
}, []);
|
|
116
101
|
|
|
102
|
+
useEffect(function() {
|
|
103
|
+
if (!window.marked || !content) {
|
|
104
|
+
setMarkup('');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
setMarkup(window.marked.parse(content, { renderer: buildRenderer() }));
|
|
108
|
+
}, [content]);
|
|
109
|
+
|
|
117
110
|
if (!changelogState || changelogState.loading) {
|
|
118
111
|
return React.createElement(
|
|
119
112
|
'div', { className: 'changelog-loading' },
|
|
@@ -130,14 +123,16 @@
|
|
|
130
123
|
);
|
|
131
124
|
}
|
|
132
125
|
|
|
133
|
-
|
|
126
|
+
if (!window.marked) {
|
|
127
|
+
return React.createElement(
|
|
128
|
+
'div', { className: 'changelog-view' },
|
|
129
|
+
React.createElement('div', { className: 'changelog-body', style: { whiteSpace: 'pre-wrap' } }, content)
|
|
130
|
+
);
|
|
131
|
+
}
|
|
134
132
|
|
|
135
133
|
return React.createElement(
|
|
136
134
|
'div', { className: 'changelog-view' },
|
|
137
|
-
React.createElement(
|
|
138
|
-
'div', { className: 'changelog-body' },
|
|
139
|
-
renderMarkdown(content)
|
|
140
|
-
)
|
|
135
|
+
React.createElement('div', { className: 'changelog-body', dangerouslySetInnerHTML: { __html: markup } })
|
|
141
136
|
);
|
|
142
137
|
};
|
|
143
138
|
|
|
@@ -5,19 +5,16 @@ var CodeReviewPanel = function CodeReviewPanel(_ref) {
|
|
|
5
5
|
var onClose = _ref.onClose;
|
|
6
6
|
|
|
7
7
|
var _React$useState = React.useState(null),
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
setRedmineIssue = _React$useState2[1];
|
|
8
|
+
redmineIssue = _React$useState[0],
|
|
9
|
+
setRedmineIssue = _React$useState[1];
|
|
11
10
|
|
|
12
11
|
var _React$useState3 = React.useState(null),
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
setRedmineError = _React$useState4[1];
|
|
12
|
+
redmineError = _React$useState3[0],
|
|
13
|
+
setRedmineError = _React$useState3[1];
|
|
16
14
|
|
|
17
15
|
var _React$useState5 = React.useState(false),
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
setIsRedmineLoading = _React$useState6[1];
|
|
16
|
+
isRedmineLoading = _React$useState5[0],
|
|
17
|
+
setIsRedmineLoading = _React$useState5[1];
|
|
21
18
|
|
|
22
19
|
var branchIdMatch = gitInfo && gitInfo.branch && gitInfo.branch.match(/(?:feature|bug|hotfix|task)\/(?:#)?(\d+)/i);
|
|
23
20
|
var issueId = branchIdMatch ? branchIdMatch[1] : null;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; })();
|
|
4
3
|
|
|
5
4
|
var _React = React;
|
|
6
5
|
var useState = _React.useState;
|
|
@@ -17,13 +16,16 @@ var CollapsibleSection = function CollapsibleSection(_ref) {
|
|
|
17
16
|
var icon = _ref$icon === undefined ? null : _ref$icon;
|
|
18
17
|
var _ref$actions = _ref.actions;
|
|
19
18
|
var actions = _ref$actions === undefined ? null : _ref$actions;
|
|
19
|
+
// A section with nowhere to collapse to. The file tree fills the sidebar and
|
|
20
|
+
// collapsing it leaves an empty panel, so it gets no chevron and no click.
|
|
21
|
+
var _ref$collapsible = _ref.collapsible;
|
|
22
|
+
var collapsible = _ref$collapsible === undefined ? true : _ref$collapsible;
|
|
20
23
|
|
|
21
24
|
var _useState = useState(isCollapsed);
|
|
22
25
|
|
|
23
|
-
var _useState2 = _slicedToArray(_useState, 2);
|
|
24
26
|
|
|
25
|
-
var localCollapsed =
|
|
26
|
-
var setLocalCollapsed =
|
|
27
|
+
var localCollapsed = _useState[0];
|
|
28
|
+
var setLocalCollapsed = _useState[1];
|
|
27
29
|
|
|
28
30
|
// Sync parent isCollapsed prop to local state when it changes
|
|
29
31
|
useEffect(function () {
|
|
@@ -44,8 +46,11 @@ var CollapsibleSection = function CollapsibleSection(_ref) {
|
|
|
44
46
|
{ className: "collapsible-section" },
|
|
45
47
|
React.createElement(
|
|
46
48
|
"div",
|
|
47
|
-
{
|
|
48
|
-
|
|
49
|
+
{
|
|
50
|
+
className: "collapsible-header" + (collapsible ? "" : " collapsible-header-static"),
|
|
51
|
+
onClick: collapsible ? toggleCollapsed : undefined
|
|
52
|
+
},
|
|
53
|
+
collapsible && React.createElement("i", { className: "collapsible-toggle fas fa-chevron-" + (localCollapsed ? 'right' : 'down') }),
|
|
49
54
|
icon && React.createElement("i", { className: "collapsible-icon " + icon }),
|
|
50
55
|
React.createElement(
|
|
51
56
|
"span",
|
|
@@ -62,7 +67,7 @@ var CollapsibleSection = function CollapsibleSection(_ref) {
|
|
|
62
67
|
actions
|
|
63
68
|
)
|
|
64
69
|
),
|
|
65
|
-
!localCollapsed && React.createElement(
|
|
70
|
+
(!collapsible || !localCollapsed) && React.createElement(
|
|
66
71
|
"div",
|
|
67
72
|
{ className: "collapsible-content" },
|
|
68
73
|
children
|
|
@@ -32,18 +32,38 @@ var CombinedDiffViewer = function CombinedDiffViewer(_ref) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Split the raw diff into per-file segments, each starting at "diff --git".
|
|
35
|
+
// Anything before the first such header is not a file diff — it's the
|
|
36
|
+
// explanatory "# ..." text tab_manager.js substitutes when there's no base
|
|
37
|
+
// branch to compare against, or nothing changed — so it must be shown
|
|
38
|
+
// rather than silently dropped, or the tab reads as a blank diff.
|
|
35
39
|
var segments = [];
|
|
36
40
|
var current = null;
|
|
41
|
+
var preamble = [];
|
|
37
42
|
diffText.split('\n').forEach(function (line) {
|
|
38
43
|
if (line.startsWith('diff --git ')) {
|
|
39
44
|
if (current) segments.push(current);
|
|
40
45
|
current = { header: line, lines: [line] };
|
|
41
46
|
} else if (current) {
|
|
42
47
|
current.lines.push(line);
|
|
48
|
+
} else {
|
|
49
|
+
preamble.push(line);
|
|
43
50
|
}
|
|
44
51
|
});
|
|
45
52
|
if (current) segments.push(current);
|
|
46
53
|
|
|
54
|
+
if (segments.length === 0) {
|
|
55
|
+
var message = preamble.join('\n').trim().replace(/^#\s*/, '');
|
|
56
|
+
// Info, not the green tick used for a genuinely empty diff above: this
|
|
57
|
+
// branch also carries "no base branch to compare against", which is a
|
|
58
|
+
// problem to act on rather than a clean result.
|
|
59
|
+
return React.createElement(
|
|
60
|
+
'div',
|
|
61
|
+
{ className: 'combined-diff-viewer combined-diff-empty' },
|
|
62
|
+
React.createElement('i', { className: 'fas fa-info-circle', style: { marginRight: 8 } }),
|
|
63
|
+
message || 'No changes.'
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
47
67
|
// Extract a clean display path from the "diff --git a/foo b/foo" header
|
|
48
68
|
function extractPath(headerLine) {
|
|
49
69
|
var m = headerLine.match(/^diff --git a\/(.+) b\/(.+)$/);
|
|
@@ -110,7 +110,7 @@ var DiffViewer = function DiffViewer(_ref) {
|
|
|
110
110
|
'js': 'javascript', 'jsx': 'javascript',
|
|
111
111
|
'ts': 'typescript', 'tsx': 'typescript',
|
|
112
112
|
'json': 'json', 'yml': 'yaml', 'yaml': 'yaml',
|
|
113
|
-
'css': 'css', 'scss': '
|
|
113
|
+
'css': 'css', 'scss': 'scss', 'less': 'less', 'sass': 'css',
|
|
114
114
|
'html': 'html', 'erb': 'erb', 'haml': 'haml',
|
|
115
115
|
'xml': 'xml', 'md': 'markdown', 'markdown': 'markdown',
|
|
116
116
|
'sh': 'shell', 'bash': 'shell', 'zsh': 'shell'
|