hivecode-mcp 0.14.0 → 0.14.2
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.
- package/HIVE_RULES.md +98 -98
- package/LICENSE +21 -21
- package/README.md +421 -421
- package/core.js +91 -91
- package/docs/ALWAYS_ON.md +310 -310
- package/docs/GITHUB_WORKFLOW.md +394 -394
- package/docs/HIVE_RULES.md +41 -41
- package/docs/MCP.md +157 -157
- package/docs/QUICKSTART.md +266 -266
- package/docs/SELF_HOSTING.md +256 -256
- package/docs/SLACK_SETUP.md +474 -450
- package/docs/TROUBLESHOOTING.md +105 -105
- package/hive-activity.js +336 -336
- package/hive-authors.js +46 -44
- package/hive-await.js +230 -230
- package/hive-branch.js +608 -607
- package/hive-busy.js +88 -88
- package/hive-coord.js +110 -110
- package/hive-gate.js +135 -135
- package/hive-github.js +2004 -1944
- package/hive-hook-install.js +179 -179
- package/hive-instructions.js +74 -74
- package/hive-land.js +695 -691
- package/hive-listen.js +81 -81
- package/hive-mailbox.js +520 -520
- package/hive-mcp-http.js +591 -580
- package/hive-mcp.js +2156 -2093
- package/hive-mux-proto.js +58 -58
- package/hive-mux.js +351 -350
- package/hive-notify.js +234 -233
- package/hive-obligations.js +247 -247
- package/hive-poll-proto.js +50 -50
- package/hive-pr-staleness.js +189 -189
- package/hive-pr.js +600 -512
- package/hive-privacy.js +327 -327
- package/hive-receipts.js +233 -233
- package/hive-relay-health.js +234 -230
- package/hive-repos.js +430 -430
- package/hive-room-key.js +210 -209
- package/hive-room-mode.js +1668 -1668
- package/hive-room-watcher.js +562 -561
- package/hive-secrets.js +89 -89
- package/hive-session.js +114 -114
- package/hive-setup.js +560 -558
- package/hive-slack-agent.js +408 -396
- package/hive-slack.js +2384 -1809
- package/hive-stdio.js +172 -172
- package/hive-stop-hook.js +293 -293
- package/hive-throttle.js +118 -118
- package/hive-time.js +71 -71
- package/hive-transport.js +578 -353
- package/hive-update.js +58 -58
- package/hive-version.js +110 -110
- package/hive-webhook.js +55 -54
- package/icr-merge.js +58 -58
- package/icr.js +851 -851
- package/lang-brace.js +305 -305
- package/lang-js.js +651 -651
- package/lang-json.js +211 -211
- package/lang-python.js +588 -588
- package/lang-ruby.js +230 -230
- package/lang-toml.js +136 -136
- package/lang-yaml.js +183 -183
- package/limits.js +413 -413
- package/package.json +192 -192
- package/reconcile.cjs +621 -614
- package/slack-app-manifest-agent.json +61 -61
- package/slack-app-manifest-inbound.json +36 -36
- package/slack-app-manifest.json +27 -27
- package/substrate.js +252 -252
- package/sync.js +5189 -4994
- package/token.js +201 -201
package/lang-brace.js
CHANGED
|
@@ -1,305 +1,305 @@
|
|
|
1
|
-
// Brace-family language provider for ICR — a dependency-free, language-agnostic
|
|
2
|
-
// structural splitter for C-style languages (TypeScript, Go, Rust, Java, C/C++,
|
|
3
|
-
// C#, Swift, Kotlin, Scala, PHP, Dart, …). It implements the ICR provider contract
|
|
4
|
-
// { id, exts, parses, units, declaredNames, splitUnit, parsesUnit } so the SAME
|
|
5
|
-
// merge engine in icr.js merges these languages by structure — different top-level
|
|
6
|
-
// declarations merge cleanly, the same declaration edited two ways is a named
|
|
7
|
-
// conflict, and edits to different members inside one class/function merge via the
|
|
8
|
-
// engine's finer-grained inner merge (splitUnit).
|
|
9
|
-
//
|
|
10
|
-
// It is a heuristic scanner, not a full parser: it tracks strings, line/block
|
|
11
|
-
// comments, and bracket depth to find top-level declarations and their byte ranges.
|
|
12
|
-
// This gives real structural merge and an approximate parses() (balanced delimiters)
|
|
13
|
-
// — honest and useful today; a tree-sitter provider can later replace it behind the
|
|
14
|
-
// same interface with zero engine changes.
|
|
15
|
-
|
|
16
|
-
const LINE = ['//']
|
|
17
|
-
const OPEN = { '{': '}', '(': ')', '[': ']' }
|
|
18
|
-
const CLOSE = { '}': '{', ')': '(', ']': '[' }
|
|
19
|
-
|
|
20
|
-
// Walk `src` once. `at(i)` returns the index just past a string or comment starting
|
|
21
|
-
// at i, or -1 if none starts there — so callers can skip over them uniformly.
|
|
22
|
-
function scannerAt(src, strings) {
|
|
23
|
-
const n = src.length
|
|
24
|
-
return function at(i) {
|
|
25
|
-
if (src[i] === '/' && src[i + 1] === '*') { let j = i + 2; while (j < n && !(src[j] === '*' && src[j + 1] === '/')) j++; return Math.min(n, j + 2) }
|
|
26
|
-
for (const lc of LINE) if (src.startsWith(lc, i)) { let j = i + lc.length; while (j < n && src[j] !== '\n') j++; return j }
|
|
27
|
-
if (strings.includes(src[i])) { const q = src[i]; let j = i + 1; while (j < n) { if (src[j] === '\\') { j += 2; continue } if (src[j] === q) { j++; break } j++ } return j }
|
|
28
|
-
return -1
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const isWS = (c) => c === ' ' || c === '\t' || c === '\r' || c === '\n'
|
|
33
|
-
|
|
34
|
-
// Tokens that CONTINUE a declaration onto the next line and can never begin a new top-level
|
|
35
|
-
// one. Used by topUnits to avoid cutting a declaration in half at a newline.
|
|
36
|
-
const CONT = /^(?:__attribute__|__asm__|__asm|__declspec|noexcept|throw\s*\(|->)/
|
|
37
|
-
|
|
38
|
-
// Extract top-level units [{start,end}] from src[from..to]. A unit is a maximal
|
|
39
|
-
// declaration/statement at bracket depth 0: either a block (signature + balanced
|
|
40
|
-
// {…}) or a statement ending at ';' or end-of-line.
|
|
41
|
-
function topUnits(src, strings, from = 0, to = src.length) {
|
|
42
|
-
const at = scannerAt(src, strings)
|
|
43
|
-
const units = []
|
|
44
|
-
let i = from
|
|
45
|
-
while (i < to) {
|
|
46
|
-
while (i < to && isWS(src[i])) i++
|
|
47
|
-
if (i >= to) break
|
|
48
|
-
|
|
49
|
-
const start = i
|
|
50
|
-
let depth = 0, sawBrace = false
|
|
51
|
-
while (i < to) {
|
|
52
|
-
const s = at(i)
|
|
53
|
-
if (s > i) { i = s; continue } // skip string / comment
|
|
54
|
-
const c = src[i]
|
|
55
|
-
if (OPEN[c]) { if (c === '{') sawBrace = true; depth++; i++; continue }
|
|
56
|
-
if (CLOSE[c]) { depth--; i++; if (depth <= 0 && sawBrace) { while (i < to && (src[i] === ' ' || src[i] === '\t')) i++; if (src[i] === ';') i++; break } continue }
|
|
57
|
-
if (depth === 0) {
|
|
58
|
-
if (c === ';') { i++; break }
|
|
59
|
-
if (c === '\n' && !sawBrace) {
|
|
60
|
-
// K&R / Allman / kernel style: the body's opening brace sits on the NEXT line, so
|
|
61
|
-
// this newline is INSIDE a declaration, not the end of one. Breaking here split
|
|
62
|
-
// every such function into two units — `stmt:<signature>` and `stmt:{` — with
|
|
63
|
-
// POSITIONAL keys, so any insertion elsewhere shifted the indices and re-paired
|
|
64
|
-
// body #N with signature #N+1. That silently produced code whose function bodies
|
|
65
|
-
// belonged to other functions, while braces stayed balanced so the parse gate
|
|
66
|
-
// passed. Measured at ~34% of real C merges before this fix.
|
|
67
|
-
// Look ahead past whitespace and comments: if a block follows, it belongs to us.
|
|
68
|
-
let k = i + 1
|
|
69
|
-
for (;;) {
|
|
70
|
-
while (k < to && isWS(src[k])) k++
|
|
71
|
-
const s2 = at(k)
|
|
72
|
-
if (s2 > k && (src[k] === '/' || src[k] === '#')) { k = s2; continue } // comment
|
|
73
|
-
break
|
|
74
|
-
}
|
|
75
|
-
if (k < to && src[k] === '{') { i++; continue } // keep scanning; the block joins this unit
|
|
76
|
-
// A declaration can also continue onto the next line WITHOUT a brace, e.g.
|
|
77
|
-
// void addReplyErrorFormat(client *c, const char *fmt, ...)
|
|
78
|
-
// __attribute__((format(printf, 2, 3)));
|
|
79
|
-
// Splitting there produced an orphan `__attribute__…;` unit whose text is IDENTICAL
|
|
80
|
-
// across many prototypes in a header, so the keys became positional (#2, #3, …) and
|
|
81
|
-
// shifted exactly like the signature/body case — orphaning attributes onto the wrong
|
|
82
|
-
// prototypes. Only tokens that can never BEGIN a new top-level declaration are listed
|
|
83
|
-
// here: `const`/`override`/`final` are deliberately excluded because in TS/Rust they
|
|
84
|
-
// legitimately start the next statement (the C++ uses of those end in `{`, which the
|
|
85
|
-
// brace lookahead above already handles).
|
|
86
|
-
if (k < to && CONT.test(src.slice(k, k + 16))) { i++; continue }
|
|
87
|
-
break
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
i++
|
|
91
|
-
}
|
|
92
|
-
units.push({ start, end: i })
|
|
93
|
-
}
|
|
94
|
-
return units
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const DECL = /\b(function|func|fn|def|fun|class|interface|struct|enum|trait|impl|type|namespace|module|package|object|record|protocol|actor|extension)\s+([A-Za-z_$][\w$]*)/
|
|
98
|
-
const VARD = /\b(const|let|var|val|static|final)\s+([A-Za-z_$][\w$]*)/
|
|
99
|
-
// Method/function signature, including TYPE-FIRST forms (`int a()`, `public static
|
|
100
|
-
// void main(String[] a)`, `std::string name()`): optional decorators + any number of
|
|
101
|
-
// modifiers + an optional return-type token, then NAME(args){. The captured name is
|
|
102
|
-
// the identifier directly before `(`.
|
|
103
|
-
// The return-type group ends with `[\s*&]+` rather than `\s+` so a pointer return type whose
|
|
104
|
-
// star is attached to the NAME still parses: `struct sk_buff *alloc_skb(int n)` must key as
|
|
105
|
-
// `alloc_skb`, not fall through to DECL and key as the return type `sk_buff`.
|
|
106
|
-
// The `Class::` group makes C++ out-of-line member definitions keyable — `int Widget::render(…)`
|
|
107
|
-
// keys as `render`. Without it every C++ method definition fell through to an unkeyed `stmt:`.
|
|
108
|
-
const METH = /^(?:@[\w.()]+\s+)*(?:(?:public|private|protected|internal|static|async|final|override|open|pub|suspend|inline|virtual|export|default|abstract|sealed|unsafe|extern|synchronized|native|constexpr)\s+)*(?:(?:function|fn|func|fun)\s+)?(?:[A-Za-z_$][\w$<>\[\].,:&*\s]*?[\s*&]+)?(?:[A-Za-z_$][\w$]*\s*::\s*)*([A-Za-z_$][\w$]*)\s*(?:<[^>]*>)?\s*\([^;{]*\)(?:\s*(?:const|noexcept|override|final|volatile|mutable|&&?|throw\s*\([^){]*\)))*\s*(?:->[^{]+|:[^={]+)?\s*\{/
|
|
109
|
-
// control-flow headers also look like `name (…) {` — never key them as methods
|
|
110
|
-
const CTRL = new Set(['if', 'for', 'while', 'switch', 'catch', 'do', 'else', 'try', 'return', 'new', 'throw', 'match', 'select', 'when', 'until', 'lock', 'using', 'foreach', 'with'])
|
|
111
|
-
|
|
112
|
-
function keyOf(text) {
|
|
113
|
-
const head = text.replace(/^\s+/, '')
|
|
114
|
-
// DECL and METH can both match one declaration, and which is RIGHT depends on whether they
|
|
115
|
-
// agree on the name:
|
|
116
|
-
// `function a() {` -> both say "a" -> keep DECL's richer `function:a`
|
|
117
|
-
// `struct sk_buff *alloc_skb(` -> DECL says "sk_buff",
|
|
118
|
-
// METH says "alloc_skb" -> DECL matched the RETURN TYPE,
|
|
119
|
-
// so METH wins and the key names the function.
|
|
120
|
-
// Preferring DECL unconditionally (the original order) meant every pointer-returning C
|
|
121
|
-
// function was keyed by its return type, feeding the rename/dangling logic wrong names.
|
|
122
|
-
const meth = head.match(METH)
|
|
123
|
-
const decl = head.match(DECL)
|
|
124
|
-
if (meth && !CTRL.has(meth[1])) {
|
|
125
|
-
if (decl && decl[2] === meth[1]) return decl[1] + ':' + decl[2]
|
|
126
|
-
return 'method:' + meth[1]
|
|
127
|
-
}
|
|
128
|
-
if (decl) return decl[1] + ':' + decl[2]
|
|
129
|
-
const m = head.match(VARD); if (m) return 'var:' + m[2]
|
|
130
|
-
return 'stmt:' + head.split('\n')[0].trim().replace(/\s+/g, ' ')
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// De-duplicate keys deterministically (repeated statements / same-named units) so the
|
|
134
|
-
// engine's keyed merge never silently drops one. Order is stable → convergent.
|
|
135
|
-
function keyedUnits(raw, src) {
|
|
136
|
-
const seen = new Map(), out = []
|
|
137
|
-
for (const u of raw) {
|
|
138
|
-
const text = src.slice(u.start, u.end)
|
|
139
|
-
let k = keyOf(text)
|
|
140
|
-
const c = (seen.get(k) || 0) + 1; seen.set(k, c)
|
|
141
|
-
if (c > 1) k += '#' + c
|
|
142
|
-
out.push({ key: k, text, start: u.start, end: u.end })
|
|
143
|
-
}
|
|
144
|
-
return out
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Generic lexer for the token-level inner merge + the intent layer. Tokens are
|
|
148
|
-
// identifiers, numbers, whole strings, and single operator chars; comments live in
|
|
149
|
-
// the gaps (same contract as the JS provider: a comment-only edit is a no-op here).
|
|
150
|
-
// Returns [{start,end,k}] — k is the raw text, so any content change is a token change.
|
|
151
|
-
function lexTokens(src, strings) {
|
|
152
|
-
const at = scannerAt(src, strings)
|
|
153
|
-
const out = []
|
|
154
|
-
const n = src.length
|
|
155
|
-
let i = 0
|
|
156
|
-
while (i < n) {
|
|
157
|
-
if (isWS(src[i])) { i++; continue }
|
|
158
|
-
const j = at(i)
|
|
159
|
-
if (j > i) {
|
|
160
|
-
if (src[i] === '/' || src[i] === '#') { i = j; continue } // comment → gap
|
|
161
|
-
out.push({ start: i, end: j, k: src.slice(i, j) }); i = j; continue // string → one token
|
|
162
|
-
}
|
|
163
|
-
const c = src[i]
|
|
164
|
-
if (/[A-Za-z_$]/.test(c)) { let e = i + 1; while (e < n && /[\w$]/.test(src[e])) e++; out.push({ start: i, end: e, k: src.slice(i, e) }); i = e; continue }
|
|
165
|
-
if (/[0-9]/.test(c)) { let e = i + 1; while (e < n && /[\w.]/.test(src[e])) e++; out.push({ start: i, end: e, k: src.slice(i, e) }); i = e; continue }
|
|
166
|
-
out.push({ start: i, end: i + 1, k: c }); i++
|
|
167
|
-
}
|
|
168
|
-
return out
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Is this token a REFERENCE-position identifier (not property access `.name`)?
|
|
172
|
-
function isRefToken(src, t) {
|
|
173
|
-
if (!/^[A-Za-z_$][\w$]*$/.test(t.k)) return false
|
|
174
|
-
let p = t.start - 1
|
|
175
|
-
while (p >= 0 && isWS(src[p])) p--
|
|
176
|
-
return !(p >= 0 && src[p] === '.')
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Approximate parse check: delimiters balanced and no unterminated string/comment,
|
|
180
|
-
// ignoring the contents of strings and comments.
|
|
181
|
-
function balanced(src, strings) {
|
|
182
|
-
const at = scannerAt(src, strings)
|
|
183
|
-
const stack = []
|
|
184
|
-
let cpp = 0 // #if/#ifdef/#ifndef nesting depth
|
|
185
|
-
let lineStart = true // only whitespace seen so far on this line
|
|
186
|
-
let i = 0
|
|
187
|
-
while (i < src.length) {
|
|
188
|
-
const s = at(i)
|
|
189
|
-
if (s > i) { if (s > src.length) return false; i = s; lineStart = false; continue }
|
|
190
|
-
const c = src[i]
|
|
191
|
-
if (OPEN[c]) stack.push(c)
|
|
192
|
-
else if (CLOSE[c]) { if (stack.pop() !== CLOSE[c]) return false }
|
|
193
|
-
// PREPROCESSOR NESTING. Brace balance alone cannot see a dropped `#endif`: the merged
|
|
194
|
-
// text stays perfectly brace-balanced while the conditional structure is broken, so the
|
|
195
|
-
// gate passed and invalid C shipped as clean. Directives are matched only at the start
|
|
196
|
-
// of a line, so `#[derive]`, `#region` and `#` inside strings/comments are unaffected
|
|
197
|
-
// (strings and comments are skipped by the scanner above).
|
|
198
|
-
else if (c === '#' && lineStart) {
|
|
199
|
-
const rest = src.slice(i + 1, i + 12).replace(/^\s+/, '')
|
|
200
|
-
if (/^(ifdef|ifndef|if)\b/.test(rest)) cpp++
|
|
201
|
-
else if (/^endif\b/.test(rest)) { cpp--; if (cpp < 0) return false }
|
|
202
|
-
}
|
|
203
|
-
if (c === '\n') lineStart = true
|
|
204
|
-
else if (c !== ' ' && c !== '\t' && c !== '\r') lineStart = false
|
|
205
|
-
i++
|
|
206
|
-
}
|
|
207
|
-
return stack.length === 0 && cpp === 0
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function makeBraceLanguage({ id, exts, strings = ['"', "'", '`'] }) {
|
|
211
|
-
const provider = {
|
|
212
|
-
id,
|
|
213
|
-
exts,
|
|
214
|
-
parses: (src) => balanced(String(src == null ? '' : src), strings),
|
|
215
|
-
units: (src) => keyedUnits(topUnits(String(src), strings), String(src)),
|
|
216
|
-
declaredNames: (src) => {
|
|
217
|
-
const names = new Set()
|
|
218
|
-
for (const u of keyedUnits(topUnits(String(src), strings), String(src))) {
|
|
219
|
-
const m = u.key.match(/^(?:[a-z]+):([A-Za-z_$][\w$]*)/)
|
|
220
|
-
if (m && !u.key.startsWith('stmt:')) names.add(m[1])
|
|
221
|
-
}
|
|
222
|
-
return names
|
|
223
|
-
},
|
|
224
|
-
parsesUnit: (src) => balanced(String(src), strings),
|
|
225
|
-
// --- intent layer (heuristic but honest): dangling refs, rename, token merge ---
|
|
226
|
-
// Every identifier used in reference position (skips `.property` access). Approximate
|
|
227
|
-
// and OVER-inclusive — which only ever makes the dangling check MORE cautious: a
|
|
228
|
-
// deleted-but-still-referenced name is surfaced as a conflict, never shipped broken.
|
|
229
|
-
usedIdentifiers: (src) => {
|
|
230
|
-
src = String(src)
|
|
231
|
-
const used = new Set()
|
|
232
|
-
for (const t of lexTokens(src, strings)) if (isRefToken(src, t)) used.add(t.k)
|
|
233
|
-
return used
|
|
234
|
-
},
|
|
235
|
-
// The declaration's text with its own name excluded, so two declarations that differ
|
|
236
|
-
// only in their name compare equal — that's how the engine spots a rename.
|
|
237
|
-
declBody: (src, name) => {
|
|
238
|
-
src = String(src)
|
|
239
|
-
for (const u of keyedUnits(topUnits(src, strings), src)) {
|
|
240
|
-
const m = u.key.match(/^([a-z]+):(.+?)(?:#\d+)?$/)
|
|
241
|
-
if (!m || m[1] === 'stmt' || m[2] !== name) continue
|
|
242
|
-
return u.text.replace(name, '').trimEnd() // trailing whitespace must not defeat the rename match
|
|
243
|
-
}
|
|
244
|
-
return null
|
|
245
|
-
},
|
|
246
|
-
// Rewrite reference-position uses of oldName (never `.oldName` property access).
|
|
247
|
-
// Back-to-front so offsets stay valid. The engine only accepts the rewrite if the
|
|
248
|
-
// result still passes parses(), so a bad rewrite can't ship.
|
|
249
|
-
renameRefs: (src, oldName, newName) => {
|
|
250
|
-
src = String(src)
|
|
251
|
-
const spots = []
|
|
252
|
-
for (const t of lexTokens(src, strings)) if (t.k === oldName && isRefToken(src, t)) spots.push(t)
|
|
253
|
-
let out = src
|
|
254
|
-
for (const t of spots.reverse()) out = out.slice(0, t.start) + newName + out.slice(t.end)
|
|
255
|
-
return out
|
|
256
|
-
},
|
|
257
|
-
// Finest granularity: token stream for the engine's token-level inner merge.
|
|
258
|
-
tokenize: (src) => lexTokens(String(src), strings),
|
|
259
|
-
// Finer granularity: descend one level into a block declaration so two agents
|
|
260
|
-
// editing DIFFERENT members of the same class/function merge instead of clashing.
|
|
261
|
-
splitUnit: (text) => {
|
|
262
|
-
const src = String(text)
|
|
263
|
-
// Only descend into NAMED containers (class/struct/impl/…), whose members are
|
|
264
|
-
// keyed by name — so editing different members merges. A function/method body is
|
|
265
|
-
// just statements (no stable keys); descending there would falsely merge two
|
|
266
|
-
// edits to the same code, so we DON'T — the whole unit becomes a clean conflict.
|
|
267
|
-
if (!/\b(class|interface|struct|enum|trait|impl|namespace|module|object|record|extension|protocol|actor)\b/.test(src.split('{')[0]))
|
|
268
|
-
return null
|
|
269
|
-
const at = scannerAt(src, strings)
|
|
270
|
-
let i = 0, open = -1
|
|
271
|
-
while (i < src.length) { const s = at(i); if (s > i) { i = s; continue } if (src[i] === '{') { open = i; break } i++ }
|
|
272
|
-
if (open < 0) return null
|
|
273
|
-
// find matching close
|
|
274
|
-
let depth = 0, j = open, close = -1
|
|
275
|
-
while (j < src.length) { const s = at(j); if (s > j) { j = s; continue } const c = src[j]; if (c === '{') depth++; else if (c === '}') { depth--; if (depth === 0) { close = j; break } } j++ }
|
|
276
|
-
if (close < 0) return null
|
|
277
|
-
const sig = src.slice(0, open)
|
|
278
|
-
const body = src.slice(open + 1, close)
|
|
279
|
-
const inner = keyedUnits(topUnits(body, strings), body)
|
|
280
|
-
// inner ranges are already in `body` coordinates (topUnits scanned body). spliceable:
|
|
281
|
-
// the engine rebuilds the body by splicing merged members back into it, so the class's
|
|
282
|
-
// original indentation and any comments between members survive (no brace-glue reflow).
|
|
283
|
-
return { sig, open: '{', close: src.slice(close), body, units: inner, join: '\n', spliceable: true }
|
|
284
|
-
},
|
|
285
|
-
}
|
|
286
|
-
return provider
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Ready-made providers covering the common C-family languages. One provider can claim
|
|
290
|
-
// many extensions; comment/string syntax defaults fit the whole family (edge cases like
|
|
291
|
-
// Rust raw strings / lifetimes are tolerated — worst case a unit is merged coarsely).
|
|
292
|
-
export const braceLanguages = [
|
|
293
|
-
makeBraceLanguage({ id: 'ts', exts: ['.ts', '.tsx', '.mts', '.cts'] }),
|
|
294
|
-
makeBraceLanguage({ id: 'jsx', exts: ['.jsx'] }),
|
|
295
|
-
makeBraceLanguage({ id: 'go', exts: ['.go'], strings: ['"', '`'] }),
|
|
296
|
-
makeBraceLanguage({ id: 'rust', exts: ['.rs'] }),
|
|
297
|
-
makeBraceLanguage({ id: 'java', exts: ['.java'] }),
|
|
298
|
-
makeBraceLanguage({ id: 'c', exts: ['.c', '.h', '.cpp', '.hpp', '.cc', '.hh', '.cxx'] }),
|
|
299
|
-
makeBraceLanguage({ id: 'csharp', exts: ['.cs'] }),
|
|
300
|
-
makeBraceLanguage({ id: 'swift', exts: ['.swift'] }),
|
|
301
|
-
makeBraceLanguage({ id: 'kotlin', exts: ['.kt', '.kts'] }),
|
|
302
|
-
makeBraceLanguage({ id: 'scala', exts: ['.scala'] }),
|
|
303
|
-
makeBraceLanguage({ id: 'php', exts: ['.php'] }),
|
|
304
|
-
makeBraceLanguage({ id: 'dart', exts: ['.dart'] }),
|
|
305
|
-
]
|
|
1
|
+
// Brace-family language provider for ICR — a dependency-free, language-agnostic
|
|
2
|
+
// structural splitter for C-style languages (TypeScript, Go, Rust, Java, C/C++,
|
|
3
|
+
// C#, Swift, Kotlin, Scala, PHP, Dart, …). It implements the ICR provider contract
|
|
4
|
+
// { id, exts, parses, units, declaredNames, splitUnit, parsesUnit } so the SAME
|
|
5
|
+
// merge engine in icr.js merges these languages by structure — different top-level
|
|
6
|
+
// declarations merge cleanly, the same declaration edited two ways is a named
|
|
7
|
+
// conflict, and edits to different members inside one class/function merge via the
|
|
8
|
+
// engine's finer-grained inner merge (splitUnit).
|
|
9
|
+
//
|
|
10
|
+
// It is a heuristic scanner, not a full parser: it tracks strings, line/block
|
|
11
|
+
// comments, and bracket depth to find top-level declarations and their byte ranges.
|
|
12
|
+
// This gives real structural merge and an approximate parses() (balanced delimiters)
|
|
13
|
+
// — honest and useful today; a tree-sitter provider can later replace it behind the
|
|
14
|
+
// same interface with zero engine changes.
|
|
15
|
+
|
|
16
|
+
const LINE = ['//']
|
|
17
|
+
const OPEN = { '{': '}', '(': ')', '[': ']' }
|
|
18
|
+
const CLOSE = { '}': '{', ')': '(', ']': '[' }
|
|
19
|
+
|
|
20
|
+
// Walk `src` once. `at(i)` returns the index just past a string or comment starting
|
|
21
|
+
// at i, or -1 if none starts there — so callers can skip over them uniformly.
|
|
22
|
+
function scannerAt(src, strings) {
|
|
23
|
+
const n = src.length
|
|
24
|
+
return function at(i) {
|
|
25
|
+
if (src[i] === '/' && src[i + 1] === '*') { let j = i + 2; while (j < n && !(src[j] === '*' && src[j + 1] === '/')) j++; return Math.min(n, j + 2) }
|
|
26
|
+
for (const lc of LINE) if (src.startsWith(lc, i)) { let j = i + lc.length; while (j < n && src[j] !== '\n') j++; return j }
|
|
27
|
+
if (strings.includes(src[i])) { const q = src[i]; let j = i + 1; while (j < n) { if (src[j] === '\\') { j += 2; continue } if (src[j] === q) { j++; break } j++ } return j }
|
|
28
|
+
return -1
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const isWS = (c) => c === ' ' || c === '\t' || c === '\r' || c === '\n'
|
|
33
|
+
|
|
34
|
+
// Tokens that CONTINUE a declaration onto the next line and can never begin a new top-level
|
|
35
|
+
// one. Used by topUnits to avoid cutting a declaration in half at a newline.
|
|
36
|
+
const CONT = /^(?:__attribute__|__asm__|__asm|__declspec|noexcept|throw\s*\(|->)/
|
|
37
|
+
|
|
38
|
+
// Extract top-level units [{start,end}] from src[from..to]. A unit is a maximal
|
|
39
|
+
// declaration/statement at bracket depth 0: either a block (signature + balanced
|
|
40
|
+
// {…}) or a statement ending at ';' or end-of-line.
|
|
41
|
+
function topUnits(src, strings, from = 0, to = src.length) {
|
|
42
|
+
const at = scannerAt(src, strings)
|
|
43
|
+
const units = []
|
|
44
|
+
let i = from
|
|
45
|
+
while (i < to) {
|
|
46
|
+
while (i < to && isWS(src[i])) i++
|
|
47
|
+
if (i >= to) break
|
|
48
|
+
|
|
49
|
+
const start = i
|
|
50
|
+
let depth = 0, sawBrace = false
|
|
51
|
+
while (i < to) {
|
|
52
|
+
const s = at(i)
|
|
53
|
+
if (s > i) { i = s; continue } // skip string / comment
|
|
54
|
+
const c = src[i]
|
|
55
|
+
if (OPEN[c]) { if (c === '{') sawBrace = true; depth++; i++; continue }
|
|
56
|
+
if (CLOSE[c]) { depth--; i++; if (depth <= 0 && sawBrace) { while (i < to && (src[i] === ' ' || src[i] === '\t')) i++; if (src[i] === ';') i++; break } continue }
|
|
57
|
+
if (depth === 0) {
|
|
58
|
+
if (c === ';') { i++; break }
|
|
59
|
+
if (c === '\n' && !sawBrace) {
|
|
60
|
+
// K&R / Allman / kernel style: the body's opening brace sits on the NEXT line, so
|
|
61
|
+
// this newline is INSIDE a declaration, not the end of one. Breaking here split
|
|
62
|
+
// every such function into two units — `stmt:<signature>` and `stmt:{` — with
|
|
63
|
+
// POSITIONAL keys, so any insertion elsewhere shifted the indices and re-paired
|
|
64
|
+
// body #N with signature #N+1. That silently produced code whose function bodies
|
|
65
|
+
// belonged to other functions, while braces stayed balanced so the parse gate
|
|
66
|
+
// passed. Measured at ~34% of real C merges before this fix.
|
|
67
|
+
// Look ahead past whitespace and comments: if a block follows, it belongs to us.
|
|
68
|
+
let k = i + 1
|
|
69
|
+
for (;;) {
|
|
70
|
+
while (k < to && isWS(src[k])) k++
|
|
71
|
+
const s2 = at(k)
|
|
72
|
+
if (s2 > k && (src[k] === '/' || src[k] === '#')) { k = s2; continue } // comment
|
|
73
|
+
break
|
|
74
|
+
}
|
|
75
|
+
if (k < to && src[k] === '{') { i++; continue } // keep scanning; the block joins this unit
|
|
76
|
+
// A declaration can also continue onto the next line WITHOUT a brace, e.g.
|
|
77
|
+
// void addReplyErrorFormat(client *c, const char *fmt, ...)
|
|
78
|
+
// __attribute__((format(printf, 2, 3)));
|
|
79
|
+
// Splitting there produced an orphan `__attribute__…;` unit whose text is IDENTICAL
|
|
80
|
+
// across many prototypes in a header, so the keys became positional (#2, #3, …) and
|
|
81
|
+
// shifted exactly like the signature/body case — orphaning attributes onto the wrong
|
|
82
|
+
// prototypes. Only tokens that can never BEGIN a new top-level declaration are listed
|
|
83
|
+
// here: `const`/`override`/`final` are deliberately excluded because in TS/Rust they
|
|
84
|
+
// legitimately start the next statement (the C++ uses of those end in `{`, which the
|
|
85
|
+
// brace lookahead above already handles).
|
|
86
|
+
if (k < to && CONT.test(src.slice(k, k + 16))) { i++; continue }
|
|
87
|
+
break
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
i++
|
|
91
|
+
}
|
|
92
|
+
units.push({ start, end: i })
|
|
93
|
+
}
|
|
94
|
+
return units
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const DECL = /\b(function|func|fn|def|fun|class|interface|struct|enum|trait|impl|type|namespace|module|package|object|record|protocol|actor|extension)\s+([A-Za-z_$][\w$]*)/
|
|
98
|
+
const VARD = /\b(const|let|var|val|static|final)\s+([A-Za-z_$][\w$]*)/
|
|
99
|
+
// Method/function signature, including TYPE-FIRST forms (`int a()`, `public static
|
|
100
|
+
// void main(String[] a)`, `std::string name()`): optional decorators + any number of
|
|
101
|
+
// modifiers + an optional return-type token, then NAME(args){. The captured name is
|
|
102
|
+
// the identifier directly before `(`.
|
|
103
|
+
// The return-type group ends with `[\s*&]+` rather than `\s+` so a pointer return type whose
|
|
104
|
+
// star is attached to the NAME still parses: `struct sk_buff *alloc_skb(int n)` must key as
|
|
105
|
+
// `alloc_skb`, not fall through to DECL and key as the return type `sk_buff`.
|
|
106
|
+
// The `Class::` group makes C++ out-of-line member definitions keyable — `int Widget::render(…)`
|
|
107
|
+
// keys as `render`. Without it every C++ method definition fell through to an unkeyed `stmt:`.
|
|
108
|
+
const METH = /^(?:@[\w.()]+\s+)*(?:(?:public|private|protected|internal|static|async|final|override|open|pub|suspend|inline|virtual|export|default|abstract|sealed|unsafe|extern|synchronized|native|constexpr)\s+)*(?:(?:function|fn|func|fun)\s+)?(?:[A-Za-z_$][\w$<>\[\].,:&*\s]*?[\s*&]+)?(?:[A-Za-z_$][\w$]*\s*::\s*)*([A-Za-z_$][\w$]*)\s*(?:<[^>]*>)?\s*\([^;{]*\)(?:\s*(?:const|noexcept|override|final|volatile|mutable|&&?|throw\s*\([^){]*\)))*\s*(?:->[^{]+|:[^={]+)?\s*\{/
|
|
109
|
+
// control-flow headers also look like `name (…) {` — never key them as methods
|
|
110
|
+
const CTRL = new Set(['if', 'for', 'while', 'switch', 'catch', 'do', 'else', 'try', 'return', 'new', 'throw', 'match', 'select', 'when', 'until', 'lock', 'using', 'foreach', 'with'])
|
|
111
|
+
|
|
112
|
+
function keyOf(text) {
|
|
113
|
+
const head = text.replace(/^\s+/, '')
|
|
114
|
+
// DECL and METH can both match one declaration, and which is RIGHT depends on whether they
|
|
115
|
+
// agree on the name:
|
|
116
|
+
// `function a() {` -> both say "a" -> keep DECL's richer `function:a`
|
|
117
|
+
// `struct sk_buff *alloc_skb(` -> DECL says "sk_buff",
|
|
118
|
+
// METH says "alloc_skb" -> DECL matched the RETURN TYPE,
|
|
119
|
+
// so METH wins and the key names the function.
|
|
120
|
+
// Preferring DECL unconditionally (the original order) meant every pointer-returning C
|
|
121
|
+
// function was keyed by its return type, feeding the rename/dangling logic wrong names.
|
|
122
|
+
const meth = head.match(METH)
|
|
123
|
+
const decl = head.match(DECL)
|
|
124
|
+
if (meth && !CTRL.has(meth[1])) {
|
|
125
|
+
if (decl && decl[2] === meth[1]) return decl[1] + ':' + decl[2]
|
|
126
|
+
return 'method:' + meth[1]
|
|
127
|
+
}
|
|
128
|
+
if (decl) return decl[1] + ':' + decl[2]
|
|
129
|
+
const m = head.match(VARD); if (m) return 'var:' + m[2]
|
|
130
|
+
return 'stmt:' + head.split('\n')[0].trim().replace(/\s+/g, ' ')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// De-duplicate keys deterministically (repeated statements / same-named units) so the
|
|
134
|
+
// engine's keyed merge never silently drops one. Order is stable → convergent.
|
|
135
|
+
function keyedUnits(raw, src) {
|
|
136
|
+
const seen = new Map(), out = []
|
|
137
|
+
for (const u of raw) {
|
|
138
|
+
const text = src.slice(u.start, u.end)
|
|
139
|
+
let k = keyOf(text)
|
|
140
|
+
const c = (seen.get(k) || 0) + 1; seen.set(k, c)
|
|
141
|
+
if (c > 1) k += '#' + c
|
|
142
|
+
out.push({ key: k, text, start: u.start, end: u.end })
|
|
143
|
+
}
|
|
144
|
+
return out
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Generic lexer for the token-level inner merge + the intent layer. Tokens are
|
|
148
|
+
// identifiers, numbers, whole strings, and single operator chars; comments live in
|
|
149
|
+
// the gaps (same contract as the JS provider: a comment-only edit is a no-op here).
|
|
150
|
+
// Returns [{start,end,k}] — k is the raw text, so any content change is a token change.
|
|
151
|
+
function lexTokens(src, strings) {
|
|
152
|
+
const at = scannerAt(src, strings)
|
|
153
|
+
const out = []
|
|
154
|
+
const n = src.length
|
|
155
|
+
let i = 0
|
|
156
|
+
while (i < n) {
|
|
157
|
+
if (isWS(src[i])) { i++; continue }
|
|
158
|
+
const j = at(i)
|
|
159
|
+
if (j > i) {
|
|
160
|
+
if (src[i] === '/' || src[i] === '#') { i = j; continue } // comment → gap
|
|
161
|
+
out.push({ start: i, end: j, k: src.slice(i, j) }); i = j; continue // string → one token
|
|
162
|
+
}
|
|
163
|
+
const c = src[i]
|
|
164
|
+
if (/[A-Za-z_$]/.test(c)) { let e = i + 1; while (e < n && /[\w$]/.test(src[e])) e++; out.push({ start: i, end: e, k: src.slice(i, e) }); i = e; continue }
|
|
165
|
+
if (/[0-9]/.test(c)) { let e = i + 1; while (e < n && /[\w.]/.test(src[e])) e++; out.push({ start: i, end: e, k: src.slice(i, e) }); i = e; continue }
|
|
166
|
+
out.push({ start: i, end: i + 1, k: c }); i++
|
|
167
|
+
}
|
|
168
|
+
return out
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Is this token a REFERENCE-position identifier (not property access `.name`)?
|
|
172
|
+
function isRefToken(src, t) {
|
|
173
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(t.k)) return false
|
|
174
|
+
let p = t.start - 1
|
|
175
|
+
while (p >= 0 && isWS(src[p])) p--
|
|
176
|
+
return !(p >= 0 && src[p] === '.')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Approximate parse check: delimiters balanced and no unterminated string/comment,
|
|
180
|
+
// ignoring the contents of strings and comments.
|
|
181
|
+
function balanced(src, strings) {
|
|
182
|
+
const at = scannerAt(src, strings)
|
|
183
|
+
const stack = []
|
|
184
|
+
let cpp = 0 // #if/#ifdef/#ifndef nesting depth
|
|
185
|
+
let lineStart = true // only whitespace seen so far on this line
|
|
186
|
+
let i = 0
|
|
187
|
+
while (i < src.length) {
|
|
188
|
+
const s = at(i)
|
|
189
|
+
if (s > i) { if (s > src.length) return false; i = s; lineStart = false; continue }
|
|
190
|
+
const c = src[i]
|
|
191
|
+
if (OPEN[c]) stack.push(c)
|
|
192
|
+
else if (CLOSE[c]) { if (stack.pop() !== CLOSE[c]) return false }
|
|
193
|
+
// PREPROCESSOR NESTING. Brace balance alone cannot see a dropped `#endif`: the merged
|
|
194
|
+
// text stays perfectly brace-balanced while the conditional structure is broken, so the
|
|
195
|
+
// gate passed and invalid C shipped as clean. Directives are matched only at the start
|
|
196
|
+
// of a line, so `#[derive]`, `#region` and `#` inside strings/comments are unaffected
|
|
197
|
+
// (strings and comments are skipped by the scanner above).
|
|
198
|
+
else if (c === '#' && lineStart) {
|
|
199
|
+
const rest = src.slice(i + 1, i + 12).replace(/^\s+/, '')
|
|
200
|
+
if (/^(ifdef|ifndef|if)\b/.test(rest)) cpp++
|
|
201
|
+
else if (/^endif\b/.test(rest)) { cpp--; if (cpp < 0) return false }
|
|
202
|
+
}
|
|
203
|
+
if (c === '\n') lineStart = true
|
|
204
|
+
else if (c !== ' ' && c !== '\t' && c !== '\r') lineStart = false
|
|
205
|
+
i++
|
|
206
|
+
}
|
|
207
|
+
return stack.length === 0 && cpp === 0
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function makeBraceLanguage({ id, exts, strings = ['"', "'", '`'] }) {
|
|
211
|
+
const provider = {
|
|
212
|
+
id,
|
|
213
|
+
exts,
|
|
214
|
+
parses: (src) => balanced(String(src == null ? '' : src), strings),
|
|
215
|
+
units: (src) => keyedUnits(topUnits(String(src), strings), String(src)),
|
|
216
|
+
declaredNames: (src) => {
|
|
217
|
+
const names = new Set()
|
|
218
|
+
for (const u of keyedUnits(topUnits(String(src), strings), String(src))) {
|
|
219
|
+
const m = u.key.match(/^(?:[a-z]+):([A-Za-z_$][\w$]*)/)
|
|
220
|
+
if (m && !u.key.startsWith('stmt:')) names.add(m[1])
|
|
221
|
+
}
|
|
222
|
+
return names
|
|
223
|
+
},
|
|
224
|
+
parsesUnit: (src) => balanced(String(src), strings),
|
|
225
|
+
// --- intent layer (heuristic but honest): dangling refs, rename, token merge ---
|
|
226
|
+
// Every identifier used in reference position (skips `.property` access). Approximate
|
|
227
|
+
// and OVER-inclusive — which only ever makes the dangling check MORE cautious: a
|
|
228
|
+
// deleted-but-still-referenced name is surfaced as a conflict, never shipped broken.
|
|
229
|
+
usedIdentifiers: (src) => {
|
|
230
|
+
src = String(src)
|
|
231
|
+
const used = new Set()
|
|
232
|
+
for (const t of lexTokens(src, strings)) if (isRefToken(src, t)) used.add(t.k)
|
|
233
|
+
return used
|
|
234
|
+
},
|
|
235
|
+
// The declaration's text with its own name excluded, so two declarations that differ
|
|
236
|
+
// only in their name compare equal — that's how the engine spots a rename.
|
|
237
|
+
declBody: (src, name) => {
|
|
238
|
+
src = String(src)
|
|
239
|
+
for (const u of keyedUnits(topUnits(src, strings), src)) {
|
|
240
|
+
const m = u.key.match(/^([a-z]+):(.+?)(?:#\d+)?$/)
|
|
241
|
+
if (!m || m[1] === 'stmt' || m[2] !== name) continue
|
|
242
|
+
return u.text.replace(name, '').trimEnd() // trailing whitespace must not defeat the rename match
|
|
243
|
+
}
|
|
244
|
+
return null
|
|
245
|
+
},
|
|
246
|
+
// Rewrite reference-position uses of oldName (never `.oldName` property access).
|
|
247
|
+
// Back-to-front so offsets stay valid. The engine only accepts the rewrite if the
|
|
248
|
+
// result still passes parses(), so a bad rewrite can't ship.
|
|
249
|
+
renameRefs: (src, oldName, newName) => {
|
|
250
|
+
src = String(src)
|
|
251
|
+
const spots = []
|
|
252
|
+
for (const t of lexTokens(src, strings)) if (t.k === oldName && isRefToken(src, t)) spots.push(t)
|
|
253
|
+
let out = src
|
|
254
|
+
for (const t of spots.reverse()) out = out.slice(0, t.start) + newName + out.slice(t.end)
|
|
255
|
+
return out
|
|
256
|
+
},
|
|
257
|
+
// Finest granularity: token stream for the engine's token-level inner merge.
|
|
258
|
+
tokenize: (src) => lexTokens(String(src), strings),
|
|
259
|
+
// Finer granularity: descend one level into a block declaration so two agents
|
|
260
|
+
// editing DIFFERENT members of the same class/function merge instead of clashing.
|
|
261
|
+
splitUnit: (text) => {
|
|
262
|
+
const src = String(text)
|
|
263
|
+
// Only descend into NAMED containers (class/struct/impl/…), whose members are
|
|
264
|
+
// keyed by name — so editing different members merges. A function/method body is
|
|
265
|
+
// just statements (no stable keys); descending there would falsely merge two
|
|
266
|
+
// edits to the same code, so we DON'T — the whole unit becomes a clean conflict.
|
|
267
|
+
if (!/\b(class|interface|struct|enum|trait|impl|namespace|module|object|record|extension|protocol|actor)\b/.test(src.split('{')[0]))
|
|
268
|
+
return null
|
|
269
|
+
const at = scannerAt(src, strings)
|
|
270
|
+
let i = 0, open = -1
|
|
271
|
+
while (i < src.length) { const s = at(i); if (s > i) { i = s; continue } if (src[i] === '{') { open = i; break } i++ }
|
|
272
|
+
if (open < 0) return null
|
|
273
|
+
// find matching close
|
|
274
|
+
let depth = 0, j = open, close = -1
|
|
275
|
+
while (j < src.length) { const s = at(j); if (s > j) { j = s; continue } const c = src[j]; if (c === '{') depth++; else if (c === '}') { depth--; if (depth === 0) { close = j; break } } j++ }
|
|
276
|
+
if (close < 0) return null
|
|
277
|
+
const sig = src.slice(0, open)
|
|
278
|
+
const body = src.slice(open + 1, close)
|
|
279
|
+
const inner = keyedUnits(topUnits(body, strings), body)
|
|
280
|
+
// inner ranges are already in `body` coordinates (topUnits scanned body). spliceable:
|
|
281
|
+
// the engine rebuilds the body by splicing merged members back into it, so the class's
|
|
282
|
+
// original indentation and any comments between members survive (no brace-glue reflow).
|
|
283
|
+
return { sig, open: '{', close: src.slice(close), body, units: inner, join: '\n', spliceable: true }
|
|
284
|
+
},
|
|
285
|
+
}
|
|
286
|
+
return provider
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Ready-made providers covering the common C-family languages. One provider can claim
|
|
290
|
+
// many extensions; comment/string syntax defaults fit the whole family (edge cases like
|
|
291
|
+
// Rust raw strings / lifetimes are tolerated — worst case a unit is merged coarsely).
|
|
292
|
+
export const braceLanguages = [
|
|
293
|
+
makeBraceLanguage({ id: 'ts', exts: ['.ts', '.tsx', '.mts', '.cts'] }),
|
|
294
|
+
makeBraceLanguage({ id: 'jsx', exts: ['.jsx'] }),
|
|
295
|
+
makeBraceLanguage({ id: 'go', exts: ['.go'], strings: ['"', '`'] }),
|
|
296
|
+
makeBraceLanguage({ id: 'rust', exts: ['.rs'] }),
|
|
297
|
+
makeBraceLanguage({ id: 'java', exts: ['.java'] }),
|
|
298
|
+
makeBraceLanguage({ id: 'c', exts: ['.c', '.h', '.cpp', '.hpp', '.cc', '.hh', '.cxx'] }),
|
|
299
|
+
makeBraceLanguage({ id: 'csharp', exts: ['.cs'] }),
|
|
300
|
+
makeBraceLanguage({ id: 'swift', exts: ['.swift'] }),
|
|
301
|
+
makeBraceLanguage({ id: 'kotlin', exts: ['.kt', '.kts'] }),
|
|
302
|
+
makeBraceLanguage({ id: 'scala', exts: ['.scala'] }),
|
|
303
|
+
makeBraceLanguage({ id: 'php', exts: ['.php'] }),
|
|
304
|
+
makeBraceLanguage({ id: 'dart', exts: ['.dart'] }),
|
|
305
|
+
]
|