common.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { ensureSyntaxTree } from '@codemirror/language'
  2. import { EditorState } from '@codemirror/state'
  3. import { IterMode, SyntaxNode, SyntaxNodeRef, Tree } from '@lezer/common'
  4. const HUNDRED_MS = 100
  5. export function iterateDescendantsOf(
  6. tree: Tree,
  7. ancestors: (string | number)[],
  8. spec: {
  9. enter(node: SyntaxNodeRef): boolean | void
  10. leave?(node: SyntaxNodeRef): void
  11. from?: number | undefined
  12. to?: number | undefined
  13. mode?: IterMode | undefined
  14. }
  15. ) {
  16. const filteredEnter = (node: SyntaxNodeRef): boolean | void => {
  17. if (!ancestors.some(x => node.type.is(x))) {
  18. return false
  19. }
  20. return spec.enter(node)
  21. }
  22. tree.iterate({ ...spec, enter: filteredEnter })
  23. }
  24. export const previousSiblingIs = (
  25. state: EditorState,
  26. pos: number,
  27. expectedName: string
  28. ): boolean | null => {
  29. const tree = ensureSyntaxTree(state, pos, HUNDRED_MS)
  30. if (!tree) {
  31. return null
  32. }
  33. const thisNode = tree.resolve(pos)
  34. const previousNode = thisNode?.prevSibling
  35. return previousNode?.type.name === expectedName
  36. }
  37. export const nextSiblingIs = (
  38. state: EditorState,
  39. pos: number,
  40. expectedName: string
  41. ): boolean | null => {
  42. const tree = ensureSyntaxTree(state, pos, HUNDRED_MS)
  43. if (!tree) {
  44. return null
  45. }
  46. const thisNode = tree.resolve(pos)
  47. const previousNode = thisNode?.nextSibling
  48. return previousNode?.type.name === expectedName
  49. }
  50. export const getOptionalArgumentText = (
  51. state: EditorState,
  52. optionalArgumentNode: SyntaxNode
  53. ): string | undefined => {
  54. const shortArgNode = optionalArgumentNode.getChild('ShortOptionalArg')
  55. if (shortArgNode) {
  56. return state.doc.sliceString(shortArgNode.from, shortArgNode.to)
  57. }
  58. }
  59. export const nodeHasError = (node: SyntaxNode): boolean => {
  60. let hasError = false
  61. node.cursor().iterate(({ type }) => {
  62. if (hasError) return false
  63. if (type.isError) {
  64. hasError = true
  65. return false
  66. }
  67. return true
  68. })
  69. return hasError
  70. }
  71. export const childOfNodeWithType = (
  72. node: SyntaxNode,
  73. ...types: (string | number)[]
  74. ): SyntaxNode | null => {
  75. let childOfType: SyntaxNode | null = null
  76. node.cursor().iterate(child => {
  77. if (childOfType !== null) {
  78. return false
  79. }
  80. for (const type of types) {
  81. if (child.type.is(type)) {
  82. childOfType = child.node
  83. return false
  84. }
  85. }
  86. return true
  87. })
  88. return childOfType
  89. }