Skip to content

fix: prevent file recreation at deleted path on save - #679

Open
pengfeixx wants to merge 1 commit into
linuxdeepin:masterfrom
pengfeixx:agent/bug/25576c27a94f
Open

pengfeixx wants to merge 1 commit into
linuxdeepin:masterfrom
pengfeixx:agent/bug/25576c27a94f

Conversation

@pengfeixx

@pengfeixx pengfeixx commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

fix: prevent file recreation at deleted path on save

  1. Root cause: closeTab() called saveFile() without checking
    whether the source file still existed on disk; deleted files
    were silently recreated at their original path, conflicting
    with trash metadata and showing "locked" in file manager
  2. Fix: closeTab() now checks fileInfo.exists() before saving;
    if the file is gone, it redirects to saveAsFile() instead;
    saveAsFileToDisk(), saveBlankFileToDisk(), and
    saveAsOtherTabFile() only set the dialog directory when the
    original file still exists, avoiding pointing to a deleted
    directory
  3. Impact: closing a tab whose source file was moved to trash
    now prompts Save As instead of recreating the file; existing
    save paths for present files are unchanged

Log: Fixed issue where saving after deleting the source file recreated it at the original path, causing trash lock conflict

Influence:

  1. Open a txt file, edit it, delete the source file, close the
    editor and choose Save — verify Save As dialog appears
  2. Open a txt file, edit it, close the editor and choose Save
    with the file still present — verify normal save works
  3. Test saveAs, saveBlank, and saveAsOtherTab when source file
    is deleted — verify dialog does not default to deleted path

fix: 修复删除源文件后保存导致原路径重建文件的问题

  1. 根因:closeTab() 在保存前未检查源文件是否仍存在于磁盘,
    直接调用 saveFile() 在原路径重建文件,与回收站元数据冲突,
    导致文件管理器回收站显示"锁定"状态
  2. 方案:closeTab() 保存前检查 fileInfo.exists(),文件不存在时
    改用 saveAsFile() 引导用户另存;saveAsFileToDisk()、
    saveBlankFileToDisk()、saveAsOtherTabFile() 仅在原始文件
    仍存在时设置对话框目录,避免指向已删除的目录
  3. 影响:关闭源文件已移入回收站的标签页时,将弹出另存对话框
    而非在原路径重建文件;文件存在时的保存行为不变

Log: 修复删除源文件后保存时在原路径重建文件导致回收站锁定的问题

Influence:

  1. 打开 txt 文件编辑后删除源文件,关闭编辑器选择保存,
    验证弹出另存对话框
  2. 打开 txt 文件编辑后正常关闭保存,验证保存功能正常
  3. 测试源文件被删除时的另存为、空白保存、其他标签页保存,
    验证对话框不默认指向已删除目录

PMS: BUG-366573

Summary by Sourcery

Prevent deleted source files from being recreated during tab closure by requiring users to choose a new save location.

Bug Fixes:

  • Prevent saving a modified tab from recreating a source file that has been deleted or moved.
  • Redirect saves to a Save As flow when the original source file no longer exists.
  • Avoid defaulting save dialogs to directories belonging to deleted source files.

1. Root cause: closeTab() called saveFile() without checking
   whether the source file still existed on disk; deleted files
   were silently recreated at their original path, conflicting
   with trash metadata and showing "locked" in file manager
2. Fix: closeTab() now checks fileInfo.exists() before saving;
   if the file is gone, it redirects to saveAsFile() instead;
   saveAsFileToDisk(), saveBlankFileToDisk(), and
   saveAsOtherTabFile() only set the dialog directory when the
   original file still exists, avoiding pointing to a deleted
   directory
3. Impact: closing a tab whose source file was moved to trash
   now prompts Save As instead of recreating the file; existing
   save paths for present files are unchanged

Log: Fixed issue where saving after deleting the source file recreated it at the original path, causing trash lock conflict

Influence:
1. Open a txt file, edit it, delete the source file, close the
   editor and choose Save — verify Save As dialog appears
2. Open a txt file, edit it, close the editor and choose Save
   with the file still present — verify normal save works
3. Test saveAs, saveBlank, and saveAsOtherTab when source file
   is deleted — verify dialog does not default to deleted path

fix: 修复删除源文件后保存导致原路径重建文件的问题

1. 根因:closeTab() 在保存前未检查源文件是否仍存在于磁盘,
   直接调用 saveFile() 在原路径重建文件,与回收站元数据冲突,
   导致文件管理器回收站显示"锁定"状态
2. 方案:closeTab() 保存前检查 fileInfo.exists(),文件不存在时
   改用 saveAsFile() 引导用户另存;saveAsFileToDisk()、
   saveBlankFileToDisk()、saveAsOtherTabFile() 仅在原始文件
   仍存在时设置对话框目录,避免指向已删除的目录
3. 影响:关闭源文件已移入回收站的标签页时,将弹出另存对话框
   而非在原路径重建文件;文件存在时的保存行为不变

Log: 修复删除源文件后保存时在原路径重建文件导致回收站锁定的问题

Influence:
1. 打开 txt 文件编辑后删除源文件,关闭编辑器选择保存,
   验证弹出另存对话框
2. 打开 txt 文件编辑后正常关闭保存,验证保存功能正常
3. 测试源文件被删除时的另存为、空白保存、其他标签页保存,
   验证对话框不默认指向已删除目录

PMS: BUG-366573
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: pengfeixx

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Oct 21, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Updates save and tab-closing flows to detect deleted source files, prompt for an alternate destination instead of recreating them, and avoid defaulting save dialogs to deleted directories while preserving existing-file behavior.

Sequence diagram for saving a tab after its source file is deleted

sequenceDiagram
    participant User
    participant Window
    participant QFileInfo
    participant SaveDialog
    participant EditWrapper

    User->>Window: closeTab(filePath)
    Window->>QFileInfo: exists()
    alt source file is missing
        Window->>Window: saveAsFile()
        Window->>SaveDialog: saveAsFileToDisk()
        SaveDialog-->>Window: alternate destination
        Window->>EditWrapper: saveFile(destination)
        Window-->>User: close tab after Save As
    else source file still exists
        Window->>EditWrapper: saveFile()
        EditWrapper-->>Window: save result
        Window-->>User: close tab after normal save
    end
Loading

File-Level Changes

Change Details Files
Redirects saves to Save As when a tab’s original file has been deleted.
  • Checks file existence before normal save during tab closure.
  • Keeps the tab open when Save As is cancelled or fails.
  • Preserves normal save behavior for files that still exist.
src/widgets/window.cpp
Prevents save dialogs from defaulting to directories belonging to deleted source files.
  • Conditionally sets the dialog directory only when the source file exists.
  • Applies the safeguard to regular Save As, blank-file save, and other-tab save flows.
src/widgets/window.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/widgets/window.cpp" line_range="1566" />
<code_context>
         qDebug() << "Setting dialog file name to draft file name:" << match.captured(0) + ".txt";
     } else {
-        dialog.setDirectory(fileInfo.dir());
+        if (fileInfo.exists()) {
+            dialog.setDirectory(fileInfo.dir());
+        }
</code_context>
<issue_to_address>
**Save As recreates deleted file**

When the source is deleted, its parent directory still exists, and the configured save location resolves to that parent, `saveAsFileToDisk()` leaves the dialog in the source's parent directory and `dialog.selectFile(fileInfo.fileName())` preselects the original pathname; accepting the default saves back to that path and recreates the deleted file.

For a missing source, choose a fallback directory and avoid preselecting the deleted file's original pathname.

Also at `src/widgets/window.cpp:1567-1570`.
</issue_to_address>

### Comment 2
<location path="src/widgets/window.cpp" line_range="1108-1111" />
<code_context>
-                    if (wrapper->saveFile()) {
+                    if (!fileInfo.exists()) {
+                        qDebug() << "File no longer exists on disk, redirecting to Save As";
+                        if (!saveAsFile()) {
+                            qWarning() << "Save as file cancelled or failed, keep tab open";
+                            return false;
+                        }
+                    } else if (wrapper->saveFile()) {
                         qDebug() << "File saved, removing wrapper and closing tab";
</code_context>
<issue_to_address>
**Saved tab stays open**

When the source file is missing and Save As succeeds while closing the tab, `closeTab()` proceeds past successful `saveAsFile()` without calling `removeWrapper()` or `m_tabbar->closeCurrentTab()`, then returns true; callers such as `closeAllFiles()` count the tab as closed even though it remains open.

Run the normal successful-save cleanup after `saveAsFile()` succeeds.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/widgets/window.cpp
qDebug() << "Setting dialog file name to draft file name:" << match.captured(0) + ".txt";
} else {
dialog.setDirectory(fileInfo.dir());
if (fileInfo.exists()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Save As recreates deleted file

When the source is deleted, its parent directory still exists, and the configured save location resolves to that parent, saveAsFileToDisk() leaves the dialog in the source's parent directory and dialog.selectFile(fileInfo.fileName()) preselects the original pathname; accepting the default saves back to that path and recreates the deleted file.

For a missing source, choose a fallback directory and avoid preselecting the deleted file's original pathname.

Also at src/widgets/window.cpp:1567-1570.

Prompt for AI agents
In `src/widgets/window.cpp` at line 1566:

**Save As recreates deleted file**

When the source is deleted, its parent directory still exists, and the configured save location resolves to that parent, `saveAsFileToDisk()` leaves the dialog in the source's parent directory and `dialog.selectFile(fileInfo.fileName())` preselects the original pathname; accepting the default saves back to that path and recreates the deleted file.

For a missing source, choose a fallback directory and avoid preselecting the deleted file's original pathname.

Also at `src/widgets/window.cpp:1567-1570`.

Comment thread src/widgets/window.cpp
Comment on lines +1108 to +1111
if (!saveAsFile()) {
qWarning() << "Save as file cancelled or failed, keep tab open";
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Saved tab stays open

When the source file is missing and Save As succeeds while closing the tab, closeTab() proceeds past successful saveAsFile() without calling removeWrapper() or m_tabbar->closeCurrentTab(), then returns true; callers such as closeAllFiles() count the tab as closed even though it remains open.

Run the normal successful-save cleanup after saveAsFile() succeeds.

Prompt for AI agents
In `src/widgets/window.cpp` at lines 1108-1111:

**Saved tab stays open**

When the source file is missing and Save As succeeds while closing the tab, `closeTab()` proceeds past successful `saveAsFile()` without calling `removeWrapper()` or `m_tabbar->closeCurrentTab()`, then returns true; callers such as `closeAllFiles()` count the tab as closed even though it remains open.

Run the normal successful-save cleanup after `saveAsFile()` succeeds.

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 100 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 本次提交修复了文件保存时在已删除路径上重新创建文件的问题,修改逻辑清晰、代码质量良好、无性能影响、无安全漏洞。三处保存对话框函数均增加了 fileInfo.exists() 守卫检查,closeTab 函数正确地将已删除文件的保存操作重定向到 Save As 流程,防御性编程到位。

🔍 详细分析

1. 语法逻辑 ✅

评价: 语法正确,逻辑清晰 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: []


2. 代码质量 ✅

评价: 代码结构清晰,注释完整 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: ['三处保存对话框函数(saveAsFileToDisk、saveBlankFileToDisk、saveAsOtherTabFile)中 fileInfo.exists() 守卫模式相同,若未来需扩展可考虑提取为公共辅助函数,但目前重复程度极低,无需立即重构。']


3. 代码性能 ✅

评价: 性能良好,资源使用合理 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: []


4. 代码安全 🔒

评价: 存在0个安全漏洞 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: []


💡 改进建议代码示例

当前代码已正确修复了问题,无需额外修改。完整修复逻辑如下: // closeTab 函数中的修复(src/widgets/window.cpp 约第1103行) if (!fileInfo.exists()) {
    qDebug() << "File no longer exists on disk, redirecting to Save As";
    if (!saveAsFile()) {
        qWarning() << "Save as file cancelled or failed, keep tab open";
        return false;
    }
} else if (wrapper->saveFile()) {
    qDebug() << "File saved, removing wrapper and closing tab";
    removeWrapper(filePath, true);
    m_tabbar->closeCurrentTab(filePath);
} // 保存对话框函数中的修复(三处相同模式) if (fileInfo.exists()) {
    dialog.setDirectory(fileInfo.dir());
}

本报告由 AI 代码审查工具自动生成

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants