Repository navigation
Index opted-in Android vendor source and cover Java/AIDL language cases #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
aff678c
195734e
c23b1ef
799e432
b497e56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -610,8 +610,10 @@ export function buildDefaultIgnore(rootDir: string): Ignore { | |
| * parent repo's own ignore rules must NOT apply — inside embedded child repos, | ||
| * whose gitignore semantics their own `git ls-files` already enforced (#514). | ||
| */ | ||
| function defaultsOnlyIgnore(): Ignore { | ||
| return excludeBuildOutputUnderSrc(ignore().add(DEFAULT_IGNORE_PATTERNS), null); | ||
| function defaultsOnlyIgnore(allowVendor = false): Ignore { | ||
| // Only the project-root Android vendor tree is includable; nested vendor | ||
| // directories remain dependency trees at every depth. | ||
| return excludeBuildOutputUnderSrc(ignore().add(DEFAULT_IGNORE_PATTERNS.map((p) => allowVendor && p === 'vendor/' ? '/*/**/vendor/' : p)), null); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -700,6 +702,29 @@ function includeStaticRoots(patterns: string[]): string[] { | |
| return all.filter((r) => !all.some((other) => other !== r && r.startsWith(other))); | ||
| } | ||
|
|
||
| interface VendorInclude { | ||
| matcher: Ignore; | ||
| roots: string[]; | ||
| } | ||
|
|
||
| /** Broad includes do not opt dependency trees in; root vendor source needs its own pattern. */ | ||
| function buildVendorInclude(patterns: string[]): VendorInclude | null { | ||
| const rooted = patterns.filter((p) => /^\/*vendor(?:\/|$)/i.test(p)); | ||
| if (rooted.length === 0) return null; | ||
| return { | ||
| matcher: ignore().add(patterns.filter((p) => p.startsWith('!') || rooted.includes(p))), | ||
| roots: includeStaticRoots(rooted), | ||
| }; | ||
| } | ||
|
|
||
| function permitsVendorInclude(rel: string, vendor: VendorInclude | null): boolean { | ||
| if (!/^vendor(?:\/|$)/i.test(rel)) return true; | ||
| if (!vendor) return false; | ||
| return rel.endsWith('/') | ||
| ? vendor.roots.some((root) => root.startsWith(rel) || rel.startsWith(root)) | ||
| : vendor.matcher.ignores(rel); | ||
| } | ||
|
|
||
| /** | ||
| * Actively discover the source files an `include` whitelist forces in. `git | ||
| * ls-files` never lists gitignored files, so a filtered filesystem walk of just | ||
|
|
@@ -716,24 +741,25 @@ function collectIncludedFiles( | |
| rootDir: string, | ||
| include: Ignore, | ||
| exclude: Ignore | null, | ||
| roots: string[], | ||
| patterns: string[], | ||
| overrides: Record<string, Language>, | ||
| ): Set<string> { | ||
| const out = new Set<string>(); | ||
| const defaults = defaultsOnlyIgnore(); | ||
| const defaults = defaultsOnlyIgnore(true); // Explicit `include` may select AOSP vendor source. | ||
| const vendor = buildVendorInclude(patterns); | ||
| const visited = new Set<string>(); | ||
|
|
||
| const consider = (abs: string, rel: string, isDir: boolean): void => { | ||
| if (isDir) { | ||
| if (defaults.ignores(rel + '/')) return; // never node_modules/dist/… via include | ||
| if (defaults.ignores(rel + '/') || !permitsVendorInclude(rel + '/', vendor)) return; | ||
| // An explicit `exclude` always wins over `include`; prune the whole subtree | ||
| // here so a large excluded dir (a committed frontend's own vendored deps, | ||
| // build output, …) is never walked — the per-file guard below still catches | ||
| // anything a directory pattern doesn't, so this is a pure efficiency win. | ||
| if (exclude && exclude.ignores(rel + '/')) return; | ||
| walk(abs); | ||
| } else { | ||
| if (defaults.ignores(rel)) return; | ||
| if (defaults.ignores(rel) || !permitsVendorInclude(rel, vendor)) return; | ||
| if (!include.ignores(rel)) return; | ||
| if (exclude && exclude.ignores(rel)) return; | ||
| if (!isSourceFile(rel, overrides, rootDir)) return; | ||
|
|
@@ -775,7 +801,7 @@ function collectIncludedFiles( | |
| } | ||
| } | ||
|
|
||
| for (const root of roots) { | ||
| for (const root of includeStaticRoots(patterns)) { | ||
| walk(root === '' ? rootDir : path.join(rootDir, root)); | ||
| } | ||
| return out; | ||
|
|
@@ -790,8 +816,7 @@ function collectIncludedFiles( | |
| function collectIncludedFilesForRoot(rootDir: string): Set<string> { | ||
| const include = loadIncludeMatcher(rootDir); | ||
| if (!include) return new Set(); | ||
| const roots = includeStaticRoots(loadIncludePatterns(rootDir)); | ||
| return collectIncludedFiles(rootDir, include, loadExcludeMatcher(rootDir), roots, loadExtensionOverrides(rootDir)); | ||
| return collectIncludedFiles(rootDir, include, loadExcludeMatcher(rootDir), loadIncludePatterns(rootDir), loadExtensionOverrides(rootDir)); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -971,6 +996,7 @@ export function preloadLanguagesForFiles( | |
| export class ScopeIgnore { | ||
| private embedded: Array<{ root: string; matcher: Ignore }>; | ||
| private defaults: Ignore = defaultsOnlyIgnore(); | ||
| private includableDefaults: Ignore = defaultsOnlyIgnore(true); | ||
| constructor( | ||
| private rootMatcher: Ignore, | ||
| embedded: Array<{ root: string; matcher: Ignore }>, | ||
|
|
@@ -1000,6 +1026,7 @@ export class ScopeIgnore { | |
| * folder named `[draft]` or `a[b` read as a glob (or did not compile). | ||
| */ | ||
| private gitIgnoredDirs: ReadonlySet<string> = new Set(), | ||
| private vendorInclude: VendorInclude | null = null, | ||
| ) { | ||
| // Longest root first so paths in nested embedded repos hit the innermost matcher. | ||
| this.embedded = [...embedded].sort((a, b) => b.root.length - a.root.length); | ||
|
|
@@ -1022,7 +1049,7 @@ export class ScopeIgnore { | |
| // User `include`: force first-party source in despite `.gitignore`. Never | ||
| // resurfaces a built-in default-ignored dir (node_modules/dist/…), so an | ||
| // include pattern can't accidentally pull in dependency/build trees. | ||
| if (this.include && !this.defaults.ignores(rel)) { | ||
| if (this.include && !this.includableDefaults.ignores(rel) && permitsVendorInclude(rel, this.vendorInclude)) { | ||
| if (rel.endsWith('/')) { | ||
| // A directory on (or leading to) an included subtree must stay walkable | ||
| // so the watcher/walker descends to reach the forced-in files. | ||
|
|
@@ -1068,6 +1095,7 @@ export function buildScopeIgnore(rootDir: string, embeddedRoots?: Iterable<strin | |
| include, | ||
| include ? includeStaticRoots(loadIncludePatterns(rootDir)) : [], | ||
| new Set(listGitIgnoredDirectories(rootDir)), | ||
| include ? buildVendorInclude(loadIncludePatterns(rootDir)) : null, | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -4087,7 +4115,15 @@ export class ExtractionOrchestrator { | |
| // Git supplies candidates, never the verdict. A committed deletion may | ||
| // have been recreated locally; a previously indexed dirty path may have | ||
| // vanished from git status after restore. Classify current disk vs DB once. | ||
| const candidates = new Set([...gitChanges.deleted, ...gitChanges.modified, ...gitChanges.added, ...dirtyPaths!]); | ||
| const gitCandidates = new Set([...gitChanges.deleted, ...gitChanges.modified, ...gitChanges.added, ...dirtyPaths!]); | ||
| const candidates = new Set(gitCandidates); | ||
| const include = loadIncludeMatcher(this.rootDir); | ||
| if (include) { | ||
| for (const file of collectIncludedFilesForRoot(this.rootDir)) candidates.add(file); | ||
|
mixxer marked this conversation as resolved.
Resolved
|
||
| for (const file of this.queries.getAllFiles()) { | ||
| if (include.ignores(file.path)) candidates.add(file.path); | ||
|
Comment on lines
+4121
to
+4124
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Retain previously indexed files as removal candidates. If a Git-ignored vendor file was indexed through 🤖 Prompt for AI Agents
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the precise case. I independently reproduced it on f1ed60e: after indexing Git-ignored vendor/oem/Device.java through include and then removing the include pattern, getChangedFiles() reports no removal. A full sync() removes the file and its nodes correctly. The same status behavior also reproduces for ordinary ignored Tools/Device.java on integrated main b6ae433, so it is shared with the existing include-configuration fast path rather than introduced by this rebase. I documented the remaining limitation and the full-sync workaround in the PR description. No configuration-reconciliation fix is included in f1ed60e; this thread remains open. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mixxer Thanks for reproducing both cases. Your results establish that this is an existing include-configuration limitation, not a regression introduced by this rebase. The finding remains valid and open. A full Would you like me to open a follow-up GitHub issue for configuration reconciliation?
You are interacting with an AI system. |
||
| } | ||
| } | ||
| const scope = this.scopedSyncMatcher(); | ||
| const overrides = loadExtensionOverrides(this.rootDir); | ||
| for (const filePath of candidates) { | ||
|
|
@@ -4097,6 +4133,18 @@ export class ExtractionOrchestrator { | |
| if (tracked) removed.push(filePath); | ||
| continue; | ||
| } | ||
| // Include-only candidates can be gitignored and need a disk check, but | ||
| // unchanged size/mtime avoids reading all vendor source on every status. | ||
| // Keep hashing paths Git or the dirty ledger explicitly reported. | ||
| if (tracked && !gitCandidates.has(filePath)) { | ||
| try { | ||
| const stat = fs.statSync(fullPath); | ||
| if (stat.size === tracked.size && Math.floor(stat.mtimeMs) === Math.floor(tracked.modifiedAt)) continue; | ||
| } catch (error) { | ||
| logDebug('Skipping unstattable file while detecting changes', { filePath, error: String(error) }); | ||
| continue; | ||
| } | ||
| } | ||
| let content: string | null; | ||
| try { content = readSourceOrStamp(fullPath); } | ||
| catch (error) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.