[RESOLVED] HLS works inconsistently or not at all inside Cabal

Hello, I am being driven crazy by HLS.

Context

I am trying to work through Advent of Code 2022 and am trying to have multiple executables in a single Cabal project to keep things efficient. Below is the tree. (day-02 and day-03 have empty Main.hs files for now.)

I am using VS Code as my editor.

.
├── CHANGELOG.md
├── data
│   ├── day_01.txt
│   ├── day_02.txt
│   └── day_03.txt
├── day-01
│   └── Main.hs
├── day-02
│   └── Main.hs
├── day-03
│   └── Main.hs
├── dist-newstyle (truncated for length)
├── hie.yaml
└── puzzles.cabal

Previously I had a lot of trouble getting HLS to work in a Cabal project, until I learnt about adding in an hie.yaml file at the root of the Cabal project. For this particular project, hie.yaml looks like this:

cradle:
  cabal:

My hie.yaml looks like this because, in another Cabal project, I struggled to get HLS to work, and only by adding these two lines was I finally able to get it running.

My puzzles.cabal file looks like this:

cabal-version:      3.6
name:               puzzles
version:            0.1.0.0
author:             My Name
extra-source-files: CHANGELOG.md

data-dir: data
data-files:
    day_01.txt
    , day_02.txt
    , day_03.txt

executable day-01
    main-is:          Main.hs

    -- LANGUAGE extensions used by modules in this package.
    -- other-extensions:
    build-depends:    base ^>= 4.17.2.0
                    , split == 0.2.4
    hs-source-dirs:   day-01
    default-language: Haskell2010

executable day-02
    main-is:          Main.hs

    -- LANGUAGE extensions used by modules in this package.
    -- other-extensions:
    build-depends:    base ^>= 4.17.2.0
                    , split ^>=0.2.4
    hs-source-dirs:   day-02
    default-language: Haskell2010

executable day-03
    main-is:          Main.hs

    -- LANGUAGE extensions used by modules in this package.
    -- other-extensions:
    build-depends:    base ^>= 4.17.2.0
                    , split ^>=0.2.4
    hs-source-dirs:   day-03
    default-language: Haskell2010

The issue

I don’t know if HLS is working properly or not. I use Main.hs in day-01 as an example.

module Main where

import Data.List.Split (splitOn)

main :: IO ()
main = do
    raw <- readFile "./data/day_01.txt"
    undefined

func :: Int -> Int -> Int
func x y = show (x + y)

parse :: String -> [[Int]]
parse input = secondPass $ firstPass input
  where
    firstPass :: String -> [String]
    firstPass = splitOn "\n\n"

    secondPass :: [String] -> [[Int]]
    secondPass = map (map read . splitOn "\n")

func was deliberately defined to be wrong. Yet, I see no highlighting anywhere in the code that there has been a mistake.

image

But if I were to mouse over a value like raw, a tooltip does appear.

In the parse function, a tooltip appears when I mouse over parse and firstPass. (I show a screenshot of parse but the form of the tooltip is the same with firstPass.)

But, when I mouse over secondPass, no tooltip appears.

My questions

  • Are these tooltips that I’m seeing part of HLS or part of something else?
  • What can I do to get HLS running again? I was able to make it work in another Cabal project, but here adding the hie.yaml file appears to have no effect on getting the language server to work

Versions used

  • ghc 9.4.7
  • cabal 3.6.2.0-p1
  • hls 2.4.0.0
  • stack 2.11.1
  • ghcup 0.1.20.0
  • macOS Ventura 13.6 (M1 Pro)

Settings JSON file in VS Code

{
    "workbench.colorTheme": "Nord",
    "editor.fontFamily": "Fira Code",
    "editor.fontLigatures": true,
    "editor.fontWeight": "450",
    "files.trimTrailingWhitespace": true,
    "gitlens.hovers.currentLine.over": "line",
    "gitlens.currentLine.enabled": false,
    "gitlens.codeLens.enabled": false,
    "gitlens.defaultDateFormat": "YYYY-MM-DD hh:mm:ss",
    "gitlens.defaultDateShortFormat": "YYYY-MM-DD",
    "gitlens.defaultTimeFormat": "hh:mm",
    "terminal.integrated.fontFamily": "Fira Code",
    "editor.wordWrap": "on",
    "haskell.manageHLS": "GHCup",
    "security.workspace.trust.untrustedFiles": "open",
    "editor.cursorBlinking": "phase",
    "editor.fontSize": 12.5,
    "git.autofetch": true,
    "git.enableSmartCommit": true,
    "terminal.integrated.fontSize": 12.5,
    "haskell.serverEnvironment": {
        "PATH": "${HOME}/.ghcup/bin:${PATH}"
    }
}

Thanks.

Yes, given that the tooltip for raw mentions its type, these tooltips are produced by Haskell-aware tooling. Unless you have other Haskell plugins, it’s most likely HLS.

The import of Data.List.Split is underlined with red on your screenshot. What’s the tooltip? If GHC cannot find an imported module, it will not make any further progress towards type checking.

1 Like

I don’t see why it shouldn’t work :slightly_frowning_face: . If you run cabal build all in a terminal does it build all the executables?

BTW: I did kind of the same for AoC last year. You can check my template which integrates nicely with hls

1 Like

Hello, thanks for your responses. I managed to resolve the issue and I’ll add the steps here for my future self.

On Data.List.Split

The import is correct and cabal run day-01 will complete without issue (and if I remove the erroneous func function, it will build and run with no error messages).

With that said, @Bodigrim’s answer gave me something to think about. I found this thread in the r/haskellquestions subreddit, where the author saw exactly the same thing with Data.List.Split. It turned out that the issue had everything to do with the hie.yaml file, as seen from u/Ok_Pianist_5509’s answer.

gen-hie not found

I next ran the following, based on what I read in the above thread (after deleting my hie.yaml file), and had the following output.

> cabal install implicit-hie
> gen-hie > hie.yaml
zsh: command not found: gen-hie

Making zsh recognise gen-hie

What followed is a bit hazy because it was a bit of trial-and-error. I first deleted the /Users/ikoh/.cabal/bin/gen-hie file (or whatever object it is – still a bit unfamiliar with symlinks). Then I installed implicit-hie again. Running gen-hie > hie.yaml a second time, the error about gen-hie not being found did not appear.

The updated hie.yaml

After running gen-hie my hie.yaml file now looked like this:

cradle:
  cabal:
    - path: "day-01/Main.hs"
      component: "puzzles:exe:day-01"

    - path: "day-02/Main.hs"
      component: "puzzles:exe:day-02"

    - path: "day-03/Main.hs"
      component: "puzzles:exe:day-03"

Back to VS Code

Back in Main.hs of day-01, I saw the following:

This was resolved by restarting HLS in VS Code (‘Haskell: Restart Haskell LSP server’).

And only after that did I see the error message I was hoping for:

Right, so problem solved I guess. Thanks once again!

2 Likes

Oh nice! I didn’t realise I could define common dependencies. I’ll give it a try. :smiley:

I see that your hie.yaml is also like this:

cradle:
  cabal:

I really don’t know why that wasn’t enough in my case, but now that I have gen-hie working that resolves things.

Probably it’d be better for me to just learn how to specify components in hie.yaml files so that I can do it without gen-hie’s help – the fact that I have to delete something and reinstall implicit-hie makes the tool seem somewhat unreliable.

1 Like

Unfortunately, good multi-component (e.g. multiple executables) support in HLS has been a problem area for a while.

There work from Well Typed that aims to fix this, and we are very close to getting it merged, so I’m hopeful that with GHC 9.4+ and the next release of HLS and Cabal the situation should be much improved.

8 Likes

Hie-BIOS is quite straightforward, except for the added “special” modules PackageInfo_NAME.hs and Paths_NAME.hs. You need it anyway, if you have stack or cabal “script” files, where you need to explicitly add GHC command line to hie.yaml to get HLS support in the script’s source file.

the fact that I have to delete something and reinstall implicit-hie makes the tool seem somewhat unreliable.

That’s cabal install's error, you can’t blame the package for that. Of course, if you meant Cabal (the executable :wink: with the tool, …
What happened with gen-hie: Cabal links (without --install-method=copy, which doesn’t always help, because deleting dynamic libraries breaks the executable anyway) the executables into ~/PATH/bin.
ls -l ~/.cabal/bin should show symbolic links (on Windows it copies AFAIK).
And the shell isn’t helping either, because this:

ln -s non_existent foo
./foo

yields

> ./foo
zsh: no such file or directory: ./foo

which is actually a great foot-gun when checking for existing links using stat, a link pointing to a non-existing file (or directory) returns “not existing”.

2 Likes