Skip to content
Merged
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
23 changes: 23 additions & 0 deletions crates/markdown-editor/src/editor/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,29 @@ impl Editor {
self.revision
}

/// Returns the file path this editor is currently bound to, if any.
///
/// Relative image references and image-paste destinations resolve against
/// this path's parent directory.
pub fn file_path(&self) -> Option<&Path> {
self.file_path.as_deref()
}

/// Binds the editor to the file it edits, or rebinds it after a rename.
///
/// Relative image references resolve against the file's parent directory,
/// and image-paste destinations derive from the file stem, so changing the
/// binding refreshes every block's image runtime. The document itself,
/// selection, history, and dirty state are untouched.
pub fn set_file_path(&mut self, path: PathBuf, cx: &mut Context<Self>) {
if self.file_path.as_deref() == Some(path.as_path()) {
return;
}
self.file_path = Some(path);
self.rebuild_image_runtimes(cx);
cx.notify();
}

/// Replaces host-only rendering services without changing the document,
/// selection, history, focus, or dirty state.
pub fn set_host_services(&mut self, services: EditorHostServices, cx: &mut Context<Self>) {
Expand Down
10 changes: 9 additions & 1 deletion crates/notes/src/markdown_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ impl NotesView {
crate::markdown_renderer::markdown_editor_theme(self.resolved_editor_theme(cx)),
crate::markdown_renderer::block_render_provider(cx),
);
let editor_file_path = descriptor.absolute_path.clone();
let editor = cx.new(move |cx| {
let mut editor = MarkdownEditor::from_markdown_embedded_with_host(
cx,
snapshot.source,
host_services,
);
editor.set_file_path(editor_file_path, cx);
editor.set_view_mode(editor_view_mode(mode), cx);
editor
});
Expand Down Expand Up @@ -326,6 +328,7 @@ impl NotesView {
&mut self,
old: &std::path::Path,
new: &std::path::Path,
cx: &mut Context<Self>,
) -> anyhow::Result<()> {
let updates = self
.markdown_sessions
Expand All @@ -336,8 +339,13 @@ impl NotesView {
for (id, relative_path) in updates {
let absolute_path = self.storage()?.descriptor(&relative_path)?.absolute_path;
if let Some(session) = self.markdown_sessions.get_mut(&id) {
session.store.set_path(absolute_path)?;
session.store.set_path(absolute_path.clone())?;
session.relative_path = relative_path;
// 重命名后同步编辑器绑定的文件路径,保证相对路径图片引用
// 仍以新路径的父目录为基准解析。
session
.editor
.update(cx, |editor, cx| editor.set_file_path(absolute_path, cx));
}
}
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion crates/notes/src/notes_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ impl NotesView {
return;
};
self.remap_tree_paths(&old_path, &new_path);
if let Err(error) = self.remap_markdown_sessions(&old_path, &new_path) {
if let Err(error) = self.remap_markdown_sessions(&old_path, &new_path, cx) {
notify_operation_error(window, cx, error);
}
self.finish_file_operation(Ok(()), window, cx);
Expand Down
39 changes: 39 additions & 0 deletions crates/notes/src/notes_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,45 @@ mod external_markdown_tests {
assert_eq!(source, std::fs::read_to_string(path).unwrap());
}

#[gpui::test]
fn standalone_markdown_binds_editor_to_the_opened_file(cx: &mut TestAppContext) {
cx.update(|cx| {
gpui_component::init(cx);
crate::init(cx);
});
let temp = tempfile::tempdir().unwrap();
let dir = temp.path().join("notes-root");
std::fs::create_dir(&dir).unwrap();
let path = dir.join("with-image.md");
std::fs::write(&path, "# Notes\n\n![logo](./assets/logo.png)\n").unwrap();
let (window, view) = cx.update(|cx| {
let mut view = None;
let window = cx
.open_window(WindowOptions::default(), |window, cx| {
let entity =
cx.new(|cx| NotesView::new_for_markdown_file(path.clone(), window, cx));
view = Some(entity.clone());
cx.new(|cx| Root::new(entity, window, cx))
})
.unwrap();
(window, view.unwrap())
});
let cx = VisualTestContext::from_window(window.into(), cx);
cx.run_until_parked();

view.read_with(&cx, |view, cx| {
let session = view
.markdown_sessions
.values()
.next()
.expect("markdown session must exist");
let editor = session.editor.read(cx);
// 编辑器必须绑定被打开文件的绝对路径,否则相对路径图片
// (如 ![logo](./assets/logo.png))会退化为按进程 CWD 解析而无法渲染。
assert_eq!(Some(path.as_path()), editor.file_path());
});
}

#[gpui::test]
fn source_toggle_is_visible_in_preview_and_returns_to_preview(cx: &mut TestAppContext) {
cx.update(|cx| {
Expand Down
Loading