Skip to content

Commit

Permalink
feat: Add annotation level information to gutter tooltip (#5101)
Browse files Browse the repository at this point in the history
* Add annotation level information to gutter tooltip
Improves gutter tooltip by adding a header indicating the total number of annotations per level for that row and an icon in front of each message indicating the annotation level for that annotation.
  • Loading branch information
akoreman authored Mar 24, 2023
1 parent 9f86bb0 commit 3cd28b8
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 19 deletions.
23 changes: 19 additions & 4 deletions src/css/editor.css.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,22 +115,24 @@ module.exports = `
background-repeat: no-repeat;
}
.ace_gutter-cell.ace_error {
.ace_gutter-cell.ace_error, .ace_icon.ace_error {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");
background-repeat: no-repeat;
background-position: 2px center;
}
.ace_gutter-cell.ace_warning {
.ace_gutter-cell.ace_warning, .ace_icon.ace_warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");
background-repeat: no-repeat;
background-position: 2px center;
}
.ace_gutter-cell.ace_info {
.ace_gutter-cell.ace_info, .ace_icon.ace_info {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");
background-repeat: no-repeat;
background-position: 2px center;
}
.ace_dark .ace_gutter-cell.ace_info {
.ace_dark .ace_gutter-cell.ace_info, .ace_dark .ace_icon.ace_info {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");
}
Expand Down Expand Up @@ -422,6 +424,19 @@ module.exports = `
outline: 1px solid black;
}
.ace_gutter-tooltip_header {
font-weight: bold;
}
.ace_gutter-tooltip_body {
padding-top: 5px;
}
.ace_gutter-tooltip .ace_icon {
display: inline-block;
width: 18px;
}
.ace_folding-enabled > .ace_gutter-cell {
padding-right: 13px;
}
Expand Down
14 changes: 8 additions & 6 deletions src/layer/gutter.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,25 @@ var Gutter = function(parentEl) {
var row = annotation.row;
var rowInfo = this.$annotations[row];
if (!rowInfo)
rowInfo = this.$annotations[row] = {text: []};
rowInfo = this.$annotations[row] = {text: [], type: []};

var annoText = annotation.text;
var annoType = annotation.type;
annoText = annoText ? lang.escapeHTML(annoText) : annotation.html || "";

if (rowInfo.text.indexOf(annoText) === -1)
if (rowInfo.text.indexOf(annoText) === -1){
rowInfo.text.push(annoText);
rowInfo.type.push(annoType);
}

var type = annotation.type;
var className = annotation.className;
if (className)
rowInfo.className = className;
else if (type == "error")
else if (annoType == "error")
rowInfo.className = " ace_error";
else if (type == "warning" && rowInfo.className != " ace_error")
else if (annoType == "warning" && rowInfo.className != " ace_error")
rowInfo.className = " ace_warning";
else if (type == "info" && (!rowInfo.className))
else if (annoType == "info" && (!rowInfo.className))
rowInfo.className = " ace_info";
}
};
Expand Down
42 changes: 33 additions & 9 deletions src/mouse/default_gutter_handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,41 @@ function GutterHandler(mouseHandler) {
return hideTooltip();
}

if (tooltipAnnotation == annotation)
return;
tooltipAnnotation = annotation.text.join("<br/>");

tooltip.setHtml(tooltipAnnotation);

var annotationClassName = annotation.className;
if (annotationClassName) {
tooltip.setClassName(annotationClassName.trim());
var annotationMessages = {error: [], warning: [], info: []};
var annotationLabels = {
error: {singular: "error", plural: "errors"},
warning: {singular: "warning", plural: "warnings"},
info: {singular: "information message", plural: "information messages"}
};

// Construct the body of the tooltip.
for (var i = 0; i < annotation.text.length; i++) {
var line = `<span class='ace_${annotation.type[i]} ace_icon' aria-label='${annotationLabels[annotation.type[i]].singular}' role=img> </span> ${annotation.text[i]}`;
annotationMessages[annotation.type[i]].push(line);
}
var tooltipBody = "<div class='ace_gutter-tooltip_body'>";
tooltipBody += [].concat(annotationMessages.error, annotationMessages.warning, annotationMessages.info).join("<br>");
tooltipBody += '</div>';

// Construct the header of the tooltip.
var isMoreThanOneAnnotationType = false;
var tooltipHeader = "<div class='ace_gutter-tooltip_header'>";
for (var i = 0; i < 3; i++){
var annotationType = ['error', 'warning', 'info'][i];
if (annotationMessages[annotationType].length > 0){
var label = annotationMessages[annotationType].length === 1 ? annotationLabels[annotationType].singular : annotationLabels[annotationType].plural;
tooltipHeader += `${isMoreThanOneAnnotationType ? ', ' : ''}${annotationMessages[annotationType].length} ${label}`;
isMoreThanOneAnnotationType = true;
}
}
tooltipHeader += "</div>";

tooltipAnnotation = tooltipHeader + tooltipBody;

tooltip.setHtml(tooltipAnnotation);
tooltip.setClassName("ace_gutter-tooltip");
tooltip.$element.setAttribute("aria-live", "polite");

tooltip.show();
editor._signal("showGutterTooltip", tooltip);
editor.on("mousewheel", hideTooltip);
Expand Down
125 changes: 125 additions & 0 deletions src/mouse/default_gutter_handler_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
if (typeof process !== "undefined") {
require("amd-loader");
require("../test/mockdom");
}

"use strict";

require("../multi_select");
require("../theme/textmate");
var Editor = require("../editor").Editor;
var Mode = require("../mode/java").Mode;
var VirtualRenderer = require("../virtual_renderer").VirtualRenderer;
var assert = require("../test/assertions");
var MouseEvent = function(type, opts){
var e = document.createEvent("MouseEvents");
e.initMouseEvent(/click|wheel/.test(type) ? type : "mouse" + type,
true, true, window,
opts.detail,
opts.x, opts.y, opts.x, opts.y,
opts.ctrl, opts.alt, opts.shift, opts.meta,
opts.button || 0, opts.relatedTarget);
return e;
};

var editor;

module.exports = {
setUp : function(next) {
this.editor = new Editor(new VirtualRenderer());
this.editor.container.style.position = "absolute";
this.editor.container.style.height = "500px";
this.editor.container.style.width = "500px";
this.editor.container.style.left = "50px";
this.editor.container.style.top = "10px";
document.body.appendChild(this.editor.container);
editor = this.editor;
next();
},

"test: gutter error tooltip" : function() {
var editor = this.editor;
var value = "";

editor.session.setMode(new Mode());
editor.setValue(value, -1);
editor.session.setAnnotations([{row: 0, column: 0, text: "error test", type: "error"}]);
editor.renderer.$loop._flush();

var lines = editor.renderer.$gutterLayer.$lines;
var annotation = lines.cells[0].element;
assert.ok(/ace_error/.test(annotation.className));

var rect = annotation.getBoundingClientRect();
annotation.dispatchEvent(new MouseEvent("move", {clientX: rect.left, clientY: rect.top}));

// Wait for the tooltip to appear after its timeout.
setTimeout(function() {
editor.renderer.$loop._flush();
var tooltipHeader = editor.container.querySelector(".ace_gutter-tooltip_header");
var tooltipBody = editor.container.querySelector(".ace_gutter-tooltip_body");
assert.ok(/1 error/.test(tooltipHeader.textContent));
assert.ok(/error test/.test(tooltipBody.textContent));
}, 100);
},
"test: gutter warning tooltip" : function() {
var editor = this.editor;
var value = "";

editor.session.setMode(new Mode());
editor.setValue(value, -1);
editor.session.setAnnotations([{row: 0, column: 0, text: "warning test", type: "warning"}]);
editor.renderer.$loop._flush();

var lines = editor.renderer.$gutterLayer.$lines;
var annotation = lines.cells[0].element;
assert.ok(/ace_warning/.test(annotation.className));

var rect = annotation.getBoundingClientRect();
annotation.dispatchEvent(new MouseEvent("move", {clientX: rect.left, clientY: rect.top}));

// Wait for the tooltip to appear after its timeout.
setTimeout(function() {
editor.renderer.$loop._flush();
var tooltipHeader = editor.container.querySelector(".ace_gutter-tooltip_header");
var tooltipBody = editor.container.querySelector(".ace_gutter-tooltip_body");
assert.ok(/1 warning/.test(tooltipHeader.textContent));
assert.ok(/warning test/.test(tooltipBody.textContent));
}, 100);
},
"test: gutter info tooltip" : function() {
var editor = this.editor;
var value = "";

editor.session.setMode(new Mode());
editor.setValue(value, -1);
editor.session.setAnnotations([{row: 0, column: 0, text: "info test", type: "info"}]);
editor.renderer.$loop._flush();

var lines = editor.renderer.$gutterLayer.$lines;
var annotation = lines.cells[0].element;
assert.ok(/ace_info/.test(annotation.className));

var rect = annotation.getBoundingClientRect();
annotation.dispatchEvent(new MouseEvent("move", {clientX: rect.left, clientY: rect.top}));

// Wait for the tooltip to appear after its timeout.
setTimeout(function() {
editor.renderer.$loop._flush();
var tooltipHeader = editor.container.querySelector(".ace_gutter-tooltip_header");
var tooltipBody = editor.container.querySelector(".ace_gutter-tooltip_body");
assert.ok(/1 information message/.test(tooltipHeader.textContent));
assert.ok(/info test/.test(tooltipBody.textContent));
}, 100);
},

tearDown : function() {
this.editor.destroy();
document.body.removeChild(this.editor.container);
}
};


if (typeof module !== "undefined" && module === require.main) {
require("asyncjs").test.testcase(module.exports).exec();
}
1 change: 1 addition & 0 deletions src/test/all_browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ var testNames = [
"ace/mode/behaviour/behaviour_test",
"ace/multi_select_test",
"ace/mouse/mouse_handler_test",
"ace/mouse/default_gutter_handler_test",
"ace/occur_test",
"ace/placeholder_test",
"ace/range_test",
Expand Down

0 comments on commit 3cd28b8

Please sign in to comment.