Skip to content

HLS not working for file without .hs extension #3567

Open
@voidus

Description

@voidus

Heya,

I've searched a bit but couldn't find this mentioned before, but I found it pretty hard to search for terms like "extension", ".hs", etc., so please forgive me if this has already been brought up.

I'm using haskell to write a ./go script. In a nutshell, it fills the same role as makefiles in projects that don't use it's only-rebuild-what's-neccessary functionality, or npm's package.json scripts stuff.

I wanted to call it literally do, but then I realized that HSL doesn't work.
I don't have a cabal file or anything since this is not even a haskell project, I'm using a nix devshell to get ghc with the packages I need.

When I rename the file to do.hs, everything works as it should.

While my use case might be better solved by a simpler approach anyways, this should work, right? Looking into :LspLog, it seems like HLS is running but doesn't care about the file. It didn't even print anything about hie-bios or sth.

Your environment

nvim + lspconfig
haskell-language-server version: 1.10.0.0 (GHC: 9.2.7) (PATH: /nix/store/cqcm8dqw7zjj363q8z0mphg17bnxrrn1-haskell-language-server-1.10.0.0/bin/haskell-language-server-wrapper)

Which OS do you use?
Arch linux, but everything is done via nix

I tried adding a hie.yaml with different content but nothing changed

If this is actually debugging-worthy, I'd be happy to trim down my stuff and upload it somewhere, reproducing should be straightforward with nix.

Activity

added
type: bugSomething isn't right: doesn't work as intended, documentation is missing/outdated, etc..
on Apr 20, 2023
fendor

fendor commented on Apr 20, 2023

@fendor
Collaborator

Hi, thank you for your bug report!

This issue is twofold, I think. First, the client needs to realise this is a haskell script, launch HLS and feed it with the file contents. I don't know whether your LSP client does that, e.g. on VSCode does not send a file without an .hs or .lhs extension to HLS. Even if it worked, HLS tries to be a multi-language Language Server, for .cabal and .hs files. It tells the difference by looking at the file extensions. If HLS accepted source files with no extension, I don't think it would be able to tell them apart easily.
I don't know how HLS can support .cabal and .hs files but still satisfy your requirements.

A quick workaround might that you create a symbolic link, e.g. do.hs that points to do. Maybe that's enough to trick HLS to work correctly? Since you are nix for developing, the nix develop could create that symbolic link automatically?

voidus

voidus commented on Apr 20, 2023

@voidus
Author

The multi-filetype thing sounds about right.
Editing through a symlink works indeed. It even works if the file is called .do.hs 😂

I think I have wired up nvim to send stuff to HLS if the filetype is marked haskell. I don't know the lsp protocol, but shouldn't HLS get that info from vim?

If not, I'd propose a way to force that filetype, like # hls: haskell after the shebang. But I'd also understand if this is not a priority.

fendor

fendor commented on Apr 20, 2023

@fendor
Collaborator

I think I have wired up nvim to send stuff to HLS if the filetype is marked haskell. I don't know the lsp protocol, but shouldn't HLS get that info from vim?

That looks possible https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem seems to have languageId. We might use that as the secondary information to distinguish the filetype.

voidus

voidus commented on Apr 20, 2023

@voidus
Author

That would be pretty cool. I'd be curious to contribute on that, but between the learning and time I'll need, I can't tell if or when I'd get around to that.

fendor

fendor commented on Apr 20, 2023

@fendor
Collaborator

As a warning, I am not confident that works, e.g. do clients actually send "haskell" or something like that?

But if you want to give it a try, I think it should be a matter of modifying
pluginResponsible to take an additional parameter for the languageId and adding a field to PluginDescriptor to add the languageId it accepts. Then, the type errors should be enough to guide you.
Every time you invoke pluginResponsible, you need to pass in the languageId field, which you can obtain from TextDocument.

voidus

voidus commented on Apr 23, 2023

@voidus
Author

Just did a small experiment with Debug.trace nvim lspconfig does indeed send either haskell, cabal or lhaskell.

This should be verified with other clients, but I'm optimistic.

The bad news is that the code needs nontrivial changes. As far as I can tell from a quick look, we're actually matching the plugins on every request. But the language ID is only sent when the document is openened, so we'd need to store it somewhere.

fendor

fendor commented on Apr 24, 2023

@fendor
Collaborator

Storing should be possible, but I can't tell you right away what changes that requires 😅. I'll try to take a look.

voidus

voidus commented on May 11, 2023

@voidus
Author

I've added a new Var to the ide state and I think I can get it threaded through.
voidus#1
(I opened the PR for easier feedback, but I didn't want to increase the noise here since it's still a very early draft)

I haven't really understood the test strategy yet, maybe I'll look into that when I find some more time.

From what I see, I get the impression that we can get away with carrying around very little state outside of the shake rules, which is great of course, but maybe this change would be time to refactor that a little?

Currently, we store whether files are modified and whether it's a file of interest. Fixing this would add the language ID.
Should we add a record to group this buffer information?

The bigger issue though is that with this change, we now need part of the global state (the language for the given file) to determine which plugin to run. Currently, we take the uri from the request. This seems like a significant complication for that part of the system.

I'd be happy to hear your thoughts on this, and I'll keep looking into this whenever I find some time.
I will need support though, and I am conscious of taking your time.

All in all, I don't think this feature is the biggest, most important thing. For me personally, it's at least partly an excuse to dig into a haskell codebase. If you don't have time for this (or just need a while to respond) that is completely okay with me.

Edit: It's interesting though that the params aren't needed anymore for deciding if a plugin should handle a request. Maybe this could be moved to startup? Can plugins be dynamically configured?

fendor

fendor commented on May 21, 2025

@fendor
Collaborator

Sorry, for some reason this got completely lost.

If you are still interested (after more than 2 years 😆 ), please open the PR against HLS itself, since I think part of why I missed it is because it is not a PR against upstream.
Otherwise, maybe we can get someone during ZuriHac to push this over the finish line.
Maybe @VeryMilkyJoe ?

Also, I think the PR looks pretty good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    can-workaroundtype: bugSomething isn't right: doesn't work as intended, documentation is missing/outdated, etc..

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @voidus@fendor

      Issue actions

        HLS not working for file without .hs extension · Issue #3567 · haskell/haskell-language-server