Skip to content

Commit 3cd28b8

Browse files
authored
feat: Add annotation level information to gutter tooltip (#5101)
* 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.
1 parent 9f86bb0 commit 3cd28b8

File tree

5 files changed

+186
-19
lines changed

5 files changed

+186
-19
lines changed

src/css/editor.css.js

+19-4
Original file line numberDiff line numberDiff line change
@@ -115,22 +115,24 @@ module.exports = `
115115
background-repeat: no-repeat;
116116
}
117117
118-
.ace_gutter-cell.ace_error {
118+
.ace_gutter-cell.ace_error, .ace_icon.ace_error {
119119
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==");
120120
background-repeat: no-repeat;
121121
background-position: 2px center;
122122
}
123123
124-
.ace_gutter-cell.ace_warning {
124+
.ace_gutter-cell.ace_warning, .ace_icon.ace_warning {
125125
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==");
126+
background-repeat: no-repeat;
126127
background-position: 2px center;
127128
}
128129
129-
.ace_gutter-cell.ace_info {
130+
.ace_gutter-cell.ace_info, .ace_icon.ace_info {
130131
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");
132+
background-repeat: no-repeat;
131133
background-position: 2px center;
132134
}
133-
.ace_dark .ace_gutter-cell.ace_info {
135+
.ace_dark .ace_gutter-cell.ace_info, .ace_dark .ace_icon.ace_info {
134136
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");
135137
}
136138
@@ -422,6 +424,19 @@ module.exports = `
422424
outline: 1px solid black;
423425
}
424426
427+
.ace_gutter-tooltip_header {
428+
font-weight: bold;
429+
}
430+
431+
.ace_gutter-tooltip_body {
432+
padding-top: 5px;
433+
}
434+
435+
.ace_gutter-tooltip .ace_icon {
436+
display: inline-block;
437+
width: 18px;
438+
}
439+
425440
.ace_folding-enabled > .ace_gutter-cell {
426441
padding-right: 13px;
427442
}

src/layer/gutter.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,25 @@ var Gutter = function(parentEl) {
5353
var row = annotation.row;
5454
var rowInfo = this.$annotations[row];
5555
if (!rowInfo)
56-
rowInfo = this.$annotations[row] = {text: []};
56+
rowInfo = this.$annotations[row] = {text: [], type: []};
5757

5858
var annoText = annotation.text;
59+
var annoType = annotation.type;
5960
annoText = annoText ? lang.escapeHTML(annoText) : annotation.html || "";
6061

61-
if (rowInfo.text.indexOf(annoText) === -1)
62+
if (rowInfo.text.indexOf(annoText) === -1){
6263
rowInfo.text.push(annoText);
64+
rowInfo.type.push(annoType);
65+
}
6366

64-
var type = annotation.type;
6567
var className = annotation.className;
6668
if (className)
6769
rowInfo.className = className;
68-
else if (type == "error")
70+
else if (annoType == "error")
6971
rowInfo.className = " ace_error";
70-
else if (type == "warning" && rowInfo.className != " ace_error")
72+
else if (annoType == "warning" && rowInfo.className != " ace_error")
7173
rowInfo.className = " ace_warning";
72-
else if (type == "info" && (!rowInfo.className))
74+
else if (annoType == "info" && (!rowInfo.className))
7375
rowInfo.className = " ace_info";
7476
}
7577
};

src/mouse/default_gutter_handler.js

+33-9
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,41 @@ function GutterHandler(mouseHandler) {
5151
return hideTooltip();
5252
}
5353

54-
if (tooltipAnnotation == annotation)
55-
return;
56-
tooltipAnnotation = annotation.text.join("<br/>");
57-
58-
tooltip.setHtml(tooltipAnnotation);
59-
60-
var annotationClassName = annotation.className;
61-
if (annotationClassName) {
62-
tooltip.setClassName(annotationClassName.trim());
54+
var annotationMessages = {error: [], warning: [], info: []};
55+
var annotationLabels = {
56+
error: {singular: "error", plural: "errors"},
57+
warning: {singular: "warning", plural: "warnings"},
58+
info: {singular: "information message", plural: "information messages"}
59+
};
60+
61+
// Construct the body of the tooltip.
62+
for (var i = 0; i < annotation.text.length; i++) {
63+
var line = `<span class='ace_${annotation.type[i]} ace_icon' aria-label='${annotationLabels[annotation.type[i]].singular}' role=img> </span> ${annotation.text[i]}`;
64+
annotationMessages[annotation.type[i]].push(line);
6365
}
66+
var tooltipBody = "<div class='ace_gutter-tooltip_body'>";
67+
tooltipBody += [].concat(annotationMessages.error, annotationMessages.warning, annotationMessages.info).join("<br>");
68+
tooltipBody += '</div>';
69+
70+
// Construct the header of the tooltip.
71+
var isMoreThanOneAnnotationType = false;
72+
var tooltipHeader = "<div class='ace_gutter-tooltip_header'>";
73+
for (var i = 0; i < 3; i++){
74+
var annotationType = ['error', 'warning', 'info'][i];
75+
if (annotationMessages[annotationType].length > 0){
76+
var label = annotationMessages[annotationType].length === 1 ? annotationLabels[annotationType].singular : annotationLabels[annotationType].plural;
77+
tooltipHeader += `${isMoreThanOneAnnotationType ? ', ' : ''}${annotationMessages[annotationType].length} ${label}`;
78+
isMoreThanOneAnnotationType = true;
79+
}
80+
}
81+
tooltipHeader += "</div>";
82+
83+
tooltipAnnotation = tooltipHeader + tooltipBody;
6484

85+
tooltip.setHtml(tooltipAnnotation);
86+
tooltip.setClassName("ace_gutter-tooltip");
87+
tooltip.$element.setAttribute("aria-live", "polite");
88+
6589
tooltip.show();
6690
editor._signal("showGutterTooltip", tooltip);
6791
editor.on("mousewheel", hideTooltip);
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
if (typeof process !== "undefined") {
2+
require("amd-loader");
3+
require("../test/mockdom");
4+
}
5+
6+
"use strict";
7+
8+
require("../multi_select");
9+
require("../theme/textmate");
10+
var Editor = require("../editor").Editor;
11+
var Mode = require("../mode/java").Mode;
12+
var VirtualRenderer = require("../virtual_renderer").VirtualRenderer;
13+
var assert = require("../test/assertions");
14+
var MouseEvent = function(type, opts){
15+
var e = document.createEvent("MouseEvents");
16+
e.initMouseEvent(/click|wheel/.test(type) ? type : "mouse" + type,
17+
true, true, window,
18+
opts.detail,
19+
opts.x, opts.y, opts.x, opts.y,
20+
opts.ctrl, opts.alt, opts.shift, opts.meta,
21+
opts.button || 0, opts.relatedTarget);
22+
return e;
23+
};
24+
25+
var editor;
26+
27+
module.exports = {
28+
setUp : function(next) {
29+
this.editor = new Editor(new VirtualRenderer());
30+
this.editor.container.style.position = "absolute";
31+
this.editor.container.style.height = "500px";
32+
this.editor.container.style.width = "500px";
33+
this.editor.container.style.left = "50px";
34+
this.editor.container.style.top = "10px";
35+
document.body.appendChild(this.editor.container);
36+
editor = this.editor;
37+
next();
38+
},
39+
40+
"test: gutter error tooltip" : function() {
41+
var editor = this.editor;
42+
var value = "";
43+
44+
editor.session.setMode(new Mode());
45+
editor.setValue(value, -1);
46+
editor.session.setAnnotations([{row: 0, column: 0, text: "error test", type: "error"}]);
47+
editor.renderer.$loop._flush();
48+
49+
var lines = editor.renderer.$gutterLayer.$lines;
50+
var annotation = lines.cells[0].element;
51+
assert.ok(/ace_error/.test(annotation.className));
52+
53+
var rect = annotation.getBoundingClientRect();
54+
annotation.dispatchEvent(new MouseEvent("move", {clientX: rect.left, clientY: rect.top}));
55+
56+
// Wait for the tooltip to appear after its timeout.
57+
setTimeout(function() {
58+
editor.renderer.$loop._flush();
59+
var tooltipHeader = editor.container.querySelector(".ace_gutter-tooltip_header");
60+
var tooltipBody = editor.container.querySelector(".ace_gutter-tooltip_body");
61+
assert.ok(/1 error/.test(tooltipHeader.textContent));
62+
assert.ok(/error test/.test(tooltipBody.textContent));
63+
}, 100);
64+
},
65+
"test: gutter warning tooltip" : function() {
66+
var editor = this.editor;
67+
var value = "";
68+
69+
editor.session.setMode(new Mode());
70+
editor.setValue(value, -1);
71+
editor.session.setAnnotations([{row: 0, column: 0, text: "warning test", type: "warning"}]);
72+
editor.renderer.$loop._flush();
73+
74+
var lines = editor.renderer.$gutterLayer.$lines;
75+
var annotation = lines.cells[0].element;
76+
assert.ok(/ace_warning/.test(annotation.className));
77+
78+
var rect = annotation.getBoundingClientRect();
79+
annotation.dispatchEvent(new MouseEvent("move", {clientX: rect.left, clientY: rect.top}));
80+
81+
// Wait for the tooltip to appear after its timeout.
82+
setTimeout(function() {
83+
editor.renderer.$loop._flush();
84+
var tooltipHeader = editor.container.querySelector(".ace_gutter-tooltip_header");
85+
var tooltipBody = editor.container.querySelector(".ace_gutter-tooltip_body");
86+
assert.ok(/1 warning/.test(tooltipHeader.textContent));
87+
assert.ok(/warning test/.test(tooltipBody.textContent));
88+
}, 100);
89+
},
90+
"test: gutter info tooltip" : function() {
91+
var editor = this.editor;
92+
var value = "";
93+
94+
editor.session.setMode(new Mode());
95+
editor.setValue(value, -1);
96+
editor.session.setAnnotations([{row: 0, column: 0, text: "info test", type: "info"}]);
97+
editor.renderer.$loop._flush();
98+
99+
var lines = editor.renderer.$gutterLayer.$lines;
100+
var annotation = lines.cells[0].element;
101+
assert.ok(/ace_info/.test(annotation.className));
102+
103+
var rect = annotation.getBoundingClientRect();
104+
annotation.dispatchEvent(new MouseEvent("move", {clientX: rect.left, clientY: rect.top}));
105+
106+
// Wait for the tooltip to appear after its timeout.
107+
setTimeout(function() {
108+
editor.renderer.$loop._flush();
109+
var tooltipHeader = editor.container.querySelector(".ace_gutter-tooltip_header");
110+
var tooltipBody = editor.container.querySelector(".ace_gutter-tooltip_body");
111+
assert.ok(/1 information message/.test(tooltipHeader.textContent));
112+
assert.ok(/info test/.test(tooltipBody.textContent));
113+
}, 100);
114+
},
115+
116+
tearDown : function() {
117+
this.editor.destroy();
118+
document.body.removeChild(this.editor.container);
119+
}
120+
};
121+
122+
123+
if (typeof module !== "undefined" && module === require.main) {
124+
require("asyncjs").test.testcase(module.exports).exec();
125+
}

src/test/all_browser.js

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ var testNames = [
6262
"ace/mode/behaviour/behaviour_test",
6363
"ace/multi_select_test",
6464
"ace/mouse/mouse_handler_test",
65+
"ace/mouse/default_gutter_handler_test",
6566
"ace/occur_test",
6667
"ace/placeholder_test",
6768
"ace/range_test",

0 commit comments

Comments
 (0)