f8d6566123370bd760fca3cb009c94f9d234f4c80d3d13c476f260d81364c229ab0e1cb32aaad73939eb6a90ec31c4b0cc3728eef2cd089c3b70a3a18d0686 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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 * as strings from '../../../base/common/strings.js';
  6. export class LineDecoration {
  7. constructor(startColumn, endColumn, className, type) {
  8. this.startColumn = startColumn;
  9. this.endColumn = endColumn;
  10. this.className = className;
  11. this.type = type;
  12. this._lineDecorationBrand = undefined;
  13. }
  14. static _equals(a, b) {
  15. return (a.startColumn === b.startColumn
  16. && a.endColumn === b.endColumn
  17. && a.className === b.className
  18. && a.type === b.type);
  19. }
  20. static equalsArr(a, b) {
  21. const aLen = a.length;
  22. const bLen = b.length;
  23. if (aLen !== bLen) {
  24. return false;
  25. }
  26. for (let i = 0; i < aLen; i++) {
  27. if (!LineDecoration._equals(a[i], b[i])) {
  28. return false;
  29. }
  30. }
  31. return true;
  32. }
  33. static extractWrapped(arr, startOffset, endOffset) {
  34. if (arr.length === 0) {
  35. return arr;
  36. }
  37. const startColumn = startOffset + 1;
  38. const endColumn = endOffset + 1;
  39. const lineLength = endOffset - startOffset;
  40. const r = [];
  41. let rLength = 0;
  42. for (const dec of arr) {
  43. if (dec.endColumn <= startColumn || dec.startColumn >= endColumn) {
  44. continue;
  45. }
  46. r[rLength++] = new LineDecoration(Math.max(1, dec.startColumn - startColumn + 1), Math.min(lineLength + 1, dec.endColumn - startColumn + 1), dec.className, dec.type);
  47. }
  48. return r;
  49. }
  50. static filter(lineDecorations, lineNumber, minLineColumn, maxLineColumn) {
  51. if (lineDecorations.length === 0) {
  52. return [];
  53. }
  54. const result = [];
  55. let resultLen = 0;
  56. for (let i = 0, len = lineDecorations.length; i < len; i++) {
  57. const d = lineDecorations[i];
  58. const range = d.range;
  59. if (range.endLineNumber < lineNumber || range.startLineNumber > lineNumber) {
  60. // Ignore decorations that sit outside this line
  61. continue;
  62. }
  63. if (range.isEmpty() && (d.type === 0 /* InlineDecorationType.Regular */ || d.type === 3 /* InlineDecorationType.RegularAffectingLetterSpacing */)) {
  64. // Ignore empty range decorations
  65. continue;
  66. }
  67. const startColumn = (range.startLineNumber === lineNumber ? range.startColumn : minLineColumn);
  68. const endColumn = (range.endLineNumber === lineNumber ? range.endColumn : maxLineColumn);
  69. result[resultLen++] = new LineDecoration(startColumn, endColumn, d.inlineClassName, d.type);
  70. }
  71. return result;
  72. }
  73. static _typeCompare(a, b) {
  74. const ORDER = [2, 0, 1, 3];
  75. return ORDER[a] - ORDER[b];
  76. }
  77. static compare(a, b) {
  78. if (a.startColumn !== b.startColumn) {
  79. return a.startColumn - b.startColumn;
  80. }
  81. if (a.endColumn !== b.endColumn) {
  82. return a.endColumn - b.endColumn;
  83. }
  84. const typeCmp = LineDecoration._typeCompare(a.type, b.type);
  85. if (typeCmp !== 0) {
  86. return typeCmp;
  87. }
  88. if (a.className !== b.className) {
  89. return a.className < b.className ? -1 : 1;
  90. }
  91. return 0;
  92. }
  93. }
  94. export class DecorationSegment {
  95. constructor(startOffset, endOffset, className, metadata) {
  96. this.startOffset = startOffset;
  97. this.endOffset = endOffset;
  98. this.className = className;
  99. this.metadata = metadata;
  100. }
  101. }
  102. class Stack {
  103. constructor() {
  104. this.stopOffsets = [];
  105. this.classNames = [];
  106. this.metadata = [];
  107. this.count = 0;
  108. }
  109. static _metadata(metadata) {
  110. let result = 0;
  111. for (let i = 0, len = metadata.length; i < len; i++) {
  112. result |= metadata[i];
  113. }
  114. return result;
  115. }
  116. consumeLowerThan(maxStopOffset, nextStartOffset, result) {
  117. while (this.count > 0 && this.stopOffsets[0] < maxStopOffset) {
  118. let i = 0;
  119. // Take all equal stopping offsets
  120. while (i + 1 < this.count && this.stopOffsets[i] === this.stopOffsets[i + 1]) {
  121. i++;
  122. }
  123. // Basically we are consuming the first i + 1 elements of the stack
  124. result.push(new DecorationSegment(nextStartOffset, this.stopOffsets[i], this.classNames.join(' '), Stack._metadata(this.metadata)));
  125. nextStartOffset = this.stopOffsets[i] + 1;
  126. // Consume them
  127. this.stopOffsets.splice(0, i + 1);
  128. this.classNames.splice(0, i + 1);
  129. this.metadata.splice(0, i + 1);
  130. this.count -= (i + 1);
  131. }
  132. if (this.count > 0 && nextStartOffset < maxStopOffset) {
  133. result.push(new DecorationSegment(nextStartOffset, maxStopOffset - 1, this.classNames.join(' '), Stack._metadata(this.metadata)));
  134. nextStartOffset = maxStopOffset;
  135. }
  136. return nextStartOffset;
  137. }
  138. insert(stopOffset, className, metadata) {
  139. if (this.count === 0 || this.stopOffsets[this.count - 1] <= stopOffset) {
  140. // Insert at the end
  141. this.stopOffsets.push(stopOffset);
  142. this.classNames.push(className);
  143. this.metadata.push(metadata);
  144. }
  145. else {
  146. // Find the insertion position for `stopOffset`
  147. for (let i = 0; i < this.count; i++) {
  148. if (this.stopOffsets[i] >= stopOffset) {
  149. this.stopOffsets.splice(i, 0, stopOffset);
  150. this.classNames.splice(i, 0, className);
  151. this.metadata.splice(i, 0, metadata);
  152. break;
  153. }
  154. }
  155. }
  156. this.count++;
  157. return;
  158. }
  159. }
  160. export class LineDecorationsNormalizer {
  161. /**
  162. * Normalize line decorations. Overlapping decorations will generate multiple segments
  163. */
  164. static normalize(lineContent, lineDecorations) {
  165. if (lineDecorations.length === 0) {
  166. return [];
  167. }
  168. const result = [];
  169. const stack = new Stack();
  170. let nextStartOffset = 0;
  171. for (let i = 0, len = lineDecorations.length; i < len; i++) {
  172. const d = lineDecorations[i];
  173. let startColumn = d.startColumn;
  174. let endColumn = d.endColumn;
  175. const className = d.className;
  176. const metadata = (d.type === 1 /* InlineDecorationType.Before */
  177. ? 2 /* LinePartMetadata.PSEUDO_BEFORE */
  178. : d.type === 2 /* InlineDecorationType.After */
  179. ? 4 /* LinePartMetadata.PSEUDO_AFTER */
  180. : 0);
  181. // If the position would end up in the middle of a high-low surrogate pair, we move it to before the pair
  182. if (startColumn > 1) {
  183. const charCodeBefore = lineContent.charCodeAt(startColumn - 2);
  184. if (strings.isHighSurrogate(charCodeBefore)) {
  185. startColumn--;
  186. }
  187. }
  188. if (endColumn > 1) {
  189. const charCodeBefore = lineContent.charCodeAt(endColumn - 2);
  190. if (strings.isHighSurrogate(charCodeBefore)) {
  191. endColumn--;
  192. }
  193. }
  194. const currentStartOffset = startColumn - 1;
  195. const currentEndOffset = endColumn - 2;
  196. nextStartOffset = stack.consumeLowerThan(currentStartOffset, nextStartOffset, result);
  197. if (stack.count === 0) {
  198. nextStartOffset = currentStartOffset;
  199. }
  200. stack.insert(currentEndOffset, className, metadata);
  201. }
  202. stack.consumeLowerThan(1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */, nextStartOffset, result);
  203. return result;
  204. }
  205. }