Utils.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // @ts-check
  2. const _ = require('lodash')
  3. /**
  4. * @typedef {import('./types').CommentOp} CommentOp
  5. * @typedef {import('./types').DeleteOp} DeleteOp
  6. * @typedef {import('./types').InsertOp} InsertOp
  7. * @typedef {import('./types').Op} Op
  8. * @typedef {import('./types').TrackedChange} TrackedChange
  9. */
  10. /**
  11. * Returns true if the op is an insert
  12. *
  13. * @param {Op} op
  14. * @returns {op is InsertOp}
  15. */
  16. function isInsert(op) {
  17. return 'i' in op && op.i != null
  18. }
  19. /**
  20. * Returns true if the op is an insert
  21. *
  22. * @param {Op} op
  23. * @returns {op is DeleteOp}
  24. */
  25. function isDelete(op) {
  26. return 'd' in op && op.d != null
  27. }
  28. /**
  29. * Returns true if the op is a comment
  30. *
  31. * @param {Op} op
  32. * @returns {op is CommentOp}
  33. */
  34. function isComment(op) {
  35. return 'c' in op && op.c != null
  36. }
  37. /**
  38. * Get the length of a document from its lines
  39. *
  40. * @param {string[]} lines
  41. * @returns {number}
  42. */
  43. function getDocLength(lines) {
  44. let docLength = _.reduce(lines, (chars, line) => chars + line.length, 0)
  45. // Add newline characters. Lines are joined by newlines, but the last line
  46. // doesn't include a newline. We must make a special case for an empty list
  47. // so that it doesn't report a doc length of -1.
  48. docLength += Math.max(lines.length - 1, 0)
  49. return docLength
  50. }
  51. /**
  52. * Adds given tracked deletes to the given content.
  53. *
  54. * The history system includes tracked deletes in the document content.
  55. *
  56. * @param {string} content
  57. * @param {TrackedChange[]} trackedChanges
  58. * @return {string} content for the history service
  59. */
  60. function addTrackedDeletesToContent(content, trackedChanges) {
  61. let cursor = 0
  62. let result = ''
  63. for (const change of trackedChanges) {
  64. if (isDelete(change.op)) {
  65. // Add the content before the tracked delete
  66. result += content.slice(cursor, change.op.p)
  67. cursor = change.op.p
  68. // Add the content of the tracked delete
  69. result += change.op.d
  70. }
  71. }
  72. // Add the content after all tracked deletes
  73. result += content.slice(cursor)
  74. return result
  75. }
  76. /**
  77. * checks if the given originOrSource should be treated as a source or origin
  78. * TODO: remove this hack and remove all "source" references
  79. */
  80. function extractOriginOrSource(originOrSource) {
  81. let source = null
  82. let origin = null
  83. if (typeof originOrSource === 'string') {
  84. source = originOrSource
  85. } else if (originOrSource && typeof originOrSource === 'object') {
  86. origin = originOrSource
  87. }
  88. return { source, origin }
  89. }
  90. module.exports = {
  91. isInsert,
  92. isDelete,
  93. isComment,
  94. addTrackedDeletesToContent,
  95. getDocLength,
  96. extractOriginOrSource,
  97. }