redscript-mc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +31 -0
  3. package/.github/ISSUE_TEMPLATE/wrong_output.md +33 -0
  4. package/.github/PULL_REQUEST_TEMPLATE.md +34 -0
  5. package/.github/workflows/ci.yml +29 -0
  6. package/.github/workflows/publish-extension.yml +35 -0
  7. package/LICENSE +21 -0
  8. package/README.md +261 -0
  9. package/README.zh.md +261 -0
  10. package/dist/__tests__/cli.test.d.ts +1 -0
  11. package/dist/__tests__/cli.test.js +140 -0
  12. package/dist/__tests__/codegen.test.d.ts +1 -0
  13. package/dist/__tests__/codegen.test.js +121 -0
  14. package/dist/__tests__/diagnostics.test.d.ts +4 -0
  15. package/dist/__tests__/diagnostics.test.js +149 -0
  16. package/dist/__tests__/e2e.test.d.ts +6 -0
  17. package/dist/__tests__/e2e.test.js +1528 -0
  18. package/dist/__tests__/lexer.test.d.ts +1 -0
  19. package/dist/__tests__/lexer.test.js +316 -0
  20. package/dist/__tests__/lowering.test.d.ts +1 -0
  21. package/dist/__tests__/lowering.test.js +819 -0
  22. package/dist/__tests__/mc-integration.test.d.ts +12 -0
  23. package/dist/__tests__/mc-integration.test.js +395 -0
  24. package/dist/__tests__/mc-syntax.test.d.ts +1 -0
  25. package/dist/__tests__/mc-syntax.test.js +112 -0
  26. package/dist/__tests__/nbt.test.d.ts +1 -0
  27. package/dist/__tests__/nbt.test.js +82 -0
  28. package/dist/__tests__/optimizer-advanced.test.d.ts +1 -0
  29. package/dist/__tests__/optimizer-advanced.test.js +124 -0
  30. package/dist/__tests__/optimizer.test.d.ts +1 -0
  31. package/dist/__tests__/optimizer.test.js +118 -0
  32. package/dist/__tests__/parser.test.d.ts +1 -0
  33. package/dist/__tests__/parser.test.js +717 -0
  34. package/dist/__tests__/repl.test.d.ts +1 -0
  35. package/dist/__tests__/repl.test.js +27 -0
  36. package/dist/__tests__/runtime.test.d.ts +1 -0
  37. package/dist/__tests__/runtime.test.js +276 -0
  38. package/dist/__tests__/structure-optimizer.test.d.ts +1 -0
  39. package/dist/__tests__/structure-optimizer.test.js +33 -0
  40. package/dist/__tests__/typechecker.test.d.ts +1 -0
  41. package/dist/__tests__/typechecker.test.js +364 -0
  42. package/dist/ast/types.d.ts +357 -0
  43. package/dist/ast/types.js +9 -0
  44. package/dist/cli.d.ts +11 -0
  45. package/dist/cli.js +407 -0
  46. package/dist/codegen/cmdblock/index.d.ts +26 -0
  47. package/dist/codegen/cmdblock/index.js +45 -0
  48. package/dist/codegen/mcfunction/index.d.ts +34 -0
  49. package/dist/codegen/mcfunction/index.js +413 -0
  50. package/dist/codegen/structure/index.d.ts +18 -0
  51. package/dist/codegen/structure/index.js +249 -0
  52. package/dist/compile.d.ts +30 -0
  53. package/dist/compile.js +152 -0
  54. package/dist/data/arena/function/__load.mcfunction +6 -0
  55. package/dist/data/arena/function/__tick.mcfunction +2 -0
  56. package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
  57. package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
  58. package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
  59. package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
  60. package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
  61. package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
  62. package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
  63. package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
  64. package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
  65. package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
  66. package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
  67. package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
  68. package/dist/data/arena/function/arena_tick.mcfunction +11 -0
  69. package/dist/data/counter/function/__load.mcfunction +5 -0
  70. package/dist/data/counter/function/__tick.mcfunction +2 -0
  71. package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
  72. package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
  73. package/dist/data/counter/function/counter_tick.mcfunction +11 -0
  74. package/dist/data/minecraft/tags/function/load.json +5 -0
  75. package/dist/data/minecraft/tags/function/tick.json +5 -0
  76. package/dist/data/quiz/function/__load.mcfunction +16 -0
  77. package/dist/data/quiz/function/__tick.mcfunction +6 -0
  78. package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
  79. package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
  80. package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
  81. package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
  82. package/dist/data/quiz/function/answer_a.mcfunction +4 -0
  83. package/dist/data/quiz/function/answer_b.mcfunction +4 -0
  84. package/dist/data/quiz/function/answer_c.mcfunction +4 -0
  85. package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
  86. package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
  87. package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
  88. package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
  89. package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
  90. package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
  91. package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
  92. package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
  93. package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
  94. package/dist/data/quiz/function/ask_question.mcfunction +7 -0
  95. package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
  96. package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
  97. package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
  98. package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
  99. package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
  100. package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
  101. package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
  102. package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
  103. package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
  104. package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
  105. package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
  106. package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
  107. package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
  108. package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
  109. package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
  110. package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
  111. package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
  112. package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
  113. package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
  114. package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
  115. package/dist/data/shop/function/__load.mcfunction +7 -0
  116. package/dist/data/shop/function/__tick.mcfunction +3 -0
  117. package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
  118. package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
  119. package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
  120. package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
  121. package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
  122. package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
  123. package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
  124. package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
  125. package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
  126. package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
  127. package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
  128. package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
  129. package/dist/data/turret/function/__load.mcfunction +5 -0
  130. package/dist/data/turret/function/__tick.mcfunction +4 -0
  131. package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
  132. package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
  133. package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
  134. package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
  135. package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
  136. package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
  137. package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
  138. package/dist/data/turret/function/turret_tick.mcfunction +5 -0
  139. package/dist/diagnostics/index.d.ts +44 -0
  140. package/dist/diagnostics/index.js +140 -0
  141. package/dist/index.d.ts +53 -0
  142. package/dist/index.js +126 -0
  143. package/dist/ir/builder.d.ts +32 -0
  144. package/dist/ir/builder.js +99 -0
  145. package/dist/ir/types.d.ts +117 -0
  146. package/dist/ir/types.js +15 -0
  147. package/dist/lexer/index.d.ts +36 -0
  148. package/dist/lexer/index.js +458 -0
  149. package/dist/lowering/index.d.ts +106 -0
  150. package/dist/lowering/index.js +2041 -0
  151. package/dist/mc-test/client.d.ts +128 -0
  152. package/dist/mc-test/client.js +174 -0
  153. package/dist/mc-test/runner.d.ts +28 -0
  154. package/dist/mc-test/runner.js +150 -0
  155. package/dist/mc-test/setup.d.ts +11 -0
  156. package/dist/mc-test/setup.js +98 -0
  157. package/dist/mc-validator/index.d.ts +17 -0
  158. package/dist/mc-validator/index.js +322 -0
  159. package/dist/nbt/index.d.ts +86 -0
  160. package/dist/nbt/index.js +250 -0
  161. package/dist/optimizer/commands.d.ts +36 -0
  162. package/dist/optimizer/commands.js +349 -0
  163. package/dist/optimizer/passes.d.ts +34 -0
  164. package/dist/optimizer/passes.js +227 -0
  165. package/dist/optimizer/structure.d.ts +8 -0
  166. package/dist/optimizer/structure.js +344 -0
  167. package/dist/pack.mcmeta +6 -0
  168. package/dist/parser/index.d.ts +76 -0
  169. package/dist/parser/index.js +1193 -0
  170. package/dist/repl.d.ts +16 -0
  171. package/dist/repl.js +165 -0
  172. package/dist/runtime/index.d.ts +101 -0
  173. package/dist/runtime/index.js +1288 -0
  174. package/dist/typechecker/index.d.ts +42 -0
  175. package/dist/typechecker/index.js +629 -0
  176. package/docs/COMPILATION_STATS.md +142 -0
  177. package/docs/IMPLEMENTATION_GUIDE.md +512 -0
  178. package/docs/LANGUAGE_REFERENCE.md +415 -0
  179. package/docs/MC_MAPPING.md +280 -0
  180. package/docs/STRUCTURE_TARGET.md +80 -0
  181. package/docs/mc-reference/commands.md +259 -0
  182. package/editors/vscode/.vscodeignore +10 -0
  183. package/editors/vscode/LICENSE +21 -0
  184. package/editors/vscode/README.md +78 -0
  185. package/editors/vscode/build.mjs +28 -0
  186. package/editors/vscode/icon.png +0 -0
  187. package/editors/vscode/mcfunction-language-configuration.json +28 -0
  188. package/editors/vscode/out/extension.js +7236 -0
  189. package/editors/vscode/package-lock.json +566 -0
  190. package/editors/vscode/package.json +137 -0
  191. package/editors/vscode/redscript-language-configuration.json +28 -0
  192. package/editors/vscode/snippets/redscript.json +114 -0
  193. package/editors/vscode/src/codeactions.ts +89 -0
  194. package/editors/vscode/src/completion.ts +130 -0
  195. package/editors/vscode/src/extension.ts +239 -0
  196. package/editors/vscode/src/hover.ts +1120 -0
  197. package/editors/vscode/src/symbols.ts +207 -0
  198. package/editors/vscode/syntaxes/mcfunction.tmLanguage.json +740 -0
  199. package/editors/vscode/syntaxes/redscript.tmLanguage.json +357 -0
  200. package/editors/vscode/tsconfig.json +13 -0
  201. package/jest.config.js +5 -0
  202. package/package.json +38 -0
  203. package/src/__tests__/cli.test.ts +130 -0
  204. package/src/__tests__/codegen.test.ts +128 -0
  205. package/src/__tests__/diagnostics.test.ts +195 -0
  206. package/src/__tests__/e2e.test.ts +1721 -0
  207. package/src/__tests__/fixtures/mc-commands-1.21.4.json +18734 -0
  208. package/src/__tests__/formatter.test.ts +46 -0
  209. package/src/__tests__/lexer.test.ts +356 -0
  210. package/src/__tests__/lowering.test.ts +962 -0
  211. package/src/__tests__/mc-integration.test.ts +409 -0
  212. package/src/__tests__/mc-syntax.test.ts +96 -0
  213. package/src/__tests__/nbt.test.ts +58 -0
  214. package/src/__tests__/optimizer-advanced.test.ts +144 -0
  215. package/src/__tests__/optimizer.test.ts +129 -0
  216. package/src/__tests__/parser.test.ts +800 -0
  217. package/src/__tests__/repl.test.ts +33 -0
  218. package/src/__tests__/runtime.test.ts +289 -0
  219. package/src/__tests__/structure-optimizer.test.ts +38 -0
  220. package/src/__tests__/typechecker.test.ts +395 -0
  221. package/src/ast/types.ts +248 -0
  222. package/src/cli.ts +445 -0
  223. package/src/codegen/cmdblock/index.ts +63 -0
  224. package/src/codegen/mcfunction/index.ts +471 -0
  225. package/src/codegen/structure/index.ts +305 -0
  226. package/src/compile.ts +188 -0
  227. package/src/diagnostics/index.ts +186 -0
  228. package/src/examples/README.md +77 -0
  229. package/src/examples/SHOWCASE_GAME.md +43 -0
  230. package/src/examples/arena.rs +44 -0
  231. package/src/examples/counter.rs +12 -0
  232. package/src/examples/pvp_arena.rs +131 -0
  233. package/src/examples/quiz.rs +90 -0
  234. package/src/examples/rpg.rs +13 -0
  235. package/src/examples/shop.rs +30 -0
  236. package/src/examples/showcase_game.rs +552 -0
  237. package/src/examples/stdlib_demo.rs +181 -0
  238. package/src/examples/turret.rs +27 -0
  239. package/src/examples/world_manager.rs +23 -0
  240. package/src/formatter/index.ts +22 -0
  241. package/src/index.ts +161 -0
  242. package/src/ir/builder.ts +114 -0
  243. package/src/ir/types.ts +119 -0
  244. package/src/lexer/index.ts +555 -0
  245. package/src/lowering/index.ts +2406 -0
  246. package/src/mc-test/client.ts +259 -0
  247. package/src/mc-test/runner.ts +140 -0
  248. package/src/mc-test/setup.ts +70 -0
  249. package/src/mc-validator/index.ts +367 -0
  250. package/src/nbt/index.ts +321 -0
  251. package/src/optimizer/commands.ts +416 -0
  252. package/src/optimizer/passes.ts +233 -0
  253. package/src/optimizer/structure.ts +441 -0
  254. package/src/parser/index.ts +1437 -0
  255. package/src/repl.ts +165 -0
  256. package/src/runtime/index.ts +1403 -0
  257. package/src/stdlib/README.md +156 -0
  258. package/src/stdlib/combat.rs +20 -0
  259. package/src/stdlib/cooldown.rs +45 -0
  260. package/src/stdlib/math.rs +49 -0
  261. package/src/stdlib/mobs.rs +99 -0
  262. package/src/stdlib/player.rs +29 -0
  263. package/src/stdlib/strings.rs +7 -0
  264. package/src/stdlib/timer.rs +51 -0
  265. package/src/templates/README.md +126 -0
  266. package/src/templates/combat.rs +96 -0
  267. package/src/templates/economy.rs +40 -0
  268. package/src/templates/mini-game-framework.rs +117 -0
  269. package/src/templates/quest.rs +78 -0
  270. package/src/test_programs/zombie_game.rs +25 -0
  271. package/src/typechecker/index.ts +737 -0
  272. package/tsconfig.json +16 -0
@@ -0,0 +1,207 @@
1
+ import * as vscode from 'vscode'
2
+
3
+ const DECL_RE = /\b(fn|let|const|struct|enum)\s+(\w+)/g
4
+
5
+ interface DeclInfo {
6
+ kind: string
7
+ name: string
8
+ range: vscode.Range
9
+ }
10
+
11
+ interface StructFieldInfo {
12
+ structName: string
13
+ fieldName: string
14
+ fieldRange: vscode.Range
15
+ }
16
+
17
+ function findDeclarations(doc: vscode.TextDocument): DeclInfo[] {
18
+ const text = doc.getText()
19
+ const decls: DeclInfo[] = []
20
+ let match: RegExpExecArray | null
21
+
22
+ DECL_RE.lastIndex = 0
23
+ while ((match = DECL_RE.exec(text)) !== null) {
24
+ const nameStart = match.index + match[0].length - match[2].length
25
+ const pos = doc.positionAt(nameStart)
26
+ const range = new vscode.Range(pos, doc.positionAt(nameStart + match[2].length))
27
+ decls.push({ kind: match[1], name: match[2], range })
28
+ }
29
+
30
+ return decls
31
+ }
32
+
33
+ /** Find all struct definitions and their field positions. */
34
+ function findStructFields(doc: vscode.TextDocument): StructFieldInfo[] {
35
+ const text = doc.getText()
36
+ const structRe = /\bstruct\s+(\w+)\s*\{([^}]*)\}/gs
37
+ const fields: StructFieldInfo[] = []
38
+ let sm: RegExpExecArray | null
39
+
40
+ while ((sm = structRe.exec(text)) !== null) {
41
+ const structName = sm[1]
42
+ const bodyStart = sm.index + sm[0].indexOf('{') + 1
43
+ const body = sm[2]
44
+ const fieldRe = /\b(\w+)\s*:/g
45
+ let fm: RegExpExecArray | null
46
+ while ((fm = fieldRe.exec(body)) !== null) {
47
+ const fieldStart = bodyStart + fm.index
48
+ const pos = doc.positionAt(fieldStart)
49
+ const range = new vscode.Range(pos, doc.positionAt(fieldStart + fm[1].length))
50
+ fields.push({ structName, fieldName: fm[1], fieldRange: range })
51
+ }
52
+ }
53
+
54
+ return fields
55
+ }
56
+
57
+ /** Check if cursor is on a struct literal field key (left side of : in { key: value }). */
58
+ function isStructLiteralField(doc: vscode.TextDocument, position: vscode.Position, word: string): string | null {
59
+ const line = doc.lineAt(position.line).text
60
+ const wordEnd = position.character + word.length
61
+
62
+ // Check if word is followed by ':' (struct literal field)
63
+ const afterWord = line.slice(wordEnd).trimStart()
64
+ if (!afterWord.startsWith(':')) return null
65
+
66
+ // Find the struct type from context: let x: StructType = { ... }
67
+ // Search backwards for "let <name>: <Type> = {"
68
+ const textBefore = doc.getText(new vscode.Range(new vscode.Position(0, 0), position))
69
+ // Match: let varname: TypeName = { ... (cursor is somewhere in the braces)
70
+ const letMatch = textBefore.match(/let\s+\w+\s*:\s*(\w+)\s*=\s*\{[^}]*$/)
71
+ if (letMatch) return letMatch[1]
72
+
73
+ // Also check for: return { ... } after -> TypeName
74
+ const fnMatch = textBefore.match(/->\s*(\w+)\s*\{[^}]*return\s*\{[^}]*$/)
75
+ if (fnMatch) return fnMatch[1]
76
+
77
+ return null
78
+ }
79
+
80
+ function findAllOccurrences(doc: vscode.TextDocument, word: string): vscode.Location[] {
81
+ const text = doc.getText()
82
+ const re = new RegExp(`\\b${escapeRegex(word)}\\b`, 'g')
83
+ const locations: vscode.Location[] = []
84
+ let match: RegExpExecArray | null
85
+
86
+ while ((match = re.exec(text)) !== null) {
87
+ const pos = doc.positionAt(match.index)
88
+ const range = new vscode.Range(pos, doc.positionAt(match.index + word.length))
89
+ locations.push(new vscode.Location(doc.uri, range))
90
+ }
91
+
92
+ return locations
93
+ }
94
+
95
+ function escapeRegex(s: string): string {
96
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
97
+ }
98
+
99
+ export function registerSymbolProviders(context: vscode.ExtensionContext): void {
100
+ const selector: vscode.DocumentSelector = { language: 'redscript', scheme: 'file' }
101
+
102
+ context.subscriptions.push(
103
+ vscode.languages.registerDefinitionProvider(selector, {
104
+ provideDefinition(doc, position) {
105
+ // If cursor is preceded by '#', it's a #mc_name — no definition to jump to
106
+ if (isMcName(doc, position)) return null
107
+
108
+ const wordRange = doc.getWordRangeAtPosition(position)
109
+ if (!wordRange) return null
110
+ const word = doc.getText(wordRange)
111
+
112
+ // Check if this is a struct literal field key: { fieldName: value }
113
+ const structType = isStructLiteralField(doc, position, word)
114
+ if (structType) {
115
+ const structFields = findStructFields(doc)
116
+ const field = structFields.find(f => f.structName === structType && f.fieldName === word)
117
+ if (field) {
118
+ return new vscode.Location(doc.uri, field.fieldRange)
119
+ }
120
+ }
121
+
122
+ const decls = findDeclarations(doc)
123
+ const decl = decls.find(d => d.name === word)
124
+ if (!decl) return null
125
+
126
+ return new vscode.Location(doc.uri, decl.range)
127
+ }
128
+ })
129
+ )
130
+
131
+ context.subscriptions.push(
132
+ vscode.languages.registerReferenceProvider(selector, {
133
+ provideReferences(doc, position) {
134
+ // For #mc_name, find all occurrences of the full #name token
135
+ if (isMcName(doc, position)) {
136
+ const mcRange = doc.getWordRangeAtPosition(position, /#[a-zA-Z_][a-zA-Z0-9_]*/)
137
+ if (!mcRange) return null
138
+ const mcWord = doc.getText(mcRange)
139
+ return findAllOccurrences(doc, mcWord)
140
+ }
141
+
142
+ const wordRange = doc.getWordRangeAtPosition(position)
143
+ if (!wordRange) return null
144
+ const word = doc.getText(wordRange)
145
+
146
+ // Exclude bare names that appear as #name elsewhere (they're different things)
147
+ return findAllOccurrences(doc, word).filter(loc => {
148
+ // Don't include occurrences that are actually #word
149
+ const charBefore = loc.range.start.character > 0
150
+ ? doc.getText(new vscode.Range(
151
+ loc.range.start.translate(0, -1),
152
+ loc.range.start
153
+ ))
154
+ : ''
155
+ return charBefore !== '#'
156
+ })
157
+ }
158
+ })
159
+ )
160
+
161
+ context.subscriptions.push(
162
+ vscode.languages.registerRenameProvider(selector, {
163
+ provideRenameEdits(doc, position, newName) {
164
+ const wordRange = doc.getWordRangeAtPosition(position)
165
+ if (!wordRange) return null
166
+ const oldName = doc.getText(wordRange)
167
+
168
+ if (isMcName(doc, position)) return null
169
+
170
+ const edits = new vscode.WorkspaceEdit()
171
+ const text = doc.getText()
172
+ const re = new RegExp(`\\b${escapeRegex(oldName)}\\b`, 'g')
173
+ let match: RegExpExecArray | null
174
+ while ((match = re.exec(text)) !== null) {
175
+ if (match.index > 0 && text[match.index - 1] === '#') continue
176
+ const start = doc.positionAt(match.index)
177
+ const end = doc.positionAt(match.index + oldName.length)
178
+ edits.replace(doc.uri, new vscode.Range(start, end), newName)
179
+ }
180
+ return edits
181
+ },
182
+
183
+ prepareRename(doc, position) {
184
+ const wordRange = doc.getWordRangeAtPosition(position)
185
+ if (!wordRange) throw new Error('Cannot rename this element')
186
+ if (isMcName(doc, position)) {
187
+ throw new Error('Cannot rename MC identifiers (#name)')
188
+ }
189
+ return wordRange
190
+ }
191
+ })
192
+ )
193
+ }
194
+
195
+ /** Returns true if the cursor is on the identifier part of a #mc_name token. */
196
+ function isMcName(doc: vscode.TextDocument, position: vscode.Position): boolean {
197
+ if (position.character === 0) return false
198
+ const charBefore = doc.getText(new vscode.Range(
199
+ position.translate(0, -1),
200
+ position
201
+ ))
202
+ if (charBefore === '#') return true
203
+ // Also check if we're in the middle of an #ident — look for # to the left
204
+ const linePrefix = doc.lineAt(position.line).text.slice(0, position.character)
205
+ const match = linePrefix.match(/#[a-zA-Z_][a-zA-Z0-9_]*$/)
206
+ return match !== null
207
+ }