| Age | Commit message (Collapse) | Author |
|
Each call to `projectile-cache-current-file' under persistent caching
scheduled a fresh 30-second idle timer that closed over a snapshot of
the file list at scheduling time. Opening N files in a session
queued N timers; once Emacs went idle they all fired and serialized
the cache N times, with the earlier ones writing stale (shorter)
lists.
Maintain a per-project pending timer in
`projectile--pending-cache-flush-timers' instead, cancelling and
rescheduling on each new file. The fired callback re-reads the
current in-memory cache, so the disk write reflects the final state
rather than whichever snapshot the last timer captured.
|
|
Previously the persistent invalidate path called
`(projectile-serialize nil ...)', leaving a four-byte file containing
the literal "nil" on disk. Resolving the long-standing TODO: just
delete the file when invalidating, so the on-disk state matches the
intent of the command (and so an orphaned cache file isn't left
behind on uninstall).
|
|
`projectile-purge-file-from-cache' writes the updated file list to
disk under persistent caching; the directory variant only mutated
the in-memory hash, so the purged subtree would reappear on the
next Emacs session. Mirror the persistence step here.
|
|
Without this, `projectile-files-cache-expire' combined with persistent
caching ended up re-reading the cache file from disk on every call to
`projectile-project-files' and never reindexing: the TTL check at the
top of `projectile-project-files' evicts the in-memory entry whenever
its cache time is missing, and `projectile-load-project-cache' was
populating only `projectile-projects-cache' on disk loads. Use the
cache file's mtime as the recorded time, so the TTL check sees a real
age and reindexing happens when (and only when) the data is actually
stale.
|
|
Final indexing review batch: cleanup, tests, docs
|
|
Three doc gaps surfaced during the indexing review:
* The `*' prefix on `projectile-globally-ignored-directories' entries
is not a glob - it is what promotes a basename match from
"anchored at the project root" to "anywhere in the tree", and it
only takes effect under hybrid indexing.
* The default `projectile-generic-command' picks `fd' when it's on
PATH and falls back to a `find ... | tr ...' pipeline. The
fallback does not exclude common build directories, which is a
trap for non-VCS projects under alien on hosts without `fd'.
* `projectile-git-use-fd' controls how Projectile handles
deleted-but-unstaged files: with `fd' they disappear immediately,
with `git ls-files' Projectile post-filters via `git ls-files -zd'.
|
|
`projectile-get-ext-command' had no direct test coverage, so the
non-git VCS branches (hg/svn/bzr/darcs/fossil/pijul/sapling/jj) were
relying on indirect coverage that didn't actually exercise the
dispatch. Add a `describe' block that pins each branch.
Also extend the `projectile-dir-files-alien' tests with two cases
that were previously uncovered: the fd path for git (verifying we
don't also call `projectile-git-deleted-files') and the no-VCS
fallback (verifying the generic command is used).
|
|
`projectile-get-immediate-sub-projects' was unconditionally shelling
out to `git submodule --quiet foreach ...' on every indexing call for
git projects, even when the project had no submodules at all. For
monorepos that re-index the project root often this is pure overhead.
Use `locate-dominating-file' to look for `.gitmodules' along the
parent chain (PATH may be inside a git repo without being its
toplevel) and skip the shell-out when none is found.
Also tighten `projectile-discover-projects-in-directory' to filter
`.' / `..' via `directory-files-no-dot-files-regexp' instead of a
post-filter `member' check, matching the indexing walker's style.
|
|
Speed up indexing's ignore filtering
|
|
Cover the new internal helpers (`projectile--list->set',
`projectile--ignored-file-fast-p', `projectile--ignored-directory-
fast-p') and add end-to-end tests for the previously uncovered
behaviour:
* `projectile-index-directory' honors dirconfig glob ignore patterns
at every directory level - previously only the alien-mode dirconfig
warning had any test coverage for this.
* Ensure (`!') entries in `.projectile' override an ignore pattern.
* `projectile-remove-ignored' drops basename matches, treats `*'-
prefixed ignored-dir entries as any-segment matches, and treats
plain entries as path-prefix matches.
|
|
Hash-set the ignored-files / ignored-directories / globally-ignored-
directory-names lists once per indexing call, instead of repeating
linear `member' scans on every file. On a project with N files and M
ignore entries this drops the inner work from O(N*M) to roughly O(N).
In the native walker (`projectile-index-directory') three additional
changes:
* Glob patterns for `.projectile' ignore/ensure are pre-expanded once
per directory level, instead of once per (file, pattern) pair via
`file-expand-wildcards' inside `projectile-check-pattern-p'.
* Discovered files accumulate into a single mutable cell threaded
through the recursion, so we don't pay for an `apply append' at each
level (which copied every file once per level it bubbled through).
* `default-directory' is rebound for glob expansion, but only after
the directory listing is captured - so callers can still pass a
relative `directory' argument.
In the hybrid post-processor (`projectile-remove-ignored'), pre-split
ignored-dirs into prefix-match and any-segment groups and hash the
segment names. The per-file segment loop becomes a hash lookup; the
basename match against ignored-files is now also a hash lookup.
The public functions (`projectile-ignored-file-p', `projectile-
ignored-directory-p', `projectile-ignored-rel-p',
`projectile-check-pattern-p') keep their existing signatures and
behaviour for external callers.
|
|
Batch hybrid indexing across + keep entries
|
|
`+' keep entries had no end-to-end test coverage. Add tests that verify:
* Multiple `+' keep entries trigger a single batched call to the
external command, with the kept paths passed as the new pathspecs
argument.
* The single-keep-dir case keeps going through `projectile-dir-files'
for each subdirectory (no behavioural change there).
* `projectile--restricted-sub-projects-files' returns all submodule
files when no subdirs are supplied, drops files outside the supplied
subdirs when they are, and normalises subdirs without a trailing
slash.
Also add a focused test for the new `pathspecs' argument of
`projectile-files-via-ext-command'.
|
|
When a project's `.projectile' declares multiple `+' keep entries,
hybrid indexing used to walk each subdirectory individually, shelling
out to the external indexing command once per entry. The TODO in
`projectile-project-files' had been there for a while.
Push the kept paths into the external command as positional pathspecs
and run `projectile-adjust-files' once over the combined result.
`git ls-files', `fd', `find', `hg locate', etc. all accept additional
path arguments at the end of the command line. For Git submodules,
`projectile-get-sub-projects-files' is queried once and the result is
filtered to only those falling under one of the kept subdirectories.
`projectile-files-via-ext-command' grows an optional `pathspecs' arg
(shell-quoted before being appended); `projectile-dir-files-alien'
grows a matching optional `subdirs' arg that threads through.
|
|
Indexing dispatch cleanups
|
|
The configuration page covered alien indexing in depth but mentioned
hybrid only in passing, so users had to read the source to learn that
hybrid is "alien with Projectile's filtering rules layered on top".
Add a dedicated section explaining what hybrid does and when to reach
for it, and a comparison table summarising which knobs (dirconfig,
global ignores/unignores, sort order, default caching, Windows
support) apply under each indexing method.
|
|
The hybrid path through `projectile-dir-files` had zero direct
coverage. Add tests that verify it applies
`projectile-globally-ignored-file-suffixes` and dirconfig ignore
patterns on top of the alien result, and that the resolved VCS is
threaded through to `projectile-dir-files-alien` exactly once.
Also adds a focused test for `projectile-dir-files-alien` honoring an
explicitly supplied VCS argument without re-running
`projectile-project-vcs`.
|
|
The dispatcher in `projectile-dir-files` already resolves the VCS for
the hybrid path. Thread it through to `projectile-dir-files-alien` via
a new optional argument so we don't pay for a second `projectile-project-vcs`
call on every indexing run. Single-argument callers (and the spies in
the test suite) keep working unchanged.
Also drop the post-hoc `(member local-f '("." ".."))` filter in
`projectile-index-directory` in favour of `directory-files`'
`directory-files-no-dot-files-regexp`, which handles the same case at
the C level.
|
|
Root detection: fix #1211 and #1836, plus cleanup
|
|
projectile-locate-dominating-file (bottommost match) and
projectile-locate-dominating-file-top-down (topmost match) only
differed in two lines: the bottom-up version exits the walk on
first match, the top-down version keeps walking and overwrites
root at each level.
Extract a shared projectile--locate-dominating-file with a
first-match-only flag. The two public functions become one-liners
that delegate. Loop semantics preserved - 277 tests still pass.
|
|
The existing projectile-invalidate-cache always tries to also clear
the per-project files cache, which means it either prompts for a
project (with prefix arg) or only operates when you're already in a
project. Users who just created a marker file in a directory that
Projectile previously considered rootless ended up picking an
unrelated project from the prompt just to get the root cache
flushed.
projectile-discard-root-cache is a focused alternative that empties
projectile-project-root-cache only. Wired into the menu next to the
existing 'Invalidate cache' entry; deliberately not bound on the
prefix map since the keymap is already dense.
|
|
- New 'Project root cache' subsection in projects.adoc covering
invalidation, when negative results bite (the recurring 'I created
.projectile and Projectile still doesn't see it' confusion from
#1936), and how to reset programmatically.
- Note in 'Customizing Project Detection' that
projectile-root-top-down only matches regular files while
projectile-root-bottom-up matches both - this difference is what
forces VCS markers like .git onto the bottom-up list.
- Expand the file-local override section with a concrete example and
call out that overrides bypass the cache.
- Rewrite projectile-project-root's docstring to mention caching,
the tramp-archive unwrap, and the disconnected-remote behaviour
that callers used to have to read the source to discover.
|
|
- projectile-root-marked: default and custom dirconfig file, miss case
- projectile-root-local: returns the buffer-local var, ignores DIR
- projectile-root-bottom-up: matches a regular-file marker (e.g. git
worktree's .git file)
- projectile-ensure-project: all three projectile-require-project-root
branches (nil / t / 'prompt) plus the dir-non-nil pass-through
- projectile-acquire-root: the success path and the no-root delegation
- projectile-project-root: tramp-archive path unwrap, verified by
observing the directory handed to a stub root function (a
defvar-backed variable is needed because byte-compiled lexical
closures don't propagate setq through cl-letf-mocked dispatch)
|
|
Previously a root function returning nil stored nil in the cache,
which is indistinguishable from a missing entry, so on the next call
the function was re-run. When a project's root is found by the third
function in projectile-project-root-functions, the first two
re-walked the tree on every call - and projectile-project-root is hot
in mode-line / company / spaceline updates.
Store the symbol 'none for unsuccessful entries and treat it as a
cache hit on lookup. The overall-failure marker already used 'none
in a separate slot, so this just extends the same convention to the
per-function entries.
|
|
projectile-root-local reads the buffer-local projectile-project-root
variable rather than inspecting its DIR argument, so caching its
result by (FUNC . DIR) was wrong: two buffers visiting the same
directory but with different file-local overrides would share the
first buffer's cached answer. Skip the cache for this function
specifically - it's a single variable read, no FS work to amortize.
Adds a regression test that visits two buffers with different
file-local roots from the same default-directory and confirms each
sees its own override.
|
|
Per-function entries are now keyed on (FUNC . DIR), and the overall
failure marker on ('none . DIR), instead of formatted strings. Same
equal semantics, no per-call format allocation, and lambda functions
in projectile-project-root-functions hash by identity instead of by
their printed repr.
|
|
Polish: align dirconfig warnings, robust cache key, small cleanups
|
|
- projectile-get-project-directories: replace the (or keep '("")) trick
with an explicit (if keep ... (list project-dir)). The behavior is
unchanged but the empty-keep case no longer reads as a string-concat
with the empty string.
- projectile-dirconfig-file: expand the docstring to spell out the
dual marker/config role — empty file is enough to mark a project,
non-empty content drives parsing.
|
|
The cache value used to be (MTIME . PARSED-RESULT) keyed on the
project root. If the user changed `projectile-dirconfig-file' to
point at a different file mid-session, the old entry kept getting
returned because the key didn't depend on the path. Store the path
alongside the mtime in the cached value and compare both before
considering a hit. The hash key remains the project root so
`projectile-invalidate-cache' continues to work as-is.
|
|
The other two dirconfig warnings (alien-mode bypass and prefix-less
entries) are gated by per-project hash sets so they fire at most
once per Emacs session. The glob-keep warning was the odd one out —
it lived inside the uncached parser and re-fired on every cache
miss, and it emitted one display-warning per offending entry rather
than a single consolidated message.
Move it into the cached wrapper alongside the others, gate it on
projectile--glob-keep-warned-projects, and roll multiple offending
entries into one warning that lists them.
|
|
Refactor dirconfig parser: struct, pure classifier, deprecation warning
|
|
The implicit "any unprefixed line is an ignore pattern" rule is the
last source of subtle parser surprises — it's the reason why a
single leading space silently changes a +-keep into a literal ignore
pattern, and it makes typo'd comments slip through as ignores.
Mark these lines as :legacy-ignore in the classifier, record them
in a new prefixless-ignore slot on the dirconfig struct, and emit a
one-time warning per project listing the offending entries. The
behavior is unchanged — the lines still go into the ignore list —
but users now get a nudge to write them as -entry. The warning can
be silenced via projectile-warn-on-prefixless-dirconfig-lines.
|
|
The old parser walked a temp buffer with point and pcase'd on
char-after, mixing IO, prefix dispatch, and bucket bookkeeping into
one function. Pull the dispatch out into projectile--dirconfig-classify-line,
which takes a string and returns a (BUCKET . VALUE) tag. The pure
function is unit-testable without buffer plumbing, the IO wrapper
shrinks to a one-shot read + dispatch, and the awkward
(pcase ((pred (lambda ...)) ...)) for the comment-prefix check
becomes a straightforward cond.
No behavior change.
|
|
Replace the positional (KEEP IGNORE ENSURE) triple with a
cl-defstruct. Every internal call site used car/cadr/caddr to pull
out a slot, which is unreadable and error-prone — slot accessors
make the intent explicit and let cl-defstruct grow a fourth field
later without touching every consumer.
Existing callers that compared against the raw triple (a couple of
internal helper tests) are updated to construct the struct with
make-projectile-dirconfig.
|
|
Improvements to .projectile (dirconfig) parsing and ergonomics
|
|
The parser silently turns every keep entry into a directory via
file-name-as-directory, which means a user-typed +*.json or +/foo.txt
becomes "*.json/" or "foo.txt/" and quietly never matches anything.
Spot the obvious misuses (anything containing *, ?, or [) at parse
time and emit a warning so the user can correct the file or move the
pattern to an ignore/ensure rule.
|
|
The existing parser tests stub insert-file-contents; nothing exercises
the full IO path. Add three sandbox-based tests: a mixed +/-/!/no-prefix
file, a non-ASCII round-trip, and a file with no trailing newline. These
guard against IO-layer regressions that the stubbed tests can't see.
|
|
Under alien indexing the dirconfig file is silently ignored, which
is the most common confusion in the issue tracker (#1322, #1075,
#1534, #1941). Show a one-shot display-warning the first time we
index a project where alien mode meets a non-empty .projectile.
The new projectile-warn-when-dirconfig-is-ignored option lets users
who already understand the trade-off silence the warning.
|
|
The parser docstring only described + and - prefixes even though the
function returns a 3-tuple including the ! ensure bucket. Fill that
in, and pull the path-vs-glob distinction out of the prose in
projects.adoc into a subsection of its own — that ambiguity is at
the root of recurring confusion (#740, #1109, #680, #1941).
|
|
The mtime-keyed cache around projectile-parse-dirconfig-file had no
direct coverage. Cover the four invariants that matter: a cache hit
on repeat calls, a re-parse when mtime advances, no caching of a nil
result for a missing file, and entry removal via
projectile-invalidate-cache (the path that previously crashed in #1854).
|
|
A user accidentally typing " -path/" or " # comment" in .projectile
would have the line silently routed to the ignore bucket with the
prefix character left intact, because the parser dispatched on the
first character of the line without trimming. Skip leading spaces and
tabs before the pcase so the +/-/! and comment-prefix markers are
matched regardless of indentation. Reported in #1508.
|
|
Fixes #1815
Fixes #1715
When two projects share the same name (e.g. both named "src"),
projectile-generate-process-name now detects the collision and
falls back to using the abbreviated project path in the buffer
name instead of just the project name.
|
|
Fixes #1959
projectile-ignored-file-p now checks projectile-globally-ignored-file-suffixes
in addition to projectile-globally-ignored-files and file patterns.
Previously the suffix check only happened in projectile-remove-ignored
which is used by hybrid indexing but not native indexing.
|
|
Closes #1684
With a prefix argument, projectile-dired, projectile-dired-other-window,
and projectile-dired-other-frame now prompt for a known project to open
in dired, rather than always using the current project.
|
|
Closes #1227
projectile-replace now intersects the files found by rg/ag/grep with
the project's file list from projectile-dir-files, ensuring that files
ignored via .projectile or other ignore rules are excluded from
replacement. Previously the external search tool's results were used
directly, which could include backup files and other ignored entries.
|
|
Closes #1001
Add a new command that lists all files under the project root using
a generic file listing command (fd or find), bypassing .gitignore,
.projectile, and other ignore mechanisms. This is useful when you
need to find files that are normally excluded from the project.
|
|
|
|
Fixes #1923
When compilation output references a file via a relative path, the
advice now also checks if the file exists relative to the project root
and adds its parent directory to the search path. This handles the
edge case where a directory only contains subdirectories (no files
directly) and therefore was not included in the file-derived directory
list from projectile-current-project-dirs.
|
|
|
|
- Test projectile-ignored-project-p truename normalization
- Test projectile-determine-find-tag-fn fallback behavior
- Update projectile-files-via-ext-command test for empty string guard
|