text.ts 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  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. import { noSpellCheckProp } from '@/features/source-editor/utils/node-props'
  7. /* A convenient wrapper around 'Normal' tokens */
  8. export class NormalTextSpan {
  9. public from: number
  10. public to: number
  11. public lineNumber: number
  12. public text: string
  13. public node: SyntaxNodeRef
  14. constructor(options: {
  15. from: number
  16. to: number
  17. lineNumber: number
  18. text: string
  19. node: SyntaxNodeRef
  20. }) {
  21. const { from, to, lineNumber, text, node } = options
  22. if (
  23. text == null ||
  24. from == null ||
  25. to == null ||
  26. lineNumber == null ||
  27. node == null
  28. ) {
  29. throw new OError('TreeQuery: invalid NormalTextSpan').withInfo({
  30. options,
  31. })
  32. }
  33. this.from = from
  34. this.to = to
  35. this.text = text
  36. this.node = node
  37. this.lineNumber = lineNumber
  38. }
  39. }
  40. export const getNormalTextSpansFromLine = (
  41. view: EditorView,
  42. line: Line
  43. ): Array<NormalTextSpan> => {
  44. const lineNumber = line.number
  45. const lineStart = line.from
  46. const lineEnd = line.to
  47. const tree = syntaxTree(view.state)
  48. const normalTextSpans: Array<NormalTextSpan> = []
  49. tree?.iterate({
  50. from: lineStart,
  51. to: lineEnd,
  52. enter: (node: SyntaxNodeRef) => {
  53. if (
  54. node.type.prop(noSpellCheckProp)?.some(context => {
  55. return node.matchContext(context)
  56. })
  57. ) {
  58. return false
  59. }
  60. if (node.type.name === 'Normal') {
  61. normalTextSpans.push(
  62. new NormalTextSpan({
  63. from: node.from,
  64. to: node.to,
  65. text: view.state.doc.sliceString(node.from, node.to),
  66. lineNumber,
  67. node,
  68. })
  69. )
  70. return false
  71. }
  72. return true
  73. },
  74. })
  75. return normalTextSpans
  76. }