code-auditor-mcp 1.3.1 → 1.6.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 +133 -2
- package/dist/analyzers/solidAnalyzer.d.ts +5 -1
- package/dist/analyzers/solidAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/solidAnalyzer.js +174 -2
- package/dist/analyzers/solidAnalyzer.js.map +1 -1
- package/dist/codeIndexDb.d.ts +47 -0
- package/dist/codeIndexDb.d.ts.map +1 -1
- package/dist/codeIndexDb.js +432 -5
- package/dist/codeIndexDb.js.map +1 -1
- package/dist/codeIndexService.d.ts +26 -0
- package/dist/codeIndexService.d.ts.map +1 -1
- package/dist/codeIndexService.js +60 -0
- package/dist/codeIndexService.js.map +1 -1
- package/dist/functionScanner.d.ts.map +1 -1
- package/dist/functionScanner.js +76 -6
- package/dist/functionScanner.js.map +1 -1
- package/dist/mcp-standalone.js +14 -4
- package/dist/mcp-standalone.js.map +1 -1
- package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +45 -0
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp.js +3 -3
- package/dist/mcp.js.map +1 -1
- package/dist/search/QueryParser.d.ts +2 -0
- package/dist/search/QueryParser.d.ts.map +1 -1
- package/dist/search/QueryParser.js +191 -26
- package/dist/search/QueryParser.js.map +1 -1
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +16 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/astUtils.d.ts +17 -1
- package/dist/utils/astUtils.d.ts.map +1 -1
- package/dist/utils/astUtils.js +121 -0
- package/dist/utils/astUtils.js.map +1 -1
- package/dist/utils/componentPatterns.d.ts +22 -0
- package/dist/utils/componentPatterns.d.ts.map +1 -0
- package/dist/utils/componentPatterns.js +273 -0
- package/dist/utils/componentPatterns.js.map +1 -0
- package/dist/utils/componentResponsibility.d.ts +39 -0
- package/dist/utils/componentResponsibility.d.ts.map +1 -0
- package/dist/utils/componentResponsibility.js +487 -0
- package/dist/utils/componentResponsibility.js.map +1 -0
- package/dist/utils/dependencyExtractor.d.ts +30 -0
- package/dist/utils/dependencyExtractor.d.ts.map +1 -0
- package/dist/utils/dependencyExtractor.js +237 -0
- package/dist/utils/dependencyExtractor.js.map +1 -0
- package/package.json +16 -15
package/dist/codeIndexDb.js
CHANGED
|
@@ -92,6 +92,13 @@ export class CodeIndexDB {
|
|
|
92
92
|
optimize: true,
|
|
93
93
|
resolution: 7,
|
|
94
94
|
weight: 2 // Return type matches
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
field: 'body',
|
|
98
|
+
tokenize: 'full',
|
|
99
|
+
optimize: true,
|
|
100
|
+
resolution: 9,
|
|
101
|
+
weight: 1 // Body content matches (lower weight to prioritize metadata matches)
|
|
95
102
|
}
|
|
96
103
|
]
|
|
97
104
|
},
|
|
@@ -173,7 +180,9 @@ export class CodeIndexDB {
|
|
|
173
180
|
},
|
|
174
181
|
returnType: 'unknown',
|
|
175
182
|
// Add tokenized name for better searching
|
|
176
|
-
tokenizedName: this.tokenizeFunctionName(func.name)
|
|
183
|
+
tokenizedName: this.tokenizeFunctionName(func.name),
|
|
184
|
+
// Extract body from metadata if present
|
|
185
|
+
body: func.metadata?.body
|
|
177
186
|
};
|
|
178
187
|
return enhanced;
|
|
179
188
|
}
|
|
@@ -294,6 +303,8 @@ export class CodeIndexDB {
|
|
|
294
303
|
stats.removed++;
|
|
295
304
|
}
|
|
296
305
|
}
|
|
306
|
+
// Update dependency graph for the affected functions
|
|
307
|
+
await this.updateDependencyGraph(filePath);
|
|
297
308
|
// Force save
|
|
298
309
|
this.db.saveDatabase();
|
|
299
310
|
}
|
|
@@ -302,6 +313,192 @@ export class CodeIndexDB {
|
|
|
302
313
|
}
|
|
303
314
|
return stats;
|
|
304
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Update dependency graph - build reverse mappings for calledBy relationships
|
|
318
|
+
* @param filePath Optional file path to limit the update scope
|
|
319
|
+
*/
|
|
320
|
+
async updateDependencyGraph(filePath) {
|
|
321
|
+
this.ensureInitialized();
|
|
322
|
+
try {
|
|
323
|
+
// Get all functions (or just those in the specified file)
|
|
324
|
+
const functions = filePath
|
|
325
|
+
? this.functionsCollection.find({ filePath })
|
|
326
|
+
: this.functionsCollection.find();
|
|
327
|
+
// Clear existing calledBy relationships for affected functions
|
|
328
|
+
for (const func of functions) {
|
|
329
|
+
if (func.metadata) {
|
|
330
|
+
func.metadata.calledBy = [];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Build calledBy relationships by iterating through all functions
|
|
334
|
+
const allFunctions = this.functionsCollection.find();
|
|
335
|
+
for (const caller of allFunctions) {
|
|
336
|
+
if (caller.metadata?.functionCalls) {
|
|
337
|
+
for (const callee of caller.metadata.functionCalls) {
|
|
338
|
+
// Find the called function
|
|
339
|
+
const calledFunc = this.findFunctionByQualifiedName(callee);
|
|
340
|
+
if (calledFunc && calledFunc.metadata) {
|
|
341
|
+
if (!calledFunc.metadata.calledBy) {
|
|
342
|
+
calledFunc.metadata.calledBy = [];
|
|
343
|
+
}
|
|
344
|
+
// Add caller to calledBy list if not already present
|
|
345
|
+
const callerName = this.getQualifiedFunctionName(caller);
|
|
346
|
+
if (!calledFunc.metadata.calledBy.includes(callerName)) {
|
|
347
|
+
calledFunc.metadata.calledBy.push(callerName);
|
|
348
|
+
this.functionsCollection.update(calledFunc);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
throw new Error(`Failed to update dependency graph: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Find a function by its qualified name (e.g., "filePath#functionName")
|
|
361
|
+
*/
|
|
362
|
+
findFunctionByQualifiedName(qualifiedName) {
|
|
363
|
+
// Handle different name formats
|
|
364
|
+
if (qualifiedName.includes('#')) {
|
|
365
|
+
const [filePath, functionName] = qualifiedName.split('#');
|
|
366
|
+
return this.functionsCollection.findOne({
|
|
367
|
+
filePath: { $regex: filePath },
|
|
368
|
+
name: functionName
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
// Simple function name - search across all files
|
|
373
|
+
return this.functionsCollection.findOne({ name: qualifiedName });
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Get qualified name for a function
|
|
378
|
+
*/
|
|
379
|
+
getQualifiedFunctionName(func) {
|
|
380
|
+
return `${func.filePath}#${func.name}`;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Get transitive dependencies for a function (functions it calls, directly and indirectly)
|
|
384
|
+
* @param functionName The function to analyze
|
|
385
|
+
* @param maxDepth Maximum depth to traverse (default 10)
|
|
386
|
+
* @returns Array of function names with their depth
|
|
387
|
+
*/
|
|
388
|
+
async getTransitiveDependencies(functionName, maxDepth = 10) {
|
|
389
|
+
this.ensureInitialized();
|
|
390
|
+
const dependencies = [];
|
|
391
|
+
const visited = new Set();
|
|
392
|
+
const traverse = (funcName, depth) => {
|
|
393
|
+
if (depth > maxDepth || visited.has(funcName))
|
|
394
|
+
return;
|
|
395
|
+
visited.add(funcName);
|
|
396
|
+
const func = this.findFunctionByQualifiedName(funcName);
|
|
397
|
+
if (!func?.metadata?.functionCalls)
|
|
398
|
+
return;
|
|
399
|
+
for (const callee of func.metadata.functionCalls) {
|
|
400
|
+
if (!visited.has(callee)) {
|
|
401
|
+
dependencies.push({ name: callee, depth });
|
|
402
|
+
traverse(callee, depth + 1);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
traverse(functionName, 1);
|
|
407
|
+
return dependencies;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get transitive callers for a function (functions that call it, directly and indirectly)
|
|
411
|
+
* @param functionName The function to analyze
|
|
412
|
+
* @param maxDepth Maximum depth to traverse (default 10)
|
|
413
|
+
* @returns Array of function names with their depth
|
|
414
|
+
*/
|
|
415
|
+
async getTransitiveCallers(functionName, maxDepth = 10) {
|
|
416
|
+
this.ensureInitialized();
|
|
417
|
+
const callers = [];
|
|
418
|
+
const visited = new Set();
|
|
419
|
+
const traverse = (funcName, depth) => {
|
|
420
|
+
if (depth > maxDepth || visited.has(funcName))
|
|
421
|
+
return;
|
|
422
|
+
visited.add(funcName);
|
|
423
|
+
const func = this.findFunctionByQualifiedName(funcName);
|
|
424
|
+
if (!func?.metadata?.calledBy)
|
|
425
|
+
return;
|
|
426
|
+
for (const caller of func.metadata.calledBy) {
|
|
427
|
+
if (!visited.has(caller)) {
|
|
428
|
+
callers.push({ name: caller, depth });
|
|
429
|
+
traverse(caller, depth + 1);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
traverse(functionName, 1);
|
|
434
|
+
return callers;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Detect circular dependencies in the codebase
|
|
438
|
+
* @returns Array of circular dependency chains
|
|
439
|
+
*/
|
|
440
|
+
async detectCircularDependencies() {
|
|
441
|
+
this.ensureInitialized();
|
|
442
|
+
const cycles = [];
|
|
443
|
+
const allFunctions = this.functionsCollection.find();
|
|
444
|
+
for (const func of allFunctions) {
|
|
445
|
+
if (!func.metadata?.functionCalls)
|
|
446
|
+
continue;
|
|
447
|
+
const funcName = this.getQualifiedFunctionName(func);
|
|
448
|
+
const visited = new Set();
|
|
449
|
+
const path = [];
|
|
450
|
+
const hasCycle = (current) => {
|
|
451
|
+
if (path.includes(current)) {
|
|
452
|
+
// Found a cycle - extract it
|
|
453
|
+
const cycleStart = path.indexOf(current);
|
|
454
|
+
const cycle = [...path.slice(cycleStart), current];
|
|
455
|
+
// Check if we already have this cycle (in any rotation)
|
|
456
|
+
const isNewCycle = !cycles.some(existing => existing.length === cycle.length &&
|
|
457
|
+
existing.every(f => cycle.includes(f)));
|
|
458
|
+
if (isNewCycle) {
|
|
459
|
+
cycles.push(cycle);
|
|
460
|
+
}
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
if (visited.has(current))
|
|
464
|
+
return false;
|
|
465
|
+
visited.add(current);
|
|
466
|
+
path.push(current);
|
|
467
|
+
const currentFunc = this.findFunctionByQualifiedName(current);
|
|
468
|
+
if (currentFunc?.metadata?.functionCalls) {
|
|
469
|
+
for (const callee of currentFunc.metadata.functionCalls) {
|
|
470
|
+
if (hasCycle(callee))
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
path.pop();
|
|
475
|
+
return false;
|
|
476
|
+
};
|
|
477
|
+
hasCycle(funcName);
|
|
478
|
+
}
|
|
479
|
+
return cycles;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Calculate the maximum dependency depth for each function
|
|
483
|
+
* Updates the dependencyDepth field in metadata
|
|
484
|
+
*/
|
|
485
|
+
async calculateDependencyDepths() {
|
|
486
|
+
this.ensureInitialized();
|
|
487
|
+
const allFunctions = this.functionsCollection.find();
|
|
488
|
+
for (const func of allFunctions) {
|
|
489
|
+
const funcName = this.getQualifiedFunctionName(func);
|
|
490
|
+
const dependencies = await this.getTransitiveDependencies(funcName);
|
|
491
|
+
const maxDepth = dependencies.length > 0
|
|
492
|
+
? Math.max(...dependencies.map(d => d.depth))
|
|
493
|
+
: 0;
|
|
494
|
+
if (!func.metadata) {
|
|
495
|
+
func.metadata = {};
|
|
496
|
+
}
|
|
497
|
+
func.metadata.dependencyDepth = maxDepth;
|
|
498
|
+
this.functionsCollection.update(func);
|
|
499
|
+
}
|
|
500
|
+
this.db.saveDatabase();
|
|
501
|
+
}
|
|
305
502
|
async searchFunctions(options) {
|
|
306
503
|
this.ensureInitialized();
|
|
307
504
|
const startTime = Date.now();
|
|
@@ -310,12 +507,41 @@ export class CodeIndexDB {
|
|
|
310
507
|
// Parse the query if provided
|
|
311
508
|
const queryParser = new QueryParser();
|
|
312
509
|
let parsedQuery;
|
|
510
|
+
// Determine search mode
|
|
511
|
+
const searchMode = options.searchMode || 'metadata';
|
|
313
512
|
if (options.query) {
|
|
314
513
|
parsedQuery = options.parsedQuery || queryParser.parse(options.query);
|
|
315
514
|
// Check if there are search terms or just filters
|
|
316
515
|
if (parsedQuery.terms.length > 0 || parsedQuery.phrases.length > 0) {
|
|
317
|
-
|
|
318
|
-
|
|
516
|
+
if (searchMode === 'content') {
|
|
517
|
+
// Content search only
|
|
518
|
+
results = await this.executeContentSearch(parsedQuery, searchScores);
|
|
519
|
+
}
|
|
520
|
+
else if (searchMode === 'both') {
|
|
521
|
+
// Both metadata and content search
|
|
522
|
+
const metadataResults = await this.executeMultiStrategySearch(parsedQuery, searchScores);
|
|
523
|
+
const contentResults = await this.executeContentSearch(parsedQuery, searchScores);
|
|
524
|
+
// Merge results, combining scores for duplicates
|
|
525
|
+
const resultMap = new Map();
|
|
526
|
+
metadataResults.forEach(doc => {
|
|
527
|
+
if (doc.$loki !== undefined) {
|
|
528
|
+
resultMap.set(doc.$loki, doc);
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
contentResults.forEach(doc => {
|
|
532
|
+
if (doc.$loki !== undefined) {
|
|
533
|
+
resultMap.set(doc.$loki, doc);
|
|
534
|
+
// Combine scores
|
|
535
|
+
const existingScore = searchScores.get(doc.$loki) || 0;
|
|
536
|
+
searchScores.set(doc.$loki, existingScore);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
results = Array.from(resultMap.values());
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
// Metadata search only (default)
|
|
543
|
+
results = await this.executeMultiStrategySearch(parsedQuery, searchScores);
|
|
544
|
+
}
|
|
319
545
|
}
|
|
320
546
|
else {
|
|
321
547
|
// No search terms, just filters - get all
|
|
@@ -331,7 +557,32 @@ export class CodeIndexDB {
|
|
|
331
557
|
parsedQuery = options.parsedQuery;
|
|
332
558
|
// Check if there are search terms or just filters
|
|
333
559
|
if (parsedQuery.terms.length > 0 || parsedQuery.phrases.length > 0) {
|
|
334
|
-
|
|
560
|
+
if (searchMode === 'content') {
|
|
561
|
+
results = await this.executeContentSearch(parsedQuery, searchScores);
|
|
562
|
+
}
|
|
563
|
+
else if (searchMode === 'both') {
|
|
564
|
+
const metadataResults = await this.executeMultiStrategySearch(parsedQuery, searchScores);
|
|
565
|
+
const contentResults = await this.executeContentSearch(parsedQuery, searchScores);
|
|
566
|
+
// Merge results
|
|
567
|
+
const resultMap = new Map();
|
|
568
|
+
metadataResults.forEach(doc => {
|
|
569
|
+
if (doc.$loki !== undefined) {
|
|
570
|
+
resultMap.set(doc.$loki, doc);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
contentResults.forEach(doc => {
|
|
574
|
+
if (doc.$loki !== undefined) {
|
|
575
|
+
resultMap.set(doc.$loki, doc);
|
|
576
|
+
// Combine scores
|
|
577
|
+
const existingScore = searchScores.get(doc.$loki) || 0;
|
|
578
|
+
searchScores.set(doc.$loki, existingScore);
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
results = Array.from(resultMap.values());
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
results = await this.executeMultiStrategySearch(parsedQuery, searchScores);
|
|
585
|
+
}
|
|
335
586
|
}
|
|
336
587
|
else {
|
|
337
588
|
// No search terms, just filters - get all
|
|
@@ -620,7 +871,25 @@ export class CodeIndexDB {
|
|
|
620
871
|
filtered = filtered.filter(doc => doc.language === filters.language);
|
|
621
872
|
}
|
|
622
873
|
if (filters.filePath) {
|
|
623
|
-
|
|
874
|
+
// Support both exact match and includes based on the filter format
|
|
875
|
+
if (filters.filePath.includes('*') || filters.filePath.includes('?')) {
|
|
876
|
+
// Glob pattern - convert to regex
|
|
877
|
+
const pattern = filters.filePath
|
|
878
|
+
.replace(/\*/g, '.*')
|
|
879
|
+
.replace(/\?/g, '.')
|
|
880
|
+
.replace(/\//g, '\\/');
|
|
881
|
+
const regex = new RegExp(pattern);
|
|
882
|
+
filtered = filtered.filter(doc => regex.test(doc.filePath));
|
|
883
|
+
}
|
|
884
|
+
else if (filters.filePath.endsWith('.ts') || filters.filePath.endsWith('.tsx') ||
|
|
885
|
+
filters.filePath.endsWith('.js') || filters.filePath.endsWith('.jsx')) {
|
|
886
|
+
// If it looks like a full filename, match the end of the path
|
|
887
|
+
filtered = filtered.filter(doc => doc.filePath.endsWith(filters.filePath));
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
// Otherwise, do substring match (for directory paths)
|
|
891
|
+
filtered = filtered.filter(doc => doc.filePath.includes(filters.filePath));
|
|
892
|
+
}
|
|
624
893
|
}
|
|
625
894
|
if (filters.fileType) {
|
|
626
895
|
filtered = filtered.filter(doc => doc.filePath.endsWith(filters.fileType));
|
|
@@ -677,6 +946,52 @@ export class CodeIndexDB {
|
|
|
677
946
|
else if (filters.metadata.hasProp) {
|
|
678
947
|
return false; // No props but filter requires one
|
|
679
948
|
}
|
|
949
|
+
// Check usesDependency
|
|
950
|
+
if (filters.metadata.usesDependency) {
|
|
951
|
+
const dep = filters.metadata.usesDependency.toLowerCase();
|
|
952
|
+
// Check in both file-level dependencies and function-specific usedImports
|
|
953
|
+
const usesDepInFile = doc.dependencies.some(d => d.toLowerCase().includes(dep));
|
|
954
|
+
const usesDepInFunction = doc.metadata.usedImports?.some(imp => imp.toLowerCase().includes(dep)) || false;
|
|
955
|
+
if (!usesDepInFile && !usesDepInFunction)
|
|
956
|
+
return false;
|
|
957
|
+
}
|
|
958
|
+
// Check callsFunction
|
|
959
|
+
if (filters.metadata.callsFunction && doc.metadata.functionCalls) {
|
|
960
|
+
const targetFunc = filters.metadata.callsFunction.toLowerCase();
|
|
961
|
+
const callsFunc = doc.metadata.functionCalls.some(call => call.toLowerCase().includes(targetFunc));
|
|
962
|
+
if (!callsFunc)
|
|
963
|
+
return false;
|
|
964
|
+
}
|
|
965
|
+
else if (filters.metadata.callsFunction) {
|
|
966
|
+
return false; // No function calls but filter requires one
|
|
967
|
+
}
|
|
968
|
+
// Check calledByFunction
|
|
969
|
+
if (filters.metadata.calledByFunction && doc.metadata.calledBy) {
|
|
970
|
+
const callerFunc = filters.metadata.calledByFunction.toLowerCase();
|
|
971
|
+
const isCalledBy = doc.metadata.calledBy.some(caller => caller.toLowerCase().includes(callerFunc));
|
|
972
|
+
if (!isCalledBy)
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
else if (filters.metadata.calledByFunction) {
|
|
976
|
+
return false; // Not called by any function but filter requires one
|
|
977
|
+
}
|
|
978
|
+
// Check dependsOnModule
|
|
979
|
+
if (filters.metadata.dependsOnModule) {
|
|
980
|
+
const module = filters.metadata.dependsOnModule.toLowerCase();
|
|
981
|
+
// Check if any import or function call references this module
|
|
982
|
+
const dependsOnFile = doc.filePath.toLowerCase().includes(module);
|
|
983
|
+
const dependsOnImport = doc.dependencies.some(dep => dep.toLowerCase().includes(module));
|
|
984
|
+
const dependsOnCall = doc.metadata.functionCalls?.some(call => call.toLowerCase().includes(module)) || false;
|
|
985
|
+
if (!dependsOnFile && !dependsOnImport && !dependsOnCall)
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
// Check hasUnusedImports
|
|
989
|
+
if (filters.metadata.hasUnusedImports) {
|
|
990
|
+
const hasUnused = doc.metadata.unusedImports &&
|
|
991
|
+
doc.metadata.unusedImports.length > 0;
|
|
992
|
+
if (!hasUnused)
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
680
995
|
return true;
|
|
681
996
|
});
|
|
682
997
|
}
|
|
@@ -905,5 +1220,117 @@ export class CodeIndexDB {
|
|
|
905
1220
|
errors
|
|
906
1221
|
};
|
|
907
1222
|
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Execute content search - search within function bodies
|
|
1225
|
+
*/
|
|
1226
|
+
async executeContentSearch(parsedQuery, searchScores) {
|
|
1227
|
+
const resultsMap = new Map();
|
|
1228
|
+
// Get all functions (we'll filter them)
|
|
1229
|
+
const allFunctions = this.functionsCollection.find();
|
|
1230
|
+
// Search in function bodies
|
|
1231
|
+
for (const doc of allFunctions) {
|
|
1232
|
+
// Check for body in multiple possible locations
|
|
1233
|
+
const body = doc.body || doc.metadata?.body;
|
|
1234
|
+
if (!body)
|
|
1235
|
+
continue;
|
|
1236
|
+
let score = 0;
|
|
1237
|
+
const bodyLower = body.toLowerCase();
|
|
1238
|
+
const matches = [];
|
|
1239
|
+
// Split body into lines for line-level matching
|
|
1240
|
+
const lines = body.split('\n');
|
|
1241
|
+
// Check for exact phrases
|
|
1242
|
+
for (const phrase of parsedQuery.phrases) {
|
|
1243
|
+
const phraseLower = phrase.toLowerCase();
|
|
1244
|
+
lines.forEach((line, lineIndex) => {
|
|
1245
|
+
const lineLower = line.toLowerCase();
|
|
1246
|
+
let columnIndex = lineLower.indexOf(phraseLower);
|
|
1247
|
+
while (columnIndex !== -1) {
|
|
1248
|
+
matches.push({
|
|
1249
|
+
term: phrase,
|
|
1250
|
+
line: (doc.lineNumber || 0) + lineIndex,
|
|
1251
|
+
column: columnIndex + 1
|
|
1252
|
+
});
|
|
1253
|
+
score += 100; // High score for exact phrase match
|
|
1254
|
+
columnIndex = lineLower.indexOf(phraseLower, columnIndex + 1);
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
// Check for individual terms
|
|
1259
|
+
for (const term of parsedQuery.terms) {
|
|
1260
|
+
const termLower = term.toLowerCase();
|
|
1261
|
+
lines.forEach((line, lineIndex) => {
|
|
1262
|
+
const lineLower = line.toLowerCase();
|
|
1263
|
+
let columnIndex = lineLower.indexOf(termLower);
|
|
1264
|
+
while (columnIndex !== -1) {
|
|
1265
|
+
matches.push({
|
|
1266
|
+
term: term,
|
|
1267
|
+
line: (doc.lineNumber || 0) + lineIndex,
|
|
1268
|
+
column: columnIndex + 1
|
|
1269
|
+
});
|
|
1270
|
+
score += 20; // Score for term match
|
|
1271
|
+
columnIndex = lineLower.indexOf(termLower, columnIndex + 1);
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
// Check for excluded terms
|
|
1276
|
+
let excluded = false;
|
|
1277
|
+
for (const excludedTerm of parsedQuery.excludedTerms) {
|
|
1278
|
+
if (bodyLower.includes(excludedTerm.toLowerCase())) {
|
|
1279
|
+
excluded = true;
|
|
1280
|
+
break;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
// Add to results if score > 0 and not excluded
|
|
1284
|
+
if (score > 0 && !excluded && doc.$loki !== undefined) {
|
|
1285
|
+
// Store match information in metadata
|
|
1286
|
+
const resultDoc = { ...doc };
|
|
1287
|
+
if (!resultDoc.metadata)
|
|
1288
|
+
resultDoc.metadata = {};
|
|
1289
|
+
resultDoc.metadata.contentMatches = matches;
|
|
1290
|
+
// Add match context (surrounding lines)
|
|
1291
|
+
const contextLines = 2; // Number of lines before and after to include
|
|
1292
|
+
const matchContexts = [];
|
|
1293
|
+
// Group matches by line to avoid duplicate context
|
|
1294
|
+
const matchesByLine = new Map();
|
|
1295
|
+
for (const match of matches) {
|
|
1296
|
+
const relativeLineNum = match.line - (doc.lineNumber || 0);
|
|
1297
|
+
if (!matchesByLine.has(relativeLineNum)) {
|
|
1298
|
+
matchesByLine.set(relativeLineNum, []);
|
|
1299
|
+
}
|
|
1300
|
+
matchesByLine.get(relativeLineNum).push(match);
|
|
1301
|
+
}
|
|
1302
|
+
// Build context for each unique line
|
|
1303
|
+
for (const [relativeLineNum, lineMatches] of matchesByLine) {
|
|
1304
|
+
if (relativeLineNum >= 0 && relativeLineNum < lines.length) {
|
|
1305
|
+
const before = [];
|
|
1306
|
+
const after = [];
|
|
1307
|
+
// Get lines before
|
|
1308
|
+
for (let i = Math.max(0, relativeLineNum - contextLines); i < relativeLineNum; i++) {
|
|
1309
|
+
before.push(lines[i]);
|
|
1310
|
+
}
|
|
1311
|
+
// Get lines after
|
|
1312
|
+
for (let i = relativeLineNum + 1; i < Math.min(lines.length, relativeLineNum + contextLines + 1); i++) {
|
|
1313
|
+
after.push(lines[i]);
|
|
1314
|
+
}
|
|
1315
|
+
// Add context for each match on this line
|
|
1316
|
+
for (const match of lineMatches) {
|
|
1317
|
+
matchContexts.push({
|
|
1318
|
+
match,
|
|
1319
|
+
context: {
|
|
1320
|
+
before,
|
|
1321
|
+
line: lines[relativeLineNum],
|
|
1322
|
+
after
|
|
1323
|
+
}
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
resultDoc.metadata.matchContexts = matchContexts;
|
|
1329
|
+
resultsMap.set(doc.$loki, resultDoc);
|
|
1330
|
+
searchScores.set(doc.$loki, score);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return Array.from(resultsMap.values());
|
|
1334
|
+
}
|
|
908
1335
|
}
|
|
909
1336
|
//# sourceMappingURL=codeIndexDB.js.map
|