Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest an appropriate module name with the 'defmodule' snippet #684

Merged
merged 5 commits into from
Apr 19, 2022

Conversation

bottlenecked
Copy link
Contributor

Hi all, this is a feature that I always wanted to have and now took some time to implement.

Basically when creating a new file and writing a defmod... prompt for auto-completion I wanted a likely module_name to be suggested along with completing the defmodule do .. end snippet.

This is what my attempt looks like

suggest_module_name.mov

There are some limitations I'm aware of (and probably tens of others that I'm not aware of!)

  • Nested module names: the users will be getting a suggestion based on the full file path instead of taking into account the parent module and basically doing nothing (let the users name their module)
  • Phoenix conventions are not observed. e.g with controller names the suggested module name will be MyProjectWeb.Controllers.PageController instead of MyProjectWeb.PageController

Still, do let me know if this PR is on the right track and if so also let me know if these limitations (and possibly others) should be fixed before attempting to ship something like this to the users.

Thanks for taking the time to review!

Copy link

@sharpfun sharpfun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff, here are some ideas)

|> String.split(".")
|> case do
[file, "ex"] ->
do_suggest_module_name(reversed_path, [file], topmost_parent: "lib")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually phoenix projects have test/support folder that have these files:

channel_case.ex
conn_case.ex
data_case.ex

Factories or other helpers are created in that folder.
Based on file extension topmost_parent will be "lib" which would be incorrect


[file, "exs"] ->
if String.ends_with?(file, "_test") do
do_suggest_module_name(reversed_path, [file], topmost_parent: "test")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen code with tests in lib folder, it was assumed, that it gives better test coverage.
Maybe there is a way to get root folder of the project?
Umbrella projects will definitely complicate root folder solution :/
Or both "lib" and "test" could be used as topmost_parent.
What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sharpfun Thanks for the suggestions, but I'm hesitant to go any further at this point before anyone of the maintainers chimes in because it might be the wrong way to go about it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't make this work for everyone. I'd aim for covering basic mix project and phoenix app as those are most commonly used.

defp snippet_for({"Kernel", "defmodule"}, %{uri: uri}) when is_binary(uri) do
# In a mix project the uri can be something like "/some/code/path/project/lib/project/sub_path/myfile.ex"
# so we'll try to guess the appropriate module name from the path
"defmodule #{suggest_module_name(uri)}$1 do\n\t$0\nend"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convert URI to path. SourceFile has function for that

do_suggest_module_name(rest, [dir_name | module_name_acc], opts)
end

defp do_suggest_module_name([], _module_name_acc, _opts) do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it optimal to go all the way up to / or C:\? We could stop at workdir level.

@lukaszsamson
Copy link
Collaborator

Nice feature @bottlenecked. I'd be happy to marge it. Can you verify if it works with umbrella apps and on windows? Those two are often tricky.

@bottlenecked
Copy link
Contributor Author

bottlenecked commented Apr 4, 2022

Thanks @lukaszsamson for the encouragement.

So I had some time to move forward with this to basically implement some of the suggestions here

  • use SourceFile.path_from_uri/1 to work with the path instead of the uri
  • umbrellas look like they're already handled by the current approach so I added a test to prevent regressions in the future when used with umbrella apps
  • Special cased the handling of known Phoenix folders directly under *_web

What I still haven't done is

  • make sure that this works on Windows (my feeling is that it will because Path.split/1 should be able to handle Windows paths just fine but we'll see)
  • handle files under the test/support folder. I'm not sure that there's a general rule that can be used like e.g. the top level module should always be MyProjectWeb.*. What do you think the rule should be like? Or maybe we just don't handle this case and we return the defmodule snippet without a suggested module name.
  • Stop traversing the path when reaching the project dir. Note: opening orphaned files from different parts of the disk into the same vscode instance might lead to unexpected behaviour, I'll see about testing that case

@bottlenecked
Copy link
Contributor Author

Some more thoughts on stopping at the project_dir level. It seems to me that the way the code is written, most of the times the traversal would stop at either the lib or the test folder levels. I guess I'd expect most projects to be structured like the mix new / mix phx.new structure the generated code, so maybe it would not be as bad to traverse the path all the way to the top when the projects are not structured as expected (should be few enough?). Plus, I think it would be pretty cheap to go all the way to the top anyway even for extreme cases of 1000 levels of deep folder nesting. What do you think @lukaszsamson?

@bottlenecked
Copy link
Contributor Author

I managed to test this on Windows (virtualbox VM on mac) and as hoped for expected Path.split/1 does the right thing on Windows paths too

windows_suggest.mov

Some plugin or setting in my editor trimmed extra spaces from the lines
but in this case it cause a test to break by changing the cursor position
of the auto-completion trigger
@bottlenecked
Copy link
Contributor Author

bottlenecked commented Apr 15, 2022

In d3b624f the broken test is fixed (extra space at end of line was previously trimmed out and now restored) and also removed the debug IO.inspect expression that was previously added

@lukaszsamson lukaszsamson merged commit 7f37d59 into elixir-lsp:master Apr 19, 2022
@noozo
Copy link

noozo commented May 4, 2022

Much needed. Now i can get rid of the "Insert Elixir Module name here" extension. Genius!

Any idea when a new release will come out that contains this?

@jared-mackey
Copy link
Contributor

After having used this for a long while now, I just wanted to chime in and say thanks again for this. It is such a great addition! 🙏🏻 ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants