Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions crates/language/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,14 @@ pub trait File: Send + Sync + Any {
/// includes the name of the worktree's root folder).
fn full_path(&self, cx: &App) -> PathBuf;

/// Returns the absolute path to this file in its backing file system.
/// For remote files, this is an absolute path on the remote host.
fn file_system_abs_path(&self, cx: &App) -> PathBuf {
self.as_local()
.map(|file| file.abs_path(cx))
.unwrap_or_else(|| self.full_path(cx))
}

/// Returns the path style of this file.
fn path_style(&self, cx: &App) -> PathStyle;

Expand Down Expand Up @@ -6012,6 +6020,20 @@ impl File for TestFile {
PathBuf::from(self.root_name.clone()).join(self.path.as_std_path())
}

fn file_system_abs_path(&self, cx: &App) -> PathBuf {
if self.local_root.is_none() {
return self.full_path(cx);
}

let abs_path = PathBuf::from(self.local_root.as_ref().unwrap()).join(&self.root_name);
// Mirror worktree::Worktree::absolutize: an empty relative path refers to the root itself.
if self.path.as_std_path().as_os_str().is_empty() {
abs_path
} else {
abs_path.join(self.path.as_std_path())
}
}

fn as_local(&self) -> Option<&dyn LocalFile> {
if self.local_root.is_some() {
Some(self)
Expand Down
26 changes: 26 additions & 0 deletions crates/language/src/buffer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext)
"Dockerfile".into(),
vec!["Dockerfile".into(), "Dockerfile.*".into()].into(),
),
("SSH Config".into(), vec!["**/.ssh/config".into()].into()),
]);
})
});
Expand Down Expand Up @@ -335,6 +336,18 @@ async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext)
},
..Default::default()
},
LanguageConfig {
name: "SSH Config".into(),
..Default::default()
},
LanguageConfig {
name: "INI".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["config".to_string()],
..Default::default()
},
..Default::default()
},
] {
languages.add(Arc::new(Language::new(config, None)));
}
Expand Down Expand Up @@ -385,6 +398,11 @@ async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext)
.read(|cx| languages.language_for_file(&file("Dockerfile.dev"), None, cx))
.unwrap();
assert_eq!(language.name(), "Dockerfile");

let language = cx
.read(|cx| languages.language_for_file(&local_file("/root/.ssh", "config"), None, cx))
.unwrap();
assert_eq!(language.name(), "SSH Config");
}

fn file(path: &str) -> Arc<dyn File> {
Expand All @@ -395,6 +413,14 @@ fn file(path: &str) -> Arc<dyn File> {
})
}

fn local_file(local_root: &str, root_name: &str) -> Arc<dyn File> {
Arc::new(TestFile {
path: Arc::from(rel_path("")),
root_name: root_name.into(),
local_root: Some(PathBuf::from(local_root)),
})
}

#[gpui::test]
fn test_edit_events(cx: &mut gpui::App) {
let mut now = Instant::now();
Expand Down
7 changes: 2 additions & 5 deletions crates/language/src/language_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,12 +668,9 @@ impl LanguageRegistry {
cx: &App,
) -> Option<AvailableLanguage> {
let user_file_types = all_language_settings(Some(file), cx);
let path = file.file_system_abs_path(cx);

self.language_for_file_internal(
&file.full_path(cx),
content,
Some(&user_file_types.file_types),
)
self.language_for_file_internal(&path, content, Some(&user_file_types.file_types))
}

pub fn language_for_file_path(self: &Arc<Self>, path: &Path) -> Option<AvailableLanguage> {
Expand Down
4 changes: 4 additions & 0 deletions crates/worktree/src/worktree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3528,6 +3528,10 @@ impl language::File for File {
self.worktree.read(cx).full_path(&self.path)
}

fn file_system_abs_path(&self, cx: &App) -> PathBuf {
self.worktree.read(cx).absolutize(&self.path)
}

/// Returns the last component of this handle's absolute path. If this handle refers to the root
/// of its worktree, then this method will return the name of the worktree itself.
fn file_name<'a>(&'a self, cx: &'a App) -> &'a str {
Expand Down
45 changes: 45 additions & 0 deletions crates/worktree/tests/integration/worktree_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5006,6 +5006,51 @@ async fn test_remote_worktree_without_git_emits_root_repo_event_after_first_upda
);
}

#[gpui::test]
async fn test_remote_single_file_worktree_abs_path(cx: &mut TestAppContext) {
cx.update(|cx| {
let store = SettingsStore::test(cx);
cx.set_global(store);
});

let worktree = cx.update(|cx| {
Worktree::remote(
1,
clock::ReplicaId::new(1),
proto::WorktreeMetadata {
id: 1,
root_name: "config".to_string(),
visible: true,
abs_path: "/home/user/.ssh/config".to_string(),
root_repo_common_dir: None,
},
AnyProtoClient::new(NoopProtoClient::new()),
PathStyle::Posix,
cx,
)
});

let file = worktree::File {
worktree,
path: Arc::from(rel_path("")),
disk_state: language::DiskState::New,
entry_id: None,
is_local: false,
is_private: false,
};

cx.read(|cx| {
assert_eq!(
language::File::full_path(&file, cx),
PathBuf::from("config")
);
assert_eq!(
language::File::file_system_abs_path(&file, cx),
PathBuf::from("/home/user/.ssh/config")
);
});
}

#[gpui::test]
async fn test_remote_worktree_with_git_emits_root_repo_event_when_repo_info_arrives(
cx: &mut TestAppContext,
Expand Down
Loading