@@ -713,17 +713,42 @@ pub(crate) async fn find_file(
cx: &mut AsyncWindowContext,
) -> Option<(Range<text::Anchor>, ResolvedPath)> {
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()).ok()?;
-
+ let scope = snapshot.language_scope_at(position);
let (range, candidate_file_path) = surrounding_filename(snapshot, position)?;
- let existing_path = project
- .update(cx, |project, cx| {
- project.resolve_existing_file_path(&candidate_file_path, buffer, cx)
- })
- .ok()?
- .await?;
+ async fn check_path(
+ candidate_file_path: &str,
+ project: &Model<Project>,
+ buffer: &Model<language::Buffer>,
+ cx: &mut AsyncWindowContext,
+ ) -> Option<ResolvedPath> {
+ project
+ .update(cx, |project, cx| {
+ project.resolve_existing_file_path(&candidate_file_path, buffer, cx)
+ })
+ .ok()?
+ .await
+ }
- Some((range, existing_path))
+ if let Some(existing_path) = check_path(&candidate_file_path, &project, buffer, cx).await {
+ return Some((range, existing_path));
+ }
+
+ if let Some(scope) = scope {
+ for suffix in scope.path_suffixes() {
+ if candidate_file_path.ends_with(format!(".{suffix}").as_str()) {
+ continue;
+ }
+
+ let suffixed_candidate = format!("{candidate_file_path}.{suffix}");
+ if let Some(existing_path) = check_path(&suffixed_candidate, &project, buffer, cx).await
+ {
+ return Some((range, existing_path));
+ }
+ }
+ }
+
+ None
}
fn surrounding_filename(
@@ -1490,7 +1515,8 @@ mod tests {
You can't go to a file that does_not_exist.txt.
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
- Or go to /root/dir/file2.rs if project is local.ˇ
+ Or go to /root/dir/file2.rs if project is local.
+ Or go to /root/dir/file2 if this is a Rust file.ˇ
"});
// File does not exist
@@ -1499,6 +1525,7 @@ mod tests {
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
+ Or go to /root/dir/file2 if this is a Rust file.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
// No highlight
@@ -1517,6 +1544,7 @@ mod tests {
Go to fˇile2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
+ Or go to /root/dir/file2 if this is a Rust file.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
@@ -1525,6 +1553,7 @@ mod tests {
Go to «file2.rsˇ» if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
+ Or go to /root/dir/file2 if this is a Rust file.
"});
// Moving the mouse over a relative path that does exist should highlight it
@@ -1533,6 +1562,7 @@ mod tests {
Go to file2.rs if you want.
Or go to ../dir/fˇile2.rs if you want.
Or go to /root/dir/file2.rs if project is local.
+ Or go to /root/dir/file2 if this is a Rust file.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
@@ -1541,6 +1571,7 @@ mod tests {
Go to file2.rs if you want.
Or go to «../dir/file2.rsˇ» if you want.
Or go to /root/dir/file2.rs if project is local.
+ Or go to /root/dir/file2 if this is a Rust file.
"});
// Moving the mouse over an absolute path that does exist should highlight it
@@ -1549,6 +1580,7 @@ mod tests {
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to /root/diˇr/file2.rs if project is local.
+ Or go to /root/dir/file2 if this is a Rust file.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
@@ -1557,6 +1589,25 @@ mod tests {
Go to file2.rs if you want.
Or go to ../dir/file2.rs if you want.
Or go to «/root/dir/file2.rsˇ» if project is local.
+ Or go to /root/dir/file2 if this is a Rust file.
+ "});
+
+ // Moving the mouse over a path that exists, if we add the language-specific suffix, it should highlight it
+ let screen_coord = cx.pixel_position(indoc! {"
+ You can't go to a file that does_not_exist.txt.
+ Go to file2.rs if you want.
+ Or go to ../dir/file2.rs if you want.
+ Or go to /root/dir/file2.rs if project is local.
+ Or go to /root/diˇr/file2 if this is a Rust file.
+ "});
+
+ cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
+ cx.assert_editor_text_highlights::<HoveredLinkState>(indoc! {"
+ You can't go to a file that does_not_exist.txt.
+ Go to file2.rs if you want.
+ Or go to ../dir/file2.rs if you want.
+ Or go to /root/dir/file2.rs if project is local.
+ Or go to «/root/dir/file2ˇ» if this is a Rust file.
"});
cx.simulate_click(screen_coord, Modifiers::secondary_key());
@@ -969,6 +969,9 @@ mod test {
fs.as_fake()
.insert_file("/root/dir/file2.rs", "This is file2.rs".as_bytes().to_vec())
.await;
+ fs.as_fake()
+ .insert_file("/root/dir/file3.rs", "go to file3".as_bytes().to_vec())
+ .await;
// Put the path to the second file into the currently open buffer
cx.set_state(indoc! {"go to fiˇle2.rs"}, Mode::Normal);
@@ -981,5 +984,21 @@ mod test {
cx.workspace(|workspace, cx| {
assert_active_item(workspace, "/root/dir/file2.rs", "This is file2.rs", cx);
});
+
+ // Update editor to point to `file2.rs`
+ cx.editor = cx.workspace(|workspace, cx| workspace.active_item_as::<Editor>(cx).unwrap());
+
+ // Put the path to the third file into the currently open buffer,
+ // but remove its suffix, because we want that lookup to happen automatically.
+ cx.set_state(indoc! {"go to fiˇle3"}, Mode::Normal);
+
+ // Go to file3.rs
+ cx.simulate_keystrokes("g f");
+
+ // We now have three items
+ cx.workspace(|workspace, cx| assert_eq!(workspace.items(cx).count(), 3));
+ cx.workspace(|workspace, cx| {
+ assert_active_item(workspace, "/root/dir/file3.rs", "go to file3", cx);
+ });
}
}