-
-
Notifications
You must be signed in to change notification settings - Fork 22
/
CodeLensManager.js
143 lines (115 loc) · 4.71 KB
/
CodeLensManager.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* global atom */
'use strict';
module.exports =
/**
* Manages visual information around code lenses returned by the server.
*/
class CodeLensManager
{
/**
* Constructor.
*/
constructor() {
this.markers = {};
this.annotations = {};
}
/**
* @param {TextEditor} editor
* @param {Array} codeLenses
* @param {Callable} executeCommandHandler
*/
process(editor, codeLenses, executeCommandHandler) {
this.removeMarkers(editor);
const {Convert} = require('atom-languageclient');
// You cannot have multiple markers on the exact same line, you need to combine them into one element and
// have one marker, so do some grouping up front.
const codeLensesGroupedByLine = codeLenses.reduce((accumulator, codeLens) => {
const range = Convert.lsRangeToAtomRange(codeLens.range);
if (!accumulator.has(range.start.row)) {
accumulator.set(range.start.row, new Array());
}
accumulator.get(range.start.row).push(codeLens);
return accumulator;
}, new Map());
codeLensesGroupedByLine.forEach((codeLenses, line) => {
this.processForLine(editor, codeLenses, line, executeCommandHandler);
});
}
/**
* @param {TextEditor} editor
* @param {Array} codeLenses
* @param {Number} line
* @param {Callable} executeCommandHandler
*/
processForLine(editor, codeLenses, line, executeCommandHandler) {
const {Range, Point} = require('atom');
const {Convert} = require('atom-languageclient');
const marker = this.registerMarker(editor, new Range(new Point(line, 0), new Point(line, 1)), {
invalidate : 'touch',
});
const codeLensLineElement = document.createElement('div');
codeLensLineElement.classList.add('php-ide-serenata-code-lens-wrapper');
let charactersTaken = 0;
codeLenses.forEach((codeLens) => {
if (!codeLens.command) {
// To support this, one would have to send a resolve request and show some sort of placeholder
// beforehand, as we wouldn't know what title to show yet.
throw new Error('Code lenses with unresolved commands are currently not supported');
}
const range = Convert.lsRangeToAtomRange(codeLens.range);
const paddingSpacesNeeded = range.start.column - charactersTaken;
// Having one marker per line (see above) means that we need to do padding ourselves when multiple code
// lenses are present. This can happen in cases where multiple properties are on one line, and more than one
// of them is an override. ot great, but it gets the job done.
const paddingSpanElement = document.createElement('span');
paddingSpanElement.innerHTML = ' '.repeat(paddingSpacesNeeded);
const anchorElement = document.createElement('a');
anchorElement.innerHTML = codeLens.command.title;
anchorElement.classList.add('badge');
anchorElement.classList.add('badge-small');
anchorElement.href = '#';
anchorElement.addEventListener('click', () => {
executeCommandHandler({
command: codeLens.command.command,
arguments: codeLens.command.arguments,
});
});
charactersTaken += paddingSpacesNeeded + anchorElement.innerHTML.length ;
const wrapperElement = document.createElement('div');
wrapperElement.classList.add('php-ide-serenata-code-lens');
// wrapperElement.style.marginLeft = range.start.column + 'em';
wrapperElement.appendChild(paddingSpanElement);
wrapperElement.appendChild(anchorElement);
codeLensLineElement.appendChild(wrapperElement);
});
editor.decorateMarker(marker, {
type: 'block',
item: codeLensLineElement,
});
}
/**
* @param {TextEditor} editor
* @param {Range} range
* @param {Object} options
*
* @return {Object}
*/
registerMarker(editor, range, options) {
const marker = editor.markBufferRange(range, options);
if (!(editor.id in this.markers)) {
this.markers[editor.id] = [];
}
this.markers[editor.id].push(marker);
return marker;
}
/**
* @param {TextEditor} editor
*/
removeMarkers(editor) {
for (let i in this.markers[editor.id]) {
const marker = this.markers[editor.id][i];
marker.destroy();
}
this.markers[editor.id] = [];
}
};