dante icon indicating copy to clipboard operation
dante copied to clipboard

xref uses wrong directory on multi-package cabal new-build projects

Open rvl opened this issue 4 years ago • 5 comments

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.

rvl avatar Aug 13 '19 03:08 rvl

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.

jyp avatar Aug 15 '19 07:08 jyp

(nvm)

sboosali avatar Aug 15 '19 09:08 sboosali

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.

jyp avatar Aug 15 '19 12:08 jyp

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

bezirg avatar Nov 21 '19 12:11 bezirg

So, there are following entities:

  1. Project root -- the absolute root that contains all packages with their .cabal files. Often contains the cabal.project file.
  2. 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?

deepfire avatar Feb 19 '21 23:02 deepfire