comment.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // @ts-check
  2. const Range = require('./range')
  3. /**
  4. * @typedef {import("../types").CommentRawData} CommentRawData
  5. */
  6. class Comment {
  7. /**
  8. * @type {Range[]}
  9. */
  10. ranges = []
  11. /**
  12. * @type {boolean}
  13. */
  14. resolved = false
  15. /**
  16. * @param {Range[]} ranges
  17. * @param {boolean} [resolved]
  18. */
  19. constructor(ranges, resolved = false) {
  20. this.resolved = resolved
  21. for (const range of ranges) {
  22. this.addRange(range)
  23. }
  24. }
  25. /**
  26. *
  27. * @param {Range} range
  28. */
  29. addRange(range) {
  30. this.ranges.push(range)
  31. this.ranges.sort((a, b) => a.start - b.start)
  32. this.mergeRanges()
  33. }
  34. /**
  35. *
  36. * @param {number} cursor
  37. * @param {number} length
  38. * @param {boolean} [extendComment]
  39. */
  40. applyInsert(cursor, length, extendComment = false) {
  41. let existingRangeExtended = false
  42. const splittedRanges = []
  43. for (const commentRange of this.ranges) {
  44. if (cursor === commentRange.end) {
  45. // insert right after the comment
  46. if (extendComment) {
  47. commentRange.extendBy(length)
  48. existingRangeExtended = true
  49. }
  50. } else if (cursor === commentRange.start) {
  51. // insert at the start of the comment
  52. if (extendComment) {
  53. commentRange.extendBy(length)
  54. existingRangeExtended = true
  55. } else {
  56. commentRange.moveBy(length)
  57. }
  58. } else if (commentRange.startIsAfter(cursor)) {
  59. // insert before the comment
  60. commentRange.moveBy(length)
  61. } else if (commentRange.containsCursor(cursor)) {
  62. // insert is inside the comment
  63. if (extendComment) {
  64. commentRange.extendBy(length)
  65. existingRangeExtended = true
  66. } else {
  67. const [rangeUpToCursor, , rangeAfterCursor] = commentRange.insertAt(
  68. cursor,
  69. length
  70. )
  71. // use current commentRange for the part before the cursor
  72. commentRange.length = rangeUpToCursor.length
  73. // add the part after the cursor as a new range
  74. splittedRanges.push(rangeAfterCursor)
  75. }
  76. }
  77. }
  78. // add the splitted ranges
  79. for (const range of splittedRanges) {
  80. this.addRange(range)
  81. }
  82. // if the insert is not inside any range, add a new range
  83. if (extendComment && !existingRangeExtended) {
  84. this.addRange(new Range(cursor, length))
  85. }
  86. }
  87. /**
  88. *
  89. * @param {Range} deletedRange
  90. */
  91. applyDelete(deletedRange) {
  92. for (const commentRange of this.ranges) {
  93. if (commentRange.overlaps(deletedRange)) {
  94. commentRange.subtract(deletedRange)
  95. } else if (commentRange.startsAfter(deletedRange)) {
  96. commentRange.pos -= deletedRange.length
  97. }
  98. }
  99. this.mergeRanges()
  100. }
  101. isEmpty() {
  102. return this.ranges.length === 0
  103. }
  104. toRaw() {
  105. return {
  106. resolved: this.resolved,
  107. ranges: this.ranges.map(range => range.toRaw()),
  108. }
  109. }
  110. mergeRanges() {
  111. /** @type {Range[]} */
  112. const mergedRanges = []
  113. for (const range of this.ranges) {
  114. if (range.isEmpty()) {
  115. continue
  116. }
  117. const lastRange = mergedRanges[mergedRanges.length - 1]
  118. if (lastRange?.canMerge(range)) {
  119. lastRange.merge(range)
  120. } else {
  121. mergedRanges.push(range)
  122. }
  123. }
  124. this.ranges = mergedRanges
  125. }
  126. /**
  127. * @param {CommentRawData} rawComment
  128. * @returns {Comment}
  129. */
  130. static fromRaw(rawComment) {
  131. return new Comment(
  132. rawComment.ranges.map(range => Range.fromRaw(range)),
  133. rawComment.resolved
  134. )
  135. }
  136. }
  137. module.exports = Comment