FractalBoy / perl-language-server

100 stars 12 forks source link

Suggest/complete names to import #131

Open AlexTalker opened 1 year ago

AlexTalker commented 1 year ago

This issue is copied from side discussion in #118.

Say, one writes:

use AnyEvent::Util qw(
);

and then proceeds to fill in imports:

use AnyEvent::Util qw(
    fork_<pressing Ctrl-Space>
);

One expects to get completion for available imports but unfortunately currently this approach yields nothing.

I feel like it might be pretty handful to handle, since you already can complete imported names.

(The expected result is to complete to fork_call, see the original)


This can be done with some ease due to two assumptions I got from testing the plugin:

  1. For modules exporting names via @EXPORT, completion/suggestion just works after one writes use MyExportModule
  2. For modules exporting names via @EXPORT_OK completion/suggestion works only for imported names but I can clearly see the connection between the name and importing module in the IDE

Thus, the knowledge necessary to associate in use statement between the module name and the list of imported names is already used, as well as the knowledge what modules export implicitly, so the hard part seems to be to stack those two features together if possible reasonably.

FractalBoy commented 1 year ago

Currently, we use the symbol table to resolve completions (i.e. you type AnyEvent::Util:: and fork_call pops up). We would require AnyEvent::Util and then look at all of the symbols available in %AnyEvent::Util::. Some of them might not be importable but it might be good enough.

We could try doing a require of AnyEvent::Util and then looking at @AnyEvent::Util::EXPORT and @AnyEvent::Util::EXPORT_OK directly, but it may not catch everything (we'd probably have to look at @ISA as well?). We'd also probably want to look at @EXPORT_TAGS. The issue with this is that plenty of modules don't use Exporter, so @EXPORT and @EXPORT_OK would not be correct.

The trickiest part is figuring out the correct way to parse the import, and why I haven't yet done this. The import list is just a list (with or without parens), so it can be provided in many different ways:

  1. In qw (with any delimiters)
  2. In single or double quotes (or q or qq with any delimiter)
  3. In single or double quotes or a combination of quote/quote like operators inside parentheses
  4. With a fat comma (for example, Test::More tests => 10)

That being said, we probably shouldn't let perfect be the enemy of the good, and perhaps we can get away with just looking at what the user is currently typing and stopping at the earliest \b. For example, if typing something complex like:

use AnyEvent::Util ((), qw(), q(fork_
                                     ^----- assuming cursor is here

Just grabbing from where the cursor is back to the paren.

AlexTalker commented 1 year ago

@FractalBoy Yeah, you're right, I mean in perfect scenario to get imports table we need to actually execute the code 100% as it usually used, as say @EXPORT* might be populated at runtime, not speaking about no strict; magic one can do like the 80s are back again xD

Yes, argument to sub import is just a list that gets the meaning as one implements it(but I personally use Exporter 90% of the time) and more often than not one still just imports names rather than doing odd black magic. So I think since this issue can be massively simplified to "get module name and do the same thing as when @EXPORT is used but just for any @EXPORT_*/%EXPORT_*" might be handy, even if it's some optional, enabled by extra setting feature.

OFC nowadays one rather writes the name itself in the code and IDE is supposed to suggest import but this is still somewhat advanced for me =)