vim: Add 'gf' command, make files cmd-clickable (#16534)
Release Notes: - vim: Added `gf` command to open files under the cursor. - Filenames can now be `cmd`/`ctrl`-clicked, which opens them. TODOs: - [x] `main_test.go` <-- works - [x] `./my-pkg/my_pkg.go` <-- works - [x] `../go.mod` <-- works - [x] `my-pkg/my_pkg.go` <-- works - [x] `my-pkg/subpkg/subpkg_test.go` <-- works - [x] `file\ with\ space\ in\ it.txt` <-- works - [x] `"file\ with\ space\ in\ it.txt"` <-- works - [x] `"main_test.go"` <-- works - [x] `/Users/thorstenball/.vimrc` <-- works, but only locally - [x] `~/.vimrc` <--works, but only locally - [x] Get it working over collab - [x] Get hover links working Demo: https://github.com/user-attachments/assets/26af7f3b-c392-4aaf-849a-95d6c3b00067 Collab demo: https://github.com/user-attachments/assets/272598bd-0e82-4556-8f9c-ba53d3a95682
This commit is contained in:
parent
1e39d407c2
commit
db0c1fd592
9 changed files with 579 additions and 43 deletions
|
@ -649,16 +649,17 @@ impl DirectoryLister {
|
|||
};
|
||||
"~/".to_string()
|
||||
}
|
||||
pub fn list_directory(&self, query: String, cx: &mut AppContext) -> Task<Result<Vec<PathBuf>>> {
|
||||
|
||||
pub fn list_directory(&self, path: String, cx: &mut AppContext) -> Task<Result<Vec<PathBuf>>> {
|
||||
match self {
|
||||
DirectoryLister::Project(project) => {
|
||||
project.update(cx, |project, cx| project.list_directory(query, cx))
|
||||
project.update(cx, |project, cx| project.list_directory(path, cx))
|
||||
}
|
||||
DirectoryLister::Local(fs) => {
|
||||
let fs = fs.clone();
|
||||
cx.background_executor().spawn(async move {
|
||||
let mut results = vec![];
|
||||
let expanded = shellexpand::tilde(&query);
|
||||
let expanded = shellexpand::tilde(&path);
|
||||
let query = Path::new(expanded.as_ref());
|
||||
let mut response = fs.read_dir(query).await?;
|
||||
while let Some(path) = response.next().await {
|
||||
|
@ -7769,6 +7770,88 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns the resolved version of `path`, that was found in `buffer`, if it exists.
|
||||
pub fn resolve_existing_file_path(
|
||||
&self,
|
||||
path: &str,
|
||||
buffer: &Model<Buffer>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Option<ResolvedPath>> {
|
||||
// TODO: ssh based remoting.
|
||||
if self.ssh_session.is_some() {
|
||||
return Task::ready(None);
|
||||
}
|
||||
|
||||
if self.is_local() {
|
||||
let expanded = PathBuf::from(shellexpand::tilde(&path).into_owned());
|
||||
|
||||
if expanded.is_absolute() {
|
||||
let fs = self.fs.clone();
|
||||
cx.background_executor().spawn(async move {
|
||||
let path = expanded.as_path();
|
||||
let exists = fs.is_file(path).await;
|
||||
|
||||
exists.then(|| ResolvedPath::AbsPath(expanded))
|
||||
})
|
||||
} else {
|
||||
self.resolve_path_in_worktrees(expanded, buffer, cx)
|
||||
}
|
||||
} else {
|
||||
let path = PathBuf::from(path);
|
||||
if path.is_absolute() || path.starts_with("~") {
|
||||
return Task::ready(None);
|
||||
}
|
||||
|
||||
self.resolve_path_in_worktrees(path, buffer, cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_path_in_worktrees(
|
||||
&self,
|
||||
path: PathBuf,
|
||||
buffer: &Model<Buffer>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Option<ResolvedPath>> {
|
||||
let mut candidates = vec![path.clone()];
|
||||
|
||||
if let Some(file) = buffer.read(cx).file() {
|
||||
if let Some(dir) = file.path().parent() {
|
||||
let joined = dir.to_path_buf().join(path);
|
||||
candidates.push(joined);
|
||||
}
|
||||
}
|
||||
|
||||
let worktrees = self.worktrees(cx).collect::<Vec<_>>();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
for worktree in worktrees {
|
||||
for candidate in candidates.iter() {
|
||||
let path = worktree
|
||||
.update(&mut cx, |worktree, _| {
|
||||
let root_entry_path = &worktree.root_entry().unwrap().path;
|
||||
|
||||
let resolved = resolve_path(&root_entry_path, candidate);
|
||||
|
||||
let stripped =
|
||||
resolved.strip_prefix(&root_entry_path).unwrap_or(&resolved);
|
||||
|
||||
worktree.entry_for_path(stripped).map(|entry| {
|
||||
ResolvedPath::ProjectPath(ProjectPath {
|
||||
worktree_id: worktree.id(),
|
||||
path: entry.path.clone(),
|
||||
})
|
||||
})
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
if path.is_some() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_directory(
|
||||
&self,
|
||||
query: String,
|
||||
|
@ -11230,6 +11313,14 @@ fn resolve_path(base: &Path, path: &Path) -> PathBuf {
|
|||
result
|
||||
}
|
||||
|
||||
/// ResolvedPath is a path that has been resolved to either a ProjectPath
|
||||
/// or an AbsPath and that *exists*.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ResolvedPath {
|
||||
ProjectPath(ProjectPath),
|
||||
AbsPath(PathBuf),
|
||||
}
|
||||
|
||||
impl Item for Buffer {
|
||||
fn try_open(
|
||||
project: &Model<Project>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue