haskell / haskell-mode

Emacs mode for Haskell
http://haskell.github.io/haskell-mode/
GNU General Public License v3.0
1.33k stars 343 forks source link

Intergrate company-mode or auto-complete #215

Closed ShadowCreator closed 8 years ago

ShadowCreator commented 10 years ago

Both are auto-completion extensions for Emacs and having decent integration with them would improve productivity

https://github.com/auto-complete/auto-complete

https://github.com/company-mode/company-mode

hvr commented 10 years ago

see also #178

vagifverdi commented 10 years ago
(require 'auto-complete)

(defun my-ac-haskell-mode ()
  (setq ac-sources '(ac-source-words-in-same-mode-buffers
                     ac-source-dictionary
                     ac-source-etags
                     )))

(add-hook 'haskell-mode-hook 'my-ac-haskell-mode)

(defun my-haskell-ac-init ()
  (when (member (file-name-extension buffer-file-name) '("hs" "lhs"))
    (auto-complete-mode t)
    (setq ac-sources '(ac-source-words-in-same-mode-buffers
                       ac-source-dictionary
                       ac-source-etags
                       ))

))

(add-hook 'find-file-hook 'my-haskell-ac-init)
gracjan commented 9 years ago

This issue report was last updated in 2013. Is it still relevant to current code state?

gracjan commented 9 years ago

@geraldus: Is this issue describing what you are working on?

geraldus commented 9 years ago

Yeah, but I'm very limited in time :( Also, integrate means dependencies, and my current goal is separate packages (backends) for company and AC, built on top of some common functionality from haskell-mode (this is step 1 I'm currently working on).

hvr commented 9 years ago

@geraldus btw, are you by any chance also tackling the issue of asynchronous lookups as you mentioned in #178 ?

geraldus commented 9 years ago

@hvr I hope I can tackle this. First of all I need to finish functions which grabs prefix and its context, this information will be used further in completion function (I'm almost done with it, and currently making some final polish).

I'm still need some research, but I suppose the best way to get synchronous completions is to search them in tags, rather than ask REPL (or maybe select tags if REPL is busy or there is no active haskell process). Also I want to use dabbrev--find-all-expansions function in case it is not possible to get completions from REPL or TAGS, which will also give completions for arbitrary words in string literals and comments.

hvr commented 9 years ago

The benefit of asking the REPL is that it knows what's in scope, and maybe in the future we may be able to ask the REPL (or maybe ide-backend -- haven't looked at it yet) to give us more context-aware completions for a given location, all stuff that require more meta-information to be known about the symbols you want to offer completions for.

geraldus commented 9 years ago

I agree with you (and I've have not looked at ide-backend too). But, there are some caveats:

  1. it is possible, that user does not use interactive-haskell-mode and REPL is not running.
  2. REPL does not provide completions for things defined in let blocks (this is why we also need dabbrev stuff).

As for ide-backend if I'm not mistaken FPco IDE is powered by it, and it will be very nice to make Emacs experience a bit closer to FPco IDE. I'm going to play with it, but I want to finish some tasks for plain haskell-mode without extra tools such as GHCi-ng, ghc-mod, and etc.

gracjan commented 9 years ago

REPL may also be hanging because currently same repl session is used both for arbitrary user interaction and completion commands. That mean sync completion may hang whole emacs and async completion may not return obvious expected results.

So REPL is the way to go, but needs to be handled more carefully.

geraldus commented 9 years ago

@gracjan btw, is there a way to check is REPL currently busy or idling?

gracjan commented 9 years ago

There is no reliable way to do that. There are hacks, some more hilarious than others. Standard comint mode decides that a shell-like session is busy if the cursor is in the first column and ready to accept input if cursor is at the end of a non-empty line (presumably prompt).

This is very fragile. That is why we have whole industry of more request-response interactions like ghc-mod, ide-backend, ghci-ng, ghcid and probably others I haven't heard about.

Would be great to split command and interactive channels inside ghci. Would be even better if command channel had a defined command/response separator.

geraldus commented 9 years ago

The only one reason we cannot include asynchronous company-backend in haskell-mode package is company-begin-backend1 function and this is required by company itself. I'm curious is there a workaround to use this without bringing company dependency?

gracjan commented 9 years ago

fboundp works in such situations.

creichert commented 9 years ago

It's difficult to tell what the overall goal is. Will this have on my current auto-complete sources, if any? Is it specifically to add support for auto completion sources directly from haskell-process or haskell-interactive-mode, maybe ide-backend as well? I'm happy this is being improved, just curious.

One related feature I am interested in helping improve is inlined haddocks in the completion popup. I'm currently populating the auto-complete help buffer with hoogle search --info commands when possible. I suppose it would be possible to use the current haskell-hoogle-command.

1434745773 797790

geraldus commented 9 years ago

hey @creichert! so, you're with auto-complete? If this case can I ask you a questions about auto-complete when/if needed, I'm using company myself and have no much time to play with auto-complete myself.

As for completions popup, company-mode have slightly different approach and your screenshot shows that's it is quite useful to show such haddock info in case of auto-complete.

For now I've updated default completion-at-point function, and there is a possibility to provide with completion list special annotation function to get extra information for completions and it's currently unimplemented. Initially I've planned to provide annotations for haskell identifiers using :info REPL command, but haddock alternative looks nicer. However I don't know how and when Emacs uses this annotation function and how this will affect company and a-c work, need some time to make a research.

As for company it has its own meaning of annotation and meta data, for identifier completions it's better to use module name as annotation and haddock info as meta data, like ghc-mod backend does:

Annotations pic 1

Type info in echo area pic 2

Haddock info in special company doc-buffer pic 3

creichert commented 9 years ago

Sure, I'd be happy to help where I can (feel free to ping me on any issues).

company-mode and auto-complete do have a slightly different process but they both support candidates and a doc-buffer. I have to take some time to test and look further at what you've already completed and see how I can help.

The doc buffer shown in my screenshot is hoogle search --info IDENT, which would be run from the 'document' source below:

;; basic auto-complete source
'((available . (lambda() ...) )
    (candidates . (lambda () ... ))
    (document . (lambda () ...))
    (symbol . "h"))

:info and other commands are definitely suitable as well (so it might need to use haskell-hoogle-command or something similar).

gracjan commented 8 years ago

@geraldus, @creichert: Is this issue still needed? What is left to be done here?

geraldus commented 8 years ago

@gracjan ahh, I have a plan to have three config files for autocompletions: Emacs native completions, company-mode and auto-complete, and update completion code to provide annotations and etc. But I don't know when I will be able to do this. Let's keep this issue for some time, if I decide to leave this idea I will ping you.

gracjan commented 8 years ago

Okay, can you describe your idea in a bit more detail here in comments so that we know what you are thinking. Somebody might be coming by and be able to help.

geraldus commented 8 years ago

Well… there is a lot of work on auto completion improvements.

Fist of all, we have two cases: non-interactive and interactive.

Non-interactive

Let's talk about non-interactive first.

Here the best we can do is to provide dynamic case sensitive completions (dabbrev). We already support hardcoded pragmas, so why not to add hardcoded keywords too (at this moment my company-mode do not gives me keyword completions)?

Then we already creating completion list for supported GHC options dynamically asking GHC. First of all, I would like to mention one caveat: we have to take in account, that it is possible that GHC is not available in PATH, but still installed in system if I'm not mistaken (stack could make possible such situation). We can create hard-coded list of completions for Prelude identifiers in similar fashion (:browse! Prelude), but in this case we have to ask GHCi instead, and in some cases this is impossible, e.g. in case of GHCJS (however Luite promised that he will merge GHCJSi branch very soon!).

Here is a quick summary:

Interactive

Interactive completions are much nicer and we can grab additional information for completions such as type signature and documentation, but I still do not know how can we get this additional data cheap and easy and also uniformly to support all possible cases: Emacs' native completion machinery, company-mode, auto-complete. GHCi itself have some issues in :complete command, I have fixed operator completion (should become available in GHC 8), but it requires at least one more improvement; also GHCi itself does not provide keyword completions (I'll try to fix this too). When I'll find time to take care about mentioned GHCi issues I will ask people on #ghc about possible improvements for :complete command, I guess it is possible to make it return not plain list of strings, but also extend this list with additional data (such as completion type, for identifiers: module name, type signature, docstring). But if I understood correctly this will go to 8.0.2 which will be released in one year term. So, due to GHCi is not perfect tool to get completions right now I think the best we can do is to offer to users some existing tools such as ghc-mod and friends. So, let's delay on interactive part at least for now. One last point: I think we should take in account possible failures when receiving interactive completions and concat to interactive completion list a list of non-interactive completions (dabbrev, keywords).

Fuzzy completions

I would be happy to have fuzzy completion function, let me explain this by example. Suppose I have few definitions: twice, toWidget, takeWhile, in this case I expect all of them should be listed as candidates of tw expansion. I haven't though much of such functionality yet, maybe it is possible to achieve this already.

Context aware auto completions

Finally, the best thing I can think of is context aware completion. Likely this is interactive only stuff. What I mean? Simply to sort completions according to given context. For example, if you type function argument then completion function could take in account expected argument type and bump completion candidates with matching type to top of the list. When typing inside string — bump non-identifier candidates. And etc.

Conclusion

We can take care of non-interactive part first.

PierreR commented 8 years ago

As an extra piece of information, company-ghc provides auto-completion in non-interactive mode via ghc-mod (this is the default setting in spacemacs). It is very good but it might get sluggish with bigger projects (see https://github.com/iquiw/company-ghc/issues/23)

PierreR commented 8 years ago

@geraldus Correct me if I am wrong but given that the plan is to do nothing extra in the interactive mode for now, I am under the impression that everything you propose is already achieved by spacemacs. In a way I am just saying it is a matter of configuration and it might not be a job for haskell-mode (at least for now). With HIE along the way are we sure such an integration worths the effort ?

gracjan commented 8 years ago

I'd like to have as much non-interactive completions happening without ghc-mod as possible.

Currently PRAGMA and LANGUAGE completions work only when haskell-interactive-mode is enabled, those should work with only pure haskell-mode.

Question: what is the difference between auto-complete and company-mode? Are those significantly different or are they supported by the same implementation mechanism?

geraldus commented 8 years ago

@PierreR I haven't used Spacemacs myself, but if I'm not mistaken it have company-mode integrated, and my major point that we should provide some uniform completion function for all cases: no auto-completion package installed, company-mode and auto-complete (and possibly some extended versions depending on if company or AC is available in user config).

everything you propose is already achieved by spacemacs

Surely? Does it provide mentioned fuzzy completions and context aware completions? If this is a case I'd definitely take a look (or are you talking about interactive part only?). And I suppose this is achieved because of some built-in extra packages, I guess this is ghc-mod. UPDATE: is it complete keywords too?

@gracjan

Currently PRAGMA and LANGUAGE completions work only when haskell-interactive-mode is enabled

Emmm… Are you sure?

UPDATE: hmm… it looks like I'm missing something, when I load Haskell file I always see Interactive minor mode, when I wrote "interactive" I meant loading project/file to REPL, that is a haskell session, and non-interactive means coding without REPL interaction. So, do we have a scenario where Interactive minor mode is not enabled and what is the difference?

what is the difference between auto-complete and company-mode

The former is simpler and synchronous and the latter is more flexible and could work asynchronously (if I'm not mistaken)

gracjan commented 8 years ago

@geraldus: I'm pretty sure that pure haskell-mode does not offer completions, see:

$ grep completion-at-point-functions *.el
haskell-interactive-mode.el:  (add-hook 'completion-at-point-functions
haskell.el:  (add-hook 'completion-at-point-functions

Am I missing something?

geraldus commented 8 years ago

@gracjan yes, you're right. But why? I think we definitely can provide non-interactive completion function which completes:

We already had some efforts to make use of dabbrev completions in past. I think we should define a function to collect dabbrev expansions along with customizable haskell-dabbrev-expansion-limit option with some sensible default value and make use of it in completion-function. This will give us some minimal non-interactive completion function, and finally we can include its results in REPL-completion function when REPL is not available or does not returned results.

Some notes about company-mode: it is already has dabbrev backends (company-dabbrev and company-dabbrev-code), and I suppose we can take two ways:

What do you think?

gracjan commented 8 years ago

In the code we should have nothing dabbrev related. On the other hand in the documentation we should have a page saying: 'haskell-mode works great with dabbrev based completion, here is how to enable it'.

cocreature commented 8 years ago

I think we should define a function to collect dabbrev expansions along with customizable haskell-dabbrev-expansion-limit option with some sensible default value and make use of it in completion-function.

Isn’t the whole point of dabbrev that it works without doing anything? It just searches everything in the buffer.

geraldus commented 8 years ago

It just searches everything in the buffer.

and other buffers too. I have already mentioned this, but will repeat: dabbrev also have some concept of friendly buffers and search expansion there first, for now we can mark all *.hs buffers as friendly, in future we can do this based on project files.

dabbrev do not provides a function to get a collection of expansions, it immediately expands thing at point, and if triggered one more time, searches next expansion. It is needed to write some code to get a list of expansions.

cocreature commented 8 years ago

Just let company,autocomplete or whatever else deal with that. There is no need to replicate that functionality inside of haskell-mode.

geraldus commented 8 years ago

@cocreature in general I agree with you, I just stuck a bit with following case: no special auto-completion package installed; non-interactive haskell-mode if we bind completion-at-point-functions to yet undefined haskell-completions-at-point-function (without REPL capabilities) then C-M-i will provide no candidates for identifiers. However, users who aware of dabbrev could expand it hitting M-/ manually.

cocreature commented 8 years ago

no special auto-completion package installed;

Why is that a problem? If people want to have auto completion they’re going to install one of those packages. It’s not like haskell-mode is the only mode requiring you to use one of those.

non-interactive haskell-mode

I agree that something basic completing extensions and the like could be nice in that case.

if we bind completion-at-point-functions to yet undefined haskell-completions-at-point-function (without REPL capabilities) then C-M-i will provide no candidates for identifiers. However, users who aware of dabbrev could expand it hitting M-/ manually.

Again let auto-complete or company take care of mixing completions and falling back on one if the other doesn’t work.

haskell-mode is already pretty big and you are proposing to replicate functionality solved perfectly in other packages making it even bigger. Leaving aside the fact that it will take a ton of work to properly replicate that functionality it’s also just useless. If people care about completion they’re going to install one of those packages so the effort should be spend on documentation for anything specific to haskell there and not on rewriting it just so that it doesn’t need to be installed. For people that don’t care about completion they don’t need some reimplementation of company-mode in haskell-mode.

gracjan commented 8 years ago

@geraldus: This is good the way you described. In case of vanilla haskell-mode there should be this:

  1. In PRAGMA context C-M-i should complete PRAGMA.
  2. In LANGUAGE context C-M-i should complete LANGUAGE.
  3. In other contexts C-M-i should do nothing.
  4. M-/ should do its own thing always.

Note that I personally would never enable dabbrev autocomplete or company because I want C-M-i to be context sensitive and M-/ to be context in-sensitive.

geraldus commented 8 years ago

@gracjan ok, it become a bit clear now. Finally let's

For interactive part:

I'd prefer a bit longer names: haskell-completion-completion-at-point and haskell-completions-sync-repl-completions-at-point.

Docs:

If everyone is agree with this I would be happy to implement all this stuff.

And I vote for closing this for now.

Related #1169 #1171

gracjan commented 8 years ago

@geraldus: Create an issue and write there exactly the same text that you wrote here. Then we will close this one.

geraldus commented 8 years ago

Done.

geraldus commented 8 years ago

Sorry for extra noise on this, but I decided to clarify type signature and info aspects a bit.

We do not want to depend on extra tool in haskell-mode itself, and due to GHCi does not provide an ability to get type signatures and info among with completion candidates yet the only way to provide this data (both for auto-complete and company-mode) would be send REPL another command after candidates are received. This is doable but I suppose this way possibly very expensive. For now the best option is to provide simple completions by default and mention some third party plugins in documentation. However, even if we'll have a single command to grab all data, simple candidates list method would be faster and it is very likely that we'll need some customization to control this. Since customization will be introduced anyway, we can do this now and provide experimental and possibly slow method to get extra info. Other customizations we likely need:

I think it's better to create new issue for this topic.

gracjan commented 8 years ago

@geraldus: Can you create this new issue for this topic? This one has grown out of manageable state.