-
-
Notifications
You must be signed in to change notification settings - Fork 873
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. |
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
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. |
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