Skip to content

Commit a700a9e

Browse files
Update text editor to use monaco. Partially addresses #571 (#1800)
* Initial commit for replacing ace with monaco editor * WIP- Remove ace from TextEditor * WIP- Integrate vim keybindings * Ensure editor hasn't been destroyed while loading vim keybindings * Remove dummy python code for '' * WIP- Add support for additional themes * WIP- Fix eslint issues, remove unused functions in LogViewerWidget * WIP- Fix more eslint and code climate fixes * WIP- Fix model dispose problem by disposing editor only * WIP-Remove console.log statement from TextEditorWidget.js Co-authored-by: Brian Broll <[email protected]>
1 parent c97136f commit a700a9e

File tree

1,057 files changed

+14797
-314908
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,057 files changed

+14797
-314908
lines changed

config/config.base.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ config.blob.fsDir = process.env.DEEPFORGE_BLOB_DIR || config.blob.fsDir;
1313

1414
config.requirejsPaths.deepforge = './src/common';
1515
config.requirejsPaths['aws-sdk-min'] = './node_modules/aws-sdk/dist/aws-sdk.min';
16-
config.requirejsPaths.ace = './src/visualizers/widgets/TextEditor/lib/ace';
16+
config.requirejsPaths.vs = './node_modules/monaco-editor/min/vs';
17+
config.requirejsPaths.MonacoVim = './node_modules/monaco-vim/dist/monaco-vim';
1718
config.seedProjects.defaultProject = 'project';
1819

1920
config.plugin.allowBrowserExecution = true;
2021
config.plugin.allowServerExecution = true;
2122

2223
config.executor.enable = true;
23-
2424
config.visualization.extraCss.push('deepforge/styles/global.css');
2525

2626
config.storage.autoMerge.enable = true;

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
"lodash.merge": "^4.6.2",
4242
"lodash.template": "^4.4.0",
4343
"minimatch": "^3.0.4",
44+
"monaco-editor": "^0.20.0",
45+
"monaco-vim": "^0.1.7",
4446
"mongodb": "^2.2.10",
4547
"node-fetch": "^2.6.0",
4648
"pacote": "^11.1.10",

src/visualizers/panels/LogViewer/LogViewerControl.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ define([
7272
};
7373

7474
LogViewerControl.prototype._onLoad = function (id) {
75-
this.getFullDescriptor(id).then(desc => this._widget.addNode(desc));
75+
this.getFullDescriptor(id).then(desc => {
76+
this._widget.addNode(desc);
77+
});
7678
};
7779

7880
LogViewerControl.prototype._onUpdate = function (id) {

src/visualizers/panels/LogViewer/LogViewerPanel.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ define([
4646

4747
//set Widget title
4848
this.setTitle('');
49+
const config = {language: 'plaintext', displayMiniMap: false};
4950

50-
this.widget = new LogViewerWidget(this.logger, this.$el);
51+
this.widget = new LogViewerWidget(this.logger, this.$el, config);
5152

5253
this.widget.setTitle = function (title) {
5354
self.setTitle(title);

src/visualizers/panels/OperationDepEditor/OperationDepEditorPanel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ define([
4646
//set Widget title
4747
this.setTitle('');
4848

49-
const config = {language: 'yaml'};
49+
const config = {language: 'yaml', displayMiniMap: false};
5050
this.widget = new TextEditorWidget(this.logger, this.$el, config);
5151

5252
this.widget.setTitle = function (title) {

src/visualizers/widgets/LogViewer/LogViewerWidget.js

Lines changed: 15 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*globals define, _*/
1+
/*globals define, _, monaco*/
22
/*jshint browser: true*/
33

44
define([
@@ -9,134 +9,38 @@ define([
99
) {
1010
'use strict';
1111

12-
var LogViewerWidget,
13-
ANSI_COLORS = [
14-
'black',
15-
'red',
16-
'green',
17-
'yellow',
18-
'blue',
19-
'magenta',
20-
'cyan',
21-
'gray'
22-
];
23-
24-
LogViewerWidget = function () {
12+
const LogViewerWidget = function () {
2513
this.readOnly = true;
2614
TextEditorWidget.apply(this, arguments);
2715
this._el.addClass('log-viewer');
28-
this.editor.setTheme('ace/theme/twilight');
29-
this.editor.setShowPrintMargin(false);
30-
this.editor.renderer.setScrollMargin(0, 75);
31-
this.addKeyListeners();
32-
33-
// Override the textlayer to add support for ansi colors
34-
this.customizeAce();
16+
this.editor.updateOptions({
17+
lineNumbers: this.getLineNumbers
18+
});
19+
this.setReadOnly(true);
3520
};
3621

3722
_.extend(LogViewerWidget.prototype, TextEditorWidget.prototype);
3823

39-
LogViewerWidget.prototype.addKeyListeners = function() {
40-
// Need to add key listeners to the container itself since ace is in read-only mode
41-
this._el.on('keydown', event => {
42-
// ctrl-alt-pagedown -> EOF
43-
if (event.key === 'PageDown' && event.altKey && (event.ctrlKey || event.metaKey)) {
44-
this.editor.gotoLine(Infinity);
45-
event.stopPropagation();
46-
event.preventDefault();
47-
}
48-
// ctrl-alt-pagedown -> beginning of file
49-
if (event.key === 'PageUp' && event.altKey && (event.ctrlKey || event.metaKey)) {
50-
this.editor.gotoLine(0);
51-
event.stopPropagation();
52-
event.preventDefault();
53-
}
54-
});
55-
};
56-
5724
LogViewerWidget.prototype.getHeader = function(desc) {
5825
return `Console logging for Operation "${desc.name}":\n`;
5926
};
6027

61-
LogViewerWidget.prototype.customizeAce = function() {
62-
var textLayer = this.editor.renderer.$textLayer,
63-
renderToken = textLayer.$renderToken;
64-
65-
textLayer.$renderToken = function(builder, col, token, value) {
66-
// check for ansi color
67-
var ansiBuilder = LogViewerWidget.renderAnsiFromText(value),
68-
newToken;
69-
70-
for (var i = 1; i < ansiBuilder.length; i+= 3) {
71-
builder.push(ansiBuilder[i-1]);
72-
value = ansiBuilder[i];
73-
newToken = {
74-
type: token.type,
75-
value: value
76-
};
77-
col = renderToken.call(this, builder, col, newToken, value);
78-
builder.push(ansiBuilder[i+1]);
79-
}
80-
81-
return col;
82-
};
83-
};
84-
85-
// Get the editor text and update wrt ansi colors
86-
LogViewerWidget.renderAnsiFromText = function(remaining) {
87-
var r = /\[[0-6][0-9]?(;[0-9]([0-7]))?m/,
88-
match,
89-
ansiCode,
90-
text,
91-
color,
92-
nextColor = 'default',
93-
builder = [];
94-
95-
color = color || nextColor;
96-
while (remaining) {
97-
match = remaining.match(r);
98-
if (match) {
99-
ansiCode = match[0];
100-
if (match[1] && match[1][1] === '3') { // foreground color
101-
nextColor = ANSI_COLORS[match[2]] || null;
102-
}
103-
text = remaining.substring(0, match.index);
104-
remaining = remaining.substring(match.index+ansiCode.length);
105-
} else {
106-
text = remaining;
107-
remaining = '';
108-
}
109-
110-
// Add a "span" node w/ the appropriate color class
111-
builder.push(`<span class='ansi-${color}'>`, text, '</span>');
112-
113-
color = nextColor;
114-
nextColor = 'default';
115-
}
116-
return builder;
28+
LogViewerWidget.prototype.getLineNumbers = function(lineno) {
29+
return lineno - 2;
11730
};
11831

119-
LogViewerWidget.prototype.getSessionOptions = function() {
120-
return {
121-
firstLineNumber: -1
122-
};
123-
};
124-
125-
LogViewerWidget.prototype.addNode = function (desc) {
126-
var atEOF = this.editor.getLastVisibleRow()+1 ===
127-
this.editor.session.getLength();
128-
32+
LogViewerWidget.prototype.addNode = function(desc) {
12933
TextEditorWidget.prototype.addNode.call(this, desc);
130-
131-
if (atEOF) { // Scroll to bottom
132-
this.editor.gotoLine(Infinity);
133-
}
34+
const revealLineno = Math.ceil(this.model.getLineCount()/2);
35+
this.editor.revealLineInCenter(
36+
revealLineno,
37+
monaco.editor.ScrollType.Smooth
38+
);
13439
};
13540

13641
LogViewerWidget.prototype.getDefaultEditorOptions = function() {
13742
const opts = TextEditorWidget.prototype.getDefaultEditorOptions.call(this);
138-
opts.fontFamily = 'bitstream vera sans mono';
139-
opts.fontSize = '10pt';
43+
opts.fontSize = 10;
14044
return opts;
14145
};
14246

src/visualizers/widgets/OperationCodeEditor/OperationCodeEditorWidget.js

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*globals define */
1+
/*globals define, monaco*/
22
/*jshint browser: true*/
33

44
define([
@@ -18,13 +18,12 @@ define([
1818
TextEditorWidget.call(this, logger, container);
1919
this.lineOffset = 0;
2020
// Add the shift-enter command
21-
this.editor.commands.addCommand({
22-
name: 'executeOrStopJob',
23-
bindKey: {
24-
mac: 'Shift-Enter',
25-
win: 'Shift-Enter'
26-
},
27-
exec: () => this.executeOrStopJob()
21+
this.editor.addCommand(
22+
monaco.KeyMod.Shift | monaco.KeyCode.Enter,
23+
this.executeOrStopJob
24+
);
25+
this.editor.updateOptions({
26+
lineNumbers: this.updateOffset.bind(this)
2827
});
2928
};
3029

@@ -50,40 +49,16 @@ define([
5049
OperationCodeEditorWidget.prototype.setLineOffset = function (offset) {
5150
if (this.lineOffset !== offset) {
5251
this.lineOffset = offset;
53-
this.updateOffset();
5452
}
5553
};
5654

57-
OperationCodeEditorWidget.prototype.updateOffset = function () {
55+
OperationCodeEditorWidget.prototype.updateOffset = function (originalLineNumber) {
5856
var lines,
5957
actualOffset;
6058

6159
lines = this.currentHeader.match(/\n/g);
62-
actualOffset = this.lineOffset - (lines ? lines.length : 0);
63-
this.editor.setOption('firstLineNumber', actualOffset);
64-
};
65-
66-
OperationCodeEditorWidget.prototype.getCompleter = function () {
67-
var completer = TextEditorWidget.prototype.getCompleter.call(this),
68-
getBasicCompletions = completer.getCompletionsFor,
69-
self = this;
70-
71-
// TODO: update completions for python stuff
72-
completer.getCompletionsFor = function(obj) {
73-
if (obj === 'attributes') {
74-
return self.getOperationAttributes().map(attr => {
75-
return {
76-
name: attr,
77-
value: attr,
78-
score: 4,
79-
meta: 'operation'
80-
};
81-
});
82-
} else {
83-
return getBasicCompletions.apply(this, arguments);
84-
}
85-
};
86-
return completer;
60+
actualOffset = this.lineOffset - (lines ? lines.length + 1 : 0);
61+
return (originalLineNumber + actualOffset);
8762
};
8863

8964
return OperationCodeEditorWidget;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"python": {
3+
"id": "python",
4+
"extensions": [
5+
".py",
6+
".pyc"
7+
],
8+
"aliases": [
9+
"py",
10+
"PY"
11+
],
12+
"mimetypes": [
13+
"application/text"
14+
],
15+
"comment": "#"
16+
},
17+
"javascript": {
18+
"id": "javascript",
19+
"extensions": [
20+
"js",
21+
"jsx"
22+
],
23+
"mimetypes": [
24+
"application/text"
25+
],
26+
"comment": "//"
27+
},
28+
"yaml": {
29+
"id": "yaml",
30+
"extensions": [
31+
"yaml",
32+
"yml"
33+
],
34+
"aliases": [
35+
"yml",
36+
"yaml",
37+
"YML",
38+
"YAML"
39+
],
40+
"mimetypes": [
41+
"application/text"
42+
],
43+
"comment": "#"
44+
},
45+
"plaintext": {
46+
"id": "plaintext",
47+
"extensions": [
48+
"txt",
49+
"text"
50+
],
51+
"aliases": [
52+
"text",
53+
"PLAINTEXT"
54+
],
55+
"mimetypes": [
56+
"application/text"
57+
],
58+
"comment": "#"
59+
}
60+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* globals monaco, define */
2+
3+
define(['text!./Themes/themelist.json'], function (ThemeList) {
4+
const DEFAULT_THEMES = ['vs-dark', 'vs', 'hc-black'];
5+
ThemeList = JSON.parse(ThemeList);
6+
7+
class MonacoThemesProvider {
8+
constructor() {
9+
this.importedThemes = DEFAULT_THEMES;
10+
this.themes = DEFAULT_THEMES.concat(Object.keys(ThemeList));
11+
}
12+
13+
async setTheme(theme) {
14+
if (this.importedThemes.includes(theme)){
15+
monaco.editor.setTheme(theme);
16+
} else if (this.themes.includes(theme)){
17+
const themeData = await this._importTheme(theme);
18+
monaco.editor.defineTheme(theme, themeData);
19+
monaco.editor.setTheme(theme);
20+
} else {
21+
monaco.editor.setTheme(DEFAULT_THEMES[0]);
22+
}
23+
}
24+
25+
async _importTheme(theme) {
26+
return new Promise((resolve, reject) => {
27+
const importName = `text!widgets/TextEditor/Themes/${(ThemeList[theme])}.json`;
28+
require([importName], (themeJSON) => {
29+
resolve(JSON.parse(themeJSON));
30+
}, reject);
31+
});
32+
}
33+
}
34+
35+
return MonacoThemesProvider;
36+
});

0 commit comments

Comments
 (0)