codebase-onboarder 0.5.0 → 0.7.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 +196 -47
- package/bin/build-if-source.js +23 -0
- package/cli/commands.d.ts +26 -0
- package/cli/commands.js +836 -811
- package/cli/commands.js.map +1 -0
- package/cli/explorer/advanced.d.ts +14 -0
- package/cli/explorer/advanced.js +449 -0
- package/cli/explorer/advanced.js.map +1 -0
- package/cli/explorer/app.d.ts +1 -0
- package/cli/explorer/app.js +396 -229
- package/cli/explorer/app.js.map +1 -0
- package/cli/explorer/assistant.d.ts +9 -0
- package/cli/explorer/assistant.js +58 -0
- package/cli/explorer/assistant.js.map +1 -0
- package/cli/explorer/commands.d.ts +17 -0
- package/cli/explorer/commands.js +400 -162
- package/cli/explorer/commands.js.map +1 -0
- package/cli/explorer/featureViews.d.ts +124 -0
- package/cli/explorer/featureViews.js +179 -0
- package/cli/explorer/featureViews.js.map +1 -0
- package/cli/explorer/github.d.ts +21 -0
- package/cli/explorer/github.js +73 -0
- package/cli/explorer/github.js.map +1 -0
- package/cli/explorer/graphs.d.ts +7 -0
- package/cli/explorer/graphs.js +173 -0
- package/cli/explorer/graphs.js.map +1 -0
- package/cli/explorer/picker.d.ts +22 -0
- package/cli/explorer/picker.js +150 -0
- package/cli/explorer/picker.js.map +1 -0
- package/cli/explorer/report.d.ts +85 -0
- package/cli/explorer/report.js +60 -0
- package/cli/explorer/report.js.map +1 -0
- package/cli/explorer/session.d.ts +449 -0
- package/cli/explorer/session.js +184 -95
- package/cli/explorer/session.js.map +1 -0
- package/cli/explorer/views.d.ts +19 -0
- package/cli/explorer/views.js +496 -412
- package/cli/explorer/views.js.map +1 -0
- package/cli/explorer/wrap.d.ts +1 -0
- package/cli/explorer/wrap.js +33 -0
- package/cli/explorer/wrap.js.map +1 -0
- package/cli/main.d.ts +1 -0
- package/cli/main.js +235 -138
- package/cli/main.js.map +1 -0
- package/cli/prompt.d.ts +5 -0
- package/cli/prompt.js +84 -80
- package/cli/prompt.js.map +1 -0
- package/cli/scanOptions.d.ts +2 -0
- package/cli/scanOptions.js +11 -0
- package/cli/scanOptions.js.map +1 -0
- package/cli/ui.d.ts +33 -0
- package/cli/ui.js +55 -35
- package/cli/ui.js.map +1 -0
- package/cli/wizard.d.ts +187 -0
- package/cli/wizard.js +304 -284
- package/cli/wizard.js.map +1 -0
- package/package.json +15 -4
- package/public/app.d.ts +1 -0
- package/public/app.js +1057 -1009
- package/public/app.js.map +1 -0
- package/public/browser.d.ts +68 -0
- package/public/browser.js +2 -0
- package/public/browser.js.map +1 -0
- package/public/index.html +30 -13
- package/public/js/about.d.ts +6 -0
- package/public/js/about.js +165 -172
- package/public/js/about.js.map +1 -0
- package/public/js/aiDraft.d.ts +3 -0
- package/public/js/aiDraft.js +116 -124
- package/public/js/aiDraft.js.map +1 -0
- package/public/js/analysisPanel.d.ts +7 -0
- package/public/js/analysisPanel.js +166 -166
- package/public/js/analysisPanel.js.map +1 -0
- package/public/js/analysisReport.d.ts +38 -0
- package/public/js/analysisReport.js +105 -110
- package/public/js/analysisReport.js.map +1 -0
- package/public/js/api.d.ts +26 -0
- package/public/js/api.js +176 -167
- package/public/js/api.js.map +1 -0
- package/public/js/atlas.d.ts +6 -0
- package/public/js/atlas.js +50 -58
- package/public/js/atlas.js.map +1 -0
- package/public/js/blameView.d.ts +2 -0
- package/public/js/blameView.js +36 -37
- package/public/js/blameView.js.map +1 -0
- package/public/js/codeTab.d.ts +3 -0
- package/public/js/codeTab.js +175 -180
- package/public/js/codeTab.js.map +1 -0
- package/public/js/codeViewer.d.ts +5 -0
- package/public/js/codeViewer.js +162 -150
- package/public/js/codeViewer.js.map +1 -0
- package/public/js/components/FileChip.d.ts +9 -0
- package/public/js/components/FileChip.js +45 -49
- package/public/js/components/FileChip.js.map +1 -0
- package/public/js/components/MetricSparkline.d.ts +7 -0
- package/public/js/components/MetricSparkline.js +35 -44
- package/public/js/components/MetricSparkline.js.map +1 -0
- package/public/js/components/RiskBadge.d.ts +9 -0
- package/public/js/components/RiskBadge.js +55 -63
- package/public/js/components/RiskBadge.js.map +1 -0
- package/public/js/deepAnalysisView.d.ts +99 -0
- package/public/js/deepAnalysisView.js +282 -302
- package/public/js/deepAnalysisView.js.map +1 -0
- package/public/js/diagramPane.d.ts +8 -0
- package/public/js/diagramPane.js +133 -135
- package/public/js/diagramPane.js.map +1 -0
- package/public/js/diffView.d.ts +6 -0
- package/public/js/diffView.js +222 -209
- package/public/js/diffView.js.map +1 -0
- package/public/js/docsView.d.ts +2 -0
- package/public/js/docsView.js +441 -442
- package/public/js/docsView.js.map +1 -0
- package/public/js/dom.d.ts +189 -0
- package/public/js/dom.js +5 -0
- package/public/js/dom.js.map +1 -0
- package/public/js/fileSourceBrowser.d.ts +5 -0
- package/public/js/fileSourceBrowser.js +33 -41
- package/public/js/fileSourceBrowser.js.map +1 -0
- package/public/js/flameGraph.d.ts +5 -0
- package/public/js/flameGraph.js +62 -72
- package/public/js/flameGraph.js.map +1 -0
- package/public/js/forceGraph.d.ts +10 -0
- package/public/js/forceGraph.js +581 -556
- package/public/js/forceGraph.js.map +1 -0
- package/public/js/heatmap.d.ts +4 -0
- package/public/js/heatmap.js +142 -164
- package/public/js/heatmap.js.map +1 -0
- package/public/js/highlight.d.ts +2 -0
- package/public/js/highlight.js +116 -107
- package/public/js/highlight.js.map +1 -0
- package/public/js/html.d.ts +2 -0
- package/public/js/html.js +14 -44
- package/public/js/html.js.map +1 -0
- package/public/js/insightsView.d.ts +5 -0
- package/public/js/insightsView.js +93 -111
- package/public/js/insightsView.js.map +1 -0
- package/public/js/inspector.d.ts +37 -0
- package/public/js/inspector.js +416 -445
- package/public/js/inspector.js.map +1 -0
- package/public/js/llm.d.ts +113 -0
- package/public/js/llm.js +233 -272
- package/public/js/llm.js.map +1 -0
- package/public/js/mapView.d.ts +3 -0
- package/public/js/mapView.js +121 -120
- package/public/js/mapView.js.map +1 -0
- package/public/js/markdown.d.ts +3 -0
- package/public/js/markdown.js +84 -91
- package/public/js/markdown.js.map +1 -0
- package/public/js/mindmap.d.ts +32 -0
- package/public/js/mindmap.js +244 -259
- package/public/js/mindmap.js.map +1 -0
- package/public/js/mobilePanels.d.ts +5 -0
- package/public/js/mobilePanels.js +50 -0
- package/public/js/mobilePanels.js.map +1 -0
- package/public/js/repoFiles.d.ts +12 -0
- package/public/js/repoFiles.js +28 -27
- package/public/js/repoFiles.js.map +1 -0
- package/public/js/reviewView.d.ts +28 -0
- package/public/js/reviewView.js +256 -0
- package/public/js/reviewView.js.map +1 -0
- package/public/js/sbomView.d.ts +3 -0
- package/public/js/sbomView.js +59 -67
- package/public/js/sbomView.js.map +1 -0
- package/public/js/scanCache.d.ts +16 -0
- package/public/js/scanCache.js +67 -65
- package/public/js/scanCache.js.map +1 -0
- package/public/js/search.d.ts +7 -0
- package/public/js/search.js +868 -800
- package/public/js/search.js.map +1 -0
- package/public/js/searchLocal.d.ts +42 -0
- package/public/js/searchLocal.js +87 -0
- package/public/js/searchLocal.js.map +1 -0
- package/public/js/serverSettings.d.ts +5 -0
- package/public/js/serverSettings.js +153 -164
- package/public/js/serverSettings.js.map +1 -0
- package/public/js/state.d.ts +798 -0
- package/public/js/state.js +114 -122
- package/public/js/state.js.map +1 -0
- package/public/js/tour.d.ts +6 -0
- package/public/js/tour.js +5 -7
- package/public/js/tour.js.map +1 -0
- package/public/js/transitions.d.ts +2 -0
- package/public/js/transitions.js +9 -10
- package/public/js/transitions.js.map +1 -0
- package/public/js/tree.d.ts +12 -0
- package/public/js/tree.js +157 -154
- package/public/js/tree.js.map +1 -0
- package/public/js/workflowsView.d.ts +3 -0
- package/public/js/workflowsView.js +15 -22
- package/public/js/workflowsView.js.map +1 -0
- package/public/search.css +495 -0
- package/public/styles.css +163 -346
- package/public/workbench.css +166 -0
- package/server/apiAuth.d.ts +3 -0
- package/server/apiAuth.js +18 -20
- package/server/apiAuth.js.map +1 -0
- package/server/apiDiff.d.ts +2 -0
- package/server/apiDiff.js +20 -18
- package/server/apiDiff.js.map +1 -0
- package/server/apiDocs.d.ts +2 -0
- package/server/apiDocs.js +38 -40
- package/server/apiDocs.js.map +1 -0
- package/server/apiFile.d.ts +1 -0
- package/server/apiFile.js +14 -17
- package/server/apiFile.js.map +1 -0
- package/server/apiGitBlame.d.ts +2 -0
- package/server/apiGitBlame.js +50 -51
- package/server/apiGitBlame.js.map +1 -0
- package/server/apiMcp.d.ts +4 -0
- package/server/apiMcp.js +64 -66
- package/server/apiMcp.js.map +1 -0
- package/server/apiReview.d.ts +2 -0
- package/server/apiReview.js +45 -0
- package/server/apiReview.js.map +1 -0
- package/server/apiScan.d.ts +5 -0
- package/server/apiScan.js +90 -71
- package/server/apiScan.js.map +1 -0
- package/server/apiSearch.d.ts +33 -0
- package/server/apiSearch.js +184 -200
- package/server/apiSearch.js.map +1 -0
- package/server/apiSettings.d.ts +4 -0
- package/server/apiSettings.js +84 -85
- package/server/apiSettings.js.map +1 -0
- package/server/apiTools.d.ts +3 -0
- package/server/apiTools.js +55 -56
- package/server/apiTools.js.map +1 -0
- package/server/auth.d.ts +5 -0
- package/server/auth.js +54 -51
- package/server/auth.js.map +1 -0
- package/server/config.d.ts +42 -0
- package/server/config.js +189 -194
- package/server/config.js.map +1 -0
- package/server/daemon.d.ts +12 -0
- package/server/daemon.js +124 -114
- package/server/daemon.js.map +1 -0
- package/server/fileSourceNode.d.ts +2 -0
- package/server/fileSourceNode.js +34 -36
- package/server/fileSourceNode.js.map +1 -0
- package/server/gitClone.d.ts +9 -0
- package/server/gitClone.js +59 -61
- package/server/gitClone.js.map +1 -0
- package/server/gitDiff.d.ts +36 -0
- package/server/gitDiff.js +271 -169
- package/server/gitDiff.js.map +1 -0
- package/server/gitHistory.d.ts +12 -0
- package/server/gitHistory.js +80 -80
- package/server/gitHistory.js.map +1 -0
- package/server/gitRemote.d.ts +12 -0
- package/server/gitRemote.js +60 -0
- package/server/gitRemote.js.map +1 -0
- package/server/htmlText.d.ts +4 -0
- package/server/htmlText.js +16 -23
- package/server/htmlText.js.map +1 -0
- package/server/http.d.ts +5 -0
- package/server/http.js +27 -30
- package/server/http.js.map +1 -0
- package/server/httpGuards.d.ts +2 -0
- package/server/httpGuards.js +47 -43
- package/server/httpGuards.js.map +1 -0
- package/server/https.d.ts +34 -0
- package/server/https.js +121 -111
- package/server/https.js.map +1 -0
- package/server/index.d.ts +40 -0
- package/server/index.js +139 -147
- package/server/index.js.map +1 -0
- package/server/layout.d.ts +26 -0
- package/server/layout.js +59 -37
- package/server/layout.js.map +1 -0
- package/server/llmProxy.d.ts +3 -0
- package/server/llmProxy.js +137 -135
- package/server/llmProxy.js.map +1 -0
- package/server/logger.d.ts +38 -0
- package/server/logger.js +198 -211
- package/server/logger.js.map +1 -0
- package/server/mcp/analysis.d.ts +29 -0
- package/server/mcp/analysis.js +99 -120
- package/server/mcp/analysis.js.map +1 -0
- package/server/mcp/http.d.ts +9 -0
- package/server/mcp/http.js +128 -137
- package/server/mcp/http.js.map +1 -0
- package/server/mcp/runner.d.ts +68 -0
- package/server/mcp/runner.js +208 -218
- package/server/mcp/runner.js.map +1 -0
- package/server/mcp/server.d.ts +29 -0
- package/server/mcp/server.js +166 -179
- package/server/mcp/server.js.map +1 -0
- package/server/mcp/standalone.d.ts +1 -0
- package/server/mcp/standalone.js +8 -10
- package/server/mcp/standalone.js.map +1 -0
- package/server/mcp/tools.d.ts +2020 -0
- package/server/mcp/tools.js +523 -574
- package/server/mcp/tools.js.map +1 -0
- package/server/paths.d.ts +3 -0
- package/server/paths.js +17 -15
- package/server/paths.js.map +1 -0
- package/server/pidfile.d.ts +9 -0
- package/server/pidfile.js +43 -45
- package/server/pidfile.js.map +1 -0
- package/server/review.d.ts +5 -0
- package/server/review.js +35 -0
- package/server/review.js.map +1 -0
- package/server/router.d.ts +33 -0
- package/server/router.js +191 -197
- package/server/router.js.map +1 -0
- package/server/scanRequest.d.ts +3 -0
- package/server/scanRequest.js +36 -0
- package/server/scanRequest.js.map +1 -0
- package/server/searchIndex.d.ts +17 -0
- package/server/searchIndex.js +83 -80
- package/server/searchIndex.js.map +1 -0
- package/server/sessions.d.ts +14 -0
- package/server/sessions.js +62 -70
- package/server/sessions.js.map +1 -0
- package/server/startup.d.ts +118 -0
- package/server/startup.js +143 -148
- package/server/startup.js.map +1 -0
- package/server/static.d.ts +6 -0
- package/server/static.js +28 -31
- package/server/static.js.map +1 -0
- package/server/tools/install.d.ts +4 -0
- package/server/tools/install.js +176 -185
- package/server/tools/install.js.map +1 -0
- package/server/tools/parse.d.ts +8 -0
- package/server/tools/parse.js +112 -114
- package/server/tools/parse.js.map +1 -0
- package/server/tools/platform.d.ts +9 -0
- package/server/tools/platform.js +49 -47
- package/server/tools/platform.js.map +1 -0
- package/server/tools/registry.d.ts +137 -0
- package/server/tools/registry.js +173 -172
- package/server/tools/registry.js.map +1 -0
- package/server/tools/scan.d.ts +34 -0
- package/server/tools/scan.js +89 -98
- package/server/tools/scan.js.map +1 -0
- package/server/tools.d.ts +34 -0
- package/server/tools.js +94 -107
- package/server/tools.js.map +1 -0
- package/server/tunnel.d.ts +29 -0
- package/server/tunnel.js +61 -66
- package/server/tunnel.js.map +1 -0
- package/shared/analyzer/docs.d.ts +16 -0
- package/shared/analyzer/docs.js +99 -93
- package/shared/analyzer/docs.js.map +1 -0
- package/shared/analyzer/explainLocal.d.ts +4 -0
- package/shared/analyzer/explainLocal.js +122 -142
- package/shared/analyzer/explainLocal.js.map +1 -0
- package/shared/analyzer/github.d.ts +37 -0
- package/shared/analyzer/github.js +79 -0
- package/shared/analyzer/github.js.map +1 -0
- package/shared/analyzer/graph.d.ts +41 -0
- package/shared/analyzer/graph.js +393 -400
- package/shared/analyzer/graph.js.map +1 -0
- package/shared/analyzer/health.d.ts +33 -0
- package/shared/analyzer/health.js +197 -193
- package/shared/analyzer/health.js.map +1 -0
- package/shared/analyzer/history.d.ts +29 -0
- package/shared/analyzer/history.js +110 -111
- package/shared/analyzer/history.js.map +1 -0
- package/shared/analyzer/jsonConfig.d.ts +2 -0
- package/shared/analyzer/jsonConfig.js +61 -0
- package/shared/analyzer/jsonConfig.js.map +1 -0
- package/shared/analyzer/languages/csharp.d.ts +26 -0
- package/shared/analyzer/languages/csharp.js +83 -88
- package/shared/analyzer/languages/csharp.js.map +1 -0
- package/shared/analyzer/languages/generic.d.ts +23 -0
- package/shared/analyzer/languages/generic.js +94 -105
- package/shared/analyzer/languages/generic.js.map +1 -0
- package/shared/analyzer/languages/go.d.ts +24 -0
- package/shared/analyzer/languages/go.js +45 -54
- package/shared/analyzer/languages/go.js.map +1 -0
- package/shared/analyzer/languages/index.d.ts +6 -0
- package/shared/analyzer/languages/index.js +19 -20
- package/shared/analyzer/languages/index.js.map +1 -0
- package/shared/analyzer/languages/java.d.ts +20 -0
- package/shared/analyzer/languages/java.js +73 -78
- package/shared/analyzer/languages/java.js.map +1 -0
- package/shared/analyzer/languages/javascript.d.ts +12 -0
- package/shared/analyzer/languages/javascript.js +258 -209
- package/shared/analyzer/languages/javascript.js.map +1 -0
- package/shared/analyzer/languages/python.d.ts +23 -0
- package/shared/analyzer/languages/python.js +92 -100
- package/shared/analyzer/languages/python.js.map +1 -0
- package/shared/analyzer/languages/rust.d.ts +17 -0
- package/shared/analyzer/languages/rust.js +96 -98
- package/shared/analyzer/languages/rust.js.map +1 -0
- package/shared/analyzer/languages/typescript.d.ts +11 -0
- package/shared/analyzer/languages/typescript.js +117 -116
- package/shared/analyzer/languages/typescript.js.map +1 -0
- package/shared/analyzer/licenses.d.ts +18 -0
- package/shared/analyzer/licenses.js +130 -133
- package/shared/analyzer/licenses.js.map +1 -0
- package/shared/analyzer/metrics.d.ts +20 -0
- package/shared/analyzer/metrics.js +112 -104
- package/shared/analyzer/metrics.js.map +1 -0
- package/shared/analyzer/pathUtil.d.ts +10 -0
- package/shared/analyzer/pathUtil.js +29 -35
- package/shared/analyzer/pathUtil.js.map +1 -0
- package/shared/analyzer/patterns.d.ts +11 -0
- package/shared/analyzer/patterns.js +231 -253
- package/shared/analyzer/patterns.js.map +1 -0
- package/shared/analyzer/scan.d.ts +62 -0
- package/shared/analyzer/scan.js +451 -442
- package/shared/analyzer/scan.js.map +1 -0
- package/shared/analyzer/scanControl.d.ts +11 -0
- package/shared/analyzer/scanControl.js +62 -0
- package/shared/analyzer/scanControl.js.map +1 -0
- package/shared/analyzer/security.d.ts +18 -0
- package/shared/analyzer/security.js +159 -159
- package/shared/analyzer/security.js.map +1 -0
- package/shared/analyzer/services.d.ts +5 -0
- package/shared/analyzer/services.js +141 -138
- package/shared/analyzer/services.js.map +1 -0
- package/shared/analyzer/stack.d.ts +6 -0
- package/shared/analyzer/stack.js +134 -141
- package/shared/analyzer/stack.js.map +1 -0
- package/shared/analyzer/tour.d.ts +2 -0
- package/shared/analyzer/tour.js +45 -53
- package/shared/analyzer/tour.js.map +1 -0
- package/shared/analyzer/util.d.ts +8 -0
- package/shared/analyzer/util.js +130 -76
- package/shared/analyzer/util.js.map +1 -0
- package/shared/analyzer/workflows.d.ts +8 -0
- package/shared/analyzer/workflows.js +139 -134
- package/shared/analyzer/workflows.js.map +1 -0
- package/shared/contracts.d.ts +250 -0
- package/shared/contracts.js +2 -0
- package/shared/contracts.js.map +1 -0
- package/shared/diagram/aiFacts.d.ts +35 -0
- package/shared/diagram/aiFacts.js +102 -105
- package/shared/diagram/aiFacts.js.map +1 -0
- package/shared/diagram/aiMermaid.d.ts +5 -0
- package/shared/diagram/aiMermaid.js +46 -45
- package/shared/diagram/aiMermaid.js.map +1 -0
- package/shared/diagram/atlas.d.ts +16 -0
- package/shared/diagram/atlas.js +51 -60
- package/shared/diagram/atlas.js.map +1 -0
- package/shared/diagram/mermaid.d.ts +43 -0
- package/shared/diagram/mermaid.js +348 -370
- package/shared/diagram/mermaid.js.map +1 -0
- package/shared/review/contracts.d.ts +95 -0
- package/shared/review/contracts.js +2 -0
- package/shared/review/contracts.js.map +1 -0
- package/shared/review/review.d.ts +10 -0
- package/shared/review/review.js +184 -0
- package/shared/review/review.js.map +1 -0
- package/shared/search/query.d.ts +32 -0
- package/shared/search/query.js +395 -388
- package/shared/search/query.js.map +1 -0
package/shared/search/query.js
CHANGED
|
@@ -27,244 +27,236 @@
|
|
|
27
27
|
// Bare words stay forgiving, because a half-remembered name should still find
|
|
28
28
|
// the file. That split is the whole design: `matchesPathFilters` is a gate,
|
|
29
29
|
// `scorePath`/`scoreContent` are a ranking.
|
|
30
|
-
|
|
31
30
|
import { baseName, extOf, isTestPath } from '../analyzer/pathUtil.js';
|
|
32
|
-
|
|
33
31
|
// A regex longer than this is either a paste accident or an attempt to make the
|
|
34
32
|
// tab hang. Real patterns people type at a search box are two orders of
|
|
35
33
|
// magnitude shorter than the cap.
|
|
36
34
|
const MAX_REGEX_LENGTH = 200;
|
|
37
|
-
|
|
38
35
|
// Quoted phrase | /regex/flags | bare token. Order matters: the quote and the
|
|
39
36
|
// slash have to win before `\S+` gets a chance to swallow them whole.
|
|
40
37
|
const TOKEN_RE = /"([^"]*)"|\/([^\s/]+)\/([gimsuy]*)|\S+/g;
|
|
41
|
-
|
|
42
38
|
// Ranges are capped as they are collected. A one-letter query against a
|
|
43
39
|
// minified bundle can match tens of thousands of times, and the UI only ever
|
|
44
40
|
// paints the first screenful.
|
|
45
41
|
const MAX_RANGES = 400;
|
|
46
|
-
|
|
47
42
|
function fold(text, caseSensitive) {
|
|
48
|
-
|
|
43
|
+
return caseSensitive ? text : text.toLowerCase();
|
|
49
44
|
}
|
|
50
|
-
|
|
51
45
|
function splitList(value) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
return value
|
|
47
|
+
.split(',')
|
|
48
|
+
.map((part) => part.trim().toLowerCase().replace(/^\./, ''))
|
|
49
|
+
.filter(Boolean);
|
|
56
50
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
51
|
export function parseQuery(raw, options = {}) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
52
|
+
const caseSensitive = !!options.caseSensitive;
|
|
53
|
+
const text = String(raw ?? '');
|
|
54
|
+
const parsed = {
|
|
55
|
+
raw: text,
|
|
56
|
+
clauses: [], // { kind, raw, start, end } — what the input was made of
|
|
57
|
+
terms: [], // bare words — fuzzy against paths, substring in content
|
|
58
|
+
phrases: [], // "quoted" — verbatim substrings
|
|
59
|
+
negatives: [], // -foo — must not appear in the path
|
|
60
|
+
exts: [], // ext:js,ts — extensions without the dot
|
|
61
|
+
paths: [], // path:src/api — path substrings
|
|
62
|
+
kinds: [], // kind:function — symbol kinds
|
|
63
|
+
isTest: false, // is:test
|
|
64
|
+
isSource: false, // is:source
|
|
65
|
+
regex: null, // the pattern as authored
|
|
66
|
+
regexSource: '',
|
|
67
|
+
regexFlags: '',
|
|
68
|
+
regexGlobal: null, // a 'g' copy used for scanning, never for matching
|
|
69
|
+
caseSensitive,
|
|
70
|
+
error: null,
|
|
71
|
+
};
|
|
72
|
+
if (!text.trim())
|
|
73
|
+
return parsed;
|
|
74
|
+
const lower = (s) => fold(s, caseSensitive);
|
|
75
|
+
for (const m of text.matchAll(TOKEN_RE)) {
|
|
76
|
+
// The span each clause came from, so the palette can turn a filter into a
|
|
77
|
+
// chip and take it back out of the input when the chip is dismissed.
|
|
78
|
+
const note = (kind) => parsed.clauses.push({
|
|
79
|
+
kind, raw: m[0], start: m.index, end: m.index + m[0].length,
|
|
80
|
+
});
|
|
81
|
+
if (m[1] !== undefined) {
|
|
82
|
+
const phrase = m[1].trim();
|
|
83
|
+
if (phrase) {
|
|
84
|
+
parsed.phrases.push(lower(phrase));
|
|
85
|
+
note('phrase');
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (m[2] !== undefined) {
|
|
90
|
+
const source = m[2];
|
|
91
|
+
parsed.regexSource = source;
|
|
92
|
+
// Flags the person typed are respected exactly; with no flags at all the
|
|
93
|
+
// case toggle decides, which is what makes `/Foo/` and the `Aa` button
|
|
94
|
+
// behave the way a reader expects.
|
|
95
|
+
const flags = m[3] || (caseSensitive ? '' : 'i');
|
|
96
|
+
parsed.regexFlags = flags;
|
|
97
|
+
if (source.length > MAX_REGEX_LENGTH) {
|
|
98
|
+
parsed.error = `Regex is too long (${source.length} > ${MAX_REGEX_LENGTH}).`;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
parsed.regex = new RegExp(source, flags.replace('g', ''));
|
|
103
|
+
parsed.regexGlobal = new RegExp(source, flags.includes('g') ? flags : flags + 'g');
|
|
104
|
+
note('regex');
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
parsed.error = 'Bad regex: ' + err.message;
|
|
108
|
+
}
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const token = m[0];
|
|
112
|
+
if (!token)
|
|
113
|
+
continue;
|
|
114
|
+
if (token.length > 1 && token.startsWith('-')) {
|
|
115
|
+
parsed.negatives.push(lower(token.slice(1)));
|
|
116
|
+
note('neg');
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const colon = token.indexOf(':');
|
|
120
|
+
if (colon > 0) {
|
|
121
|
+
const field = token.slice(0, colon).toLowerCase();
|
|
122
|
+
const value = token.slice(colon + 1);
|
|
123
|
+
if (!value)
|
|
124
|
+
continue;
|
|
125
|
+
if (field === 'ext' || field === 'exts' || field === 'type') {
|
|
126
|
+
parsed.exts.push(...splitList(value));
|
|
127
|
+
note('ext');
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (field === 'path' || field === 'in') {
|
|
131
|
+
parsed.paths.push(lower(value));
|
|
132
|
+
note('path');
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (field === 'kind') {
|
|
136
|
+
parsed.kinds.push(value.toLowerCase());
|
|
137
|
+
note('kind');
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (field === 'is') {
|
|
141
|
+
const v = value.toLowerCase();
|
|
142
|
+
if (v === 'test' || v === 'tests') {
|
|
143
|
+
parsed.isTest = true;
|
|
144
|
+
note('is');
|
|
145
|
+
}
|
|
146
|
+
else if (v === 'source' || v === 'src') {
|
|
147
|
+
parsed.isSource = true;
|
|
148
|
+
note('is');
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
parsed.terms.push(lower(token));
|
|
152
|
+
note('term');
|
|
153
|
+
}
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
// An unrecognized `foo:bar` is more likely a real word (a URL, a
|
|
157
|
+
// protocol, `Read:`) than a typo'd clause. Search for it as text.
|
|
158
|
+
}
|
|
159
|
+
parsed.terms.push(lower(token));
|
|
160
|
+
note('term');
|
|
96
161
|
}
|
|
97
|
-
|
|
98
|
-
if (m[2] !== undefined) {
|
|
99
|
-
const source = m[2];
|
|
100
|
-
parsed.regexSource = source;
|
|
101
|
-
// Flags the person typed are respected exactly; with no flags at all the
|
|
102
|
-
// case toggle decides, which is what makes `/Foo/` and the `Aa` button
|
|
103
|
-
// behave the way a reader expects.
|
|
104
|
-
const flags = m[3] || (caseSensitive ? '' : 'i');
|
|
105
|
-
parsed.regexFlags = flags;
|
|
106
|
-
if (source.length > MAX_REGEX_LENGTH) {
|
|
107
|
-
parsed.error = `Regex is too long (${source.length} > ${MAX_REGEX_LENGTH}).`;
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
parsed.regex = new RegExp(source, flags.replace('g', ''));
|
|
112
|
-
parsed.regexGlobal = new RegExp(source, flags.includes('g') ? flags : flags + 'g');
|
|
113
|
-
note('regex');
|
|
114
|
-
} catch (err) {
|
|
115
|
-
parsed.error = 'Bad regex: ' + err.message;
|
|
116
|
-
}
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const token = m[0];
|
|
121
|
-
if (!token) continue;
|
|
122
|
-
|
|
123
|
-
if (token.length > 1 && token.startsWith('-')) {
|
|
124
|
-
parsed.negatives.push(lower(token.slice(1)));
|
|
125
|
-
note('neg');
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const colon = token.indexOf(':');
|
|
130
|
-
if (colon > 0) {
|
|
131
|
-
const field = token.slice(0, colon).toLowerCase();
|
|
132
|
-
const value = token.slice(colon + 1);
|
|
133
|
-
if (!value) continue;
|
|
134
|
-
if (field === 'ext' || field === 'exts' || field === 'type') {
|
|
135
|
-
parsed.exts.push(...splitList(value));
|
|
136
|
-
note('ext');
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (field === 'path' || field === 'in') {
|
|
140
|
-
parsed.paths.push(lower(value));
|
|
141
|
-
note('path');
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
if (field === 'kind') {
|
|
145
|
-
parsed.kinds.push(value.toLowerCase());
|
|
146
|
-
note('kind');
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
if (field === 'is') {
|
|
150
|
-
const v = value.toLowerCase();
|
|
151
|
-
if (v === 'test' || v === 'tests') { parsed.isTest = true; note('is'); }
|
|
152
|
-
else if (v === 'source' || v === 'src') { parsed.isSource = true; note('is'); }
|
|
153
|
-
else { parsed.terms.push(lower(token)); note('term'); }
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
// An unrecognized `foo:bar` is more likely a real word (a URL, a
|
|
157
|
-
// protocol, `Read:`) than a typo'd clause. Search for it as text.
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
parsed.terms.push(lower(token));
|
|
161
|
-
note('term');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return parsed;
|
|
162
|
+
return parsed;
|
|
165
163
|
}
|
|
166
|
-
|
|
167
164
|
// Take one clause back out of the raw query, leaving the rest of the text
|
|
168
165
|
// exactly as it was typed. The palette uses this when a filter chip is
|
|
169
166
|
// dismissed — hand-editing a query that contains a quoted phrase is otherwise
|
|
170
167
|
// a counting exercise.
|
|
171
168
|
export function removeClause(raw, clause) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
const text = String(raw ?? '');
|
|
170
|
+
if (!clause)
|
|
171
|
+
return text;
|
|
172
|
+
const start = Math.max(0, Math.min(Number(clause.start) || 0, text.length));
|
|
173
|
+
const end = Math.max(start, Math.min(Number(clause.end) || start, text.length));
|
|
174
|
+
const head = text.slice(0, start).replace(/\s+$/, '');
|
|
175
|
+
const tail = text.slice(end).replace(/^\s+/, '');
|
|
176
|
+
if (head && tail)
|
|
177
|
+
return `${head} ${tail}`;
|
|
178
|
+
return head || tail;
|
|
180
179
|
}
|
|
181
|
-
|
|
182
180
|
// Is there anything to search *for*? A query of pure filters still filters —
|
|
183
181
|
// see `hasFilters` — but it has no text to rank with, and that difference is
|
|
184
182
|
// what tells the palette to stop asking the server for content matches.
|
|
185
183
|
export function queryIsEmpty(parsed) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
&& !parsed.kinds.length && !parsed.isTest && !parsed.isSource
|
|
190
|
-
);
|
|
184
|
+
return !parsed || (!parsed.terms.length && !parsed.phrases.length && !parsed.regex
|
|
185
|
+
&& !parsed.negatives.length && !parsed.exts.length && !parsed.paths.length
|
|
186
|
+
&& !parsed.kinds.length && !parsed.isTest && !parsed.isSource);
|
|
191
187
|
}
|
|
192
|
-
|
|
193
188
|
export function hasFilters(parsed) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|| parsed.kinds.length || parsed.isTest || parsed.isSource
|
|
197
|
-
));
|
|
189
|
+
return !!(parsed && (parsed.negatives.length || parsed.exts.length || parsed.paths.length
|
|
190
|
+
|| parsed.kinds.length || parsed.isTest || parsed.isSource));
|
|
198
191
|
}
|
|
199
|
-
|
|
200
192
|
// Advanced means "the strict clauses are in play": filters, a phrase, a regex,
|
|
201
193
|
// or the case toggle. A query of bare words alone keeps the forgiving ranked
|
|
202
194
|
// behaviour the palette always had — partial matches included — which is what a
|
|
203
195
|
// half-remembered name deserves. Anything else is a decision, and decisions are
|
|
204
196
|
// enforced rather than ranked.
|
|
205
197
|
export function isAdvanced(parsed) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
198
|
+
if (!parsed)
|
|
199
|
+
return false;
|
|
200
|
+
return hasFilters(parsed)
|
|
201
|
+
|| parsed.phrases.length > 0
|
|
202
|
+
|| parsed.regex !== null
|
|
203
|
+
|| parsed.caseSensitive;
|
|
211
204
|
}
|
|
212
|
-
|
|
213
205
|
// ---- the gate -------------------------------------------------------------
|
|
214
|
-
|
|
215
206
|
// Everything that can be decided from the path alone. Cheap, so it runs first
|
|
216
207
|
// and lets the expensive content pass skip the file entirely.
|
|
217
208
|
export function matchesPathFilters(parsed, path) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (parsed.paths.length) {
|
|
228
|
-
const folded = fold(path, parsed.caseSensitive);
|
|
229
|
-
for (const needle of parsed.paths) {
|
|
230
|
-
if (!folded.includes(needle)) return false;
|
|
209
|
+
if (!parsed)
|
|
210
|
+
return true;
|
|
211
|
+
if (parsed.exts.length) {
|
|
212
|
+
// `extOf` hands back the dot (`'.js'`); the filter is stored without it,
|
|
213
|
+
// because `ext:js` is what people type.
|
|
214
|
+
const want = extOf(path).replace(/^\./, '');
|
|
215
|
+
if (!parsed.exts.includes(want))
|
|
216
|
+
return false;
|
|
231
217
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
218
|
+
if (parsed.paths.length) {
|
|
219
|
+
const folded = fold(path, parsed.caseSensitive);
|
|
220
|
+
for (const needle of parsed.paths) {
|
|
221
|
+
if (!folded.includes(needle))
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (parsed.isTest && !isTestPath(path))
|
|
226
|
+
return false;
|
|
227
|
+
if (parsed.isSource && isTestPath(path))
|
|
228
|
+
return false;
|
|
229
|
+
if (parsed.negatives.length) {
|
|
230
|
+
const folded = fold(path, parsed.caseSensitive);
|
|
231
|
+
for (const needle of parsed.negatives) {
|
|
232
|
+
if (folded.includes(needle))
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (parsed.kinds.length) {
|
|
237
|
+
// A kind filter is a statement about symbols, not paths: the caller has to
|
|
238
|
+
// know the symbol to answer it. Answer "yes" here and let the symbol pass
|
|
239
|
+
// apply it, so a kind filter never silently hides every file.
|
|
240
|
+
return true;
|
|
241
241
|
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (parsed.kinds.length) {
|
|
245
|
-
// A kind filter is a statement about symbols, not paths: the caller has to
|
|
246
|
-
// know the symbol to answer it. Answer "yes" here and let the symbol pass
|
|
247
|
-
// apply it, so a kind filter never silently hides every file.
|
|
248
242
|
return true;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return true;
|
|
252
243
|
}
|
|
253
|
-
|
|
254
244
|
// Decimals matter only for ordering, and this keeps the ordering stable: exact
|
|
255
245
|
// matches win, then a prefix, then a word boundary, then anywhere.
|
|
256
246
|
function occurrenceScore(hay, needle, weight) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
247
|
+
const at = hay.indexOf(needle);
|
|
248
|
+
if (at === -1)
|
|
249
|
+
return 0;
|
|
250
|
+
let score = weight - at;
|
|
251
|
+
if (at === 0)
|
|
252
|
+
score += weight * 0.6;
|
|
253
|
+
else if (/[^a-z0-9]/.test(hay[at - 1]))
|
|
254
|
+
score += weight * 0.35;
|
|
255
|
+
if (hay === needle)
|
|
256
|
+
score += weight * 1.2;
|
|
257
|
+
return score;
|
|
264
258
|
}
|
|
265
|
-
|
|
266
259
|
// ---- the ranking ----------------------------------------------------------
|
|
267
|
-
|
|
268
260
|
// Subsequence match, the fzf idea: `rt` finds `router.js` because the letters
|
|
269
261
|
// appear in order. Adjacent runs, word boundaries and camel humps are worth
|
|
270
262
|
// more than letters sprinkled through a long path, which is what stops `rt`
|
|
@@ -273,121 +265,133 @@ function occurrenceScore(hay, needle, weight) {
|
|
|
273
265
|
// `raw` is kept alongside the folded haystack so the camel hump can be seen —
|
|
274
266
|
// folding throws away the case that carries the signal.
|
|
275
267
|
function fuzzyScore(needle, hay, raw) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
268
|
+
let score = 0;
|
|
269
|
+
let at = 0;
|
|
270
|
+
let streak = 0;
|
|
271
|
+
let first = -1;
|
|
272
|
+
for (let i = 0; i < needle.length; i++) {
|
|
273
|
+
const found = hay.indexOf(needle[i], at);
|
|
274
|
+
if (found === -1)
|
|
275
|
+
return 0;
|
|
276
|
+
if (first === -1)
|
|
277
|
+
first = found;
|
|
278
|
+
streak = found === at && i > 0 ? streak + 1 : 0;
|
|
279
|
+
score += 10 + streak * 6;
|
|
280
|
+
if (found === 0)
|
|
281
|
+
score += 14;
|
|
282
|
+
else if (/[^a-z0-9]/.test(hay[found - 1]))
|
|
283
|
+
score += 12;
|
|
284
|
+
else if (raw && /[a-z0-9]/.test(raw[found - 1] || '') && /[A-Z]/.test(raw[found] || ''))
|
|
285
|
+
score += 10;
|
|
286
|
+
at = found + 1;
|
|
287
|
+
}
|
|
288
|
+
score -= first; // earlier is better
|
|
289
|
+
score -= Math.max(0, hay.length - needle.length) * 0.4; // shorter is better
|
|
290
|
+
return Math.max(1, score);
|
|
299
291
|
}
|
|
300
|
-
|
|
301
292
|
// How well a path answers the free-text part of the query. Filters are not
|
|
302
293
|
// consulted here — `matchesPathFilters` owns those.
|
|
303
294
|
export function scorePath(parsed, path) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
295
|
+
if (!parsed || queryIsEmpty(parsed))
|
|
296
|
+
return 1;
|
|
297
|
+
const hay = fold(path, parsed.caseSensitive);
|
|
298
|
+
const raw = path;
|
|
299
|
+
const base = baseName(path);
|
|
300
|
+
const baseHay = fold(base, parsed.caseSensitive);
|
|
301
|
+
let score = 0;
|
|
302
|
+
for (const phrase of parsed.phrases) {
|
|
303
|
+
if (!hay.includes(phrase))
|
|
304
|
+
return 0;
|
|
305
|
+
score += occurrenceScore(hay, phrase, 90);
|
|
306
|
+
}
|
|
307
|
+
if (parsed.regex) {
|
|
308
|
+
parsed.regex.lastIndex = 0;
|
|
309
|
+
if (!parsed.regex.test(path))
|
|
310
|
+
return 0;
|
|
311
|
+
score += 80;
|
|
312
|
+
}
|
|
313
|
+
for (const term of parsed.terms) {
|
|
314
|
+
const exact = baseHay === term ? 320 : 0;
|
|
315
|
+
if (exact) {
|
|
316
|
+
score += exact;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const inBase = occurrenceScore(baseHay, term, 150);
|
|
320
|
+
if (inBase) {
|
|
321
|
+
score += inBase;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
const inPath = occurrenceScore(hay, term, 70);
|
|
325
|
+
if (inPath) {
|
|
326
|
+
score += inPath;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const fuzzy = fuzzyScore(term, baseHay, base) || fuzzyScore(term, hay, raw);
|
|
330
|
+
if (!fuzzy)
|
|
331
|
+
return 0;
|
|
332
|
+
score += fuzzy;
|
|
333
|
+
}
|
|
334
|
+
// A path with no holder for a bare term is not a match at all. (Filter-only
|
|
335
|
+
// queries have no terms and are already satisfied by the gate.)
|
|
336
|
+
if (!score && parsed.terms.length)
|
|
337
|
+
return 0;
|
|
338
|
+
// Nothing to rank by: a query of pure filters is a set, not an ordering, so
|
|
339
|
+
// it answers with a flat 1 and skips the tie-breaking below.
|
|
340
|
+
if (!parsed.terms.length)
|
|
341
|
+
return score || 1;
|
|
342
|
+
// Shallower files win ties. When the same name appears at three depths — a
|
|
343
|
+
// page, a feature folder and a barrel — the one nearest the top of the tree
|
|
344
|
+
// is usually the one meant. The clamp matters: a match must stay strictly
|
|
345
|
+
// positive, because zero is how the callers spell "no match at all".
|
|
346
|
+
const adjusted = score - path.split('/').length * 0.5;
|
|
347
|
+
return adjusted > 0 ? adjusted : 0.5;
|
|
348
348
|
}
|
|
349
|
-
|
|
350
349
|
// A symbol is a name plus the kind it was declared with. `kind:` and the bare
|
|
351
350
|
// terms both apply here, and nothing about extensions or paths does.
|
|
352
351
|
export function scoreSymbol(parsed, name, kind) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
parsed.regex
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (foldedName.includes(negative)) return 0;
|
|
367
|
-
}
|
|
368
|
-
for (const phrase of parsed.phrases) {
|
|
369
|
-
if (!foldedName.includes(phrase)) return 0;
|
|
370
|
-
}
|
|
371
|
-
if (!parsed.terms.length) return parsed.regex || parsed.phrases.length || parsed.kinds.length ? 60 : 0;
|
|
372
|
-
|
|
373
|
-
let score = 0;
|
|
374
|
-
for (const term of parsed.terms) {
|
|
375
|
-
if (foldedName === term) score += 240;
|
|
376
|
-
else {
|
|
377
|
-
const hit = occurrenceScore(foldedName, term, 130);
|
|
378
|
-
if (hit) score += hit;
|
|
379
|
-
else {
|
|
380
|
-
const fuzzy = fuzzyScore(term, foldedName, name);
|
|
381
|
-
if (!fuzzy) return 0;
|
|
382
|
-
score += fuzzy;
|
|
383
|
-
}
|
|
352
|
+
if (!parsed)
|
|
353
|
+
return 0;
|
|
354
|
+
const foldedName = fold(name || '', parsed.caseSensitive);
|
|
355
|
+
const foldedKind = (kind || '').toLowerCase();
|
|
356
|
+
if (parsed.kinds.length) {
|
|
357
|
+
const kindHit = parsed.kinds.some((k) => foldedKind.includes(k) || foldedName.endsWith(k));
|
|
358
|
+
if (!kindHit)
|
|
359
|
+
return 0;
|
|
360
|
+
}
|
|
361
|
+
if (parsed.regex) {
|
|
362
|
+
parsed.regex.lastIndex = 0;
|
|
363
|
+
if (!parsed.regex.test(name || ''))
|
|
364
|
+
return 0;
|
|
384
365
|
}
|
|
385
|
-
|
|
386
|
-
|
|
366
|
+
for (const negative of parsed.negatives) {
|
|
367
|
+
if (foldedName.includes(negative))
|
|
368
|
+
return 0;
|
|
369
|
+
}
|
|
370
|
+
for (const phrase of parsed.phrases) {
|
|
371
|
+
if (!foldedName.includes(phrase))
|
|
372
|
+
return 0;
|
|
373
|
+
}
|
|
374
|
+
if (!parsed.terms.length)
|
|
375
|
+
return parsed.regex || parsed.phrases.length || parsed.kinds.length ? 60 : 0;
|
|
376
|
+
let score = 0;
|
|
377
|
+
for (const term of parsed.terms) {
|
|
378
|
+
if (foldedName === term)
|
|
379
|
+
score += 240;
|
|
380
|
+
else {
|
|
381
|
+
const hit = occurrenceScore(foldedName, term, 130);
|
|
382
|
+
if (hit)
|
|
383
|
+
score += hit;
|
|
384
|
+
else {
|
|
385
|
+
const fuzzy = fuzzyScore(term, foldedName, name);
|
|
386
|
+
if (!fuzzy)
|
|
387
|
+
return 0;
|
|
388
|
+
score += fuzzy;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return score;
|
|
387
393
|
}
|
|
388
|
-
|
|
389
394
|
// ---- content --------------------------------------------------------------
|
|
390
|
-
|
|
391
395
|
// How well a file's text answers the query, plus the offset of the best hit so
|
|
392
396
|
// the caller can turn it into a line number and a snippet without searching
|
|
393
397
|
// again. Returns `{ score: 0, index: -1 }` when the file does not answer it.
|
|
@@ -398,121 +402,124 @@ export function scoreSymbol(parsed, name, kind) {
|
|
|
398
402
|
// paths (see `matchesPathFilters`), and reading it as "this file must not
|
|
399
403
|
// mention the word vendor" would hide exactly the code you were looking for.
|
|
400
404
|
export function scoreContent(parsed, text) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
405
|
+
if (!parsed || !text || queryIsEmpty(parsed))
|
|
406
|
+
return { score: 0, index: -1 };
|
|
407
|
+
const hay = fold(text, parsed.caseSensitive);
|
|
408
|
+
let score = 0;
|
|
409
|
+
let best = -1;
|
|
410
|
+
const note = (at) => {
|
|
411
|
+
if (at !== -1 && (best === -1 || at < best))
|
|
412
|
+
best = at;
|
|
413
|
+
};
|
|
414
|
+
for (const phrase of parsed.phrases) {
|
|
415
|
+
const at = hay.indexOf(phrase);
|
|
416
|
+
if (at === -1)
|
|
417
|
+
return { score: 0, index: -1 };
|
|
418
|
+
score += occurrenceScore(hay, phrase, 120);
|
|
419
|
+
note(at);
|
|
420
|
+
}
|
|
421
|
+
if (parsed.regex) {
|
|
422
|
+
parsed.regex.lastIndex = 0;
|
|
423
|
+
const at = hay.search(parsed.regex);
|
|
424
|
+
if (at === -1)
|
|
425
|
+
return { score: 0, index: -1 };
|
|
426
|
+
score += 100;
|
|
427
|
+
note(at);
|
|
428
|
+
}
|
|
429
|
+
for (const term of parsed.terms) {
|
|
430
|
+
const at = hay.indexOf(term);
|
|
431
|
+
if (at === -1)
|
|
432
|
+
return { score: 0, index: -1 };
|
|
433
|
+
score += occurrenceScore(hay, term, 60);
|
|
434
|
+
note(at);
|
|
435
|
+
}
|
|
436
|
+
return { score, index: best };
|
|
433
437
|
}
|
|
434
|
-
|
|
435
438
|
// ---- highlighting ---------------------------------------------------------
|
|
436
|
-
|
|
437
439
|
function mergeRanges(ranges) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
440
|
+
if (!ranges.length)
|
|
441
|
+
return [];
|
|
442
|
+
const sorted = ranges.slice().sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
443
|
+
const out = [sorted[0]];
|
|
444
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
445
|
+
const last = out[out.length - 1];
|
|
446
|
+
if (sorted[i][0] <= last[1])
|
|
447
|
+
last[1] = Math.max(last[1], sorted[i][1]);
|
|
448
|
+
else
|
|
449
|
+
out.push(sorted[i]);
|
|
450
|
+
}
|
|
451
|
+
return out;
|
|
447
452
|
}
|
|
448
|
-
|
|
449
453
|
// Every span of `text` the query matches, as `[start, end)` offsets, merged so
|
|
450
454
|
// overlapping hits paint once. This is what lets a snippet mark *all* the words
|
|
451
455
|
// instead of only the first — the old highlighter stopped at one, which made a
|
|
452
456
|
// multi-word query look like it had only matched half of itself.
|
|
453
457
|
export function highlightRanges(text, parsed) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
parsed.regexGlobal
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
458
|
+
if (!text || !parsed)
|
|
459
|
+
return [];
|
|
460
|
+
const ranges = [];
|
|
461
|
+
const add = (start, length) => {
|
|
462
|
+
if (length > 0 && start >= 0 && ranges.length < MAX_RANGES)
|
|
463
|
+
ranges.push([start, start + length]);
|
|
464
|
+
};
|
|
465
|
+
if (parsed.regexGlobal) {
|
|
466
|
+
parsed.regexGlobal.lastIndex = 0;
|
|
467
|
+
let m;
|
|
468
|
+
let guard = 0;
|
|
469
|
+
while ((m = parsed.regexGlobal.exec(text)) !== null && guard++ < MAX_RANGES) {
|
|
470
|
+
add(m.index, m[0].length);
|
|
471
|
+
// A pattern that can match the empty string would spin forever here.
|
|
472
|
+
if (m[0].length === 0)
|
|
473
|
+
parsed.regexGlobal.lastIndex++;
|
|
474
|
+
}
|
|
468
475
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
476
|
+
const needles = parsed.phrases.concat(parsed.terms);
|
|
477
|
+
if (needles.length) {
|
|
478
|
+
const hay = fold(text, parsed.caseSensitive);
|
|
479
|
+
for (const needle of needles) {
|
|
480
|
+
if (!needle)
|
|
481
|
+
continue;
|
|
482
|
+
let from = 0;
|
|
483
|
+
let at;
|
|
484
|
+
while ((at = hay.indexOf(needle, from)) !== -1) {
|
|
485
|
+
add(at, needle.length);
|
|
486
|
+
from = at + needle.length;
|
|
487
|
+
if (ranges.length >= MAX_RANGES)
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
483
491
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
return mergeRanges(ranges);
|
|
492
|
+
return mergeRanges(ranges);
|
|
487
493
|
}
|
|
488
|
-
|
|
489
494
|
// ---- turning an offset into a place to stand ------------------------------
|
|
490
|
-
|
|
491
495
|
// 1-based line number for an offset. Used for the "Line 42" label on a result
|
|
492
496
|
// and, more importantly, to put the cursor there when the result is opened.
|
|
493
497
|
export function lineOfIndex(text, index) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
498
|
+
if (!text || index <= 0)
|
|
499
|
+
return 1;
|
|
500
|
+
let line = 1;
|
|
501
|
+
for (let i = 0; i < index && i < text.length; i++) {
|
|
502
|
+
if (text.charCodeAt(i) === 10)
|
|
503
|
+
line++;
|
|
504
|
+
}
|
|
505
|
+
return line;
|
|
500
506
|
}
|
|
501
|
-
|
|
502
507
|
// The trimmed text of the line containing `index`, shortened around the match
|
|
503
508
|
// so the interesting part is not the part that gets cut off.
|
|
504
509
|
export function snippetAt(text, index, maxLength = 120) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
510
|
+
if (!text)
|
|
511
|
+
return '';
|
|
512
|
+
const start = index < 0 ? 0 : index;
|
|
513
|
+
let lineStart = text.lastIndexOf('\n', start - 1) + 1;
|
|
514
|
+
let lineEnd = text.indexOf('\n', start);
|
|
515
|
+
if (lineEnd === -1)
|
|
516
|
+
lineEnd = text.length;
|
|
517
|
+
let line = text.slice(lineStart, lineEnd).replace(/\r$/, '');
|
|
518
|
+
if (line.length > maxLength) {
|
|
519
|
+
const column = Math.max(0, start - lineStart);
|
|
520
|
+
const from = Math.max(0, column - Math.floor(maxLength / 3));
|
|
521
|
+
line = (from > 0 ? '…' : '') + line.slice(from, from + maxLength - 2);
|
|
522
|
+
}
|
|
523
|
+
return line.trim();
|
|
524
|
+
}
|
|
525
|
+
//# sourceMappingURL=query.js.map
|