diff --git a/CHANGELOG.md b/CHANGELOG.md index 05693146d..5051a7048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ ### New features +* Add `projectile-uniquify-dirname-transform`, a project-aware value for `uniquify-dirname-transform` that disambiguates same-named buffers using the project name. Mirrors `project.el`'s `project-uniquify-dirname-transform`. * [#2008](https://github.com/bbatsov/projectile/issues/2008): Add `projectile-remove-project-type` to unregister a project type. This is the supported way to stop Projectile auto-detecting a type; clearing its `marker-files` does not work (see the related bug fix below). * [#1936](https://github.com/bbatsov/projectile/issues/1936): Add `projectile-discard-root-cache` command to clear `projectile-project-root-cache` without touching other Projectile caches. Useful after creating or removing a project marker, since the existing `projectile-invalidate-cache` either also drops the file list cache or prompts for a project depending on context. * Warn once per session when `projectile-indexing-method' is `alien' but the project has a non-empty `.projectile' file, so users notice their dirconfig rules are being bypassed. Controlled by the new `projectile-warn-when-dirconfig-is-ignored' option. diff --git a/doc/modules/ROOT/pages/projects.adoc b/doc/modules/ROOT/pages/projects.adoc index a0ea312b3..62578f468 100644 --- a/doc/modules/ROOT/pages/projects.adoc +++ b/doc/modules/ROOT/pages/projects.adoc @@ -1200,6 +1200,21 @@ When you run `projectile-kill-buffers` (kbd:[s-p k]), the variable You can also set it to a custom predicate function that receives a buffer and returns non-nil if the buffer should be killed. +=== Uniquifying buffer names by project + +When you open same-named files from different projects (say two `README.md` +files), Emacs's `uniquify` library disambiguates the buffer names using parent +directory components. Projectile can make that disambiguation use the project +name instead, via `projectile-uniquify-dirname-transform`: + +[source,elisp] +---- +(setq uniquify-dirname-transform #'projectile-uniquify-dirname-transform) +---- + +Outside of a project the directory is left untouched, so this is safe to set +globally. This mirrors `project.el`'s `project-uniquify-dirname-transform`. + == Configure a Project's Lifecycle Commands and Other Attributes There are a few variables that are intended to be customized via `.dir-locals.el`. diff --git a/projectile.el b/projectile.el index 1b4677b39..5a2fd0321 100644 --- a/projectile.el +++ b/projectile.el @@ -1684,6 +1684,25 @@ If PROJECT is not specified acts on the current project." "-")))) +(defun projectile-uniquify-dirname-transform (dirname) + "Project-aware transform for `uniquify-dirname-transform'. +When DIRNAME is inside a project, return a path with the project name +spliced in, so buffers visiting same-named files in different projects +get distinct, project-qualified names. Outside a project DIRNAME is +returned unchanged. + +To enable, set `uniquify-dirname-transform' to this function: + + (setq uniquify-dirname-transform #\\='projectile-uniquify-dirname-transform)" + (if-let* ((root (projectile-project-root dirname))) + (expand-file-name + (file-name-concat + (file-name-directory root) + (projectile-project-name root) + (file-relative-name dirname root))) + dirname)) + + ;;; Project indexing (defun projectile-get-project-directories (project-dir) "Get the list of PROJECT-DIR directories that are of interest to the user. diff --git a/test/projectile-test.el b/test/projectile-test.el index c28266679..e8347a4ef 100644 --- a/test/projectile-test.el +++ b/test/projectile-test.el @@ -173,6 +173,17 @@ by `projectile-files-via-ext-command')." (let ((projectile-project-name-function (lambda (dir) dir))) (expect (projectile-project-name) :to-equal "current/project")))) +(describe "projectile-uniquify-dirname-transform" + (it "splices the project name into a directory inside a project" + (spy-on 'projectile-project-root :and-return-value "/home/me/work/checkout-42/") + (spy-on 'projectile-project-name :and-return-value "myproject") + (expect (projectile-uniquify-dirname-transform "/home/me/work/checkout-42/src/") + :to-equal "/home/me/work/checkout-42/myproject/src/")) + (it "returns the directory unchanged outside a project" + (spy-on 'projectile-project-root :and-return-value nil) + (expect (projectile-uniquify-dirname-transform "/tmp/not-a-project/") + :to-equal "/tmp/not-a-project/"))) + (describe "projectile-prepend-project-name" (it "prepends the project name to its parameter" (spy-on 'projectile-project-name :and-return-value "project")