mbeditor 0.12.7 → 0.12.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 +103 -0
- data/app/assets/javascripts/mbeditor/components/EditorPanel.js +48 -23
- data/app/assets/javascripts/mbeditor/components/MbeditorApp.js +139 -50
- data/app/assets/javascripts/mbeditor/components/ProblemsPanel.js +56 -7
- data/app/assets/javascripts/mbeditor/editor_plugins.js +74 -92
- data/app/assets/javascripts/mbeditor/tab_manager.js +34 -0
- data/app/assets/stylesheets/mbeditor/editor.css +34 -2
- data/app/controllers/mbeditor/editors_controller.rb +14 -1
- data/app/services/mbeditor/js_syntax_check_service.rb +212 -0
- data/app/services/mbeditor/search_replace_service.rb +49 -8
- data/lib/mbeditor/configuration.rb +2 -1
- data/lib/mbeditor/version.rb +1 -1
- metadata +2 -2
|
@@ -96,6 +96,34 @@ var ProblemsPanel = (function () {
|
|
|
96
96
|
var _filter = React.useState('');
|
|
97
97
|
var filter = _filter[0], setFilter = _filter[1];
|
|
98
98
|
|
|
99
|
+
// Which severities the list shows. Multi-select rather than a single
|
|
100
|
+
// dropdown: "errors and warnings, but not the convention noise" is the
|
|
101
|
+
// view people actually want, and a one-of-three picker can't express it.
|
|
102
|
+
// Persisted, because a filter you have to re-set every time you open the
|
|
103
|
+
// panel is one you stop using.
|
|
104
|
+
var _severities = React.useState(function () {
|
|
105
|
+
try {
|
|
106
|
+
var saved = JSON.parse(window.localStorage.getItem('mbeditorProblemsSeverities'));
|
|
107
|
+
if (saved && typeof saved === 'object') {
|
|
108
|
+
return { error: saved.error !== false, warning: saved.warning !== false, info: saved.info !== false };
|
|
109
|
+
}
|
|
110
|
+
} catch (e) { /* unparseable or storage blocked — fall through to the default */ }
|
|
111
|
+
return { error: true, warning: true, info: true };
|
|
112
|
+
});
|
|
113
|
+
var severities = _severities[0], setSeverities = _severities[1];
|
|
114
|
+
|
|
115
|
+
var toggleSeverity = function (kind) {
|
|
116
|
+
setSeverities(function (prev) {
|
|
117
|
+
var next = Object.assign({}, prev);
|
|
118
|
+
next[kind] = !next[kind];
|
|
119
|
+
// Turning the last one off would show an empty panel with no hint as
|
|
120
|
+
// to why, so the final active severity stays latched on.
|
|
121
|
+
if (!next.error && !next.warning && !next.info) return prev;
|
|
122
|
+
try { window.localStorage.setItem('mbeditorProblemsSeverities', JSON.stringify(next)); } catch (e) {}
|
|
123
|
+
return next;
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
99
127
|
// Exceptions raised by the host app. Unlike markers these are not per-model
|
|
100
128
|
// — a runtime failure isn't a property of a file you happen to have open —
|
|
101
129
|
// so they live in their own section above the marker list.
|
|
@@ -165,11 +193,14 @@ var ProblemsPanel = (function () {
|
|
|
165
193
|
}, []);
|
|
166
194
|
|
|
167
195
|
var needle = filter.trim().toLowerCase();
|
|
168
|
-
var
|
|
196
|
+
var allSeverities = severities.error && severities.warning && severities.info;
|
|
197
|
+
var shown = (needle || !allSeverities)
|
|
169
198
|
? problems.byFile.map(function (entry) {
|
|
170
199
|
return {
|
|
171
200
|
path: entry.path,
|
|
172
201
|
markers: entry.markers.filter(function (item) {
|
|
202
|
+
if (!severities[SEVERITY_KIND[item.marker.severity] || 'info']) return false;
|
|
203
|
+
if (!needle) return true;
|
|
173
204
|
return (item.marker.message + ' ' + item.code + ' ' + entry.path)
|
|
174
205
|
.toLowerCase().indexOf(needle) !== -1;
|
|
175
206
|
})
|
|
@@ -192,13 +223,31 @@ var ProblemsPanel = (function () {
|
|
|
192
223
|
{ className: 'ide-problems-header' },
|
|
193
224
|
React.createElement('i', { className: 'fas fa-bug' }),
|
|
194
225
|
React.createElement('span', { className: 'ide-problems-title' }, 'Problems'),
|
|
226
|
+
// No prose summary: the severity chips below carry the same three
|
|
227
|
+
// counts, and restating them was the longest thing in the header.
|
|
195
228
|
React.createElement(
|
|
196
|
-
'
|
|
197
|
-
{ className: 'ide-problems-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
',
|
|
201
|
-
'
|
|
229
|
+
'div',
|
|
230
|
+
{ className: 'ide-problems-severity-filter' },
|
|
231
|
+
[
|
|
232
|
+
{ kind: 'error', count: problems.errors.length },
|
|
233
|
+
{ kind: 'warning', count: problems.warnings.length },
|
|
234
|
+
{ kind: 'info', count: problems.infos.length }
|
|
235
|
+
].map(function (s) {
|
|
236
|
+
var on = severities[s.kind];
|
|
237
|
+
return React.createElement(
|
|
238
|
+
'button',
|
|
239
|
+
{
|
|
240
|
+
key: s.kind,
|
|
241
|
+
type: 'button',
|
|
242
|
+
className: 'ide-problems-sev-chip ide-problems-sev-' + s.kind + (on ? ' is-on' : ''),
|
|
243
|
+
title: (on ? 'Hide' : 'Show') + ' ' + SEVERITY_LABEL[s.kind].toLowerCase() + 's',
|
|
244
|
+
'aria-pressed': on ? 'true' : 'false',
|
|
245
|
+
onClick: function () { toggleSeverity(s.kind); }
|
|
246
|
+
},
|
|
247
|
+
React.createElement('i', { className: 'fas ' + SEVERITY_ICON[s.kind] }),
|
|
248
|
+
React.createElement('span', null, s.count)
|
|
249
|
+
);
|
|
250
|
+
})
|
|
202
251
|
),
|
|
203
252
|
React.createElement('input', {
|
|
204
253
|
className: 'ide-problems-filter',
|
|
@@ -816,48 +816,37 @@
|
|
|
816
816
|
return handled;
|
|
817
817
|
});
|
|
818
818
|
|
|
819
|
-
// ──
|
|
819
|
+
// ── Indent on paste ──────────────────────────────────────────────────────
|
|
820
820
|
//
|
|
821
|
-
//
|
|
822
|
-
//
|
|
823
|
-
//
|
|
824
|
-
// the
|
|
825
|
-
//
|
|
826
|
-
//
|
|
821
|
+
// Pasted code is re-indented to match where it landed, and nothing else is
|
|
822
|
+
// touched. This deliberately does NOT run the formatter: routing paste
|
|
823
|
+
// through Prettier reprinted the whole enclosing statement (and, for a
|
|
824
|
+
// paste that filled the document, every line of it), so pasting a snippet
|
|
825
|
+
// rewrote code the user never touched and buried the paste in an unrelated
|
|
826
|
+
// diff.
|
|
827
827
|
//
|
|
828
|
-
//
|
|
829
|
-
//
|
|
830
|
-
//
|
|
831
|
-
//
|
|
832
|
-
//
|
|
833
|
-
//
|
|
834
|
-
// Either way the result comes back at the editor's own tab/space setting,
|
|
835
|
-
// which is the point: code copied in from a spaces project lands as tabs.
|
|
828
|
+
// `editor.action.reindentselectedlines` is Monaco's own re-indenter — it
|
|
829
|
+
// uses the language's indentation rules, the same ones that already run
|
|
830
|
+
// when you press Enter, and it only ever changes leading whitespace.
|
|
831
|
+
// Monaco's `formatOnPaste` option stays off (EditorPanel passes false):
|
|
832
|
+
// its contribution declines to run here anyway.
|
|
836
833
|
var pasteDisposable = editor.onDidPaste(function (e) {
|
|
837
834
|
var prefs = (typeof EditorStore !== 'undefined' && EditorStore.getState().editorPrefs) || {};
|
|
838
|
-
if (prefs.
|
|
835
|
+
if (prefs.indentOnPaste === false) return;
|
|
839
836
|
|
|
840
837
|
var pasteModel = editor.getModel();
|
|
841
838
|
if (!pasteModel || pasteModel.isDisposed()) return;
|
|
842
839
|
|
|
843
|
-
var
|
|
844
|
-
var wholeDocument = e.range.startLineNumber <= full.startLineNumber &&
|
|
845
|
-
e.range.endLineNumber >= full.endLineNumber;
|
|
846
|
-
|
|
847
|
-
var action = editor.getAction(wholeDocument ? 'editor.action.formatDocument' : 'editor.action.formatSelection');
|
|
840
|
+
var action = editor.getAction('editor.action.reindentselectedlines');
|
|
848
841
|
if (!action) return;
|
|
849
842
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
return;
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// formatSelection works on the selection, so point it at the pasted range
|
|
856
|
-
// and put the cursor back where the paste left it.
|
|
843
|
+
// The action works on the selection, so point it at the pasted range and
|
|
844
|
+
// put the cursor back where the paste left it.
|
|
857
845
|
var restore = editor.getSelections();
|
|
858
846
|
editor.setSelection(e.range);
|
|
859
847
|
action.run()["catch"](function () {})["finally"](function () {
|
|
860
|
-
|
|
848
|
+
var m = editor.getModel();
|
|
849
|
+
if (restore && restore.length && m && !m.isDisposed()) editor.setSelections(restore);
|
|
861
850
|
});
|
|
862
851
|
});
|
|
863
852
|
|
|
@@ -1188,6 +1177,20 @@
|
|
|
1188
1177
|
});
|
|
1189
1178
|
}
|
|
1190
1179
|
|
|
1180
|
+
// Monaco ships no indentationRules for JavaScript, which makes
|
|
1181
|
+
// `editor.action.reindentselectedlines` — what indent-on-paste runs — a
|
|
1182
|
+
// silent no-op in JS/JSX files. These are VS Code's own JS rules.
|
|
1183
|
+
// setLanguageConfiguration merges, so this adds indentation without
|
|
1184
|
+
// disturbing the brackets/comments config the bundle already registers.
|
|
1185
|
+
['javascript', 'typescript'].forEach(function (langId) {
|
|
1186
|
+
monaco.languages.setLanguageConfiguration(langId, {
|
|
1187
|
+
indentationRules: {
|
|
1188
|
+
increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/,
|
|
1189
|
+
decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[})\]].*$/
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1191
1194
|
monaco.languages.setLanguageConfiguration('ruby', {
|
|
1192
1195
|
comments: { lineComment: '#', blockComment: ['=begin', '=end'] },
|
|
1193
1196
|
brackets: [['(', ')'], ['{', '}'], ['[', ']']],
|
|
@@ -1220,13 +1223,24 @@
|
|
|
1220
1223
|
// Single-line comments
|
|
1221
1224
|
[/#.*$/, 'comment'],
|
|
1222
1225
|
|
|
1223
|
-
// Heredoc start — capture the terminator word
|
|
1226
|
+
// Heredoc start — capture the terminator word. A tag naming a
|
|
1227
|
+
// language hands the body to Monaco's own tokenizer for that
|
|
1228
|
+
// language via nextEmbedded, so <<~JS is highlighted as real
|
|
1229
|
+
// JavaScript, not an imitation.
|
|
1224
1230
|
[/<<[-~]?(['"]?)(\w+)\1/, {
|
|
1225
1231
|
cases: {
|
|
1226
|
-
'$2~(?i:SQL)':
|
|
1227
|
-
'$2~(?i:HTML
|
|
1228
|
-
'$2~(?i:JS|JAVASCRIPT)':
|
|
1229
|
-
'
|
|
1232
|
+
'$2~(?i:SQL)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'sql' },
|
|
1233
|
+
'$2~(?i:HTML?|ERB)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'html' },
|
|
1234
|
+
'$2~(?i:JS|JAVASCRIPT)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'javascript' },
|
|
1235
|
+
'$2~(?i:CSS)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'css' },
|
|
1236
|
+
'$2~(?i:SCSS)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'scss' },
|
|
1237
|
+
'$2~(?i:JSON)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'json' },
|
|
1238
|
+
'$2~(?i:XML|SVG)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'xml' },
|
|
1239
|
+
'$2~(?i:YAML|YML)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'yaml' },
|
|
1240
|
+
'$2~(?i:SH|BASH|SHELL)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'shell' },
|
|
1241
|
+
'$2~(?i:GRAPHQL|GQL)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'graphql' },
|
|
1242
|
+
'$2~(?i:RUBY)': { token: 'string.heredoc.delimiter', next: '@heredocEmbedded.$2', nextEmbedded: 'ruby' },
|
|
1243
|
+
'@default': { token: 'string.heredoc.delimiter', next: '@heredoc.$2' }
|
|
1230
1244
|
}
|
|
1231
1245
|
}],
|
|
1232
1246
|
|
|
@@ -1360,9 +1374,12 @@
|
|
|
1360
1374
|
[/\s+/, '']
|
|
1361
1375
|
],
|
|
1362
1376
|
|
|
1363
|
-
// Generic heredoc — all content is string.heredoc
|
|
1377
|
+
// Generic heredoc — all content is string.heredoc. The terminator may
|
|
1378
|
+
// be indented: that is the whole point of <<~ (and <<-), and the old
|
|
1379
|
+
// /^(\w+)$/ rule missed it, leaving the rest of the file painted as a
|
|
1380
|
+
// string.
|
|
1364
1381
|
heredoc: [
|
|
1365
|
-
[
|
|
1382
|
+
[/^\s*(\w+)\s*$/, {
|
|
1366
1383
|
cases: {
|
|
1367
1384
|
'$1==$S2': { token: 'string.heredoc.delimiter', next: '@pop' },
|
|
1368
1385
|
'@default': 'string.heredoc'
|
|
@@ -1371,67 +1388,23 @@
|
|
|
1371
1388
|
[/.+/, 'string.heredoc']
|
|
1372
1389
|
],
|
|
1373
1390
|
|
|
1374
|
-
//
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
}],
|
|
1382
|
-
[/.+/, { token: '@rematch', next: '@heredocSQLLine' }]
|
|
1383
|
-
],
|
|
1384
|
-
|
|
1385
|
-
heredocSQLLine: [
|
|
1386
|
-
[/--.*$/, { token: 'comment.sql', next: '@pop' }],
|
|
1387
|
-
[/'[^']*'/, 'string.sql'],
|
|
1388
|
-
[/\b\d+(?:\.\d+)?\b/, 'number.sql'],
|
|
1389
|
-
[/\b(?:SELECT|FROM|WHERE|INSERT|UPDATE|DELETE|JOIN|LEFT|RIGHT|INNER|OUTER|ON|GROUP|ORDER|BY|HAVING|LIMIT|OFFSET|CREATE|DROP|ALTER|TABLE|INDEX|INTO|VALUES|SET|AS|AND|OR|NOT|NULL|IS|IN|LIKE|BETWEEN|DISTINCT|COUNT|SUM|AVG|MIN|MAX)\b/i, 'keyword.sql'],
|
|
1390
|
-
[/[^\s\w'"-]+/, 'string.heredoc'],
|
|
1391
|
-
[/\w+/, 'string.heredoc'],
|
|
1392
|
-
[/$/, { token: '', next: '@pop' }]
|
|
1393
|
-
],
|
|
1394
|
-
|
|
1395
|
-
// HTML heredoc — tag/attribute tokenization
|
|
1396
|
-
heredocHTML: [
|
|
1397
|
-
[/^(\w+)\s*$/, {
|
|
1391
|
+
// Language-tagged heredoc body. While embedded, Monarch only consults
|
|
1392
|
+
// this state to find the leaving rule; everything before it is
|
|
1393
|
+
// tokenized by the embedded language. @rematch + switchTo hands the
|
|
1394
|
+
// terminator line to @heredocEnd so it gets the delimiter colour
|
|
1395
|
+
// rather than being re-lexed as Ruby.
|
|
1396
|
+
heredocEmbedded: [
|
|
1397
|
+
[/^\s*(\w+)\s*$/, {
|
|
1398
1398
|
cases: {
|
|
1399
|
-
'$1==$S2': { token: '
|
|
1400
|
-
'@default': { token: '
|
|
1399
|
+
'$1==$S2': { token: '@rematch', switchTo: '@heredocEnd.$S2', nextEmbedded: '@pop' },
|
|
1400
|
+
'@default': { token: '' }
|
|
1401
1401
|
}
|
|
1402
1402
|
}],
|
|
1403
|
-
[
|
|
1403
|
+
[/.*/, { token: '' }]
|
|
1404
1404
|
],
|
|
1405
1405
|
|
|
1406
|
-
|
|
1407
|
-
[
|
|
1408
|
-
[/[a-zA-Z_:][a-zA-Z0-9_:\-\.]*(?=\s*=)/, 'attribute.name.html'],
|
|
1409
|
-
[/\/?>/, 'tag.html'],
|
|
1410
|
-
[/[^<>]+/, 'string.heredoc'],
|
|
1411
|
-
[/$/, { token: '', next: '@pop' }]
|
|
1412
|
-
],
|
|
1413
|
-
|
|
1414
|
-
// JS heredoc — keyword/string/number/comment tokenization
|
|
1415
|
-
heredocJS: [
|
|
1416
|
-
[/^(\w+)\s*$/, {
|
|
1417
|
-
cases: {
|
|
1418
|
-
'$1==$S2': { token: 'string.heredoc.delimiter', next: '@pop' },
|
|
1419
|
-
'@default': { token: '@rematch', next: '@heredocJSLine' }
|
|
1420
|
-
}
|
|
1421
|
-
}],
|
|
1422
|
-
[/.+/, { token: '@rematch', next: '@heredocJSLine' }]
|
|
1423
|
-
],
|
|
1424
|
-
|
|
1425
|
-
heredocJSLine: [
|
|
1426
|
-
[/\/\/.*$/, { token: 'comment', next: '@pop' }],
|
|
1427
|
-
[/"(?:[^"\\]|\\.)*"/, 'string'],
|
|
1428
|
-
[/'(?:[^'\\]|\\.)*'/, 'string'],
|
|
1429
|
-
[/`(?:[^`\\]|\\.)*`/, 'string'],
|
|
1430
|
-
[/\b\d+(?:\.\d+)?\b/, 'number'],
|
|
1431
|
-
[/\b(?:var|let|const|function|return|if|else|for|while|do|switch|case|break|continue|new|delete|typeof|instanceof|in|of|class|extends|import|export|default|null|undefined|true|false|this|super|async|await|try|catch|finally|throw|void|yield)\b/, 'keyword'],
|
|
1432
|
-
[/[^\s\w'"`;\/]+/, 'string.heredoc'],
|
|
1433
|
-
[/\w+/, 'string.heredoc'],
|
|
1434
|
-
[/$/, { token: '', next: '@pop' }]
|
|
1406
|
+
heredocEnd: [
|
|
1407
|
+
[/^\s*\w+\s*$/, { token: 'string.heredoc.delimiter', next: '@pop' }]
|
|
1435
1408
|
],
|
|
1436
1409
|
|
|
1437
1410
|
// %w[] %W[] word arrays
|
|
@@ -1646,6 +1619,15 @@
|
|
|
1646
1619
|
// their "open this" through here.
|
|
1647
1620
|
monaco.editor.registerEditorOpener({
|
|
1648
1621
|
openCodeEditor: function (_source, resource, selectionOrPosition) {
|
|
1622
|
+
// Only file:// resources name a workspace file. Models are created
|
|
1623
|
+
// without an explicit URI, so Monaco gives each one an
|
|
1624
|
+
// `inmemory://model/N` identity — and the TS worker returns exactly
|
|
1625
|
+
// that when a JS definition resolves inside the file you are already
|
|
1626
|
+
// in. Stripping it to a path opened a phantom tab called "57".
|
|
1627
|
+
// Handing those back to Monaco lets it reveal the position in the
|
|
1628
|
+
// current editor, which is what the gesture meant.
|
|
1629
|
+
if (String(resource.scheme || '') !== 'file') return false;
|
|
1630
|
+
|
|
1649
1631
|
var path = String(resource.path || '').replace(/^\/+/, '');
|
|
1650
1632
|
if (!path || typeof TabManager === 'undefined' || !TabManager.openTab) return false;
|
|
1651
1633
|
|
|
@@ -650,8 +650,42 @@ var TabManager = (function () {
|
|
|
650
650
|
_updateTab(paneId, path, { gotoLine: null, gotoCol: null });
|
|
651
651
|
}
|
|
652
652
|
|
|
653
|
+
// VS Code-style scratch buffer: a tab with no file behind it. Nothing is
|
|
654
|
+
// written anywhere until the user saves, at which point the save flow asks
|
|
655
|
+
// for a real path and converts the tab.
|
|
656
|
+
function openUntitledTab(forcePaneId) {
|
|
657
|
+
var state = EditorStore.getState();
|
|
658
|
+
var paneId = forcePaneId || state.focusedPaneId;
|
|
659
|
+
var pane = state.panes.find(function (p) { return p.id === paneId; });
|
|
660
|
+
if (!pane) return;
|
|
661
|
+
|
|
662
|
+
var used = {};
|
|
663
|
+
state.panes.forEach(function (p) {
|
|
664
|
+
p.tabs.forEach(function (t) { if (t.isUntitled) used[t.name] = true; });
|
|
665
|
+
});
|
|
666
|
+
var n = 1;
|
|
667
|
+
while (used['Untitled-' + n]) n++;
|
|
668
|
+
var name = 'Untitled-' + n;
|
|
669
|
+
var path = 'untitled://' + name;
|
|
670
|
+
|
|
671
|
+
var newTab = {
|
|
672
|
+
id: path, path: path, name: name,
|
|
673
|
+
dirty: false, content: '', cleanContent: '',
|
|
674
|
+
viewState: null, isUntitled: true, loading: false,
|
|
675
|
+
externalContentVersion: 1
|
|
676
|
+
};
|
|
677
|
+
var newPanes = state.panes.map(function (p) {
|
|
678
|
+
if (p.id === paneId) {
|
|
679
|
+
return Object.assign({}, p, { tabs: p.tabs.concat(newTab), activeTabId: path });
|
|
680
|
+
}
|
|
681
|
+
return p;
|
|
682
|
+
});
|
|
683
|
+
EditorStore.setState({ panes: newPanes, focusedPaneId: paneId, activeTabId: path });
|
|
684
|
+
}
|
|
685
|
+
|
|
653
686
|
return {
|
|
654
687
|
openTab: openTab,
|
|
688
|
+
openUntitledTab: openUntitledTab,
|
|
655
689
|
getRecentFiles: getRecentFiles,
|
|
656
690
|
openDiffTab: openDiffTab,
|
|
657
691
|
openCombinedDiffTab: openCombinedDiffTab,
|
|
@@ -2998,9 +2998,41 @@ button:not(.pico-btn) { margin-bottom: 0; }
|
|
|
2998
2998
|
color: var(--ide-fg-muted, #ccc);
|
|
2999
2999
|
}
|
|
3000
3000
|
.ide-problems-title { font-weight: 600; }
|
|
3001
|
-
.
|
|
3002
|
-
.
|
|
3001
|
+
/* Severity toggles. Pushed to the right of the summary; the text filter that
|
|
3002
|
+
follows keeps its own left margin off `auto` so the two sit together. */
|
|
3003
|
+
.ide-problems-severity-filter {
|
|
3003
3004
|
margin-left: auto;
|
|
3005
|
+
display: flex;
|
|
3006
|
+
gap: 4px;
|
|
3007
|
+
}
|
|
3008
|
+
.ide-problems-sev-chip {
|
|
3009
|
+
display: inline-flex;
|
|
3010
|
+
align-items: center;
|
|
3011
|
+
gap: 4px;
|
|
3012
|
+
background: transparent;
|
|
3013
|
+
border: 1px solid transparent;
|
|
3014
|
+
border-radius: 3px;
|
|
3015
|
+
padding: 1px 6px;
|
|
3016
|
+
font-size: 11px;
|
|
3017
|
+
font-variant-numeric: tabular-nums;
|
|
3018
|
+
cursor: pointer;
|
|
3019
|
+
/* Off is legible but clearly recessive — a disabled-looking chip reads as
|
|
3020
|
+
"broken" rather than "toggled off". */
|
|
3021
|
+
color: var(--ide-text-muted, #858585);
|
|
3022
|
+
opacity: 0.55;
|
|
3023
|
+
}
|
|
3024
|
+
.ide-problems-sev-chip:hover { opacity: 0.85; }
|
|
3025
|
+
.ide-problems-sev-chip.is-on {
|
|
3026
|
+
opacity: 1;
|
|
3027
|
+
border-color: var(--ide-border, #333);
|
|
3028
|
+
background: var(--ide-input-bg, #2d2d2d);
|
|
3029
|
+
}
|
|
3030
|
+
.ide-problems-sev-chip.is-on.ide-problems-sev-error { color: var(--ide-error, #f14c4c); }
|
|
3031
|
+
.ide-problems-sev-chip.is-on.ide-problems-sev-warning { color: var(--ide-warning, #cca700); }
|
|
3032
|
+
.ide-problems-sev-chip.is-on.ide-problems-sev-info { color: var(--ide-info, #3794ff); }
|
|
3033
|
+
|
|
3034
|
+
.ide-problems-filter {
|
|
3035
|
+
margin-left: 8px;
|
|
3004
3036
|
background: var(--ide-input-bg, #2d2d2d);
|
|
3005
3037
|
color: var(--ide-fg, #eee);
|
|
3006
3038
|
border: 1px solid var(--ide-border, #333);
|
|
@@ -931,7 +931,20 @@ module Mbeditor
|
|
|
931
931
|
startLine: line, startCol: col, endLine: line, endCol: col + 1
|
|
932
932
|
}]
|
|
933
933
|
else
|
|
934
|
-
|
|
934
|
+
# Only when the file parses: the scope lint would re-hit the same
|
|
935
|
+
# parse error and report nothing useful on top of the marker above.
|
|
936
|
+
JsSyntaxCheckService.scope_lint(workspace_root, code).map do |w|
|
|
937
|
+
line = w["line"] || 1
|
|
938
|
+
col = (w["column"] || 0) + 1
|
|
939
|
+
{
|
|
940
|
+
severity: "warning",
|
|
941
|
+
copName: "BabelScope",
|
|
942
|
+
correctable: false,
|
|
943
|
+
message: "[babel] #{w['message']}",
|
|
944
|
+
startLine: line, startCol: col,
|
|
945
|
+
endLine: line, endCol: col + [w["name"].to_s.length, 1].max
|
|
946
|
+
}
|
|
947
|
+
end
|
|
935
948
|
end
|
|
936
949
|
return render json: { markers: markers }
|
|
937
950
|
end
|