Skip to content

Commit

Permalink
unify diagnostic and occurrence markers
Browse files Browse the repository at this point in the history
  • Loading branch information
nightwing committed Apr 17, 2023
1 parent d484166 commit de81b71
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 231 deletions.
26 changes: 11 additions & 15 deletions ace.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,23 +275,19 @@ export namespace Ace {
type: string;
}

export interface TooltipMarker {
range: Range;
tooltipText: string;
className?: string;
priority?: number;
}
export interface TooltipMarker {
range: Range;
tooltipText: string;
className?: string;
priority?: number;
}

export class MarkerGroup {
constructor();
setMarkers: (markers: TooltipMarker[]) => void;
}
export class MarkerGroup {
constructor();
setMarkers: (markers: TooltipMarker[]) => void;
getMarkerAtPos: (pos: Position) => TooltipMarker;
}

export class TooltipMarkerManager {
constructor(editor: Editor);
setMarkerGroupForSession: (markerGroup: MarkerGroup, session: EditSession) => void;
destroy: () => void;
}

export interface Command {
name?: string;
Expand Down
127 changes: 88 additions & 39 deletions demo/kitchen-sink/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ var ElasticTabstopsLite = require("ace/ext/elastic_tabstops_lite").ElasticTabsto
var IncrementalSearch = require("ace/incremental_search").IncrementalSearch;

var TokenTooltip = require("./token_tooltip").TokenTooltip;
var TooltipMarkerManager = require("ace/tooltip_marker").TooltipMarkerManager;
var TooltipMarkerManager = require("ace/marker_group").TooltipMarkerManager;
require("ace/config").defineOptions(Editor.prototype, "editor", {
showTokenInfo: {
set: function(val) {
Expand All @@ -62,28 +62,6 @@ require("ace/config").defineOptions(Editor.prototype, "editor", {
return !!this.tokenTooltip;
},
handlesSet: true
},
showTooltipMarkers: {
set: function(val) {
if (val) {
this.tooltipMarkerManager = this.tooltipMarkerManager || new TooltipMarkerManager(this);
const singleLineMarker = {
range: new Range(0, 0, 0, 5),
tooltipText: "Single line marker",
className: "ace_tooltip-marker_test",
};
const multiLineMarker = {
range: new Range(3, 2, 4, 5),
tooltipText: "Multi line marker",
className: "ace_tooltip-marker_test",
};
editor.session.setTooltipMarkers([singleLineMarker, multiLineMarker], this.tooltipMarkerManager);
} else if (this.tooltipMarkerManager) {
editor.session.setTooltipMarkers([], this.tooltipMarkerManager);
this.tooltipMarkerManager.destroy();
delete this.tooltipMarkerManager;
}
}
}
});

Expand All @@ -103,29 +81,104 @@ require("ace/config").defineOptions(Editor.prototype, "editor", {
});

var {HoverTooltip} = require("ace/tooltip");
var MarkerGroup = require("ace/marker_group").MarkerGroup;
var docTooltip = new HoverTooltip();
function loadLanguageProvider(editor) {
require([
"https://www.unpkg.com/ace-linters/build/ace-linters.js"
], (m) => {
window.languageProvider = m.LanguageProvider.fromCdn("https://www.unpkg.com/ace-linters/build");
window.languageProvider.registerEditor(editor);
], function(m) {
var languageProvider = m.LanguageProvider.fromCdn("https://www.unpkg.com/ace-linters/build", {
functionality: {
hover: true,
completion: {
overwriteCompleters: true
},
completionResolve: true,
format: true,
documentHighlights: true,
signatureHelp: false
}
});
window.languageProvider = languageProvider;
languageProvider.registerEditor(editor);
if (languageProvider.$descriptionTooltip)
editor.off("mousemove", languageProvider.$descriptionTooltip.onMouseMove);
languageProvider.$messageController.$worker.addEventListener("message", function(e) {
var id = e.data.sessionId.split(".")[0];
var session = languageProvider.$getSessionLanguageProvider({id: id})?.session;
if (e.data.type == 6) {
e.stopPropagation();
if (session) {
showAnnotations(session, e.data.value);
}
} else if (e.data.type == 13) {
// ignore signatures
if (session) showOccurrenceMarkers(session, e.data.value);
}
}, true);
function showOccurrenceMarkers(session, positions) {
if (!session.state.occurrenceMarkers) {
session.state.occurrenceMarkers = new MarkerGroup(session);
}
session.state.occurrenceMarkers.setMarkers(positions.map(function(el) {
var r = el.range;
return {
range: new Range(r.start.line, r.start.character, r.end.line, r.end.character),
className: el.kind == 3
? "language_highlight_occurrence_main"
: "language_highlight_occurrence_other"
};
}));
}
function showAnnotations(session, diagnostics) {
session.clearAnnotations();
let annotations = diagnostics.map((el) => {
console.log(el.severity, el)
return {
row: el.range.start.line,
column: el.range.start.character,
text: el.message,
type: el.severity === 1 ? "error" : el.severity === 2 ? "warning" : "info"
};
});
if (annotations && annotations.length > 0) {
session.setAnnotations(annotations);
}

if (!session.state) session.state = {}
if (!session.state.diagnosticMarkers) {
session.state.diagnosticMarkers = new MarkerGroup(session);
}
session.state.diagnosticMarkers.setMarkers(diagnostics.map(function(el) {
var r = el.range;
return {
range: new Range(r.start.line, r.start.character, r.end.line, r.end.character),
tooltipText: el.message,
className: "language_highlight_error"
};
}));
};

docTooltip.setDataProvider(function(e, editor) {
var renderer = editor.renderer;

let session = editor.session;
let docPos = e.getDocumentPosition() ;
let docPos = e.getDocumentPosition();

languageProvider.doHover(session, docPos, function(hover) {
if (!hover) {
return;
}
// todo should ace itself handle markdown?
var domNode = dom.buildDom(["p", {}, hover.content.text]);
docTooltip.showForRange(editor, hover.range, domNode, e);
var errorMarker = session.state?.diagnosticMarkers.getMarkerAtPosition(docPos);
var range = hover?.range || errorMarker?.range;
if (!range) return;
var hoverNode = hover && dom.buildDom(["div", {}])
if (hoverNode) {
hover.content.text = hover.content.text.replace(/(?!^)`{3}/gm, "\n$&");
// todo render markdown using ace markdown mode
hoverNode.innerHTML = languageProvider.getTooltipText(hover);
};

var domNode = dom.buildDom(["div", {},
hoverNode,
errorMarker && ["div", {}, errorMarker.tooltipText.trim()]
]);
docTooltip.showForRange(editor, range, domNode, e);
});
});

Expand Down Expand Up @@ -463,10 +516,6 @@ optionsPanel.add({
return !!originalAutocompleteCommand;
}
},
"Show tooltip markers for active session": {
path: "showTooltipMarkers",
position: 2000
},
"Use Ace Linters": {
position: 3000,
path: "useAceLinters"
Expand Down
59 changes: 39 additions & 20 deletions demo/kitchen-sink/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -94,24 +94,43 @@ body {
font-size: 1em!important;
}

.ace_tooltip-marker_test {
position: absolute;
}
.ace_tooltip-marker_test::after,
.ace_tooltip-marker_test::before {
content: '';
position: absolute;
width: 100%;
height: inherit;
background-size: 6px 100%;
}
.ace_tooltip-marker_test::before {
top: 2px;
background-image: linear-gradient(45deg, red 35%, transparent 0),
linear-gradient(-45deg, red 35%, transparent 0);
}
.ace_tooltip-marker_test::after {
top: 4px;
background-image: linear-gradient(45deg, white 35%, transparent 0),
linear-gradient(-45deg, white 35%, transparent 0);

.language_highlight_error {
position: absolute;
border-bottom: dotted 1px #e00404;
z-index: 2000;
border-radius: 0;
}
.language_highlight_warning {
position: absolute;
border-bottom: solid 1px #DDC50F;
z-index: 2000;
border-radius: 0;
}
.language_highlight_info {
position: absolute;
border-bottom: dotted 1px #999;
z-index: 2000;
border-radius: 0;
}
.language_highlight_occurrence_main {
position: absolute;
box-sizing: border-box;
border: solid 1px #888;
z-index: 2000;
}
.ace_dark .language_highlight_occurrence_main {
border: solid 1px #888;
}
.language_highlight_occurrence_other {
position: absolute;
box-sizing: border-box;
border: solid 1px #888;
z-index: 2000;
}
.ace_dark .language_highlight_occurrence_other {
border: solid 1px #888;
}
.ace_doc-tooltip pre {
margin: 0;
}
17 changes: 0 additions & 17 deletions src/edit_session.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ var Range = require("./range").Range;
var Document = require("./document").Document;
var BackgroundTokenizer = require("./background_tokenizer").BackgroundTokenizer;
var SearchHighlight = require("./search_highlight").SearchHighlight;
var MarkerGroup = require("./tooltip_marker").MarkerGroup;

//{ events
/**
Expand Down Expand Up @@ -641,22 +640,6 @@ class EditSession {
return range;
}

/**
* Sets an array of tooltip markers to be displayed for the session.
* These markers display the content of `tooltipText` on hover.
*
* @param {Ace.TooltipMarker[]} markers an array of tooltip marker definitions.
* @param {Ace.TooltipMarkerManager} tooltipMarkerManager controls display of tooltips on hover over markers.
*/
setTooltipMarkers(markers, tooltipMarkerManager) {
if (!this.$tooltipMarkerGroup) {
const tooltipMarkerGroup = new MarkerGroup();
this.$tooltipMarkerGroup = this.addDynamicMarker(tooltipMarkerGroup);
tooltipMarkerManager.setMarkerGroupForSession(this.$tooltipMarkerGroup, this);
}
this.$tooltipMarkerGroup.setMarkers(markers);
}

/*
* Error:
* {
Expand Down
83 changes: 83 additions & 0 deletions src/marker_group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use strict";

/*
Potential improvements:
- cap the length of rendered marker range
- save rendered markers and only search through rendered markers when looking for hover match
- don't cut off total amount of markers at 500
*/

class MarkerGroup {
constructor(session) {
this.markers = [];
this.session = session;
session.addDynamicMarker(this);
}

getMarkerAtPosition(pos) {
return this.markers.find(function(marker) {
return marker.range.contains(pos.row, pos.column);
});
}

/**
* Comparator for Array.sort function, which sorts marker definitions by their positions
*
* @param {Ace.TooltipMarker} a first marker.
* @param {Ace.TooltipMarker} b second marker.
* @returns {number} negative number if a should be before b, positive number if b should be before a, 0 otherwise.
*/
markersComparator(a, b) {
return a.range.start.row - b.range.start.row;
}

/**
* Sets marker definitions to be rendered. Limits the number of markers at MAX_MARKERS.
* @param {Ace.TooltipMarker[]} markers an array of marker definitions.
*/
setMarkers(markers) {
this.markers = markers.sort(this.markersComparator).slice(0, this.MAX_MARKERS);
this.session._signal("changeBackMarker");
}

update(html, markerLayer, session, config) {
if (!this.markers || !this.markers.length)
return;
var visibleRangeStartRow = config.firstRow, visibleRangeEndRow = config.lastRow;
this.renderedMarkerRanges = {};

// TODO: if there are markers with overlapping ranges, do we merge them? if yes, how do we merge them?

for (var i = 0; i < this.markers.length; i++) {
var marker = this.markers[i];

if (marker.range.end.row < visibleRangeStartRow) continue;
if (marker.range.start.row > visibleRangeEndRow) continue;

var markerVisibleRange = marker.range.clipRows(visibleRangeStartRow, visibleRangeEndRow);
if (markerVisibleRange.start.row === markerVisibleRange.end.row
&& markerVisibleRange.start.column === markerVisibleRange.end.column) {
continue; // visible range is empty
}

var rangeToAddMarkerTo = markerVisibleRange.toScreenRange(session);
var rangeAsString = rangeToAddMarkerTo.toString();
if (this.renderedMarkerRanges[rangeAsString]) continue;

this.renderedMarkerRanges[rangeAsString] = true;
if (rangeToAddMarkerTo.isMultiLine()) {
markerLayer.drawTextMarker(html, rangeToAddMarkerTo, marker.className, config);
} else {
markerLayer.drawSingleLineMarker(html, rangeToAddMarkerTo, marker.className, config);
}
}
};

};

// this caps total amount of markers at 500 - should it maybe be done only for rendered markers?
// on top of it, do we need to cap the length of a rendered marker range to avoid performance issues?
MarkerGroup.prototype.MAX_MARKERS = 500;

exports.MarkerGroup = MarkerGroup;

2 changes: 1 addition & 1 deletion src/tooltip_marker_test.js → src/marker_group_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var ace = require("./ace");
var assert = require("./test/assertions");
var EditSession = require("./edit_session").EditSession;
var Range = require("./range").Range;
var TooltipMarkerManager = require("./tooltip_marker").TooltipMarkerManager;
var TooltipMarkerManager = require("./marker_group").TooltipMarkerManager;
var editor, tooltipMarkerManager;
var session1, session2;

Expand Down
Loading

0 comments on commit de81b71

Please sign in to comment.