8b0c228574129a6ab00be0dd52ffcdd48a91b9a70ea7291988542ecf2d47f2c35945034ebecbf01820df18a8007a2e84906e5187a8395cad1db933d3cc5456 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { isFalsyOrEmpty, isNonEmptyArray } from '../../../base/common/arrays.js';
  6. import { DebounceEmitter } from '../../../base/common/event.js';
  7. import { Iterable } from '../../../base/common/iterator.js';
  8. import { ResourceMap } from '../../../base/common/map.js';
  9. import { Schemas } from '../../../base/common/network.js';
  10. import { URI } from '../../../base/common/uri.js';
  11. import { MarkerSeverity } from './markers.js';
  12. class DoubleResourceMap {
  13. constructor() {
  14. this._byResource = new ResourceMap();
  15. this._byOwner = new Map();
  16. }
  17. set(resource, owner, value) {
  18. let ownerMap = this._byResource.get(resource);
  19. if (!ownerMap) {
  20. ownerMap = new Map();
  21. this._byResource.set(resource, ownerMap);
  22. }
  23. ownerMap.set(owner, value);
  24. let resourceMap = this._byOwner.get(owner);
  25. if (!resourceMap) {
  26. resourceMap = new ResourceMap();
  27. this._byOwner.set(owner, resourceMap);
  28. }
  29. resourceMap.set(resource, value);
  30. }
  31. get(resource, owner) {
  32. const ownerMap = this._byResource.get(resource);
  33. return ownerMap === null || ownerMap === void 0 ? void 0 : ownerMap.get(owner);
  34. }
  35. delete(resource, owner) {
  36. let removedA = false;
  37. let removedB = false;
  38. const ownerMap = this._byResource.get(resource);
  39. if (ownerMap) {
  40. removedA = ownerMap.delete(owner);
  41. }
  42. const resourceMap = this._byOwner.get(owner);
  43. if (resourceMap) {
  44. removedB = resourceMap.delete(resource);
  45. }
  46. if (removedA !== removedB) {
  47. throw new Error('illegal state');
  48. }
  49. return removedA && removedB;
  50. }
  51. values(key) {
  52. var _a, _b, _c, _d;
  53. if (typeof key === 'string') {
  54. return (_b = (_a = this._byOwner.get(key)) === null || _a === void 0 ? void 0 : _a.values()) !== null && _b !== void 0 ? _b : Iterable.empty();
  55. }
  56. if (URI.isUri(key)) {
  57. return (_d = (_c = this._byResource.get(key)) === null || _c === void 0 ? void 0 : _c.values()) !== null && _d !== void 0 ? _d : Iterable.empty();
  58. }
  59. return Iterable.map(Iterable.concat(...this._byOwner.values()), map => map[1]);
  60. }
  61. }
  62. class MarkerStats {
  63. constructor(service) {
  64. this.errors = 0;
  65. this.infos = 0;
  66. this.warnings = 0;
  67. this.unknowns = 0;
  68. this._data = new ResourceMap();
  69. this._service = service;
  70. this._subscription = service.onMarkerChanged(this._update, this);
  71. }
  72. dispose() {
  73. this._subscription.dispose();
  74. }
  75. _update(resources) {
  76. for (const resource of resources) {
  77. const oldStats = this._data.get(resource);
  78. if (oldStats) {
  79. this._substract(oldStats);
  80. }
  81. const newStats = this._resourceStats(resource);
  82. this._add(newStats);
  83. this._data.set(resource, newStats);
  84. }
  85. }
  86. _resourceStats(resource) {
  87. const result = { errors: 0, warnings: 0, infos: 0, unknowns: 0 };
  88. // TODO this is a hack
  89. if (resource.scheme === Schemas.inMemory || resource.scheme === Schemas.walkThrough || resource.scheme === Schemas.walkThroughSnippet || resource.scheme === Schemas.vscodeSourceControl) {
  90. return result;
  91. }
  92. for (const { severity } of this._service.read({ resource })) {
  93. if (severity === MarkerSeverity.Error) {
  94. result.errors += 1;
  95. }
  96. else if (severity === MarkerSeverity.Warning) {
  97. result.warnings += 1;
  98. }
  99. else if (severity === MarkerSeverity.Info) {
  100. result.infos += 1;
  101. }
  102. else {
  103. result.unknowns += 1;
  104. }
  105. }
  106. return result;
  107. }
  108. _substract(op) {
  109. this.errors -= op.errors;
  110. this.warnings -= op.warnings;
  111. this.infos -= op.infos;
  112. this.unknowns -= op.unknowns;
  113. }
  114. _add(op) {
  115. this.errors += op.errors;
  116. this.warnings += op.warnings;
  117. this.infos += op.infos;
  118. this.unknowns += op.unknowns;
  119. }
  120. }
  121. export class MarkerService {
  122. constructor() {
  123. this._onMarkerChanged = new DebounceEmitter({
  124. delay: 0,
  125. merge: MarkerService._merge
  126. });
  127. this.onMarkerChanged = this._onMarkerChanged.event;
  128. this._data = new DoubleResourceMap();
  129. this._stats = new MarkerStats(this);
  130. }
  131. dispose() {
  132. this._stats.dispose();
  133. this._onMarkerChanged.dispose();
  134. }
  135. remove(owner, resources) {
  136. for (const resource of resources || []) {
  137. this.changeOne(owner, resource, []);
  138. }
  139. }
  140. changeOne(owner, resource, markerData) {
  141. if (isFalsyOrEmpty(markerData)) {
  142. // remove marker for this (owner,resource)-tuple
  143. const removed = this._data.delete(resource, owner);
  144. if (removed) {
  145. this._onMarkerChanged.fire([resource]);
  146. }
  147. }
  148. else {
  149. // insert marker for this (owner,resource)-tuple
  150. const markers = [];
  151. for (const data of markerData) {
  152. const marker = MarkerService._toMarker(owner, resource, data);
  153. if (marker) {
  154. markers.push(marker);
  155. }
  156. }
  157. this._data.set(resource, owner, markers);
  158. this._onMarkerChanged.fire([resource]);
  159. }
  160. }
  161. static _toMarker(owner, resource, data) {
  162. let { code, severity, message, source, startLineNumber, startColumn, endLineNumber, endColumn, relatedInformation, tags, } = data;
  163. if (!message) {
  164. return undefined;
  165. }
  166. // santize data
  167. startLineNumber = startLineNumber > 0 ? startLineNumber : 1;
  168. startColumn = startColumn > 0 ? startColumn : 1;
  169. endLineNumber = endLineNumber >= startLineNumber ? endLineNumber : startLineNumber;
  170. endColumn = endColumn > 0 ? endColumn : startColumn;
  171. return {
  172. resource,
  173. owner,
  174. code,
  175. severity,
  176. message,
  177. source,
  178. startLineNumber,
  179. startColumn,
  180. endLineNumber,
  181. endColumn,
  182. relatedInformation,
  183. tags,
  184. };
  185. }
  186. changeAll(owner, data) {
  187. const changes = [];
  188. // remove old marker
  189. const existing = this._data.values(owner);
  190. if (existing) {
  191. for (const data of existing) {
  192. const first = Iterable.first(data);
  193. if (first) {
  194. changes.push(first.resource);
  195. this._data.delete(first.resource, owner);
  196. }
  197. }
  198. }
  199. // add new markers
  200. if (isNonEmptyArray(data)) {
  201. // group by resource
  202. const groups = new ResourceMap();
  203. for (const { resource, marker: markerData } of data) {
  204. const marker = MarkerService._toMarker(owner, resource, markerData);
  205. if (!marker) {
  206. // filter bad markers
  207. continue;
  208. }
  209. const array = groups.get(resource);
  210. if (!array) {
  211. groups.set(resource, [marker]);
  212. changes.push(resource);
  213. }
  214. else {
  215. array.push(marker);
  216. }
  217. }
  218. // insert all
  219. for (const [resource, value] of groups) {
  220. this._data.set(resource, owner, value);
  221. }
  222. }
  223. if (changes.length > 0) {
  224. this._onMarkerChanged.fire(changes);
  225. }
  226. }
  227. read(filter = Object.create(null)) {
  228. let { owner, resource, severities, take } = filter;
  229. if (!take || take < 0) {
  230. take = -1;
  231. }
  232. if (owner && resource) {
  233. // exactly one owner AND resource
  234. const data = this._data.get(resource, owner);
  235. if (!data) {
  236. return [];
  237. }
  238. else {
  239. const result = [];
  240. for (const marker of data) {
  241. if (MarkerService._accept(marker, severities)) {
  242. const newLen = result.push(marker);
  243. if (take > 0 && newLen === take) {
  244. break;
  245. }
  246. }
  247. }
  248. return result;
  249. }
  250. }
  251. else if (!owner && !resource) {
  252. // all
  253. const result = [];
  254. for (const markers of this._data.values()) {
  255. for (const data of markers) {
  256. if (MarkerService._accept(data, severities)) {
  257. const newLen = result.push(data);
  258. if (take > 0 && newLen === take) {
  259. return result;
  260. }
  261. }
  262. }
  263. }
  264. return result;
  265. }
  266. else {
  267. // of one resource OR owner
  268. const iterable = this._data.values(resource !== null && resource !== void 0 ? resource : owner);
  269. const result = [];
  270. for (const markers of iterable) {
  271. for (const data of markers) {
  272. if (MarkerService._accept(data, severities)) {
  273. const newLen = result.push(data);
  274. if (take > 0 && newLen === take) {
  275. return result;
  276. }
  277. }
  278. }
  279. }
  280. return result;
  281. }
  282. }
  283. static _accept(marker, severities) {
  284. return severities === undefined || (severities & marker.severity) === marker.severity;
  285. }
  286. // --- event debounce logic
  287. static _merge(all) {
  288. const set = new ResourceMap();
  289. for (const array of all) {
  290. for (const item of array) {
  291. set.set(item, true);
  292. }
  293. }
  294. return Array.from(set.keys());
  295. }
  296. }