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.
akoreman authored Mar 24, 2023
1 parent 9f86bb0 commit 3cd28b8
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
@@ -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");
}
@@ -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;
}
14 changes: 8 additions & 6 deletions src/layer/gutter.js
Original file line number Diff line number Diff line change
@@ -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";
}
};
42 changes: 33 additions & 9 deletions src/mouse/default_gutter_handler.js
Original file line number Diff line number Diff line change
@@ -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);
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
@@ -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",

0 comments on commit 3cd28b8

Please sign in to comment.