491a88c427e6c322670d9e9fec35efbb1d84dab465d48667223dbc315354db71f7016021e5afe9262ff61de2118af4f68da9070d88c0479f434ebe9e9c6535 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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 { Position } from './position.js';
  6. /**
  7. * A range in the editor. (startLineNumber,startColumn) is <= (endLineNumber,endColumn)
  8. */
  9. export class Range {
  10. constructor(startLineNumber, startColumn, endLineNumber, endColumn) {
  11. if ((startLineNumber > endLineNumber) || (startLineNumber === endLineNumber && startColumn > endColumn)) {
  12. this.startLineNumber = endLineNumber;
  13. this.startColumn = endColumn;
  14. this.endLineNumber = startLineNumber;
  15. this.endColumn = startColumn;
  16. }
  17. else {
  18. this.startLineNumber = startLineNumber;
  19. this.startColumn = startColumn;
  20. this.endLineNumber = endLineNumber;
  21. this.endColumn = endColumn;
  22. }
  23. }
  24. /**
  25. * Test if this range is empty.
  26. */
  27. isEmpty() {
  28. return Range.isEmpty(this);
  29. }
  30. /**
  31. * Test if `range` is empty.
  32. */
  33. static isEmpty(range) {
  34. return (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn);
  35. }
  36. /**
  37. * Test if position is in this range. If the position is at the edges, will return true.
  38. */
  39. containsPosition(position) {
  40. return Range.containsPosition(this, position);
  41. }
  42. /**
  43. * Test if `position` is in `range`. If the position is at the edges, will return true.
  44. */
  45. static containsPosition(range, position) {
  46. if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) {
  47. return false;
  48. }
  49. if (position.lineNumber === range.startLineNumber && position.column < range.startColumn) {
  50. return false;
  51. }
  52. if (position.lineNumber === range.endLineNumber && position.column > range.endColumn) {
  53. return false;
  54. }
  55. return true;
  56. }
  57. /**
  58. * Test if `position` is in `range`. If the position is at the edges, will return false.
  59. * @internal
  60. */
  61. static strictContainsPosition(range, position) {
  62. if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) {
  63. return false;
  64. }
  65. if (position.lineNumber === range.startLineNumber && position.column <= range.startColumn) {
  66. return false;
  67. }
  68. if (position.lineNumber === range.endLineNumber && position.column >= range.endColumn) {
  69. return false;
  70. }
  71. return true;
  72. }
  73. /**
  74. * Test if range is in this range. If the range is equal to this range, will return true.
  75. */
  76. containsRange(range) {
  77. return Range.containsRange(this, range);
  78. }
  79. /**
  80. * Test if `otherRange` is in `range`. If the ranges are equal, will return true.
  81. */
  82. static containsRange(range, otherRange) {
  83. if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) {
  84. return false;
  85. }
  86. if (otherRange.startLineNumber > range.endLineNumber || otherRange.endLineNumber > range.endLineNumber) {
  87. return false;
  88. }
  89. if (otherRange.startLineNumber === range.startLineNumber && otherRange.startColumn < range.startColumn) {
  90. return false;
  91. }
  92. if (otherRange.endLineNumber === range.endLineNumber && otherRange.endColumn > range.endColumn) {
  93. return false;
  94. }
  95. return true;
  96. }
  97. /**
  98. * Test if `range` is strictly in this range. `range` must start after and end before this range for the result to be true.
  99. */
  100. strictContainsRange(range) {
  101. return Range.strictContainsRange(this, range);
  102. }
  103. /**
  104. * Test if `otherRange` is strictly in `range` (must start after, and end before). If the ranges are equal, will return false.
  105. */
  106. static strictContainsRange(range, otherRange) {
  107. if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) {
  108. return false;
  109. }
  110. if (otherRange.startLineNumber > range.endLineNumber || otherRange.endLineNumber > range.endLineNumber) {
  111. return false;
  112. }
  113. if (otherRange.startLineNumber === range.startLineNumber && otherRange.startColumn <= range.startColumn) {
  114. return false;
  115. }
  116. if (otherRange.endLineNumber === range.endLineNumber && otherRange.endColumn >= range.endColumn) {
  117. return false;
  118. }
  119. return true;
  120. }
  121. /**
  122. * A reunion of the two ranges.
  123. * The smallest position will be used as the start point, and the largest one as the end point.
  124. */
  125. plusRange(range) {
  126. return Range.plusRange(this, range);
  127. }
  128. /**
  129. * A reunion of the two ranges.
  130. * The smallest position will be used as the start point, and the largest one as the end point.
  131. */
  132. static plusRange(a, b) {
  133. let startLineNumber;
  134. let startColumn;
  135. let endLineNumber;
  136. let endColumn;
  137. if (b.startLineNumber < a.startLineNumber) {
  138. startLineNumber = b.startLineNumber;
  139. startColumn = b.startColumn;
  140. }
  141. else if (b.startLineNumber === a.startLineNumber) {
  142. startLineNumber = b.startLineNumber;
  143. startColumn = Math.min(b.startColumn, a.startColumn);
  144. }
  145. else {
  146. startLineNumber = a.startLineNumber;
  147. startColumn = a.startColumn;
  148. }
  149. if (b.endLineNumber > a.endLineNumber) {
  150. endLineNumber = b.endLineNumber;
  151. endColumn = b.endColumn;
  152. }
  153. else if (b.endLineNumber === a.endLineNumber) {
  154. endLineNumber = b.endLineNumber;
  155. endColumn = Math.max(b.endColumn, a.endColumn);
  156. }
  157. else {
  158. endLineNumber = a.endLineNumber;
  159. endColumn = a.endColumn;
  160. }
  161. return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
  162. }
  163. /**
  164. * A intersection of the two ranges.
  165. */
  166. intersectRanges(range) {
  167. return Range.intersectRanges(this, range);
  168. }
  169. /**
  170. * A intersection of the two ranges.
  171. */
  172. static intersectRanges(a, b) {
  173. let resultStartLineNumber = a.startLineNumber;
  174. let resultStartColumn = a.startColumn;
  175. let resultEndLineNumber = a.endLineNumber;
  176. let resultEndColumn = a.endColumn;
  177. const otherStartLineNumber = b.startLineNumber;
  178. const otherStartColumn = b.startColumn;
  179. const otherEndLineNumber = b.endLineNumber;
  180. const otherEndColumn = b.endColumn;
  181. if (resultStartLineNumber < otherStartLineNumber) {
  182. resultStartLineNumber = otherStartLineNumber;
  183. resultStartColumn = otherStartColumn;
  184. }
  185. else if (resultStartLineNumber === otherStartLineNumber) {
  186. resultStartColumn = Math.max(resultStartColumn, otherStartColumn);
  187. }
  188. if (resultEndLineNumber > otherEndLineNumber) {
  189. resultEndLineNumber = otherEndLineNumber;
  190. resultEndColumn = otherEndColumn;
  191. }
  192. else if (resultEndLineNumber === otherEndLineNumber) {
  193. resultEndColumn = Math.min(resultEndColumn, otherEndColumn);
  194. }
  195. // Check if selection is now empty
  196. if (resultStartLineNumber > resultEndLineNumber) {
  197. return null;
  198. }
  199. if (resultStartLineNumber === resultEndLineNumber && resultStartColumn > resultEndColumn) {
  200. return null;
  201. }
  202. return new Range(resultStartLineNumber, resultStartColumn, resultEndLineNumber, resultEndColumn);
  203. }
  204. /**
  205. * Test if this range equals other.
  206. */
  207. equalsRange(other) {
  208. return Range.equalsRange(this, other);
  209. }
  210. /**
  211. * Test if range `a` equals `b`.
  212. */
  213. static equalsRange(a, b) {
  214. return (!!a &&
  215. !!b &&
  216. a.startLineNumber === b.startLineNumber &&
  217. a.startColumn === b.startColumn &&
  218. a.endLineNumber === b.endLineNumber &&
  219. a.endColumn === b.endColumn);
  220. }
  221. /**
  222. * Return the end position (which will be after or equal to the start position)
  223. */
  224. getEndPosition() {
  225. return Range.getEndPosition(this);
  226. }
  227. /**
  228. * Return the end position (which will be after or equal to the start position)
  229. */
  230. static getEndPosition(range) {
  231. return new Position(range.endLineNumber, range.endColumn);
  232. }
  233. /**
  234. * Return the start position (which will be before or equal to the end position)
  235. */
  236. getStartPosition() {
  237. return Range.getStartPosition(this);
  238. }
  239. /**
  240. * Return the start position (which will be before or equal to the end position)
  241. */
  242. static getStartPosition(range) {
  243. return new Position(range.startLineNumber, range.startColumn);
  244. }
  245. /**
  246. * Transform to a user presentable string representation.
  247. */
  248. toString() {
  249. return '[' + this.startLineNumber + ',' + this.startColumn + ' -> ' + this.endLineNumber + ',' + this.endColumn + ']';
  250. }
  251. /**
  252. * Create a new range using this range's start position, and using endLineNumber and endColumn as the end position.
  253. */
  254. setEndPosition(endLineNumber, endColumn) {
  255. return new Range(this.startLineNumber, this.startColumn, endLineNumber, endColumn);
  256. }
  257. /**
  258. * Create a new range using this range's end position, and using startLineNumber and startColumn as the start position.
  259. */
  260. setStartPosition(startLineNumber, startColumn) {
  261. return new Range(startLineNumber, startColumn, this.endLineNumber, this.endColumn);
  262. }
  263. /**
  264. * Create a new empty range using this range's start position.
  265. */
  266. collapseToStart() {
  267. return Range.collapseToStart(this);
  268. }
  269. /**
  270. * Create a new empty range using this range's start position.
  271. */
  272. static collapseToStart(range) {
  273. return new Range(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn);
  274. }
  275. // ---
  276. static fromPositions(start, end = start) {
  277. return new Range(start.lineNumber, start.column, end.lineNumber, end.column);
  278. }
  279. static lift(range) {
  280. if (!range) {
  281. return null;
  282. }
  283. return new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
  284. }
  285. /**
  286. * Test if `obj` is an `IRange`.
  287. */
  288. static isIRange(obj) {
  289. return (obj
  290. && (typeof obj.startLineNumber === 'number')
  291. && (typeof obj.startColumn === 'number')
  292. && (typeof obj.endLineNumber === 'number')
  293. && (typeof obj.endColumn === 'number'));
  294. }
  295. /**
  296. * Test if the two ranges are touching in any way.
  297. */
  298. static areIntersectingOrTouching(a, b) {
  299. // Check if `a` is before `b`
  300. if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn < b.startColumn)) {
  301. return false;
  302. }
  303. // Check if `b` is before `a`
  304. if (b.endLineNumber < a.startLineNumber || (b.endLineNumber === a.startLineNumber && b.endColumn < a.startColumn)) {
  305. return false;
  306. }
  307. // These ranges must intersect
  308. return true;
  309. }
  310. /**
  311. * Test if the two ranges are intersecting. If the ranges are touching it returns true.
  312. */
  313. static areIntersecting(a, b) {
  314. // Check if `a` is before `b`
  315. if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn <= b.startColumn)) {
  316. return false;
  317. }
  318. // Check if `b` is before `a`
  319. if (b.endLineNumber < a.startLineNumber || (b.endLineNumber === a.startLineNumber && b.endColumn <= a.startColumn)) {
  320. return false;
  321. }
  322. // These ranges must intersect
  323. return true;
  324. }
  325. /**
  326. * A function that compares ranges, useful for sorting ranges
  327. * It will first compare ranges on the startPosition and then on the endPosition
  328. */
  329. static compareRangesUsingStarts(a, b) {
  330. if (a && b) {
  331. const aStartLineNumber = a.startLineNumber | 0;
  332. const bStartLineNumber = b.startLineNumber | 0;
  333. if (aStartLineNumber === bStartLineNumber) {
  334. const aStartColumn = a.startColumn | 0;
  335. const bStartColumn = b.startColumn | 0;
  336. if (aStartColumn === bStartColumn) {
  337. const aEndLineNumber = a.endLineNumber | 0;
  338. const bEndLineNumber = b.endLineNumber | 0;
  339. if (aEndLineNumber === bEndLineNumber) {
  340. const aEndColumn = a.endColumn | 0;
  341. const bEndColumn = b.endColumn | 0;
  342. return aEndColumn - bEndColumn;
  343. }
  344. return aEndLineNumber - bEndLineNumber;
  345. }
  346. return aStartColumn - bStartColumn;
  347. }
  348. return aStartLineNumber - bStartLineNumber;
  349. }
  350. const aExists = (a ? 1 : 0);
  351. const bExists = (b ? 1 : 0);
  352. return aExists - bExists;
  353. }
  354. /**
  355. * A function that compares ranges, useful for sorting ranges
  356. * It will first compare ranges on the endPosition and then on the startPosition
  357. */
  358. static compareRangesUsingEnds(a, b) {
  359. if (a.endLineNumber === b.endLineNumber) {
  360. if (a.endColumn === b.endColumn) {
  361. if (a.startLineNumber === b.startLineNumber) {
  362. return a.startColumn - b.startColumn;
  363. }
  364. return a.startLineNumber - b.startLineNumber;
  365. }
  366. return a.endColumn - b.endColumn;
  367. }
  368. return a.endLineNumber - b.endLineNumber;
  369. }
  370. /**
  371. * Test if the range spans multiple lines.
  372. */
  373. static spansMultipleLines(range) {
  374. return range.endLineNumber > range.startLineNumber;
  375. }
  376. toJSON() {
  377. return this;
  378. }
  379. }