Skip to content

Commit dc63ba9

Browse files
authoredApr 21, 2023
feat: summary of annotations in folded lines (#5117)
Currently, when there are gutter annotations for lines which are folded away, the gutter annotations are invisible. This change adds a (optional) one-line summary of the annotations in the folded code.
1 parent 3a3849b commit dc63ba9

8 files changed

+261
-94
lines changed
 

‎ace.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ export namespace Ace {
195195
hasCssTransforms: boolean;
196196
maxPixelHeight: number;
197197
useSvgGutterIcons: boolean;
198+
showFoldedAnnotations: boolean;
198199
}
199200

200201
export interface MouseHandlerOptions {

‎src/css/editor.css.js

+23-16
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ module.exports = `
110110
pointer-events: none;
111111
}
112112
113-
.ace_gutter-cell, .ace_gutter-cell_svg-icons {
113+
.ace_gutter-cell, .ace_gutter-cell_svg-icons {
114114
position: absolute;
115115
top: 0;
116116
left: 0;
@@ -120,18 +120,23 @@ module.exports = `
120120
background-repeat: no-repeat;
121121
}
122122
123-
.ace_gutter-cell_svg-icons .ace_icon_svg{
123+
.ace_gutter-cell_svg-icons .ace_icon_svg {
124124
margin-left: -14px;
125125
float: left;
126126
}
127127
128-
.ace_gutter-cell.ace_error, .ace_icon.ace_error {
128+
.ace_gutter-cell .ace_icon {
129+
margin-left: -18px;
130+
float: left;
131+
}
132+
133+
.ace_gutter-cell.ace_error, .ace_icon.ace_error, .ace_icon.ace_error_fold {
129134
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==");
130135
background-repeat: no-repeat;
131136
background-position: 2px center;
132137
}
133138
134-
.ace_gutter-cell.ace_warning, .ace_icon.ace_warning {
139+
.ace_gutter-cell.ace_warning, .ace_icon.ace_warning, .ace_icon.ace_warning_fold {
135140
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==");
136141
background-repeat: no-repeat;
137142
background-position: 2px center;
@@ -147,18 +152,27 @@ module.exports = `
147152
}
148153
149154
.ace_icon_svg.ace_error {
150-
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJyZWQiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIj4KPGNpcmNsZSBmaWxsPSJub25lIiBjeD0iOCIgY3k9IjgiIHI9IjciIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPGxpbmUgeDE9IjExIiB5MT0iNSIgeDI9IjUiIHkyPSIxMSIvPgo8bGluZSB4MT0iMTEiIHkxPSIxMSIgeDI9IjUiIHkyPSI1Ii8+CjwvZz4KPC9zdmc+");
155+
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJyZWQiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIj4KPGNpcmNsZSBmaWxsPSJub25lIiBjeD0iOCIgY3k9IjgiIHI9IjciIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPGxpbmUgeDE9IjExIiB5MT0iNSIgeDI9IjUiIHkyPSIxMSIvPgo8bGluZSB4MT0iMTEiIHkxPSIxMSIgeDI9IjUiIHkyPSI1Ii8+CjwvZz4KPC9zdmc+");
151156
background-color: crimson;
152157
}
153158
.ace_icon_svg.ace_warning {
154-
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJkYXJrb3JhbmdlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+Cjxwb2x5Z29uIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiIHBvaW50cz0iOCAxIDE1IDE1IDEgMTUgOCAxIi8+CjxyZWN0IHg9IjgiIHk9IjEyIiB3aWR0aD0iMC4wMSIgaGVpZ2h0PSIwLjAxIi8+CjxsaW5lIHgxPSI4IiB5MT0iNiIgeDI9IjgiIHkyPSIxMCIvPgo8L2c+Cjwvc3ZnPg==");
159+
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJkYXJrb3JhbmdlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+Cjxwb2x5Z29uIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiIHBvaW50cz0iOCAxIDE1IDE1IDEgMTUgOCAxIi8+CjxyZWN0IHg9IjgiIHk9IjEyIiB3aWR0aD0iMC4wMSIgaGVpZ2h0PSIwLjAxIi8+CjxsaW5lIHgxPSI4IiB5MT0iNiIgeDI9IjgiIHkyPSIxMCIvPgo8L2c+Cjwvc3ZnPg==");
155160
background-color: darkorange;
156161
}
157162
.ace_icon_svg.ace_info {
158-
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJibHVlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+CjxjaXJjbGUgZmlsbD0ibm9uZSIgY3g9IjgiIGN5PSI4IiByPSI3IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjggMTEgOCA4Ii8+Cjxwb2x5bGluZSBwb2ludHM9IjkgOCA2IDgiLz4KPGxpbmUgeDE9IjEwIiB5MT0iMTEiIHgyPSI2IiB5Mj0iMTEiLz4KPHJlY3QgeD0iOCIgeT0iNSIgd2lkdGg9IjAuMDEiIGhlaWdodD0iMC4wMSIvPgo8L2c+Cjwvc3ZnPg==");
163+
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiI+CjxnIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSJibHVlIiBzaGFwZS1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiI+CjxjaXJjbGUgZmlsbD0ibm9uZSIgY3g9IjgiIGN5PSI4IiByPSI3IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjggMTEgOCA4Ii8+Cjxwb2x5bGluZSBwb2ludHM9IjkgOCA2IDgiLz4KPGxpbmUgeDE9IjEwIiB5MT0iMTEiIHgyPSI2IiB5Mj0iMTEiLz4KPHJlY3QgeD0iOCIgeT0iNSIgd2lkdGg9IjAuMDEiIGhlaWdodD0iMC4wMSIvPgo8L2c+Cjwvc3ZnPg==");
159164
background-color: royalblue;
160165
}
161166
167+
.ace_icon_svg.ace_error_fold {
168+
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAxNiIgZmlsbD0ibm9uZSI+CiAgPHBhdGggZD0ibSAxOC45Mjk4NTEsNy44Mjk4MDc2IGMgMC4xNDYzNTMsNi4zMzc0NjA0IC02LjMyMzE0Nyw3Ljc3Nzg0NDQgLTcuNDc3OTEyLDcuNzc3ODQ0NCAtMi4xMDcyNzI2LC0wLjEyODc1IDUuMTE3Njc4LDAuMzU2MjQ5IDUuMDUxNjk4LC03Ljg3MDA2MTggLTAuNjA0NjcyLC04LjAwMzk3MzQ5IC03LjA3NzI3MDYsLTcuNTYzMTE4OSAtNC44NTczLC03LjQzMDM5NTU2IDEuNjA2LC0wLjExNTE0MjI1IDYuODk3NDg1LDEuMjYyNTQ1OTYgNy4yODM1MTQsNy41MjI2MTI5NiB6IiBmaWxsPSJjcmltc29uIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0ibSA4LjExNDc1NjIsMi4wNTI5ODI4IGMgMy4zNDkxNjk4LDAgNi4wNjQxMzI4LDIuNjc2ODYyNyA2LjA2NDEzMjgsNS45Nzg5NTMgMCwzLjMwMjExMjIgLTIuNzE0OTYzLDUuOTc4OTIwMiAtNi4wNjQxMzI4LDUuOTc4OTIwMiAtMy4zNDkxNDczLDAgLTYuMDY0MTc3MiwtMi42NzY4MDggLTYuMDY0MTc3MiwtNS45Nzg5MjAyIDAuMDA1MzksLTMuMjk5ODg2MSAyLjcxNzI2NTYsLTUuOTczNjQwOCA2LjA2NDE3NzIsLTUuOTc4OTUzIHogbSAwLC0xLjczNTgyNzE5IGMgLTQuMzIxNDgzNiwwIC03LjgyNDc0MDM4LDMuNDU0MDE4NDkgLTcuODI0NzQwMzgsNy43MTQ3ODAxOSAwLDQuMjYwNzI4MiAzLjUwMzI1Njc4LDcuNzE0NzQ1MiA3LjgyNDc0MDM4LDcuNzE0NzQ1MiA0LjMyMTQ0OTgsMCA3LjgyNDY5OTgsLTMuNDU0MDE3IDcuODI0Njk5OCwtNy43MTQ3NDUyIDAsLTIuMDQ2MDkxNCAtMC44MjQzOTIsLTQuMDA4MzY3MiAtMi4yOTE3NTYsLTUuNDU1MTc0NiBDIDEyLjE4MDIyNSwxLjEyOTk2NDggMTAuMTkwMDEzLDAuMzE3MTU1NjEgOC4xMTQ3NTYyLDAuMzE3MTU1NjEgWiBNIDYuOTM3NDU2Myw4LjI0MDU5ODUgNC42NzE4Njg1LDEwLjQ4NTg1MiA2LjAwODY4MTQsMTEuODc2NzI4IDguMzE3MDAzNSw5LjYwMDc5MTEgMTAuNjI1MzM3LDExLjg3NjcyOCAxMS45NjIxMzgsMTAuNDg1ODUyIDkuNjk2NTUwOCw4LjI0MDU5ODUgMTEuOTYyMTM4LDYuMDA2ODA2NiAxMC41NzMyNDYsNC42Mzc0MzM1IDguMzE3MDAzNSw2Ljg3MzQyOTcgNi4wNjA3NjA3LDQuNjM3NDMzNSA0LjY3MTg2ODUsNi4wMDY4MDY2IFoiIGZpbGw9ImNyaW1zb24iIHN0cm9rZS13aWR0aD0iMiIvPgo8L3N2Zz4=");
169+
background-color: crimson;
170+
}
171+
.ace_icon_svg.ace_warning_fold {
172+
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAyMCAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNC43NzY5IDE0LjczMzdMOC42NTE5MiAyLjQ4MzY5QzguMzI5NDYgMS44Mzg3NyA3LjQwOTEzIDEuODM4NzcgNy4wODY2NyAyLjQ4MzY5TDAuOTYxNjY5IDE0LjczMzdDMC42NzA3NzUgMTUuMzE1NSAxLjA5MzgzIDE2IDEuNzQ0MjkgMTZIMTMuOTk0M0MxNC42NDQ4IDE2IDE1LjA2NzggMTUuMzE1NSAxNC43NzY5IDE0LjczMzdaTTMuMTYwMDcgMTQuMjVMNy44NjkyOSA0LjgzMTU2TDEyLjU3ODUgMTQuMjVIMy4xNjAwN1pNOC43NDQyOSAxMS42MjVWMTMuMzc1SDYuOTk0MjlWMTEuNjI1SDguNzQ0MjlaTTYuOTk0MjkgMTAuNzVWNy4yNUg4Ljc0NDI5VjEwLjc1SDYuOTk0MjlaIiBmaWxsPSIjRUM3MjExIi8+CjxwYXRoIGQ9Ik0xMS4xOTkxIDIuOTUyMzhDMTAuODgwOSAyLjMxNDY3IDEwLjM1MzcgMS44MDUyNiA5LjcwNTUgMS41MDlMMTEuMDQxIDEuMDY5NzhDMTEuNjg4MyAwLjk0OTgxNCAxMi4zMzcgMS4yNzI2MyAxMi42MzE3IDEuODYxNDFMMTcuNjEzNiAxMS44MTYxQzE4LjM1MjcgMTMuMjkyOSAxNy41OTM4IDE1LjA4MDQgMTYuMDE4IDE1LjU3NDVDMTYuNDA0NCAxNC40NTA3IDE2LjMyMzEgMTMuMjE4OCAxNS43OTI0IDEyLjE1NTVMMTEuMTk5MSAyLjk1MjM4WiIgZmlsbD0iI0VDNzIxMSIvPgo8L3N2Zz4=");
173+
background-color: darkorange;
174+
}
175+
162176
.ace_scrollbar {
163177
contain: strict;
164178
position: absolute;
@@ -452,17 +466,10 @@ module.exports = `
452466
outline: 1px solid #5E9ED6;
453467
}
454468
455-
.ace_gutter-tooltip_header {
456-
font-weight: bold;
457-
}
458-
459-
.ace_gutter-tooltip_body {
460-
padding-top: 5px;
461-
}
462-
463-
.ace_gutter-tooltip .ace_icon {
469+
.ace_icon {
464470
display: inline-block;
465471
width: 18px;
472+
vertical-align: top;
466473
}
467474
468475
.ace_icon_svg {

‎src/editor.js

+1
Original file line numberDiff line numberDiff line change
@@ -2932,6 +2932,7 @@ config.defineOptions(Editor.prototype, "editor", {
29322932
useTextareaForIME: "renderer",
29332933
useResizeObserver: "renderer",
29342934
useSvgGutterIcons: "renderer",
2935+
showFoldedAnnotations: "renderer",
29352936

29362937
scrollSpeed: "$mouseHandler",
29372938
dragDelay: "$mouseHandler",

‎src/ext/options.js

+3
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ var optionGroups = {
198198
"Use SVG gutter icons": {
199199
path: "useSvgGutterIcons"
200200
},
201+
"Annotations for folded lines": {
202+
path: "showFoldedAnnotations"
203+
},
201204
"Keyboard Accessibility Mode": {
202205
path: "enableKeyboardAccessibility"
203206
}

‎src/layer/gutter.js

+48-22
Original file line numberDiff line numberDiff line change
@@ -290,25 +290,9 @@ class Gutter{
290290

291291
var lineHeight = config.lineHeight + "px";
292292

293-
var className;
294-
if (this.$useSvgGutterIcons){
295-
className = "ace_gutter-cell_svg-icons ";
296-
297-
if (this.$annotations[row]){
298-
annotationNode.className = "ace_icon_svg" + this.$annotations[row].className;
299-
300-
dom.setStyle(annotationNode.style, "height", lineHeight);
301-
dom.setStyle(annotationNode.style, "display", "block");
302-
}
303-
else {
304-
dom.setStyle(annotationNode.style, "display", "none");
305-
}
306-
}
307-
else {
308-
className = "ace_gutter-cell ";
309-
dom.setStyle(annotationNode.style, "display", "none");
310-
}
311-
293+
var className = this.$useSvgGutterIcons ? "ace_gutter-cell_svg-icons " : "ace_gutter-cell ";
294+
var iconClassName = this.$useSvgGutterIcons ? "ace_icon_svg" : "ace_icon";
295+
312296
if (this.$highlightGutterLine) {
313297
if (row == this.$cursorRow || (fold && row < this.$cursorRow && row >= foldStart && this.$cursorRow <= fold.end.row)) {
314298
className += "ace_gutter-active-line ";
@@ -324,7 +308,7 @@ class Gutter{
324308
className += breakpoints[row];
325309
if (decorations[row])
326310
className += decorations[row];
327-
if (this.$annotations[row])
311+
if (this.$annotations[row] && row !== foldStart)
328312
className += this.$annotations[row].className;
329313
if (element.className != className)
330314
element.className = className;
@@ -338,8 +322,29 @@ class Gutter{
338322

339323
if (c) {
340324
var className = "ace_fold-widget ace_" + c;
341-
if (c == "start" && row == foldStart && row < fold.end.row)
325+
if (c == "start" && row == foldStart && row < fold.end.row){
342326
className += " ace_closed";
327+
var foldAnnotationClass;
328+
var annotationInFold = false;
329+
330+
for (var i = row + 1; i <= fold.end.row; i++){
331+
if (!this.$annotations[i])
332+
continue;
333+
334+
if (this.$annotations[i].className === " ace_error"){
335+
annotationInFold = true;
336+
foldAnnotationClass = " ace_error_fold";
337+
break;
338+
}
339+
if (this.$annotations[i].className === " ace_warning"){
340+
annotationInFold = true;
341+
foldAnnotationClass = " ace_warning_fold";
342+
continue;
343+
}
344+
}
345+
346+
element.className += foldAnnotationClass;
347+
}
343348
else
344349
className += " ace_open";
345350
if (foldWidget.className != className)
@@ -352,6 +357,28 @@ class Gutter{
352357
dom.setStyle(foldWidget.style, "display", "none");
353358
}
354359
}
360+
361+
if (annotationInFold && this.$showFoldedAnnotations){
362+
annotationNode.className = iconClassName;
363+
annotationNode.className += foldAnnotationClass;
364+
365+
dom.setStyle(annotationNode.style, "height", lineHeight);
366+
dom.setStyle(annotationNode.style, "display", "block");
367+
}
368+
else if (this.$annotations[row]){
369+
annotationNode.className = iconClassName;
370+
371+
if (this.$useSvgGutterIcons)
372+
annotationNode.className += this.$annotations[row].className;
373+
else
374+
element.classList.add(this.$annotations[row].className.replace(" ", ""));
375+
376+
dom.setStyle(annotationNode.style, "height", lineHeight);
377+
dom.setStyle(annotationNode.style, "display", "block");
378+
}
379+
else {
380+
dom.setStyle(annotationNode.style, "display", "none");
381+
}
355382

356383
var text = (gutterRenderer
357384
? gutterRenderer.getText(session, row)
@@ -372,7 +399,6 @@ class Gutter{
372399
this.$highlightGutterLine = highlightGutterLine;
373400
}
374401

375-
376402
setShowLineNumbers(show) {
377403
this.$renderer = !show && {
378404
getWidth: function() {return 0;},

‎src/mouse/default_gutter_handler.js

+73-37
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,60 @@ function GutterHandler(mouseHandler) {
3434
});
3535

3636

37-
var tooltipTimeout, mouseEvent, tooltipAnnotation;
37+
var tooltipTimeout, mouseEvent, tooltipContent;
38+
39+
var annotationLabels = {
40+
error: {singular: "error", plural: "errors"},
41+
warning: {singular: "warning", plural: "warnings"},
42+
info: {singular: "information message", plural: "information messages"}
43+
};
3844

3945
function showTooltip() {
4046
var row = mouseEvent.getDocumentPosition().row;
41-
var annotation = gutter.$annotations[row];
42-
if (!annotation)
47+
var annotationsInRow = gutter.$annotations[row];
48+
var annotation;
49+
50+
if (annotationsInRow)
51+
annotation = {text: Array.from(annotationsInRow.text), type: Array.from(annotationsInRow.type)};
52+
else
53+
annotation = {text: [], type: []};
54+
55+
// If the tooltip is for a row which has a closed fold, check whether there are
56+
// annotations in the folded lines. If so, add a summary to the list of annotations.
57+
var fold = gutter.session.getFoldLine(row);
58+
if (fold && gutter.$showFoldedAnnotations){
59+
var annotationsInFold = {error: [], warning: [], info: []};
60+
var mostSevereAnnotationInFoldType;
61+
62+
for (var i = row + 1; i <= fold.end.row; i++){
63+
if (!gutter.$annotations[i])
64+
continue;
65+
66+
for (var j = 0; j < gutter.$annotations[i].text.length; j++) {
67+
var annotationType = gutter.$annotations[i].type[j];
68+
annotationsInFold[annotationType].push(gutter.$annotations[i].text[j]);
69+
70+
if (annotationType === "error"){
71+
mostSevereAnnotationInFoldType = "error_fold";
72+
continue;
73+
}
74+
75+
if (annotationType === "warning"){
76+
mostSevereAnnotationInFoldType = "warning_fold";
77+
continue;
78+
}
79+
}
80+
}
81+
82+
if (mostSevereAnnotationInFoldType === "error_fold" || mostSevereAnnotationInFoldType === "warning_fold"){
83+
var summaryFoldedAnnotations = `${annotationsToSummaryString(annotationsInFold)} in folded code.`;
84+
85+
annotation.text.push(summaryFoldedAnnotations);
86+
annotation.type.push(mostSevereAnnotationInFoldType);
87+
}
88+
}
89+
90+
if (annotation.text.length === 0)
4391
return hideTooltip();
4492

4593
var maxRow = editor.session.getLength();
@@ -51,39 +99,16 @@ function GutterHandler(mouseHandler) {
5199
}
52100

53101
var annotationMessages = {error: [], warning: [], info: []};
54-
var annotationLabels = {
55-
error: {singular: "error", plural: "errors"},
56-
warning: {singular: "warning", plural: "warnings"},
57-
info: {singular: "information message", plural: "information messages"}
58-
};
59-
60102
var iconClassName = gutter.$useSvgGutterIcons ? "ace_icon_svg" : "ace_icon";
61103

62-
// Construct the body of the tooltip.
104+
// Construct the contents of the tooltip.
63105
for (var i = 0; i < annotation.text.length; i++) {
64-
var line = `<span class='ace_${annotation.type[i]} ${iconClassName}' aria-label='${annotationLabels[annotation.type[i]].singular}' role=img> </span> ${annotation.text[i]}`;
65-
annotationMessages[annotation.type[i]].push(line);
106+
var line = `<span class='ace_${annotation.type[i]} ${iconClassName}' aria-label='${annotationLabels[annotation.type[i].replace("_fold","")].singular}' role=img> </span> ${annotation.text[i]}`;
107+
annotationMessages[annotation.type[i].replace("_fold","")].push(line);
66108
}
67-
var tooltipBody = "<div class='ace_gutter-tooltip_body'>";
68-
tooltipBody += [].concat(annotationMessages.error, annotationMessages.warning, annotationMessages.info).join("<br>");
69-
tooltipBody += '</div>';
70-
71-
// Construct the header of the tooltip.
72-
var isMoreThanOneAnnotationType = false;
73-
var tooltipHeader = "<div class='ace_gutter-tooltip_header'>";
74-
for (var i = 0; i < 3; i++){
75-
var annotationType = ['error', 'warning', 'info'][i];
76-
if (annotationMessages[annotationType].length > 0){
77-
var label = annotationMessages[annotationType].length === 1 ? annotationLabels[annotationType].singular : annotationLabels[annotationType].plural;
78-
tooltipHeader += `${isMoreThanOneAnnotationType ? ', ' : ''}${annotationMessages[annotationType].length} ${label}`;
79-
isMoreThanOneAnnotationType = true;
80-
}
81-
}
82-
tooltipHeader += "</div>";
83-
84-
tooltipAnnotation = tooltipHeader + tooltipBody;
85-
86-
tooltip.setHtml(tooltipAnnotation);
109+
tooltipContent = [].concat(annotationMessages.error, annotationMessages.warning, annotationMessages.info).join("<br>");
110+
111+
tooltip.setHtml(tooltipContent);
87112
tooltip.setClassName("ace_gutter-tooltip");
88113
tooltip.$element.setAttribute("aria-live", "polite");
89114

@@ -97,7 +122,7 @@ function GutterHandler(mouseHandler) {
97122
if (mouseHandler.$tooltipFollowsMouse) {
98123
moveTooltip(mouseEvent);
99124
} else {
100-
var gutterElement = mouseEvent.domEvent.target;
125+
var gutterElement = gutter.$lines.cells[row].element.querySelector("[class*=ace_icon]");
101126
var rect = gutterElement.getBoundingClientRect();
102127
var style = tooltip.getElement().style;
103128
style.left = rect.right + "px";
@@ -108,14 +133,25 @@ function GutterHandler(mouseHandler) {
108133
function hideTooltip() {
109134
if (tooltipTimeout)
110135
tooltipTimeout = clearTimeout(tooltipTimeout);
111-
if (tooltipAnnotation) {
136+
if (tooltipContent) {
112137
tooltip.hide();
113-
tooltipAnnotation = null;
138+
tooltipContent = null;
114139
editor._signal("hideGutterTooltip", tooltip);
115140
editor.off("mousewheel", hideTooltip);
116141
}
117142
}
118143

144+
function annotationsToSummaryString(annotations) {
145+
const summary = [];
146+
const annotationTypes = ['error', 'warning', 'info'];
147+
for (const annotationType of annotationTypes) {
148+
if (!annotations[annotationType].length) continue;
149+
const label = annotations[annotationType].length === 1 ? annotationLabels[annotationType].singular : annotationLabels[annotationType].plural;
150+
summary.push(`${annotations[annotationType].length} ${label}`);
151+
}
152+
return summary.join(", ");
153+
}
154+
119155
function moveTooltip(e) {
120156
tooltip.setPosition(e.x, e.y);
121157
}
@@ -125,7 +161,7 @@ function GutterHandler(mouseHandler) {
125161
if (dom.hasCssClass(target, "ace_fold-widget"))
126162
return hideTooltip();
127163

128-
if (tooltipAnnotation && mouseHandler.$tooltipFollowsMouse)
164+
if (tooltipContent && mouseHandler.$tooltipFollowsMouse)
129165
moveTooltip(e);
130166

131167
mouseEvent = e;
@@ -142,7 +178,7 @@ function GutterHandler(mouseHandler) {
142178

143179
event.addListener(editor.renderer.$gutter, "mouseout", function(e) {
144180
mouseEvent = null;
145-
if (!tooltipAnnotation || tooltipTimeout)
181+
if (!tooltipContent || tooltipTimeout)
146182
return;
147183

148184
tooltipTimeout = setTimeout(function() {

‎src/mouse/default_gutter_handler_test.js

+106-19
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ module.exports = {
3636
editor = this.editor;
3737
next();
3838
},
39-
39+
4040
"test: gutter error tooltip" : function() {
4141
var editor = this.editor;
4242
var value = "";
@@ -51,15 +51,13 @@ module.exports = {
5151
assert.ok(/ace_error/.test(annotation.className));
5252

5353
var rect = annotation.getBoundingClientRect();
54-
annotation.dispatchEvent(new MouseEvent("move", {clientX: rect.left, clientY: rect.top}));
54+
annotation.dispatchEvent(new MouseEvent("move", {x: rect.left, y: rect.top}));
5555

5656
// Wait for the tooltip to appear after its timeout.
5757
setTimeout(function() {
5858
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));
59+
var tooltip = editor.container.querySelector(".ace_tooltip");
60+
assert.ok(/error test/.test(tooltip.textContent));
6361
}, 100);
6462
},
6563
"test: gutter warning tooltip" : function() {
@@ -76,15 +74,13 @@ module.exports = {
7674
assert.ok(/ace_warning/.test(annotation.className));
7775

7876
var rect = annotation.getBoundingClientRect();
79-
annotation.dispatchEvent(new MouseEvent("move", {clientX: rect.left, clientY: rect.top}));
77+
annotation.dispatchEvent(new MouseEvent("move", {x: rect.left, y: rect.top}));
8078

8179
// Wait for the tooltip to appear after its timeout.
8280
setTimeout(function() {
8381
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));
82+
var tooltip = editor.container.querySelector(".ace_tooltip");
83+
assert.ok(/warning test/.test(tooltip.textContent));
8884
}, 100);
8985
},
9086
"test: gutter info tooltip" : function() {
@@ -101,15 +97,13 @@ module.exports = {
10197
assert.ok(/ace_info/.test(annotation.className));
10298

10399
var rect = annotation.getBoundingClientRect();
104-
annotation.dispatchEvent(new MouseEvent("move", {clientX: rect.left, clientY: rect.top}));
100+
annotation.dispatchEvent(new MouseEvent("move", {x: rect.left, y: rect.top}));
105101

106102
// Wait for the tooltip to appear after its timeout.
107103
setTimeout(function() {
108104
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));
105+
var tooltip = editor.container.querySelector(".ace_tooltip");
106+
assert.ok(/info test/.test(tooltip.textContent));
113107
}, 100);
114108
},
115109
"test: gutter svg icons" : function() {
@@ -129,15 +123,108 @@ module.exports = {
129123
var annotation = line.children[2];
130124
assert.ok(/ace_icon_svg/.test(annotation.className));
131125
},
132-
133-
126+
"test: error show up in fold" : function() {
127+
var editor = this.editor;
128+
var value = "x {" + "\n".repeat(50) + "}";
129+
value = value.repeat(50);
130+
editor.session.setMode(new Mode());
131+
editor.setOption("showFoldedAnnotations", true);
132+
editor.setValue(value, -1);
133+
editor.session.setAnnotations([{row: 1, column: 0, type: "error", text: "error test"}]);
134+
editor.renderer.$loop._flush();
135+
136+
// Fold the line containing the annotation.
137+
var lines = editor.renderer.$gutterLayer.$lines;
138+
assert.equal(lines.cells[1].element.textContent, "2");
139+
var toggler = lines.cells[0].element.children[1];
140+
var rect = toggler.getBoundingClientRect();
141+
if (!rect.left) rect.left = 100; // for mockdom
142+
toggler.dispatchEvent(MouseEvent("click", {x: rect.left, y: rect.top}));
143+
editor.renderer.$loop._flush();
144+
assert.ok(/ace_closed/.test(toggler.className));
145+
assert.equal(lines.cells[1].element.textContent, "51");
146+
147+
// Annotation node should have fold class.
148+
var annotation = lines.cells[0].element.children[2];
149+
assert.ok(/ace_error_fold/.test(annotation.className));
150+
151+
var rect = annotation.getBoundingClientRect();
152+
annotation.dispatchEvent(new MouseEvent("move", {x: rect.left, y: rect.top}));
153+
154+
// Wait for the tooltip to appear after its timeout.
155+
setTimeout(function() {
156+
editor.renderer.$loop._flush();
157+
var tooltip = editor.container.querySelector(".ace_tooltip");
158+
assert.ok(/error in folded/.test(tooltip.textContent));
159+
}, 100);
160+
},
161+
"test: warning show up in fold" : function() {
162+
var editor = this.editor;
163+
var value = "x {" + "\n".repeat(50) + "}";
164+
value = value.repeat(50);
165+
editor.session.setMode(new Mode());
166+
editor.setOption("showFoldedAnnotations", true);
167+
editor.setValue(value, -1);
168+
editor.session.setAnnotations([{row: 1, column: 0, type: "warning", text: "warning test"}]);
169+
editor.renderer.$loop._flush();
170+
171+
// Fold the line containing the annotation.
172+
var lines = editor.renderer.$gutterLayer.$lines;
173+
assert.equal(lines.cells[1].element.textContent, "2");
174+
var toggler = lines.cells[0].element.children[1];
175+
var rect = toggler.getBoundingClientRect();
176+
if (!rect.left) rect.left = 100; // for mockdom
177+
toggler.dispatchEvent(MouseEvent("click", {x: rect.left, y: rect.top}));
178+
editor.renderer.$loop._flush();
179+
assert.ok(/ace_closed/.test(toggler.className));
180+
assert.equal(lines.cells[1].element.textContent, "51");
181+
182+
// Annotation node should have fold class.
183+
var annotation = lines.cells[0].element.children[2];
184+
assert.ok(/ace_warning_fold/.test(annotation.className));
185+
186+
var rect = annotation.getBoundingClientRect();
187+
annotation.dispatchEvent(new MouseEvent("move", {x: rect.left, y: rect.top}));
188+
189+
// Wait for the tooltip to appear after its timeout.
190+
setTimeout(function() {
191+
editor.renderer.$loop._flush();
192+
var tooltip = editor.container.querySelector(".ace_tooltip");
193+
assert.ok(/warning in folded/.test(tooltip.textContent));
194+
}, 100);
195+
},
196+
"test: info not show up in fold" : function() {
197+
var editor = this.editor;
198+
var value = "x {" + "\n".repeat(50) + "}";
199+
value = value.repeat(50);
200+
editor.session.setMode(new Mode());
201+
editor.setOption("showFoldedAnnotations", true);
202+
editor.setValue(value, -1);
203+
editor.session.setAnnotations([{row: 1, column: 0, type: "info", text: "info test"}]);
204+
editor.renderer.$loop._flush();
205+
206+
// Fold the line containing the annotation.
207+
var lines = editor.renderer.$gutterLayer.$lines;
208+
assert.equal(lines.cells[1].element.textContent, "2");
209+
var toggler = lines.cells[0].element.children[1];
210+
var rect = toggler.getBoundingClientRect();
211+
if (!rect.left) rect.left = 100; // for mockdom
212+
toggler.dispatchEvent(MouseEvent("click", {x: rect.left, y: rect.top}));
213+
editor.renderer.$loop._flush();
214+
assert.ok(/ace_closed/.test(toggler.className));
215+
assert.equal(lines.cells[1].element.textContent, "51");
216+
217+
// Annotation node should NOT have fold class.
218+
var annotation = lines.cells[0].element.children[2];
219+
assert.notOk(/fold/.test(annotation.className));
220+
},
221+
134222
tearDown : function() {
135223
this.editor.destroy();
136224
document.body.removeChild(this.editor.container);
137225
}
138226
};
139227

140-
141228
if (typeof module !== "undefined" && module === require.main) {
142229
require("asyncjs").test.testcase(module.exports).exec();
143230
}

‎src/virtual_renderer.js

+6
Original file line numberDiff line numberDiff line change
@@ -1908,6 +1908,12 @@ config.defineOptions(VirtualRenderer.prototype, "renderer", {
19081908
},
19091909
initialValue: false
19101910
},
1911+
showFoldedAnnotations: {
1912+
set: function(value){
1913+
this.$gutterLayer.$showFoldedAnnotations = value;
1914+
},
1915+
initialValue: false
1916+
},
19111917
fadeFoldWidgets: {
19121918
set: function(show) {
19131919
dom.setCssClass(this.$gutter, "ace_fade-fold-widgets", show);

0 commit comments

Comments
 (0)
Please sign in to comment.