dante
dante copied to clipboard
xref uses wrong directory on multi-package cabal new-build projects
Thanks for the excellent emacs mode.
I have a project with the following structure:
/home/rodney/dev/myproject
├── .dir-locals.el
├── cabal.project
├── pkga
│ ├── pkga.cabal
│ ├── Module1.hs
│ └── Module2.hs
└── pkgb
├── pkgb.cabal
└── Main.hs
Dante chooses the new-build
method. I have configured dante-project-root
to be nil
, so it gets set automatically to the directory of cabal.project
, which is /home/rodney/dev/myproject/
.
I am editing pkga/Module1.hs
and have set dante-target
to pkga
.
When I run xref-find-definitions
on a symbol, I get taken to the non-existent file /home/rodney/dev/myproject/Module2.hs
. The package directory pkga
is missing from this path. It has got a path relative to pkga.cabal
and appended it to the dante-project-root
, which is the location of cabal.project
.
My workaround is to customize the new-build
entry of dante-methods-alist
, and set the dominating file predicate to (lambda (d) (directory-files d t ".cabal$"))
. Cabal v2-build correctly locates the cabal.project
and everything works. This means that the bare-cabal
method will never be chosen, but that is not a problem for me.
Thanks for the report. Unfortunately it is unclear to me what should be changed on the emacs side, it seems to be more of a bug in the GHC side.
(nvm)
Ok, I suppose the bug is in cabal, which loads GHC from another directory than the one it was started in, causing GHC to report wrong relative paths.
I ran into the exact same problem, this time using new-impure-nix
dante method.
Suggestion: wouldn't this problem be fixed if dante.el
prepends the the paths returned from cabal usually beginning src/...
I tried this and it seems to work in my case of new-impure-nix
:
(defun dante--match-src-span (string)
"Extract a location from a ghc span STRING."
;; On external symbols, GHC may return a location such as integer-gmp-1.0.0.1:integer-gmp-1.0.0.1:GHC.Integer.Type
(when (string-match "\\(.*?\\):(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))$" string)
(let ((file (match-string 1 string))
(line (string-to-number (match-string 2 string)))
(col (string-to-number (match-string 3 string))))
(xref-make-file-location
(or (gethash file dante-original-buffer-map)
(expand-file-name file (expand-file-name dante-target dante-project-root)))
line (1- col)))))
I don't know however how it will affect people that do not rely on dante-target
set in .dir-locals.el
So, there are following entities:
- Project root -- the absolute root that contains all packages with their
.cabal
files. Often contains thecabal.project
file. - Packages -- the set of arbitrary subdirectories, each containing a
.cabal
file.
AIUI, what xref
is handed over is something relative to №2, whereas what we want it to have is a project-root-relative path of a file, which really is:
projectRelativePath = packageSubpath </> packageRelativeSubpath
Next question is -- does Dante know the packageSubpath
(i.e. the subdir that contains the .cabal
file) for any given source file?