mcmodding-mcp 0.3.0 → 0.4.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.
- package/README.md +173 -15
- package/dist/cli/manage.d.ts.map +1 -1
- package/dist/cli/manage.js +17 -0
- package/dist/cli/manage.js.map +1 -1
- package/dist/db-versioning.js +9 -9
- package/dist/db-versioning.js.map +1 -1
- package/dist/index.js +49 -1
- package/dist/index.js.map +1 -1
- package/dist/services/mappings-service.d.ts +96 -0
- package/dist/services/mappings-service.d.ts.map +1 -0
- package/dist/services/mappings-service.js +788 -0
- package/dist/services/mappings-service.js.map +1 -0
- package/dist/tools/mappings.d.ts +234 -0
- package/dist/tools/mappings.d.ts.map +1 -0
- package/dist/tools/mappings.js +542 -0
- package/dist/tools/mappings.js.map +1 -0
- package/package.json +10 -2
- package/scripts/postinstall.js +14 -2
|
@@ -0,0 +1,788 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
const ABBREVIATION_MAP = {
|
|
5
|
+
msg: ['message'],
|
|
6
|
+
btn: ['button'],
|
|
7
|
+
inv: ['inventory'],
|
|
8
|
+
pos: ['position'],
|
|
9
|
+
vel: ['velocity'],
|
|
10
|
+
dir: ['direction'],
|
|
11
|
+
cfg: ['config', 'configuration'],
|
|
12
|
+
pkt: ['packet'],
|
|
13
|
+
tex: ['texture'],
|
|
14
|
+
gui: ['gui', 'screen', 'interface'],
|
|
15
|
+
nbt: ['nbt', 'tag', 'compound'],
|
|
16
|
+
mc: ['minecraft'],
|
|
17
|
+
bb: ['bounding', 'boundingbox'],
|
|
18
|
+
aabb: ['aabb', 'boundingbox'],
|
|
19
|
+
ai: ['ai', 'artificial'],
|
|
20
|
+
dmg: ['damage'],
|
|
21
|
+
hp: ['health'],
|
|
22
|
+
xp: ['experience'],
|
|
23
|
+
lvl: ['level'],
|
|
24
|
+
idx: ['index'],
|
|
25
|
+
len: ['length'],
|
|
26
|
+
cnt: ['count'],
|
|
27
|
+
num: ['number'],
|
|
28
|
+
str: ['string'],
|
|
29
|
+
obj: ['object'],
|
|
30
|
+
arr: ['array'],
|
|
31
|
+
vec: ['vector'],
|
|
32
|
+
mat: ['matrix', 'material'],
|
|
33
|
+
col: ['color', 'column', 'collision'],
|
|
34
|
+
rot: ['rotation'],
|
|
35
|
+
trans: ['translation', 'transform'],
|
|
36
|
+
ent: ['entity'],
|
|
37
|
+
blk: ['block'],
|
|
38
|
+
itm: ['item'],
|
|
39
|
+
ply: ['player'],
|
|
40
|
+
srv: ['server'],
|
|
41
|
+
cli: ['client'],
|
|
42
|
+
net: ['network'],
|
|
43
|
+
reg: ['register', 'registry'],
|
|
44
|
+
evt: ['event'],
|
|
45
|
+
cb: ['callback'],
|
|
46
|
+
fn: ['function'],
|
|
47
|
+
ctx: ['context'],
|
|
48
|
+
req: ['request'],
|
|
49
|
+
res: ['response', 'result'],
|
|
50
|
+
err: ['error'],
|
|
51
|
+
def: ['default', 'definition'],
|
|
52
|
+
init: ['initialize', 'initial'],
|
|
53
|
+
desc: ['descriptor', 'description'],
|
|
54
|
+
info: ['information'],
|
|
55
|
+
src: ['source'],
|
|
56
|
+
dst: ['destination'],
|
|
57
|
+
tmp: ['temporary'],
|
|
58
|
+
max: ['maximum'],
|
|
59
|
+
min: ['minimum'],
|
|
60
|
+
avg: ['average'],
|
|
61
|
+
rnd: ['random', 'render'],
|
|
62
|
+
gen: ['generate', 'generator'],
|
|
63
|
+
sync: ['synchronized', 'synchronize'],
|
|
64
|
+
async: ['asynchronous'],
|
|
65
|
+
};
|
|
66
|
+
const INTERNAL_PREFIXES = ['_', 'lambda$', 'access$', '$'];
|
|
67
|
+
function isInternalName(name) {
|
|
68
|
+
if (!name)
|
|
69
|
+
return false;
|
|
70
|
+
for (const prefix of INTERNAL_PREFIXES) {
|
|
71
|
+
if (name.startsWith(prefix))
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (name.includes('$') && !name.endsWith('$'))
|
|
75
|
+
return true;
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
function expandAbbreviations(tokens) {
|
|
79
|
+
const expanded = [];
|
|
80
|
+
for (const token of tokens) {
|
|
81
|
+
expanded.push(token);
|
|
82
|
+
const expansions = ABBREVIATION_MAP[token.toLowerCase()];
|
|
83
|
+
if (expansions) {
|
|
84
|
+
for (const exp of expansions) {
|
|
85
|
+
if (!expanded.includes(exp)) {
|
|
86
|
+
expanded.push(exp);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return expanded;
|
|
92
|
+
}
|
|
93
|
+
function tokenizeIdentifier(identifier) {
|
|
94
|
+
if (!identifier)
|
|
95
|
+
return [];
|
|
96
|
+
let normalized = identifier.replace(/[_-]/g, ' ');
|
|
97
|
+
normalized = normalized.replace(/([a-z])([A-Z])/g, '$1 $2');
|
|
98
|
+
normalized = normalized.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2');
|
|
99
|
+
return normalized
|
|
100
|
+
.toLowerCase()
|
|
101
|
+
.split(/\s+/)
|
|
102
|
+
.filter((t) => t.length > 0);
|
|
103
|
+
}
|
|
104
|
+
function levenshteinDistance(a, b) {
|
|
105
|
+
if (a.length === 0)
|
|
106
|
+
return b.length;
|
|
107
|
+
if (b.length === 0)
|
|
108
|
+
return a.length;
|
|
109
|
+
const matrix = [];
|
|
110
|
+
for (let i = 0; i <= b.length; i++) {
|
|
111
|
+
matrix[i] = new Array(a.length + 1).fill(0);
|
|
112
|
+
}
|
|
113
|
+
for (let i = 0; i <= b.length; i++) {
|
|
114
|
+
matrix[i][0] = i;
|
|
115
|
+
}
|
|
116
|
+
for (let j = 0; j <= a.length; j++) {
|
|
117
|
+
matrix[0][j] = j;
|
|
118
|
+
}
|
|
119
|
+
for (let i = 1; i <= b.length; i++) {
|
|
120
|
+
for (let j = 1; j <= a.length; j++) {
|
|
121
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
122
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return matrix[b.length][a.length];
|
|
130
|
+
}
|
|
131
|
+
function stringSimilarity(a, b) {
|
|
132
|
+
if (a === b)
|
|
133
|
+
return 1.0;
|
|
134
|
+
if (a.length === 0 || b.length === 0)
|
|
135
|
+
return 0.0;
|
|
136
|
+
const distance = levenshteinDistance(a.toLowerCase(), b.toLowerCase());
|
|
137
|
+
const maxLength = Math.max(a.length, b.length);
|
|
138
|
+
return 1 - distance / maxLength;
|
|
139
|
+
}
|
|
140
|
+
function containsCI(str, substr) {
|
|
141
|
+
return str.toLowerCase().includes(substr.toLowerCase());
|
|
142
|
+
}
|
|
143
|
+
function tokenizeQuery(query) {
|
|
144
|
+
const parts = query.toLowerCase().split(/[\s_-]+/);
|
|
145
|
+
const tokens = [];
|
|
146
|
+
for (const part of parts) {
|
|
147
|
+
tokens.push(...tokenizeIdentifier(part));
|
|
148
|
+
}
|
|
149
|
+
const uniqueTokens = [...new Set(tokens)].filter((t) => t.length >= 2);
|
|
150
|
+
return expandAbbreviations(uniqueTokens);
|
|
151
|
+
}
|
|
152
|
+
function calculateSemanticScore(identifier, queryTokens, originalQuery) {
|
|
153
|
+
if (!identifier || queryTokens.length === 0)
|
|
154
|
+
return 0;
|
|
155
|
+
const identifierLower = identifier.toLowerCase();
|
|
156
|
+
const originalLower = originalQuery.toLowerCase().replace(/[\s_-]+/g, '');
|
|
157
|
+
const internalPenalty = isInternalName(identifier) ? 25 : 0;
|
|
158
|
+
if (identifierLower === originalLower) {
|
|
159
|
+
return 100 - internalPenalty;
|
|
160
|
+
}
|
|
161
|
+
if (identifierLower.startsWith(originalLower)) {
|
|
162
|
+
const lengthBonus = Math.max(0, 10 - (identifier.length - originalQuery.length));
|
|
163
|
+
return Math.min(95, 85 + lengthBonus) - internalPenalty;
|
|
164
|
+
}
|
|
165
|
+
const wholeStringSimilarity = stringSimilarity(originalLower, identifierLower);
|
|
166
|
+
if (wholeStringSimilarity >= 0.75) {
|
|
167
|
+
const fuzzyScore = 50 + wholeStringSimilarity * 40;
|
|
168
|
+
return Math.min(Math.round(fuzzyScore), 90) - internalPenalty;
|
|
169
|
+
}
|
|
170
|
+
const identifierTokens = tokenizeIdentifier(identifier);
|
|
171
|
+
const baseQueryTokens = originalQuery
|
|
172
|
+
.toLowerCase()
|
|
173
|
+
.split(/[\s_-]+/)
|
|
174
|
+
.flatMap((p) => tokenizeIdentifier(p))
|
|
175
|
+
.filter((t) => t.length >= 2);
|
|
176
|
+
const baseTokenSet = new Set(baseQueryTokens);
|
|
177
|
+
let exactMatches = 0;
|
|
178
|
+
let prefixMatches = 0;
|
|
179
|
+
let containsMatches = 0;
|
|
180
|
+
let fuzzyMatches = 0;
|
|
181
|
+
let totalMatchScore = 0;
|
|
182
|
+
for (const queryToken of queryTokens) {
|
|
183
|
+
let bestMatch = 0;
|
|
184
|
+
const isBaseToken = baseTokenSet.has(queryToken);
|
|
185
|
+
const tokenWeight = isBaseToken ? 1.0 : 0.6;
|
|
186
|
+
for (const idToken of identifierTokens) {
|
|
187
|
+
if (idToken === queryToken) {
|
|
188
|
+
bestMatch = Math.max(bestMatch, 1.0);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
if (idToken.startsWith(queryToken)) {
|
|
192
|
+
const prefixScore = 0.9 - (idToken.length - queryToken.length) * 0.02;
|
|
193
|
+
bestMatch = Math.max(bestMatch, Math.max(0.75, prefixScore));
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (queryToken.startsWith(idToken) && idToken.length >= 3) {
|
|
197
|
+
const prefixScore = 0.8 - (queryToken.length - idToken.length) * 0.03;
|
|
198
|
+
bestMatch = Math.max(bestMatch, Math.max(0.65, prefixScore));
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (queryToken.length >= 3 && idToken.includes(queryToken)) {
|
|
202
|
+
bestMatch = Math.max(bestMatch, 0.7);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (idToken.length >= 3 && queryToken.includes(idToken)) {
|
|
206
|
+
bestMatch = Math.max(bestMatch, 0.6);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const minLen = Math.min(idToken.length, queryToken.length);
|
|
210
|
+
const fuzzyThreshold = minLen >= 6 ? 0.65 : minLen >= 4 ? 0.7 : 0.75;
|
|
211
|
+
const similarity = stringSimilarity(idToken, queryToken);
|
|
212
|
+
if (similarity >= fuzzyThreshold) {
|
|
213
|
+
const fuzzyScore = similarity * 0.7;
|
|
214
|
+
bestMatch = Math.max(bestMatch, fuzzyScore);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (bestMatch >= 0.95) {
|
|
218
|
+
exactMatches++;
|
|
219
|
+
}
|
|
220
|
+
else if (bestMatch >= 0.75) {
|
|
221
|
+
prefixMatches++;
|
|
222
|
+
}
|
|
223
|
+
else if (bestMatch >= 0.55) {
|
|
224
|
+
containsMatches++;
|
|
225
|
+
}
|
|
226
|
+
else if (bestMatch > 0.3) {
|
|
227
|
+
fuzzyMatches++;
|
|
228
|
+
}
|
|
229
|
+
totalMatchScore += bestMatch * tokenWeight;
|
|
230
|
+
}
|
|
231
|
+
let score = 0;
|
|
232
|
+
const totalBaseTokens = baseQueryTokens.length || 1;
|
|
233
|
+
const matchedBase = exactMatches + prefixMatches >= totalBaseTokens * 0.8 ? totalBaseTokens : exactMatches;
|
|
234
|
+
if (matchedBase === totalBaseTokens) {
|
|
235
|
+
score = 75 + (totalMatchScore / queryTokens.length) * 15;
|
|
236
|
+
const joinedQuery = baseQueryTokens.join('');
|
|
237
|
+
if (identifierLower.includes(joinedQuery)) {
|
|
238
|
+
score += 5;
|
|
239
|
+
}
|
|
240
|
+
if (identifierTokens[0] === baseQueryTokens[0]) {
|
|
241
|
+
score += 10;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else if (exactMatches + prefixMatches >= totalBaseTokens * 0.7) {
|
|
245
|
+
score = 55 + (totalMatchScore / queryTokens.length) * 20;
|
|
246
|
+
}
|
|
247
|
+
else if (exactMatches + prefixMatches + containsMatches >= totalBaseTokens * 0.5) {
|
|
248
|
+
score = 40 + (totalMatchScore / queryTokens.length) * 20;
|
|
249
|
+
}
|
|
250
|
+
else if (fuzzyMatches > 0 || totalMatchScore > 0.3) {
|
|
251
|
+
score = 20 + (totalMatchScore / queryTokens.length) * 30;
|
|
252
|
+
}
|
|
253
|
+
if (identifier.length < 15) {
|
|
254
|
+
score += 5;
|
|
255
|
+
}
|
|
256
|
+
else if (identifier.length > 40) {
|
|
257
|
+
score -= 5;
|
|
258
|
+
}
|
|
259
|
+
if (containsCI(identifier, originalLower) && originalLower.length >= 3) {
|
|
260
|
+
const containsScore = 55 + (originalLower.length / identifier.length) * 20;
|
|
261
|
+
score = Math.max(score, containsScore);
|
|
262
|
+
}
|
|
263
|
+
score -= internalPenalty;
|
|
264
|
+
return Math.min(Math.max(Math.round(score), 0), 100);
|
|
265
|
+
}
|
|
266
|
+
export class MappingsService {
|
|
267
|
+
db;
|
|
268
|
+
static dbPath = path.join(process.cwd(), 'data', 'parchment-mappings.db');
|
|
269
|
+
constructor(dbPath) {
|
|
270
|
+
const finalPath = dbPath || MappingsService.dbPath;
|
|
271
|
+
console.error(`[MappingsService] Using database at: ${finalPath}`);
|
|
272
|
+
this.db = new Database(finalPath, { readonly: true });
|
|
273
|
+
}
|
|
274
|
+
static isAvailable() {
|
|
275
|
+
return fs.existsSync(MappingsService.dbPath);
|
|
276
|
+
}
|
|
277
|
+
static getDbPath() {
|
|
278
|
+
return MappingsService.dbPath;
|
|
279
|
+
}
|
|
280
|
+
getStats() {
|
|
281
|
+
const counts = this.db
|
|
282
|
+
.prepare(`
|
|
283
|
+
SELECT
|
|
284
|
+
(SELECT COUNT(*) FROM classes) as totalClasses,
|
|
285
|
+
(SELECT COUNT(*) FROM methods) as totalMethods,
|
|
286
|
+
(SELECT COUNT(*) FROM fields) as totalFields,
|
|
287
|
+
(SELECT COUNT(*) FROM parameters) as totalParameters,
|
|
288
|
+
(SELECT COUNT(*) FROM methods WHERE javadoc IS NOT NULL AND javadoc != '') as documentedMethods,
|
|
289
|
+
(SELECT COUNT(*) FROM fields WHERE javadoc IS NOT NULL AND javadoc != '') as documentedFields
|
|
290
|
+
`)
|
|
291
|
+
.get();
|
|
292
|
+
const versions = this.db
|
|
293
|
+
.prepare(`SELECT DISTINCT minecraft_version FROM classes ORDER BY minecraft_version DESC`)
|
|
294
|
+
.all();
|
|
295
|
+
const topPackages = this.db
|
|
296
|
+
.prepare(`
|
|
297
|
+
SELECT package_name as packageName, COUNT(*) as count
|
|
298
|
+
FROM classes
|
|
299
|
+
GROUP BY package_name
|
|
300
|
+
ORDER BY count DESC
|
|
301
|
+
LIMIT 10
|
|
302
|
+
`)
|
|
303
|
+
.all();
|
|
304
|
+
return {
|
|
305
|
+
...counts,
|
|
306
|
+
minecraftVersions: versions.map((v) => v.minecraft_version),
|
|
307
|
+
topPackages,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
getMinecraftVersions() {
|
|
311
|
+
const versions = this.db
|
|
312
|
+
.prepare(`SELECT DISTINCT minecraft_version FROM classes ORDER BY minecraft_version DESC`)
|
|
313
|
+
.all();
|
|
314
|
+
return versions.map((v) => v.minecraft_version);
|
|
315
|
+
}
|
|
316
|
+
getLatestVersion() {
|
|
317
|
+
const result = this.db
|
|
318
|
+
.prepare(`
|
|
319
|
+
SELECT minecraft_version FROM classes
|
|
320
|
+
ORDER BY minecraft_version DESC
|
|
321
|
+
LIMIT 1
|
|
322
|
+
`)
|
|
323
|
+
.get();
|
|
324
|
+
return result?.minecraft_version || null;
|
|
325
|
+
}
|
|
326
|
+
search(options) {
|
|
327
|
+
const { query, type = 'all', minecraftVersion, packageFilter, includeJavadoc = true, limit = 20, } = options;
|
|
328
|
+
const results = [];
|
|
329
|
+
const queryTokens = tokenizeQuery(query);
|
|
330
|
+
console.error(`[MappingsService] Search query: "${query}" → tokens: [${queryTokens.join(', ')}]`);
|
|
331
|
+
const version = minecraftVersion || this.getLatestVersion();
|
|
332
|
+
if (!version) {
|
|
333
|
+
return [];
|
|
334
|
+
}
|
|
335
|
+
const sqlPatterns = this.generateSqlPatterns(query, queryTokens);
|
|
336
|
+
if (type === 'all' || type === 'class') {
|
|
337
|
+
const classResults = this.searchClassesSemantic(sqlPatterns, queryTokens, query, version, packageFilter, limit * 3);
|
|
338
|
+
for (const cls of classResults) {
|
|
339
|
+
results.push({
|
|
340
|
+
type: 'class',
|
|
341
|
+
name: cls.name,
|
|
342
|
+
fullName: `${cls.packageName}.${cls.name}`,
|
|
343
|
+
obfuscatedName: cls.obfuscatedName,
|
|
344
|
+
descriptor: null,
|
|
345
|
+
javadoc: includeJavadoc ? cls.javadoc : null,
|
|
346
|
+
className: null,
|
|
347
|
+
packageName: cls.packageName,
|
|
348
|
+
minecraftVersion: cls.minecraftVersion,
|
|
349
|
+
score: cls.score,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (type === 'all' || type === 'method') {
|
|
354
|
+
const methodResults = this.searchMethodsSemantic(sqlPatterns, queryTokens, query, version, packageFilter, limit * 3);
|
|
355
|
+
for (const method of methodResults) {
|
|
356
|
+
results.push({
|
|
357
|
+
type: 'method',
|
|
358
|
+
name: method.name,
|
|
359
|
+
fullName: `${method.className}.${method.name}`,
|
|
360
|
+
obfuscatedName: method.obfuscatedName,
|
|
361
|
+
descriptor: method.descriptor,
|
|
362
|
+
javadoc: includeJavadoc ? method.javadoc : null,
|
|
363
|
+
className: method.className,
|
|
364
|
+
packageName: method.packageName,
|
|
365
|
+
minecraftVersion: method.minecraftVersion,
|
|
366
|
+
parameters: method.parameters,
|
|
367
|
+
score: method.score,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (type === 'all' || type === 'field') {
|
|
372
|
+
const fieldResults = this.searchFieldsSemantic(sqlPatterns, queryTokens, query, version, packageFilter, limit * 3);
|
|
373
|
+
for (const field of fieldResults) {
|
|
374
|
+
results.push({
|
|
375
|
+
type: 'field',
|
|
376
|
+
name: field.name,
|
|
377
|
+
fullName: `${field.className}.${field.name}`,
|
|
378
|
+
obfuscatedName: field.obfuscatedName,
|
|
379
|
+
descriptor: field.descriptor,
|
|
380
|
+
javadoc: includeJavadoc ? field.javadoc : null,
|
|
381
|
+
className: field.className,
|
|
382
|
+
packageName: field.packageName,
|
|
383
|
+
minecraftVersion: field.minecraftVersion,
|
|
384
|
+
score: field.score,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
results.sort((a, b) => b.score - a.score);
|
|
389
|
+
return results.slice(0, limit);
|
|
390
|
+
}
|
|
391
|
+
generateSqlPatterns(query, tokens) {
|
|
392
|
+
const patterns = [];
|
|
393
|
+
const queryLower = query.toLowerCase();
|
|
394
|
+
patterns.push(`%${query}%`);
|
|
395
|
+
const joinedQuery = query.replace(/\s+/g, '');
|
|
396
|
+
patterns.push(`%${joinedQuery}%`);
|
|
397
|
+
for (const token of tokens) {
|
|
398
|
+
if (token.length >= 2) {
|
|
399
|
+
patterns.push(`%${token}%`);
|
|
400
|
+
if (token.length >= 4) {
|
|
401
|
+
for (let i = 0; i < token.length; i++) {
|
|
402
|
+
const variant = token.slice(0, i) + token.slice(i + 1);
|
|
403
|
+
if (variant.length >= 3) {
|
|
404
|
+
patterns.push(`%${variant}%`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
for (let i = 1; i < token.length; i++) {
|
|
408
|
+
const variant = token.slice(0, i) + '%' + token.slice(i);
|
|
409
|
+
patterns.push(`%${variant}%`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (token.length >= 3) {
|
|
413
|
+
patterns.push(`${token}%`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
for (let i = 0; i < tokens.length - 1; i++) {
|
|
418
|
+
const pair = `%${tokens[i]}%${tokens[i + 1]}%`;
|
|
419
|
+
patterns.push(pair);
|
|
420
|
+
patterns.push(`%${tokens[i]}${tokens[i + 1]}%`);
|
|
421
|
+
}
|
|
422
|
+
if (queryLower.length <= 4 && tokens.length === 1) {
|
|
423
|
+
const common = ['er', 'ing', 'ed', 'tion', 'ment', 'able', 'ible'];
|
|
424
|
+
for (const suffix of common) {
|
|
425
|
+
patterns.push(`%${tokens[0]}${suffix}%`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return [...new Set(patterns)];
|
|
429
|
+
}
|
|
430
|
+
searchClassesSemantic(patterns, queryTokens, originalQuery, version, packageFilter, limit = 60) {
|
|
431
|
+
const patternConditions = patterns.map(() => 'name LIKE ?').join(' OR ');
|
|
432
|
+
const retrievalLimit = Math.max(limit * 3, 200);
|
|
433
|
+
let sql = `
|
|
434
|
+
SELECT id, name, obfuscated_name as obfuscatedName, javadoc, package_name as packageName, minecraft_version as minecraftVersion
|
|
435
|
+
FROM classes
|
|
436
|
+
WHERE minecraft_version = ?
|
|
437
|
+
AND (${patternConditions})
|
|
438
|
+
`;
|
|
439
|
+
const params = [version, ...patterns];
|
|
440
|
+
if (packageFilter) {
|
|
441
|
+
sql += ` AND package_name LIKE ?`;
|
|
442
|
+
params.push(`%${packageFilter}%`);
|
|
443
|
+
}
|
|
444
|
+
sql += ` LIMIT ?`;
|
|
445
|
+
params.push(retrievalLimit);
|
|
446
|
+
const rawResults = this.db.prepare(sql).all(...params);
|
|
447
|
+
return rawResults
|
|
448
|
+
.map((cls) => ({
|
|
449
|
+
...cls,
|
|
450
|
+
score: calculateSemanticScore(cls.name, queryTokens, originalQuery),
|
|
451
|
+
}))
|
|
452
|
+
.filter((cls) => cls.score > 10)
|
|
453
|
+
.sort((a, b) => b.score - a.score)
|
|
454
|
+
.slice(0, limit);
|
|
455
|
+
}
|
|
456
|
+
searchMethodsSemantic(patterns, queryTokens, originalQuery, version, packageFilter, limit = 60) {
|
|
457
|
+
const patternConditions = patterns.map(() => 'm.name LIKE ?').join(' OR ');
|
|
458
|
+
const retrievalLimit = Math.max(limit * 3, 200);
|
|
459
|
+
let sql = `
|
|
460
|
+
SELECT
|
|
461
|
+
m.id,
|
|
462
|
+
m.name,
|
|
463
|
+
m.obfuscated_name as obfuscatedName,
|
|
464
|
+
m.descriptor,
|
|
465
|
+
m.javadoc,
|
|
466
|
+
c.name as className,
|
|
467
|
+
c.package_name as packageName,
|
|
468
|
+
c.minecraft_version as minecraftVersion
|
|
469
|
+
FROM methods m
|
|
470
|
+
JOIN classes c ON m.class_id = c.id
|
|
471
|
+
WHERE c.minecraft_version = ?
|
|
472
|
+
AND (${patternConditions})
|
|
473
|
+
`;
|
|
474
|
+
const params = [version, ...patterns];
|
|
475
|
+
if (packageFilter) {
|
|
476
|
+
sql += ` AND c.package_name LIKE ?`;
|
|
477
|
+
params.push(`%${packageFilter}%`);
|
|
478
|
+
}
|
|
479
|
+
sql += ` LIMIT ?`;
|
|
480
|
+
params.push(retrievalLimit);
|
|
481
|
+
const rawResults = this.db.prepare(sql).all(...params);
|
|
482
|
+
const paramStmt = this.db.prepare(`
|
|
483
|
+
SELECT id, method_id as methodId, param_index as "index", name, javadoc
|
|
484
|
+
FROM parameters
|
|
485
|
+
WHERE method_id = ?
|
|
486
|
+
ORDER BY param_index
|
|
487
|
+
`);
|
|
488
|
+
return rawResults
|
|
489
|
+
.map((method) => ({
|
|
490
|
+
...method,
|
|
491
|
+
parameters: paramStmt.all(method.id),
|
|
492
|
+
score: calculateSemanticScore(method.name, queryTokens, originalQuery),
|
|
493
|
+
}))
|
|
494
|
+
.filter((method) => method.score > 10)
|
|
495
|
+
.sort((a, b) => b.score - a.score)
|
|
496
|
+
.slice(0, limit);
|
|
497
|
+
}
|
|
498
|
+
searchFieldsSemantic(patterns, queryTokens, originalQuery, version, packageFilter, limit = 60) {
|
|
499
|
+
const patternConditions = patterns.map(() => 'f.name LIKE ?').join(' OR ');
|
|
500
|
+
const retrievalLimit = Math.max(limit * 3, 200);
|
|
501
|
+
let sql = `
|
|
502
|
+
SELECT
|
|
503
|
+
f.id,
|
|
504
|
+
f.name,
|
|
505
|
+
f.obfuscated_name as obfuscatedName,
|
|
506
|
+
f.descriptor,
|
|
507
|
+
f.javadoc,
|
|
508
|
+
c.name as className,
|
|
509
|
+
c.package_name as packageName,
|
|
510
|
+
c.minecraft_version as minecraftVersion
|
|
511
|
+
FROM fields f
|
|
512
|
+
JOIN classes c ON f.class_id = c.id
|
|
513
|
+
WHERE c.minecraft_version = ?
|
|
514
|
+
AND (${patternConditions})
|
|
515
|
+
`;
|
|
516
|
+
const params = [version, ...patterns];
|
|
517
|
+
if (packageFilter) {
|
|
518
|
+
sql += ` AND c.package_name LIKE ?`;
|
|
519
|
+
params.push(`%${packageFilter}%`);
|
|
520
|
+
}
|
|
521
|
+
sql += ` LIMIT ?`;
|
|
522
|
+
params.push(retrievalLimit);
|
|
523
|
+
const rawResults = this.db.prepare(sql).all(...params);
|
|
524
|
+
return rawResults
|
|
525
|
+
.map((field) => ({
|
|
526
|
+
...field,
|
|
527
|
+
score: calculateSemanticScore(field.name, queryTokens, originalQuery),
|
|
528
|
+
}))
|
|
529
|
+
.filter((field) => field.score > 10)
|
|
530
|
+
.sort((a, b) => b.score - a.score)
|
|
531
|
+
.slice(0, limit);
|
|
532
|
+
}
|
|
533
|
+
getClass(fullName, minecraftVersion) {
|
|
534
|
+
const version = minecraftVersion || this.getLatestVersion();
|
|
535
|
+
if (!version)
|
|
536
|
+
return null;
|
|
537
|
+
const parts = fullName.split('.');
|
|
538
|
+
const className = parts.pop() || fullName;
|
|
539
|
+
const packageName = parts.join('.');
|
|
540
|
+
let sql = `
|
|
541
|
+
SELECT
|
|
542
|
+
c.id,
|
|
543
|
+
c.name,
|
|
544
|
+
c.obfuscated_name as obfuscatedName,
|
|
545
|
+
c.javadoc,
|
|
546
|
+
c.package_name as packageName,
|
|
547
|
+
c.minecraft_version as minecraftVersion,
|
|
548
|
+
(SELECT COUNT(*) FROM methods WHERE class_id = c.id) as methodCount,
|
|
549
|
+
(SELECT COUNT(*) FROM fields WHERE class_id = c.id) as fieldCount
|
|
550
|
+
FROM classes c
|
|
551
|
+
WHERE c.minecraft_version = ?
|
|
552
|
+
AND c.name = ?
|
|
553
|
+
`;
|
|
554
|
+
const params = [version, className];
|
|
555
|
+
if (packageName) {
|
|
556
|
+
sql += ` AND c.package_name = ?`;
|
|
557
|
+
params.push(packageName);
|
|
558
|
+
}
|
|
559
|
+
return this.db.prepare(sql).get(...params);
|
|
560
|
+
}
|
|
561
|
+
getClassMethods(classId) {
|
|
562
|
+
const methods = this.db
|
|
563
|
+
.prepare(`
|
|
564
|
+
SELECT
|
|
565
|
+
m.id,
|
|
566
|
+
m.class_id as classId,
|
|
567
|
+
c.name as className,
|
|
568
|
+
m.name,
|
|
569
|
+
m.obfuscated_name as obfuscatedName,
|
|
570
|
+
m.descriptor,
|
|
571
|
+
m.javadoc,
|
|
572
|
+
c.minecraft_version as minecraftVersion
|
|
573
|
+
FROM methods m
|
|
574
|
+
JOIN classes c ON m.class_id = c.id
|
|
575
|
+
WHERE m.class_id = ?
|
|
576
|
+
ORDER BY m.name
|
|
577
|
+
`)
|
|
578
|
+
.all(classId);
|
|
579
|
+
const paramStmt = this.db.prepare(`
|
|
580
|
+
SELECT id, method_id as methodId, param_index as "index", name, javadoc
|
|
581
|
+
FROM parameters
|
|
582
|
+
WHERE method_id = ?
|
|
583
|
+
ORDER BY param_index
|
|
584
|
+
`);
|
|
585
|
+
return methods.map((method) => ({
|
|
586
|
+
...method,
|
|
587
|
+
parameters: paramStmt.all(method.id),
|
|
588
|
+
}));
|
|
589
|
+
}
|
|
590
|
+
getClassFields(classId) {
|
|
591
|
+
return this.db
|
|
592
|
+
.prepare(`
|
|
593
|
+
SELECT
|
|
594
|
+
f.id,
|
|
595
|
+
f.class_id as classId,
|
|
596
|
+
c.name as className,
|
|
597
|
+
f.name,
|
|
598
|
+
f.obfuscated_name as obfuscatedName,
|
|
599
|
+
f.descriptor,
|
|
600
|
+
f.javadoc,
|
|
601
|
+
c.minecraft_version as minecraftVersion
|
|
602
|
+
FROM fields f
|
|
603
|
+
JOIN classes c ON f.class_id = c.id
|
|
604
|
+
WHERE f.class_id = ?
|
|
605
|
+
ORDER BY f.name
|
|
606
|
+
`)
|
|
607
|
+
.all(classId);
|
|
608
|
+
}
|
|
609
|
+
getMethod(className, methodName, minecraftVersion) {
|
|
610
|
+
const version = minecraftVersion || this.getLatestVersion();
|
|
611
|
+
if (!version)
|
|
612
|
+
return null;
|
|
613
|
+
const method = this.db
|
|
614
|
+
.prepare(`
|
|
615
|
+
SELECT
|
|
616
|
+
m.id,
|
|
617
|
+
m.class_id as classId,
|
|
618
|
+
c.name as className,
|
|
619
|
+
m.name,
|
|
620
|
+
m.obfuscated_name as obfuscatedName,
|
|
621
|
+
m.descriptor,
|
|
622
|
+
m.javadoc,
|
|
623
|
+
c.minecraft_version as minecraftVersion
|
|
624
|
+
FROM methods m
|
|
625
|
+
JOIN classes c ON m.class_id = c.id
|
|
626
|
+
WHERE c.minecraft_version = ?
|
|
627
|
+
AND (c.name = ? OR (c.package_name || '.' || c.name) = ?)
|
|
628
|
+
AND m.name = ?
|
|
629
|
+
LIMIT 1
|
|
630
|
+
`)
|
|
631
|
+
.get(version, className, className, methodName);
|
|
632
|
+
if (!method)
|
|
633
|
+
return null;
|
|
634
|
+
const parameters = this.db
|
|
635
|
+
.prepare(`
|
|
636
|
+
SELECT id, method_id as methodId, param_index as "index", name, javadoc
|
|
637
|
+
FROM parameters
|
|
638
|
+
WHERE method_id = ?
|
|
639
|
+
ORDER BY param_index
|
|
640
|
+
`)
|
|
641
|
+
.all(method.id);
|
|
642
|
+
return { ...method, parameters };
|
|
643
|
+
}
|
|
644
|
+
lookupObfuscated(obfuscatedName, minecraftVersion) {
|
|
645
|
+
const version = minecraftVersion || this.getLatestVersion();
|
|
646
|
+
if (!version)
|
|
647
|
+
return null;
|
|
648
|
+
const cls = this.db
|
|
649
|
+
.prepare(`
|
|
650
|
+
SELECT id, name, obfuscated_name as obfuscatedName, javadoc, package_name as packageName, minecraft_version as minecraftVersion
|
|
651
|
+
FROM classes
|
|
652
|
+
WHERE minecraft_version = ? AND obfuscated_name = ?
|
|
653
|
+
LIMIT 1
|
|
654
|
+
`)
|
|
655
|
+
.get(version, obfuscatedName);
|
|
656
|
+
if (cls) {
|
|
657
|
+
return {
|
|
658
|
+
type: 'class',
|
|
659
|
+
name: cls.name,
|
|
660
|
+
fullName: `${cls.packageName}.${cls.name}`,
|
|
661
|
+
obfuscatedName: cls.obfuscatedName,
|
|
662
|
+
descriptor: null,
|
|
663
|
+
javadoc: cls.javadoc,
|
|
664
|
+
className: null,
|
|
665
|
+
packageName: cls.packageName,
|
|
666
|
+
minecraftVersion: cls.minecraftVersion,
|
|
667
|
+
score: 1.0,
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
const method = this.db
|
|
671
|
+
.prepare(`
|
|
672
|
+
SELECT
|
|
673
|
+
m.id,
|
|
674
|
+
m.name,
|
|
675
|
+
m.obfuscated_name as obfuscatedName,
|
|
676
|
+
m.descriptor,
|
|
677
|
+
m.javadoc,
|
|
678
|
+
c.name as className,
|
|
679
|
+
c.package_name as packageName,
|
|
680
|
+
c.minecraft_version as minecraftVersion
|
|
681
|
+
FROM methods m
|
|
682
|
+
JOIN classes c ON m.class_id = c.id
|
|
683
|
+
WHERE c.minecraft_version = ? AND m.obfuscated_name = ?
|
|
684
|
+
LIMIT 1
|
|
685
|
+
`)
|
|
686
|
+
.get(version, obfuscatedName);
|
|
687
|
+
if (method) {
|
|
688
|
+
const parameters = this.db
|
|
689
|
+
.prepare(`
|
|
690
|
+
SELECT id, method_id as methodId, param_index as "index", name, javadoc
|
|
691
|
+
FROM parameters
|
|
692
|
+
WHERE method_id = ?
|
|
693
|
+
ORDER BY param_index
|
|
694
|
+
`)
|
|
695
|
+
.all(method.id);
|
|
696
|
+
return {
|
|
697
|
+
type: 'method',
|
|
698
|
+
name: method.name,
|
|
699
|
+
fullName: `${method.className}.${method.name}`,
|
|
700
|
+
obfuscatedName: method.obfuscatedName,
|
|
701
|
+
descriptor: method.descriptor,
|
|
702
|
+
javadoc: method.javadoc,
|
|
703
|
+
className: method.className,
|
|
704
|
+
packageName: method.packageName,
|
|
705
|
+
minecraftVersion: method.minecraftVersion,
|
|
706
|
+
parameters,
|
|
707
|
+
score: 1.0,
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
const field = this.db
|
|
711
|
+
.prepare(`
|
|
712
|
+
SELECT
|
|
713
|
+
f.id,
|
|
714
|
+
f.name,
|
|
715
|
+
f.obfuscated_name as obfuscatedName,
|
|
716
|
+
f.descriptor,
|
|
717
|
+
f.javadoc,
|
|
718
|
+
c.name as className,
|
|
719
|
+
c.package_name as packageName,
|
|
720
|
+
c.minecraft_version as minecraftVersion
|
|
721
|
+
FROM fields f
|
|
722
|
+
JOIN classes c ON f.class_id = c.id
|
|
723
|
+
WHERE c.minecraft_version = ? AND f.obfuscated_name = ?
|
|
724
|
+
LIMIT 1
|
|
725
|
+
`)
|
|
726
|
+
.get(version, obfuscatedName);
|
|
727
|
+
if (field) {
|
|
728
|
+
return {
|
|
729
|
+
type: 'field',
|
|
730
|
+
name: field.name,
|
|
731
|
+
fullName: `${field.className}.${field.name}`,
|
|
732
|
+
obfuscatedName: field.obfuscatedName,
|
|
733
|
+
descriptor: field.descriptor,
|
|
734
|
+
javadoc: field.javadoc,
|
|
735
|
+
className: field.className,
|
|
736
|
+
packageName: field.packageName,
|
|
737
|
+
minecraftVersion: field.minecraftVersion,
|
|
738
|
+
score: 1.0,
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
743
|
+
getPackages(minecraftVersion) {
|
|
744
|
+
const version = minecraftVersion || this.getLatestVersion();
|
|
745
|
+
if (!version)
|
|
746
|
+
return [];
|
|
747
|
+
const packages = this.db
|
|
748
|
+
.prepare(`
|
|
749
|
+
SELECT DISTINCT
|
|
750
|
+
CASE
|
|
751
|
+
WHEN INSTR(package_name, '.') > 0
|
|
752
|
+
THEN SUBSTR(package_name, 1, INSTR(package_name, '.') - 1)
|
|
753
|
+
ELSE package_name
|
|
754
|
+
END as topPackage
|
|
755
|
+
FROM classes
|
|
756
|
+
WHERE minecraft_version = ?
|
|
757
|
+
ORDER BY topPackage
|
|
758
|
+
`)
|
|
759
|
+
.all(version);
|
|
760
|
+
return packages.map((p) => p.topPackage);
|
|
761
|
+
}
|
|
762
|
+
getClassesInPackage(packageName, minecraftVersion) {
|
|
763
|
+
const version = minecraftVersion || this.getLatestVersion();
|
|
764
|
+
if (!version)
|
|
765
|
+
return [];
|
|
766
|
+
return this.db
|
|
767
|
+
.prepare(`
|
|
768
|
+
SELECT
|
|
769
|
+
c.id,
|
|
770
|
+
c.name,
|
|
771
|
+
c.obfuscated_name as obfuscatedName,
|
|
772
|
+
c.javadoc,
|
|
773
|
+
c.package_name as packageName,
|
|
774
|
+
c.minecraft_version as minecraftVersion,
|
|
775
|
+
(SELECT COUNT(*) FROM methods WHERE class_id = c.id) as methodCount,
|
|
776
|
+
(SELECT COUNT(*) FROM fields WHERE class_id = c.id) as fieldCount
|
|
777
|
+
FROM classes c
|
|
778
|
+
WHERE c.minecraft_version = ?
|
|
779
|
+
AND c.package_name LIKE ?
|
|
780
|
+
ORDER BY c.name
|
|
781
|
+
`)
|
|
782
|
+
.all(version, `${packageName}%`);
|
|
783
|
+
}
|
|
784
|
+
close() {
|
|
785
|
+
this.db.close();
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
//# sourceMappingURL=mappings-service.js.map
|