indents.scm 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. ; STATEMENT BLOCKS
  2. ; ================
  3. ; More accurate indentation matching for all blocks delimited by braces.
  4. (statement_block "}" @match
  5. (#set! indent.matchIndentOf parent.firstChild.startPosition))
  6. ; SWITCH STATEMENTS
  7. ; =================
  8. ; The closing brace of a switch statement's body should match the indentation
  9. ; of the line where the switch statement starts.
  10. (switch_statement
  11. body: (switch_body "}" @match
  12. (#is? test.last true))
  13. (#set! indent.matchIndentOf parent.startPosition))
  14. ; By default, `case` and `default` need to be indented one level more than
  15. ; their containing `switch`.
  16. ([
  17. (switch_case "case" @match)
  18. (switch_default "default" @match)
  19. ; We include this one (and check for a switch_statment ancestor) to handle
  20. ; a commonly encountered error state when the user is in the middle of typing
  21. ; a switch statement.
  22. (ERROR "case" @match)
  23. ]
  24. (#is? test.descendantOfType switch_statement)
  25. (#set! indent.matchIndentOf parent.parent.startPosition)
  26. (#set! indent.offsetIndent 1)
  27. (#is-not? test.config "language-javascript.indentation.alignCaseWithSwitch"))
  28. ; When this config setting is enabled, `case` and `default` need to be indented
  29. ; to match their containing `switch`.
  30. ([
  31. (switch_case "case" @match)
  32. (switch_default "default" @match)
  33. ; We include this one (and check for a switch_statment ancestor) to handle
  34. ; a commonly encountered error state when the user is in the middle of typing
  35. ; a switch statement.
  36. (ERROR "case" @match)
  37. ]
  38. (#is? test.descendantOfType switch_statement)
  39. (#set! indent.matchIndentOf parent.parent.startPosition)
  40. (#set! indent.offsetIndent 0)
  41. (#is? test.config "language-javascript.indentation.alignCaseWithSwitch"))
  42. ; ONE-LINE CONDITIONALS
  43. ; =====================
  44. ; An `if` statement without an opening brace should indent the next line…
  45. (if_statement
  46. condition: (parenthesized_expression ")" @indent
  47. (#is? test.lastTextOnRow true)
  48. (#is? test.config "language-javascript.indentation.indentAfterBracelessIf")))
  49. ; (as should a braceless `else`…)
  50. ("else" @indent
  51. (#is? test.lastTextOnRow true)
  52. (#is? test.config "language-javascript.indentation.indentAfterBracelessIf"))
  53. ; …and keep that indent level if the user types a comment before the
  54. ; consequence…
  55. (if_statement
  56. consequence: (empty_statement) @match
  57. (#is-not? test.startsOnSameRowAs parent.startPosition)
  58. (#set! indent.matchIndentOf parent.startPosition)
  59. (#set! indent.offsetIndent 1)
  60. (#is? test.config "language-javascript.indentation.indentAfterBracelessIf"))
  61. ; …and keep that indent level after the user starts typing…
  62. (if_statement
  63. condition: (_) @indent
  64. consequence: [
  65. (expression_statement)
  66. (return_statement)
  67. (continue_statement)
  68. (break_statement)
  69. (throw_statement)
  70. (debugger_statement)
  71. ] @match
  72. ; When an opening curly brace is unpaired, it might get interpreted as part
  73. ; of an `expression_statement`, for some reason.
  74. (#not-match? @match "^\\s*{")
  75. (#set! indent.matchIndentOf parent.startPosition)
  76. (#set! indent.offsetIndent 1)
  77. (#is? test.config "language-javascript.indentation.indentAfterBracelessIf"))
  78. ; …but dedent after exactly one statement.
  79. (if_statement
  80. condition: (_) @indent
  81. consequence: [
  82. (expression_statement)
  83. (return_statement)
  84. (continue_statement)
  85. (break_statement)
  86. (throw_statement)
  87. (debugger_statement)
  88. ] @dedent.next
  89. ; When an opening curly brace is unpaired, it might get interpreted as part
  90. ; of an `expression_statement`, for some reason.
  91. (#not-match? @dedent.next "^\\s*{")
  92. (#is? test.config "language-javascript.indentation.indentAfterBracelessIf"))
  93. (else_clause
  94. [
  95. (expression_statement)
  96. (return_statement)
  97. (continue_statement)
  98. (break_statement)
  99. (throw_statement)
  100. (debugger_statement)
  101. ] @dedent.next
  102. (#is-not? test.startsOnSameRowAs parent.startPosition)
  103. (#is? test.config "language-javascript.indentation.indentAfterBracelessIf"))
  104. ; HANGING INDENT ON SPLIT LINES
  105. ; =============================
  106. ; Any of these at the end of a line indicate the next line should be indented…
  107. (["||" "&&"] @indent
  108. (#is? test.config "language-javascript.indentation.addHangingIndentAfterLogicalOperators")
  109. (#is? test.lastTextOnRow true))
  110. ("?" @indent
  111. (#is? test.config "language-javascript.indentation.addHangingIndentAfterTernaryOperators")
  112. (#is? test.lastTextOnRow true))
  113. ; …and the line after that should be dedented…
  114. (binary_expression
  115. ["||" "&&"]
  116. right: (_) @dedent.next
  117. (#is? test.config "language-javascript.indentation.addHangingIndentAfterLogicalOperators")
  118. (#is-not? test.startsOnSameRowAs parent.startPosition)
  119. ; …unless the right side of the expression spans multiple lines.
  120. (#is? test.endsOnSameRowAs startPosition))
  121. ; …unless it's a ternary, in which case the dedent should wait until the
  122. ; alternative clause.
  123. ;
  124. ; let foo = this.longTernaryCondition() ?
  125. ; consequenceWhichIsItselfRatherLong :
  126. ; alternativeThatIsNotBrief;
  127. ;
  128. (ternary_expression
  129. alternative: (_) @dedent.next
  130. (#is? test.config "language-javascript.indentation.addHangingIndentAfterTernaryOperators")
  131. (#is-not? test.startsOnSameRowAs parent.startPosition)
  132. ; Only dedent the next line if the alternative doesn't itself span multiple
  133. ; lines.
  134. (#is? test.endsOnSameRowAs startPosition))
  135. ; GENERAL
  136. ; =======
  137. ; Weed out `}`s that should not signal dedents.
  138. (template_substitution "}" @_IGNORE_ (#set! capture.final true))
  139. ; As strange as it may seem to make all of these basic indentation hints
  140. ; configurable, some brace styles are incompatible with some of these choices;
  141. ; see https://github.com/orgs/pulsar-edit/discussions/249.
  142. ("{" @indent
  143. (#is? test.config "language-javascript.indentation.indentBraces"))
  144. ("}" @dedent
  145. (#is? test.config "language-javascript.indentation.indentBraces"))
  146. ("[" @indent
  147. (#is? test.config "language-javascript.indentation.indentBrackets"))
  148. ("]" @dedent
  149. (#is? test.config "language-javascript.indentation.indentBrackets"))
  150. ("(" @indent
  151. (#is? test.config "language-javascript.indentation.indentParentheses"))
  152. (")" @dedent
  153. (#is? test.config "language-javascript.indentation.indentParentheses"))
  154. (switch_case "case" @indent)
  155. (switch_default "default" @indent)
  156. ; JSX
  157. ; ===
  158. (jsx_opening_element [">"] @indent)
  159. ; Support things like…
  160. ; <div
  161. ; className={'foo'}
  162. ; onClick={onClick}
  163. ; >
  164. (jsx_opening_element ["<"] @indent
  165. (#is-not? test.startsOnSameRowAs parent.lastChild.startPosition))
  166. ((jsx_opening_element [">"] @dedent)
  167. (#is-not? test.startsOnSameRowAs parent.firstChild.startPosition))
  168. ; We match on the whole node here because
  169. ; (a) we need to please the dedent-while-typing heuristic, so the line text
  170. ; must match the capture node's text; and
  171. ; (b) we don't want the dedent to trigger until the very last `>` has been
  172. ; typed.
  173. (jsx_closing_element ">") @dedent
  174. ; Support things like…
  175. ; <img
  176. ; height={40}
  177. ; width={40}
  178. ; alt={'foo'}
  179. ; />
  180. (jsx_self_closing_element "<" @indent
  181. (#is-not? test.startsOnSameRowAs parent.lastChild.startPosition))
  182. ; There isn't a single node whose exact text will match the line content at any
  183. ; point, so the usual heuristic won't work. Instead we set `indent.force` and
  184. ; use `test.lastTextOnRow` to ensure that the dedent fires exactly once while
  185. ; typing.
  186. ((jsx_self_closing_element "/>" @dedent)
  187. (#is-not? test.startsOnSameRowAs parent.firstChild.startPosition)
  188. (#is? test.lastTextOnRow)
  189. (#set! indent.force true))