blob: 2aa76cf399a3334e8e5a2abca87fa1c968baf315 [file] [log] [blame] [raw]
RabsRincon6ef87b52018-02-27 14:58:21 +01001// Copyright (c) 2017, Matt Godbolt
Matt Godbolteb747762017-01-04 17:12:52 -06002// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// * Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9// * Redistributions in binary form must reproduce the above copyright
10// notice, this list of conditions and the following disclaimer in the
11// documentation and/or other materials provided with the distribution.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23// POSSIBILITY OF SUCH DAMAGE.
24
jaredwy8be70d12017-12-05 17:03:07 +110025"use strict";
Matt Godbolteb747762017-01-04 17:12:52 -060026
RabsRincon79f3e902018-07-16 07:48:47 +020027var FontScale = require('../fontscale');
RabsRincon6a9095f2019-07-22 14:37:49 +020028var monaco = require('monaco-editor');
jaredwy8be70d12017-12-05 17:03:07 +110029var _ = require('underscore');
30var $ = require('jquery');
RabsRincon79f3e902018-07-16 07:48:47 +020031var ga = require('../analytics');
Matt Godbolt476df0f2017-02-02 08:35:08 -060032
RabsRincon79f3e902018-07-16 07:48:47 +020033require('../modes/asm-mode');
jaredwy8be70d12017-12-05 17:03:07 +110034require('selectize');
Matt Godbolteb747762017-01-04 17:12:52 -060035
jaredwy8be70d12017-12-05 17:03:07 +110036function State(id, model) {
37 this.id = id;
38 this.model = model;
39 this.compiler = null;
40 this.result = null;
41}
Matt Godbolteb747762017-01-04 17:12:52 -060042
jaredwy8be70d12017-12-05 17:03:07 +110043State.prototype.update = function (id, compiler, result) {
44 if (this.id !== id) return false;
45 this.compiler = compiler;
46 this.result = result;
47 var asm = result.asm || [];
48 this.model.setValue(_.pluck(asm, 'text').join("\n"));
49 return true;
50};
Matt Godbolteb747762017-01-04 17:12:52 -060051
jaredwy8be70d12017-12-05 17:03:07 +110052function Diff(hub, container, state) {
53 this.container = container;
54 this.eventHub = hub.createEventHub();
55 this.domRoot = container.getElement();
56 this.domRoot.html($('#diff').html());
57 this.compilers = {};
Matt Godbolteb747762017-01-04 17:12:52 -060058
jaredwy8be70d12017-12-05 17:03:07 +110059 this.outputEditor = monaco.editor.createDiffEditor(this.domRoot.find(".monaco-placeholder")[0], {
RabsRincon46244d52018-02-15 19:19:59 +010060 fontFamily: 'Consolas, "Liberation Mono", Courier, monospace',
jaredwy8be70d12017-12-05 17:03:07 +110061 scrollBeyondLastLine: false,
62 readOnly: true,
63 language: 'asm'
64 });
Matt Godbolteb747762017-01-04 17:12:52 -060065
jaredwy8be70d12017-12-05 17:03:07 +110066 this.lhs = new State(state.lhs, monaco.editor.createModel('', 'asm'));
67 this.rhs = new State(state.rhs, monaco.editor.createModel('', 'asm'));
68 this.outputEditor.setModel({original: this.lhs.model, modified: this.rhs.model});
Matt Godbolt37c62912017-02-04 08:46:15 -060069
jaredwy8be70d12017-12-05 17:03:07 +110070 var selectize = this.domRoot.find(".diff-picker").selectize({
71 sortField: 'name',
72 valueField: 'id',
73 labelField: 'name',
74 searchField: ['name'],
75 options: [],
76 items: [],
77 render: {
78 option: function (item, escape) {
79 return '<div>' +
80 '<span class="compiler">' + escape(item.compiler.name) + '</span>' +
81 '<span class="options">' + escape(item.options) + '</span>' +
82 '<ul class="meta">' +
83 '<li class="editor">Editor #' + escape(item.editorId) + '</li>' +
84 '<li class="compilerId">Compiler #' + escape(item.id) + '</li>' +
85 '</ul></div>';
Matt Godbolt60e1a862017-03-26 08:37:59 -050086 }
RabsRincona9ceec42018-10-07 16:44:19 +020087 },
88 dropdownParent: 'body'
jaredwy8be70d12017-12-05 17:03:07 +110089 }).on('change', _.bind(function (e) {
90 var target = $(e.target);
91 var compiler = this.compilers[target.val()];
Matt Godbolt476df0f2017-02-02 08:35:08 -060092 if (!compiler) return;
jaredwy8be70d12017-12-05 17:03:07 +110093 if (target.hasClass('lhs')) {
94 this.lhs.compiler = compiler;
95 this.lhs.id = compiler.id;
96 } else {
97 this.rhs.compiler = compiler;
98 this.rhs.id = compiler.id;
99 }
100 this.onDiffSelect(compiler.id);
101 }, this));
102 this.selectize = {lhs: selectize[0].selectize, rhs: selectize[1].selectize};
103
RabsRincon8a24a392018-03-25 19:04:48 +0200104 this.initButtons(state);
105 this.initCallbacks();
jaredwy8be70d12017-12-05 17:03:07 +1100106
107 this.updateCompilerNames();
108 this.updateCompilers();
RabsRinconf6e9c632018-07-15 20:08:00 +0200109 ga.proxy('send', {
110 hitType: 'event',
RabsRincon98450182018-07-28 19:39:54 +0200111 eventCategory: 'OpenViewPane',
112 eventAction: 'Diff'
RabsRinconf6e9c632018-07-15 20:08:00 +0200113 });
jaredwy8be70d12017-12-05 17:03:07 +1100114}
115
116// TODO: de-dupe with compiler etc
117Diff.prototype.resize = function () {
RabsRinconc6e42852018-03-28 20:59:50 +0200118 var topBarHeight = this.topBar.outerHeight(true);
jaredwy8be70d12017-12-05 17:03:07 +1100119 this.outputEditor.layout({
120 width: this.domRoot.width(),
121 height: this.domRoot.height() - topBarHeight
122 });
123};
124
125Diff.prototype.onDiffSelect = function (id) {
126 this.eventHub.emit('resendCompilation', id);
127 this.updateCompilerNames();
128 this.updateState();
129};
130
131Diff.prototype.onCompileResult = function (id, compiler, result) {
132 // both sides must be updated, don't be tempted to rewrite this as
133 // var changes = lhs.update() || rhs.update();
134 var lhsChanged = this.lhs.update(id, compiler, result);
135 var rhsChanged = this.rhs.update(id, compiler, result);
136 if (lhsChanged || rhsChanged) {
137 this.updateCompilerNames();
138 }
139};
140
RabsRincon8a24a392018-03-25 19:04:48 +0200141Diff.prototype.initButtons = function (state) {
142 this.fontScale = new FontScale(this.domRoot, state, this.outputEditor);
RabsRinconc6e42852018-03-28 20:59:50 +0200143
144 this.topBar = this.domRoot.find(".top-bar");
RabsRincon8a24a392018-03-25 19:04:48 +0200145};
146
147Diff.prototype.initCallbacks = function () {
148 this.fontScale.on('change', _.bind(this.updateState, this));
149
150 this.eventHub.on('compileResult', this.onCompileResult, this);
151 this.eventHub.on('compiler', this.onCompiler, this);
152 this.eventHub.on('compilerClose', this.onCompilerClose, this);
153 this.eventHub.on('settingsChange', this.onSettingsChange, this);
154 this.eventHub.on('themeChange', this.onThemeChange, this);
155 this.container.on('destroy', function () {
156 this.eventHub.unsubscribe();
157 this.outputEditor.dispose();
158 }, this);
159 this.container.on('resize', this.resize, this);
160 this.container.on('shown', this.resize, this);
161
162 this.eventHub.emit('resendCompilation', this.lhs.id);
163 this.eventHub.emit('resendCompilation', this.rhs.id);
164 this.eventHub.emit('findCompilers');
165 this.eventHub.emit('requestTheme');
166 this.eventHub.emit('requestSettings');
167};
168
jaredwy8be70d12017-12-05 17:03:07 +1100169Diff.prototype.onCompiler = function (id, compiler, options, editorId) {
170 if (!compiler) return;
171 options = options || "";
172 var name = compiler.name + " " + options;
173 // TODO: selectize doesn't play nicely with CSS tricks for truncation; this is the best I can do
174 // There's a plugin at: http://www.benbybenjacobs.com/blog/2014/04/09/no-wrap-plugin-for-selectize-dot-js
175 // but it doesn't look easy to integrate.
176 var maxLength = 30;
177 if (name.length > maxLength - 3) name = name.substr(0, maxLength - 3) + "...";
178 this.compilers[id] = {
179 id: id,
180 name: name,
181 options: options,
182 editorId: editorId,
183 compiler: compiler
184 };
185 if (!this.lhs.id) {
186 this.lhs.compiler = this.compilers[id];
187 this.lhs.id = id;
188 this.onDiffSelect(id);
189 } else if (!this.rhs.id) {
190 this.rhs.compiler = this.compilers[id];
191 this.rhs.id = id;
192 this.onDiffSelect(id);
193 }
194 this.updateCompilers();
195};
196
197Diff.prototype.onCompilerClose = function (id) {
198 delete this.compilers[id];
199 this.updateCompilers();
200};
201
202Diff.prototype.updateCompilerNames = function () {
203 var name = "Diff";
204 if (this.lhs.compiler && this.rhs.compiler)
205 name += " " + this.lhs.compiler.name + " vs " + this.rhs.compiler.name;
206 this.container.setTitle(name);
207};
208
209Diff.prototype.updateCompilersFor = function (selectize, id) {
210 selectize.clearOptions();
211 _.each(this.compilers, function (compiler) {
212 selectize.addOption(compiler);
213 }, this);
214 if (this.compilers[id]) {
215 selectize.setValue(id);
216 }
217};
218
219Diff.prototype.updateCompilers = function () {
220 this.updateCompilersFor(this.selectize.lhs, this.lhs.id);
221 this.updateCompilersFor(this.selectize.rhs, this.rhs.id);
222};
223
224Diff.prototype.updateState = function () {
225 var state = {
226 lhs: this.lhs.id,
227 rhs: this.rhs.id
228 };
229 this.fontScale.addState(state);
230 this.container.setState(state);
231};
232
233Diff.prototype.onThemeChange = function (newTheme) {
234 if (this.outputEditor)
235 this.outputEditor.updateOptions({theme: newTheme.monaco});
236};
237
RabsRinconabb391f2018-03-24 23:12:27 +0100238Diff.prototype.onSettingsChange = function (newSettings) {
239 this.outputEditor.updateOptions({
240 minimap: {
241 enabled: newSettings.showMinimap
RabsRincon5706da42018-12-26 21:45:38 +0100242 },
Rubén Rincón6e4ec412019-11-01 16:25:37 +0100243 fontFamily: newSettings.editorsFFont,
Rubén Rincón9b31ce22019-11-04 16:47:14 +0100244 fontLigatures: newSettings.editorsFLigatures
RabsRinconabb391f2018-03-24 23:12:27 +0100245 });
246};
247
RabsRincon62e68302018-02-07 23:58:21 +0100248module.exports = {
jaredwy8be70d12017-12-05 17:03:07 +1100249 Diff: Diff,
250 getComponent: function (lhs, rhs) {
251 return {
252 type: 'component',
253 componentName: 'diff',
RabsRincon62e68302018-02-07 23:58:21 +0100254 componentState: {lhs: lhs, rhs: rhs}
Matt Godbolt60e1a862017-03-26 08:37:59 -0500255 };
jaredwy8be70d12017-12-05 17:03:07 +1100256 }
257};