-
-
Notifications
You must be signed in to change notification settings - Fork 887
Add create unknown module code action #4888
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
base: main
Are you sure you want to change the base?
Add create unknown module code action #4888
Conversation
|
Hi, thank you for this! This seems like something we want, but we generally don't accept unplanned PRs. Would you mind making an issue about this, so we can discuss how exactly we want this to work? I feel I would want this code action available for all modules, not just sub-modules of the current one, though others may have different opinions. |
|
Absolutely! I've moved this to draft for now and created an issue here: #4894 |
ac76b78 to
78681dc
Compare
|
Is this ready for review? |
Yup! I've just moved it off draft. |
lpil
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! This is looking really nice. I've left a small comments inline, and the build is failing currently.
Would you mind updating the changelog file too please 🙏
| /// ```gleam | ||
| /// // foo.gleam | ||
| /// // Diagnostic: Unknown module | ||
| /// import foo/bar/baz |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Remove foobar references please, it has a not-very-cheery WW2 association so we avoid it in favour of more lighthearted examples.
| /// | ||
| /// ```gleam | ||
| /// // foo/bar/baz.gleam | ||
| /// ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two code examples are not very clear, so let's describe this code action using words instead.
| .input_path | ||
| .as_str() | ||
| .strip_suffix(&format!("{}.gleam", self.module.name)) | ||
| .expect("origin is ancestor of module path"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Path manipulation can only happen in the ProjectPaths class, so pass that into the code action class from the LS engine instance and use module.origin to determine which directory to use.
This also enables us to avoid panicking, which we always try to avoid.
| let uri = Url::from_file_path(format!("{origin_path}/{}.gleam", unknown_module.name)) | ||
| .expect("origin path is absolute"); | ||
|
|
||
| CodeActionBuilder::new(format!("Create module {}.gleam", unknown_module.name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should either be Create module wibble/wobble or Create src/wibble/wobble.gleam as module names don't have a .gleam suffix, Gleam file paths do.
| }); | ||
|
|
||
| for unknown_module in unknown_modules { | ||
| if !self.code_action_span.intersects(*unknown_module.location) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Contains rather than intersects please. We don't want to trigger this if someone selects a larger block that happens to include this error.
78681dc to
7fcc8f7
Compare
|
Thanks for the feedback @lpil! I've addressed your comments, let me know if there is anything else that should be addressed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fantastic! Thank you! Looks really good.
I missed in the last review that there are no tests, sorry about that. Could you add tests, including ones that make sure that src, test, and dev are appropriately picked for the new file.
When you are ready for a review please un-draft this PR and tag me for a review. Thank you!
7fcc8f7 to
332f6ed
Compare
|
@lpil Thanks! Tests are added. Let me know what you think of the refactoring needed to support defining an origin & module with |
332f6ed to
f254f5e
Compare
lpil
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, very nice. The tests look great, but I've left some notes inline about the changes to the test API. Once that is sorted we can merge, thanks!
| Module(Origin, &'a str, Position), | ||
| } | ||
|
|
||
| impl From<Position> for Cursor<'_> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never implement traits in this codebase please.
| .expect("Module doesn't exist") | ||
| }; | ||
|
|
||
| let ((mut engine, params), code) = match cursor { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than overloading this function please create a new function for positioning within a different file, if needed.
|
|
||
| #[test] | ||
| fn create_unknown_module_under_src() { | ||
| assert_code_action_file_operations!( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we not teach the existing macro how to understand these file operation changes too? It has the nice output which we want to have here, and it would be good to to have to maintain twice as many macros.
|
|
||
| let response = engine.compile_please(); | ||
| assert!(response.result.is_ok()); | ||
| let _response = engine.compile_please(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Compile errors no longer cause the tests to fail!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code action requires the type error to be present, positioned_with_io allows them so I can test when invoking code actions in self.src but if we assert on compiler errors under positioned_with_io_in_src/dev/test then I can't trigger this code action within specific modules.
How do you suggest we go about that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have existing code actions that only trigger when code fails to compile, how are those tested?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They all only use TestProject::at / positioned_with_io which doesn't make this assertion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @lpil, friendly bump. How'd you like to move forward on this?
f254f5e to
4d373c3
Compare
|
@lpil Appreciate the great feedback. I've addressed your comments, though see #4888 (comment) |
4f7ca62 to
383f986
Compare
383f986 to
655236a
Compare
|
Hey @lpil checking in on this, can I get another review / finalize a decision on #4888 (comment)? I've rebased and this is otherwise good to go. |
lpil
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't understand why so many any so large changes have been made to the test APIs, there doesn't seem to be anything particularly unusual about this new code action.
Why is it that the tests need to be made so much more complex?
All changes revolve around needing a This code action's behavior is dependent on which origin it is invoked in and I need to test that the new module is properly created within that origin. It also seems generally useful to be able to trigger a code action from anywhere, not just the root I'm happy to abandon these changes and only test the code action against |
655236a to
5a5e1cd
Compare
5a5e1cd to
cff104a
Compare
Adds a new code action to create an unknown module. This allows you to easily define new modules without having to leave your editor.
Screen.Recording.2025-08-27.at.11.19.35.PM.mov