text.ts 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { syntaxTree } from '@codemirror/language'
  2. import { Line } from '@codemirror/state'
  3. import { EditorView } from '@codemirror/view'
  4. import { SyntaxNodeRef } from '@lezer/common'
  5. import OError from '@overleaf/o-error'
  6. /* A convenient wrapper around 'Normal' tokens */
  7. export class NormalTextSpan {
  8. public from: number
  9. public to: number
  10. public lineNumber: number
  11. public text: string
  12. public node: SyntaxNodeRef
  13. constructor(options: {
  14. from: number
  15. to: number
  16. lineNumber: number
  17. text: string
  18. node: SyntaxNodeRef
  19. }) {
  20. const { from, to, lineNumber, text, node } = options
  21. if (
  22. text == null ||
  23. from == null ||
  24. to == null ||
  25. lineNumber == null ||
  26. node == null
  27. ) {
  28. throw new OError('TreeQuery: invalid NormalTextSpan').withInfo({
  29. options,
  30. })
  31. }
  32. this.from = from
  33. this.to = to
  34. this.text = text
  35. this.node = node
  36. this.lineNumber = lineNumber
  37. }
  38. }
  39. const NotNormalNodeAncestors = [
  40. 'Include',
  41. 'Input',
  42. 'IncludeGraphics',
  43. 'Cite',
  44. 'LabelArgument',
  45. 'UsePackage',
  46. 'PackageArgument',
  47. 'EnvNameGroup',
  48. 'RefArgument',
  49. ]
  50. export const getNormalTextSpansFromLine = (
  51. view: EditorView,
  52. line: Line
  53. ): Array<NormalTextSpan> => {
  54. const lineNumber = line.number
  55. const lineStart = line.from
  56. const lineEnd = line.to
  57. const tree = syntaxTree(view.state)
  58. const normalTextSpans: Array<NormalTextSpan> = []
  59. tree?.iterate({
  60. from: lineStart,
  61. to: lineEnd,
  62. enter: (node: SyntaxNodeRef) => {
  63. if (NotNormalNodeAncestors.includes(node.type.name)) {
  64. return false
  65. }
  66. if (node.type.name === 'Normal') {
  67. normalTextSpans.push(
  68. new NormalTextSpan({
  69. from: node.from,
  70. to: node.to,
  71. text: view.state.doc.sliceString(node.from, node.to),
  72. lineNumber,
  73. node,
  74. })
  75. )
  76. return false
  77. }
  78. return true
  79. },
  80. })
  81. return normalTextSpans
  82. }