summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2025-12-15 10:05:21 +0800
committer魏曹先生 <1992414357@qq.com>2025-12-15 10:05:21 +0800
commit1e43def95472d9c906cff50534b38be2864690f4 (patch)
treee7296275d74efb4e1545d657bc936759291b48fe
parentb8ac6982f9b81bd686c2c8deb34669e13efd5ba7 (diff)
Update help documentation and move command functionality
- Redesign move command to modify upstream mappings with support for erase operations - Add erased items support to align command and status display - Update help text to reflect new move mapping semantics and add erased item instructions - Add auto-update timeout configuration via JV_OUTDATED_MINUTES environment variable - Improve status display with separate structural and content change modes - Add force flag to hold/throw commands to skip pre-checks - Update completion scripts to include erased items in align command
-rw-r--r--locales/help_docs/en.yml143
-rw-r--r--locales/help_docs/zh-CN.yml142
-rw-r--r--scripts/completions/bash/completion_jv.sh8
-rw-r--r--scripts/completions/powershell/completion_jv.ps18
-rw-r--r--scripts/jv_cli.ps18
-rw-r--r--scripts/jv_cli.sh8
-rw-r--r--src/bin/jv.rs471
-rw-r--r--src/bin/jvii.rs14
-rw-r--r--src/utils/env.rs25
9 files changed, 649 insertions, 178 deletions
diff --git a/locales/help_docs/en.yml b/locales/help_docs/en.yml
index c47f39d..b133865 100644
--- a/locales/help_docs/en.yml
+++ b/locales/help_docs/en.yml
@@ -241,8 +241,7 @@ jv:
export <FILE> <SHEET_NAME> - Export files to other sheets [REMOTE]
**FILE OPERATIONS**:
- move <FILE> <TO> - Safely rename files
- move auto - Automatically handle local file moves or renames
+ move <FILE> <TO> - Safely rename files [REMOTE]
track <FILE> - Track files to latest version [REMOTE]
hold <FILE> - Hold, sync and lock file [REMOTE]
throw <FILE> - Throw, sync and unlock file [REMOTE]
@@ -295,6 +294,10 @@ jv:
jv sheet align <ITEM> confirm - Confirm this file is lost
jv sheet align lost confirm - Confirm all lost items
+ For erased items:
+ jv sheet align <ITEM> confirm - Confirm this file is erased
+ jv sheet align erased confirm - Confirm all erased items
+
jv sheet align --work - Use editor mode to align files
Sheets are core concepts in JustEnoughVCS, each sheet represents an independent file collection.
@@ -324,9 +327,9 @@ jv:
Displays detailed information about current directory files, including:
- File name, size, version number
- Current file holder
- - Latest version commit information
+ - Latest version update description
- **Tip**: Use `jv here --desc` to view the last commit description for local files
+ **Tip**: Use `jv here --desc` to view the last update description for local files
status: |
**Display Current Sheet Status Information**
@@ -374,15 +377,19 @@ jv:
If you have made changes to the file but haven't tracked them, throwing will lose those changes.
move: |
- **Move Local Files**
+ **Move Mapping**
**Usage**:
- jv move <SOURCE_FILE> <TARGET_LOCATION> - Safely rename or move files
+ jv move <mapping> <target_mapping> - Modify upstream mapping
+ jv move <mapping> --erase - Erase upstream mapping
- **Example**:
- jv move old_name.txt new_name.txt
- jv move src/old_dir/file.rs src/new_dir/file.rs
+ **Examples**:
+ jv move draft/character.png done/character.png - Move mapping
+ jv move character.png player.png - Rename
+ jv move . ../publish/ - Batch move
+ jv move temp/ --erase - Erase mapping
- Safe move operations preserve file version history, while auto-move detects and handles all renames.
+ The move mapping operation modifies the upstream mapping and synchronizes the local structure (use `--only-remote` to cancel local modification)
+ After moving, you usually need `jv align moved remote` to synchronize the local structure to the upstream
export: |
**Export Files to Import Area of Other Sheets**
@@ -476,8 +483,8 @@ jv:
3. For files not held unless the version is frozen, download logic will always be executed to get the latest version
**CURRENT**:
- **DOWN**: %{old_files} to update, %{download_files} to download
- **UP** : %{new_files} to track, %{modified_files} to commit
+ **DOWN**: %{old_files} to sync, %{download_files} to download
+ **UP** : %{new_files} to track, %{modified_files} to update
fail:
std:
@@ -485,6 +492,24 @@ jv:
current_dir_name: Failed to get current directory name
set_current_dir: Failed to set current directory to %{dir}
+ move:
+ rename_failed: |
+ **Warning**: Failed to move local file `%{from}` to `%{to}`: %{error}
+
+ has_rename_failed: |
+ **Tip**: Because the file move was skipped, a deviation will occur.
+ After moving the file, be sure to use `jv align` to resolve the deviation.
+
+ no_target_dir: |
+ You did not specify a target directory to move to!
+ Please use `jv move <mapping> <target_address>` to move the mapping
+ or use `jv move <mapping> --erase` to erase the mapping
+
+ count_doesnt_match: |
+ You specified multiple mappings, but the target address is a single mapping.
+ Please use `jv move multiple_mappings directory/` to move multiple mappings
+ or use `jv move single_mapping mapping_name` to rename that mapping.
+
format_path: |
Failed to format directory %{path}: %{error}.
@@ -561,7 +586,9 @@ jv:
In the %{num} selected files, there are items that failed pre-check!
Add `--details` after the command to view specific details
- **Tip**: Add `--skip-failed` after the command to skip the current failed items and proceed with the operation
+ **Tip**:
+ Add `--skip-failed` after the command to skip the current failed items and proceed with the operation
+ Add `--force` to ignore checks and proceed (UNSAFE)
check_failed_details:
In the %{num} selected files, %{failed} files failed pre-check!
@@ -660,7 +687,7 @@ jv:
remote_path: REMOTE_FILE
no_changes: |
- Great, there are no struct changes in the local workspace!
+ Great, no structural deviations in the local workspace, no alignment needed!
docs:
list:
@@ -741,34 +768,43 @@ jv:
%{dir_count} dir(s), %{file_count} file(s). Total %{size}.
status:
- header: |
- Viewing sheet %{sheet_name}.
- Before tracking file changes, please confirm:
-
- content: |
- Structure changes:
- %{moved_items}%{lost_items}%{created_items}
- Content modifications:
+ struct_changes_display: |
+ Viewing sheet %{sheet_name} (%{h}h %{m}min %{s}secs ago).
+
+ Now in structural change mode:
+ %{moved_items}%{lost_items}%{erased_items}%{created_items}
+ **Tip**: Use `jv align` to align moved, lost, and erased changes,
+ Use `jv track` to track created changes
+
+ content_modifies_display: |
+ Viewing sheet %{sheet_name} (%{h}h %{m}min %{s}secs ago).
+
+ Now in content change mode:
%{modified_items}
- Struct info from update %{h}h %{m}m %{s}s seconds ago
- Use `jv update` to update from upstream
+ **Tip**: Use `jv track` to track your changes
+
+ no_changes: |
+ Your workspace is synchronized with upstream, you can proceed with structural and content editing based on this state!
created_item: |
- + Created: %{path}
+ + Created: %{path}
lost_item: |
- - Lost: %{path}
+ - Lost: %{path}
moved_item: |
- > Moved: %{from}
- To: %{to}
+ > Moved: Remote %{from}
+ Local %{to}
+
+ erased_item: |
+ & Erased: %{path}
modified_item: |
- * Modified: %{path}
+ * Modified: %{path}
invalid_modified_item: |
- x Modified: %{path} (%{reason})
+ x Modified: %{path} (%{reason})
invalid_modified_reasons:
not_holder: Modified without holding
@@ -914,36 +950,58 @@ jv:
not_held: |
You are not holding file %{path}!
- This means you modified the file without holding it, and the upstream vault blocked your commit attempt
+ This means you modified the file without holding it, and the upstream vault blocked your update attempt
(Sorry, JustEnoughVCS collaboration is based on serial editing - parallel editing and merging is not allowed)
- **Tip**: If you really need to commit this file, you can follow these steps:
- 1. First move the file outside the workspace and commit the correct version here
+ **Tip**: If you really need to update this file, you can follow these steps:
+ 1. First move the file outside the workspace and update the correct version here
2. Use `jv info <this_file> --holder` to query the member currently editing it
3. Try to contact them, describe your situation, and wait for them to release editing rights
4. After editing rights are released, use `jv track <this_file>` to get the latest version from that member
5. Manually merge your backed-up version into the latest version
- 6. Commit your modified latest version, then release editing rights
+ 6. Update your modified latest version, then release editing rights
Finally: You can use `jv here` to check file status in the directory before editing files to ensure you can edit
version_dismatch: |
The base version of the file you edited does not match the version in the upstream vault!
- Your version is %{version_current} while the upstream version is %{version_latest}, the upstream vault blocked your commit
+ Your version is %{version_current} while the upstream version is %{version_latest}, the upstream vault blocked your update
**Tip**:
- You can use `jv jump <file> %{version_current}` to jump the version to your local version and commit again
- If you don't want to force override the version, you can backup the file version, commit your local version to the latest, then manually merge the files and commit
+ You can use `jv jump <file> %{version_current}` to jump the version to your local version and update again
+ If you don't want to force override the version, you can backup the file version, update your local version to the latest, then manually merge the files and update
Finally: You can use `jv here` to check file status in the directory before editing files to ensure you can edit
update_but_no_description: |
- You are committing files to the latest version, but we don't know your modification content and new version number
- You can use `jv track <file> --desc <description> -v <version>` to commit files
- or use `jv track . --work` to enter the editor environment for committing
+ There are update items in the files you specified, but no information provided
+ You can use `jv track <file> --desc <description> -v <version>` to update files
+ or use `jv track . --work` to enter the editor environment for updating
version_already_exist: |
- The version %{version} of file %{path} you are committing already exists in the upstream vault, please use a different version number!
+ The version %{version} of file %{path} you are updating already exists in the upstream vault, please use a different version number!
+
+ move:
+ success: |
+ Successfully modified mapping!
+ Upstream information has changed, please use `jv update` to sync to latest information
+
+ mapping_not_found: |
+ Mapping `%{path}` does not exist!
+ Please check if the path you entered is correct, or use `jv update` to update workspace status
+
+ invalid_move:
+ no_target: |
+ You did not specify a target address for the mapping `%{path}` to move to!
+ Please use `jv move <mapping> <target_mapping>` to move the mapping
+ or use `jv move <mapping> --erase` to erase the mapping
+
+ duplicate_mapping: |
+ Move operation failed because target path `%{path}` already has a mapping!
+ Please change to another path, or erase the existing mapping first
+
+ unknown: |
+ Unknown move operation result!
jvii:
hints: |
@@ -977,9 +1035,8 @@ editor:
# - Fill in the version after the arrow
%{modified_lines}
-
----------------------------------------------------------------
- # Update description
+ # Fill description here, tell others about the changes you made
%{description}
modified_line:
diff --git a/locales/help_docs/zh-CN.yml b/locales/help_docs/zh-CN.yml
index bfea30b..6a36a5d 100644
--- a/locales/help_docs/zh-CN.yml
+++ b/locales/help_docs/zh-CN.yml
@@ -190,7 +190,7 @@ jv:
help: |
**JustEnoughVCS 本地工作区命令**
- 该程序将连接至上游库,用以同步、提交本地工作区文件的变化,以供协同创作
+ 该程序将连接至上游库,用以同步、更新本地工作区文件的变化,以供协同创作
**常用别名**:
jv u 下载最新信息,jv t 追踪文件,jv a 对齐文件结构到表,jv in/out 导入或导出文件
@@ -232,7 +232,7 @@ jv:
export <文件> <表名称> - 导出文件到其他表 [远程]
**文件操作**:
- move <文件> <到> - 安全地重命名文件
+ move <文件> <到> - 安全地重命名文件 [远程]
track <文件> - 追踪文件内容到最新版本 [远程]
hold <文件> - 拿取文件,同步版本并获得编辑权 [远程]
throw <文件> - 丢弃文件,同步版本并放弃编辑权 [远程]
@@ -286,9 +286,13 @@ jv:
jv sheet align <项> confirm - 确认该文件已丢失
jv sheet align lost confirm - 确认所有丢失项
+ 对于擦除项:
+ jv sheet align <项> confirm - 确认该文件已擦除
+ jv sheet align erased confirm - 确认所有擦除项
+
jv sheet align --work - 使用编辑器模式对齐文件
- 表是 JustEnoughVCS 中的核心概念,每个表代表一块独立的文件集合
+ 表是 JustEnoughVCS 中的核心概念,每张表代表一块独立的文件结构
您可以在不同的表之间切换工作,或者将文件从一张表导出到另一张表
@@ -318,9 +322,9 @@ jv:
显示当前目录文件的详细信息,包括:
- 文件名称、大小、版本号
- 文件当前的持有人
- - 文件最新版本的提交信息
+ - 文件最新版本的更新信息
- **提示**:使用 `jv here --desc` 查看本地文件最后一次的提交信息
+ **提示**:使用 `jv here --desc` 查看本地文件最后一次的更新信息
status: |
**显示当前表的状态信息**
@@ -369,16 +373,19 @@ jv:
move: |
- **移动本地文件**
+ **移动映射**
**用法**:
- jv move <源文件> <目标位置> - 安全地重命名或移动文件
+ jv move <映射> <目标映射> - 修改上游映射
+ jv move <映射> --erase - 擦除上游映射
**例如**:
- jv move old_name.txt new_name.txt
- jv move src/old_dir/file.rs src/new_dir/file.rs
-
- 安全移动操作会保持文件的版本历史,而自动移动会检测并处理所有重命名
+ jv move draft/character.png done/character.png - 移动映射
+ jv move character.png player.png - 重命名
+ jv move . ../publish/ - 批量移动
+ jv move temp/ --erase - 擦除映射
+ 移动映射操作会修改上游的映射,并同步修改本地结构(使用 `--only-remote` 取消同步修改)
+ 在移动完成后,通常需要 `jv align moved remote` 将本地结构同步至上游
export: |
**将文件导出至其他表的待导入区**
@@ -476,8 +483,8 @@ jv:
3. 未持有文件,除非冻结版本,否则永远执行下载和更新最新版本
**当前**:
- **下行**:%{old_files} 个待更新,%{download_files} 个待下载
- **上行**:%{new_files} 个待追踪,%{modified_files} 个待提交
+ **下行**:%{old_files} 个待同步,%{download_files} 个待下载
+ **上行**:%{new_files} 个待追踪,%{modified_files} 个待更新
fail:
std:
@@ -485,6 +492,24 @@ jv:
current_dir_name: 无法获得当前目录的名称
set_current_dir: 无法设置到目录 %{dir}
+ move:
+ rename_failed: |
+ **警告**:移动本地文件 `%{from}` 至 `%{to}` 失败:%{error}
+
+ has_rename_failed: |
+ **提示**:因为已跳过文件的移动,所以会产生偏差,
+ 在可移动文件后,请务必使用 `jv align` 解决偏差
+
+ no_target_dir: |
+ 您未指定需要移动的目录!
+ 请使用 `jv move <映射> <目标映射名>` 的方式移动映射
+ 或使用 `jv move <映射> --erase` 将映射擦除
+
+ count_doesnt_match: |
+ 您指定了多个映射,但目标地址为单个映射
+ 请使用 `jv move 多个映射 目录/` 来移动多个映射
+ 或使用 `jv move 单个映射 映射名称` 来重命名该映射
+
format_path: |
格式化目录 %{path} 失败:%{error}.
@@ -561,7 +586,9 @@ jv:
在您选中的 %{num} 个文件中,存在预检查失败的项!
在命令后添加 `--details` 查看具体事项
- **提示**:命令后添加 `--skip-failed` 可跳过当前检查失败的项进行操作
+ **提示**:
+ 添加 `--skip-failed` 可跳过当前检查失败的项进行操作
+ 添加 `--force` 可无视检查进行操作 (不安全的操作)
check_failed_details: |
在您选中的 %{num} 个文件中,有 %{failed} 个文件预先检查未通过!
@@ -661,7 +688,7 @@ jv:
remote_path: 远程文件
no_changes: |
- 很好,本地工作区并没有结构变更!
+ 很好,本地工作区未产生结构偏差,无需对齐!
docs:
list:
@@ -742,17 +769,24 @@ jv:
%{dir_count} 目录、%{file_count} 文件,共计 %{size}
status:
- header: |
- 您正在查看表 %{sheet_name} 的状态,在追踪文件变更之前,请确认:
+ struct_changes_display: |
+ 表 %{sheet_name} 的状态基于 %{h} 小时 %{m} 分钟 %{s} 秒前
+
+ 您的工作区处于结构变更状态:
+ %{moved_items}%{lost_items}%{erased_items}%{created_items}
+ **提示**:使用 `jv align` 对齐移动、丢失和擦除变更,
+ 使用 `jv track` 追踪创建变更
- content: |
- 结构变更:
- %{moved_items}%{lost_items}%{created_items}
- 内容修改:
+ content_modifies_display: |
+ 表 %{sheet_name} 的状态基于 %{h} 小时 %{m} 分钟 %{s} 秒前
+
+ 您的工作区处于内容变更状态:
%{modified_items}
- 结构信息来自 %{h} 小时 %{m} 分钟 %{s} 秒前的更新
- 使用 `jv update` 从上游更新
+ **提示**:使用 `jv track` 追踪您的变更
+
+ no_changes: |
+ 您的工作区与上游保持同步,可基于该状态进行结构、内容的编辑!
created_item: |
+ 创建: %{path}
@@ -761,8 +795,11 @@ jv:
- 丢失: %{path}
moved_item: |
- > 移动: %{from}
- 至: %{to}
+ > 移动:远程 %{from}
+ 本地 %{to}
+
+ erased_item: |
+ & 擦除: %{path}
modified_item: |
* 修改: %{path}
@@ -890,8 +927,8 @@ jv:
这意味着该表在上游库中已被删除,或该表不属于您
create_file_on_exist_path: |
- 提交并创建文件失败!
- 您要提交的文件路径 `%{path}` 在远程表中已存在,请更换至其他路径提交
+ 创建文件失败!
+ 您要创建的文件路径 `%{path}` 在远程表中已存在,请更换至其他路径创建
update_failed:
verify:
@@ -913,36 +950,58 @@ jv:
not_held: |
您并未持有文件 %{path}!
- 这说明,您在未持有文件的时修改了它,并在尝试提交时被上游库阻拦
+ 这说明,您在未持有文件的时修改了它,并在尝试更新时被上游库阻拦
(非常抱歉,JustEnoughVCS 协作基于串行编辑,并行编辑后合并是不允许的)
- **提示**:如果您确实需要提交该文件,可以参考以下步骤:
- 1. 首先将文件移动到工作区以外的地方,并重新在此处提交正确的版本
+ **提示**:如果您确实需要更新该文件,可以参考以下步骤:
+ 1. 首先将文件移动到工作区以外的地方,并重新在此处更新正确的版本
2. 使用 `jv info <该文件> --holder` 查询正在编辑的成员
3. 尝试联系他,并描述您的情况,并等待该成员释放编辑权
4. 释放编辑权后,使用 `jv track <该文件>` 拿到该成员的最新版本
5. 手动地将您备份的版本合并至最新版本中
- 6. 将您修改的最新版提交,然后释放编辑权
+ 6. 将您修改的最新版更新,然后释放编辑权
最后:您可以在编辑文件前,使用 `jv here` 查看所在目录的文件状态,以确保自己可以编辑
version_dismatch: |
您编辑的文件基准版本和上游库中的版本不匹配!
- 您的版本是 %{version_current} 而上游版本是 %{version_latest},上游库禁止了您的提交
+ 您的版本是 %{version_current} 而上游版本是 %{version_latest},上游库禁止了您的更新
**提示**:
- 您可以使用 `jv jump <文件> %{version_current}` 将版本跳转至您的本地版本,并再次提交
- 若您不期望强制覆盖版本,可以选择将文件版本备份,并提交本地版本至最新后,再手动地合并文件并提交
+ 您可以使用 `jv jump <文件> %{version_current}` 将版本跳转至您的本地版本,并再次更新
+ 若您不期望强制覆盖版本,可以选择将文件版本备份,并更新本地版本至最新后,再手动地合并文件并更新
最后:您可以在编辑文件前,使用 `jv here` 查看所在目录的文件状态,以确保自己可以编辑
update_but_no_description: |
- 您正在提交文件至最新版本,但是我们并不知道您的修改内容和新的版本号
- 使用 `jv track <文件> --desc <描述> -v <版本>` 提交文件
- 或使用 `jv track . --work` 进入编辑器环境提交
+ 您指定的文件中存在更新项,但是您并未指定更新信息
+ 使用 `jv track <文件> --desc <描述> -v <版本>` 更新文件
+ 或使用 `jv track . --work` 进入编辑器环境更新
version_already_exist: |
- 您正在提交的文件 %{path} 的版本 %{version} 在上游库中已存在,请使用其他版本号!
+ 您正在更新的文件 %{path} 的版本 %{version} 在上游库中已存在,请使用其他版本号!
+
+ move:
+ success: |
+ 成功修改映射!
+ 上游信息已变更,请使用 `jv update` 同步至最新信息
+
+ mapping_not_found: |
+ 映射 `%{path}` 不存在!
+ 请检查您输入的路径是否正确,或使用 `jv update` 更新工作区状态
+
+ invalid_move:
+ no_target: |
+ 您未指定需要移动的映射 `%{path}` 的目标地址!
+ 请使用 `jv move <映射> <目标映射名>` 的方式移动映射
+ 或使用 `jv move <映射> --erase` 将映射擦除
+
+ duplicate_mapping: |
+ 移动操作失败,因为目标路径 `%{path}` 已存在映射!
+ 请更换至其他路径,或先擦除已存在的映射
+
+ unknown: |
+ 未知的移动操作结果!
jvii:
hints: |
@@ -970,13 +1029,12 @@ jvii:
editor:
update_editor: |
- # 您正在使用编辑器模式追踪和提交文件
- # 以下文件将被提交:(行首添加 `#` 视为放弃提交,尾部箭头后请填写版本)
+ # 您正在使用编辑器模式追踪和更新文件
+ # 以下文件将被更新:(行首添加 `#` 视为放弃更新,尾部箭头后请填写版本)
%{modified_lines}
-
----------------------------------------------------------------------
- # 请在后续填写提交描述,以告诉其他成员您做了什么
+ # 此处填写更新描述,告诉其他成员您做了什么
%{description}
modified_line:
diff --git a/scripts/completions/bash/completion_jv.sh b/scripts/completions/bash/completion_jv.sh
index ff600ed..be5afc6 100644
--- a/scripts/completions/bash/completion_jv.sh
+++ b/scripts/completions/bash/completion_jv.sh
@@ -99,7 +99,7 @@ _jv_completion() {
;;
"align")
if [[ $cword -eq 3 ]]; then
- local align_items="lost moved"
+ local align_items="lost moved erased"
local unsolved_items
unsolved_items=$($cmd sheet align --unsolved --raw 2>/dev/null)
COMPREPLY=($(compgen -W "$align_items $unsolved_items" -- "$cur"))
@@ -115,6 +115,8 @@ _jv_completion() {
align_operations="confirm $created_items"
elif [[ "$item" == "moved" || "$item" == moved:* ]]; then
align_operations="local remote"
+ elif [[ "$item" == "erased" || "$item" == erased:* ]]; then
+ align_operations="confirm"
else
align_operations="local remote confirm $created_items"
fi
@@ -129,7 +131,7 @@ _jv_completion() {
# Completion align
if [[ "$subcmd" == "align" ]]; then
if [[ $cword -eq 2 ]]; then
- local align_items="lost moved"
+ local align_items="lost moved erased"
local unsolved_items
unsolved_items=$($cmd sheet align --unsolved --raw 2>/dev/null)
COMPREPLY=($(compgen -W "$align_items $unsolved_items" -- "$cur"))
@@ -145,6 +147,8 @@ _jv_completion() {
align_operations="confirm $created_items"
elif [[ "$item" == "moved" || "$item" == moved:* ]]; then
align_operations="local remote"
+ elif [[ "$item" == "erased" || "$item" == erased:* ]]; then
+ align_operations="confirm"
else
align_operations="local remote confirm $created_items"
fi
diff --git a/scripts/completions/powershell/completion_jv.ps1 b/scripts/completions/powershell/completion_jv.ps1
index 48ab3ec..b756fbd 100644
--- a/scripts/completions/powershell/completion_jv.ps1
+++ b/scripts/completions/powershell/completion_jv.ps1
@@ -89,7 +89,7 @@ Register-ArgumentCompleter -Native -CommandName jv -ScriptBlock {
}
"align" {
if ($currentIndex -eq 3) {
- $alignItems = @("lost", "moved")
+ $alignItems = @("lost", "moved", "erased")
$unsolvedItems = & $cmd sheet align --unsolved --raw 2>$null
$completions = $alignItems + $unsolvedItems
return $completions | Where-Object { $_ -like "$wordToComplete*" }
@@ -104,6 +104,8 @@ Register-ArgumentCompleter -Native -CommandName jv -ScriptBlock {
$alignOperations = @("confirm") + $createdItems
} elseif ($item -eq "moved" -or $item -like "moved:*") {
$alignOperations = @("local", "remote")
+ } elseif ($item -eq "erased" -or $item -like "erased:*") {
+ $alignOperations = @("confirm")
} else {
$alignOperations = @("local", "remote", "confirm") + $createdItems
}
@@ -118,7 +120,7 @@ Register-ArgumentCompleter -Native -CommandName jv -ScriptBlock {
# Completion for align command
if ($subcmd -eq "align") {
if ($currentIndex -eq 2) {
- $alignItems = @("lost", "moved")
+ $alignItems = @("lost", "moved", "erased")
$unsolvedItems = & $cmd sheet align --unsolved --raw 2>$null
$completions = $alignItems + $unsolvedItems
return $completions | Where-Object { $_ -like "$wordToComplete*" }
@@ -133,6 +135,8 @@ Register-ArgumentCompleter -Native -CommandName jv -ScriptBlock {
$alignOperations = @("confirm") + $createdItems
} elseif ($item -eq "moved" -or $item -like "moved:*") {
$alignOperations = @("local", "remote")
+ } elseif ($item -eq "erased" -or $item -like "erased:*") {
+ $alignOperations = @("confirm")
} else {
$alignOperations = @("local", "remote", "confirm") + $createdItems
}
diff --git a/scripts/jv_cli.ps1 b/scripts/jv_cli.ps1
index 8e16646..22836c7 100644
--- a/scripts/jv_cli.ps1
+++ b/scripts/jv_cli.ps1
@@ -13,6 +13,14 @@ $SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Definition
# Next `jv` command will auto-run `jv update`
$env:JV_AUTO_UPDATE = "yes"
+# Use JV_OUTDATED_MINUTES to set the expiration time (in minutes), requires JV_AUTO_UPDATE to be enabled
+# Next time the `jv` command is used, if the content is outdated, `jv update` will be automatically executed
+# When the set number is < 0, timeout-based update is disabled
+# When the set number = 0, update runs every time (not recommended)
+# When the set number > 0, update according to the specified time
+# If not set, the default is -1
+# $env:JV_OUTDATED_MINUTES = "5"
+
# Use JV_TEXT_EDITOR to set text editor for `jv track --work` `jv align --work`
# DEFAULT: $EDITOR environment variable, falling back to "jvii" if not set
# $env:JV_TEXT_EDITOR = "nano"
diff --git a/scripts/jv_cli.sh b/scripts/jv_cli.sh
index e05df3d..d732d95 100644
--- a/scripts/jv_cli.sh
+++ b/scripts/jv_cli.sh
@@ -14,6 +14,14 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Next `jv` command will auto-run `jv update`
export JV_AUTO_UPDATE=yes
+# Use JV_OUTDATED_MINUTES to set the expiration time (in minutes), requires JV_AUTO_UPDATE to be enabled
+# Next time the `jv` command is used, if the content is outdated, `jv update` will be automatically executed
+# When the set number is < 0, timeout-based update is disabled
+# When the set number = 0, update runs every time (not recommended)
+# When the set number > 0, update according to the specified time
+# If not set, the default is -1
+# export JV_OUTDATED_MINUTES=5
+
# Use JV_TEXT_EDITOR to set text editor for `jv track --work` `jv align --work`
# DEFAULT: $EDITOR environment variable, falling back to "jvii" if not set
# export JV_TEXT_EDITOR=nano
diff --git a/src/bin/jv.rs b/src/bin/jv.rs
index 2aa7b92..f2f5269 100644
--- a/src/bin/jv.rs
+++ b/src/bin/jv.rs
@@ -4,7 +4,7 @@ use just_enough_vcs::{
utils::{
cfg_file::config::ConfigFile,
data_struct::dada_sort::quick_sort_with_cmp,
- string_proc::{self, snake_case},
+ string_proc::{self, format_path::format_path, snake_case},
tcp_connection::instance::ConnectionInstance,
},
vcs::{
@@ -14,8 +14,9 @@ use just_enough_vcs::{
proc_update_to_latest_info_action,
},
sheet_actions::{
- DropSheetActionResult, MakeSheetActionResult, proc_drop_sheet_action,
- proc_make_sheet_action,
+ DropSheetActionResult, EditMappingActionArguments, EditMappingActionResult,
+ EditMappingOperations, InvalidMoveReason, MakeSheetActionResult, OperationArgument,
+ proc_drop_sheet_action, proc_edit_mapping_action, proc_make_sheet_action,
},
track_action::{
CreateTaskResult, NextVersion, SyncTaskResult, TrackFileActionArguments,
@@ -34,9 +35,14 @@ use just_enough_vcs::{
current::{correct_current_dir, current_cfg_dir, current_local_path},
data::{
local::{
- LocalWorkspace, align::AlignTasks, cached_sheet::CachedSheet, config::LocalConfig,
- file_status::AnalyzeResult, latest_file_data::LatestFileData,
- latest_info::LatestInfo, vault_modified::check_vault_modified,
+ LocalWorkspace,
+ align::AlignTasks,
+ cached_sheet::CachedSheet,
+ config::LocalConfig,
+ file_status::{AnalyzeResult, FromRelativePathBuf},
+ latest_file_data::LatestFileData,
+ latest_info::LatestInfo,
+ vault_modified::check_vault_modified,
},
member::{Member, MemberId},
sheet::{SheetData, SheetMappingMetadata},
@@ -68,7 +74,7 @@ use just_enough_vcs_cli::{
},
utils::{
display::{SimpleTable, display_width, md, size_str},
- env::{current_locales, enable_auto_update},
+ env::{auto_update_outdate, current_locales, enable_auto_update},
fs::move_across_partitions,
globber::{GlobItem, Globber},
input::{confirm_hint, confirm_hint_or, input_with_editor, show_in_pager},
@@ -146,7 +152,7 @@ enum JustEnoughVcsWorkspaceCommand {
/// Move or rename files safely
#[command(alias = "mv")]
- Move(MoveFileArgs),
+ Move(MoveMappingArgs),
/// Export files to other worksheet
#[command(alias = "out")]
@@ -558,6 +564,10 @@ struct HoldFileArgs {
/// Skip failed items
#[arg(short = 'S', long)]
skip_failed: bool,
+
+ /// Skip check
+ #[arg(short = 'F', long)]
+ force: bool,
}
#[derive(Parser, Debug)]
@@ -576,13 +586,31 @@ struct ThrowFileArgs {
/// Skip failed items
#[arg(short = 'S', long)]
skip_failed: bool,
+
+ /// Skip check
+ #[arg(short = 'F', long)]
+ force: bool,
}
#[derive(Parser, Debug)]
-struct MoveFileArgs {
+struct MoveMappingArgs {
/// Show help information
#[arg(short, long)]
help: bool,
+
+ /// Move mapping pattern
+ move_mapping_pattern: Option<String>,
+
+ /// To mapping pattern
+ to_mapping_pattern: Option<String>,
+
+ /// Erase
+ #[arg(short = 'e', long)]
+ erase: bool,
+
+ /// Only modify upstream mapping
+ #[arg(short = 'r', long)]
+ only_remote: bool,
}
#[derive(Parser, Debug)]
@@ -673,8 +701,20 @@ async fn main() {
#[cfg(windows)]
colored::control::set_virtual_terminal(true).unwrap();
+ // Outdate update
+ let required_outdated_minutes = auto_update_outdate();
+ let outdate_update_enabled = required_outdated_minutes >= 0;
+
// Auto update
- if enable_auto_update() && check_vault_modified().await {
+ let enable_auto_update = enable_auto_update();
+
+ // The following conditions will trigger automatic update:
+ // 1. Auto-update is enabled
+ // 2. Vault has been modified OR (timeout update is enabled AND timeout is set to 0)
+ if enable_auto_update
+ && (check_vault_modified().await
+ || outdate_update_enabled && required_outdated_minutes == 0)
+ {
// Record current directory
let path = match current_dir() {
Ok(path) => path,
@@ -702,6 +742,36 @@ async fn main() {
);
return;
}
+ } else
+ // If automatic update and timeout update are enabled,
+ // but required time > 0 (not in disabled or always-update state)
+ if enable_auto_update && outdate_update_enabled && required_outdated_minutes > 0 {
+ // Read the last update time and calculate the duration
+ if let Some(local_cfg) = LocalConfig::read().await.ok() {
+ if let Some(local_dir) = current_local_path() {
+ if let Ok(latest_info) = LatestInfo::read_from(LatestInfo::latest_info_path(
+ &local_dir,
+ &local_cfg.current_account(),
+ ))
+ .await
+ {
+ if let Some(update_instant) = latest_info.update_instant {
+ let now = Instant::now();
+ let duration_secs = now.duration_since(update_instant).as_secs();
+
+ if duration_secs > required_outdated_minutes as u64 * 60 {
+ // Update
+ // This will change the current current_dir
+ jv_update(UpdateArgs {
+ help: false,
+ silent: true,
+ })
+ .await
+ }
+ }
+ }
+ };
+ };
}
let Ok(parser) = JustEnoughVcsWorkspace::try_parse() else {
@@ -773,13 +843,14 @@ async fn main() {
if let Some(instant) = latest_info.update_instant {
let now = Instant::now();
let duration = now.duration_since(instant);
- if duration.as_secs() > 60 * 15 {
- // More than 15 minutes
+
+ if duration.as_secs() > 60 * required_outdated_minutes.clamp(5, i64::MAX) as u64 {
+ // Automatically prompt if exceeding the set timeout (at least 5 minutes)
let hours = duration.as_secs() / 3600;
let minutes = (duration.as_secs() % 3600) / 60;
- println!();
+
println!(
- "{}",
+ "\n{}",
t!("jv.tip.outdated", hour = hours, minutes = minutes)
.trim()
.yellow()
@@ -1053,6 +1124,10 @@ async fn main() {
.await
}
JustEnoughVcsWorkspaceCommand::Align(sheet_align_args) => {
+ if sheet_align_args.help {
+ println!("{}", md(t!("jv.align")));
+ return;
+ }
jv_sheet_align(sheet_align_args).await
}
JustEnoughVcsWorkspaceCommand::As(args) => {
@@ -1676,11 +1751,6 @@ async fn jv_status(_args: StatusArgs) {
return;
};
- println!(
- "{}",
- t!("jv.success.status.header", sheet_name = sheet_name)
- );
-
// Format created items
let mut created_items: Vec<String> = analyzed
.created
@@ -1696,21 +1766,40 @@ async fn jv_status(_args: StatusArgs) {
})
.collect();
- // Format lost items
- let mut lost_items: Vec<String> = analyzed
- .lost
+ // Format erased items
+ let mut erased_items: Vec<String> = analyzed
+ .erased
.iter()
.map(|path| {
t!(
- "jv.success.status.lost_item",
+ "jv.success.status.erased_item",
path = path.display().to_string()
)
.trim()
- .red()
+ .magenta()
.to_string()
})
.collect();
+ // Format lost items
+ let mut lost_items: Vec<String> = analyzed
+ .lost
+ .iter()
+ .filter_map(|path| {
+ let path_str = path.display().to_string();
+ if analyzed.erased.contains(path) {
+ return None;
+ } else {
+ return Some(
+ t!("jv.success.status.lost_item", path = path_str)
+ .trim()
+ .red()
+ .to_string(),
+ );
+ }
+ })
+ .collect();
+
// Format moved items
let mut moved_items: Vec<String> = analyzed
.moved
@@ -1792,13 +1881,16 @@ async fn jv_status(_args: StatusArgs) {
})
.collect();
- let has_struct_changes =
- !created_items.is_empty() || !lost_items.is_empty() || !moved_items.is_empty();
+ let has_struct_changes = !created_items.is_empty()
+ || !lost_items.is_empty()
+ || !erased_items.is_empty()
+ || !moved_items.is_empty();
let has_file_modifications = !modified_items.is_empty();
if has_struct_changes {
sort_paths(&mut created_items);
sort_paths(&mut lost_items);
+ sort_paths(&mut erased_items);
sort_paths(&mut moved_items);
}
if has_file_modifications {
@@ -1812,57 +1904,58 @@ async fn jv_status(_args: StatusArgs) {
let m = (duration.as_secs() % 3600) / 60;
let s = duration.as_secs() % 60;
- println!(
- "{}",
- md(t!(
- "jv.success.status.content",
- moved_items = if has_struct_changes {
- if moved_items.is_empty() {
+ if has_struct_changes {
+ println!(
+ "{}",
+ md(t!(
+ "jv.success.status.struct_changes_display",
+ sheet_name = sheet_name,
+ moved_items = if moved_items.is_empty() {
"".to_string()
} else {
moved_items.join("\n") + "\n"
- }
- } else {
- t!("jv.success.status.no_structure_changes")
- .trim()
- .to_string()
- + "\n"
- },
- lost_items = if has_struct_changes {
- if lost_items.is_empty() {
+ },
+ lost_items = if lost_items.is_empty() {
"".to_string()
} else {
lost_items.join("\n") + "\n"
- }
- } else {
- "".to_string()
- },
- created_items = if has_struct_changes {
- if created_items.is_empty() {
+ },
+ erased_items = if erased_items.is_empty() {
+ "".to_string()
+ } else {
+ erased_items.join("\n") + "\n"
+ },
+ created_items = if created_items.is_empty() {
"".to_string()
} else {
created_items.join("\n") + "\n"
- }
- } else {
- "".to_string()
- },
- modified_items = if has_file_modifications {
- if modified_items.is_empty() {
+ },
+ h = h,
+ m = m,
+ s = s
+ ))
+ .trim()
+ );
+ } else if has_file_modifications {
+ println!(
+ "{}",
+ md(t!(
+ "jv.success.status.content_modifies_display",
+ sheet_name = sheet_name,
+ modified_items = if modified_items.is_empty() {
"".to_string()
} else {
modified_items.join("\n")
- }
- } else {
- t!("jv.success.status.no_file_modifications")
- .trim()
- .to_string()
- },
- h = h,
- m = m,
- s = s
- ))
- .trim()
- );
+ },
+ h = h,
+ m = m,
+ s = s
+ ))
+ .trim()
+ );
+ } else {
+ println!("{}", md(t!("jv.success.status.no_changes")));
+ }
}
async fn jv_sheet_list(args: SheetListArgs) {
@@ -2246,6 +2339,7 @@ async fn jv_sheet_align(args: SheetAlignArgs) {
align_tasks.created.iter().for_each(|i| println!("{}", i.0));
align_tasks.moved.iter().for_each(|i| println!("{}", i.0));
align_tasks.lost.iter().for_each(|i| println!("{}", i.0));
+ align_tasks.erased.iter().for_each(|i| println!("{}", i.0));
return;
}
if args.list_created {
@@ -2255,6 +2349,7 @@ async fn jv_sheet_align(args: SheetAlignArgs) {
if args.list_unsolved {
align_tasks.moved.iter().for_each(|i| println!("{}", i.0));
align_tasks.lost.iter().for_each(|i| println!("{}", i.0));
+ align_tasks.erased.iter().for_each(|i| println!("{}", i.0));
return;
}
return;
@@ -2270,7 +2365,7 @@ async fn jv_sheet_align(args: SheetAlignArgs) {
},
]);
- let mut empty_count = 0;
+ let mut need_align = 0;
if !align_tasks.created.is_empty() {
align_tasks.created.iter().for_each(|(n, p)| {
@@ -2280,8 +2375,6 @@ async fn jv_sheet_align(args: SheetAlignArgs) {
"".to_string(),
]);
});
- } else {
- empty_count += 1;
}
if !align_tasks.lost.is_empty() {
@@ -2292,8 +2385,18 @@ async fn jv_sheet_align(args: SheetAlignArgs) {
"".to_string(),
]);
});
- } else {
- empty_count += 1;
+ need_align += 1;
+ }
+
+ if !align_tasks.erased.is_empty() {
+ align_tasks.erased.iter().for_each(|(n, p)| {
+ table.push_item(vec![
+ format!("& {}", n).magenta().to_string(),
+ p.display().to_string().magenta().to_string(),
+ "".to_string(),
+ ]);
+ });
+ need_align += 1;
}
if !align_tasks.moved.is_empty() {
@@ -2304,17 +2407,16 @@ async fn jv_sheet_align(args: SheetAlignArgs) {
rp.display().to_string(),
]);
});
- } else {
- empty_count += 1;
+ need_align += 1;
}
- if empty_count == 3 {
- println!("{}", md(t!("jv.success.sheet.align.no_changes").trim()));
- } else {
+ if need_align > 0 {
println!(
"{}",
md(t!("jv.success.sheet.align.list", tasks = table.to_string()))
);
+ } else {
+ println!("{}", md(t!("jv.success.sheet.align.no_changes").trim()));
}
return;
@@ -2706,6 +2808,7 @@ async fn jv_hold(args: HoldFileArgs) {
EditRightChangeBehaviour::Hold,
args.show_fail_details,
args.skip_failed,
+ args.force,
)
.await;
}
@@ -2733,6 +2836,7 @@ async fn jv_throw(args: ThrowFileArgs) {
EditRightChangeBehaviour::Throw,
args.show_fail_details,
args.skip_failed,
+ args.force,
)
.await;
}
@@ -2742,6 +2846,7 @@ async fn jv_change_edit_right(
behaviour: EditRightChangeBehaviour,
show_fail_details: bool,
mut skip_failed: bool,
+ force: bool,
) {
// If both `--details` and `--skip-failed` are set, only enable `--details`
if show_fail_details && skip_failed {
@@ -2830,6 +2935,12 @@ async fn jv_change_edit_right(
for file in files {
let exists = file.exists();
+ // If force is enabled, add to the list regardless
+ if force {
+ passed_files.push(file);
+ continue;
+ }
+
// Mapping exists
let Some(cached_mapping) = cached_sheet.mapping().get(&file) else {
let reason = t!(
@@ -3082,8 +3193,214 @@ async fn jv_change_edit_right(
}
}
-async fn jv_move(_args: MoveFileArgs) {
- todo!()
+async fn jv_move(args: MoveMappingArgs) {
+ let local_dir = match current_local_path() {
+ Some(dir) => dir,
+ None => {
+ eprintln!("{}", t!("jv.fail.workspace_not_found").trim());
+ return;
+ }
+ };
+
+ let move_files = if let Some(from_pattern) = args.move_mapping_pattern.clone() {
+ let from = glob(from_pattern, &local_dir).await;
+ from.iter()
+ .filter_map(|f| PathBuf::from_str(f.0).ok())
+ .collect::<Vec<_>>()
+ } else {
+ println!("{}", md(t!("jv.move")));
+ return;
+ };
+
+ let to_pattern = if args.to_mapping_pattern.is_some() {
+ args.to_mapping_pattern.unwrap()
+ } else {
+ if args.erase {
+ "".to_string()
+ } else {
+ eprintln!("{}", md(t!("jv.fail.move.no_target_dir")));
+ return;
+ }
+ };
+
+ let is_to_pattern_a_dir = to_pattern.ends_with('/') || to_pattern.ends_with('\\');
+
+ let from_mappings = move_files
+ .iter()
+ .map(|f| f.display().to_string())
+ .collect::<Vec<_>>();
+
+ let base_path = Globber::from(&to_pattern).base().clone();
+ let base_path = format_path(base_path.strip_prefix(&local_dir).unwrap().join("./")).unwrap();
+ let to_path = base_path.join(to_pattern);
+
+ let mut edit_mapping_args: EditMappingActionArguments = EditMappingActionArguments {
+ operations: HashMap::<FromRelativePathBuf, OperationArgument>::new(),
+ };
+
+ if args.erase {
+ // Generate erase operation parameters
+ for from_mapping in from_mappings {
+ edit_mapping_args
+ .operations
+ .insert(from_mapping.into(), (EditMappingOperations::Erase, None));
+ }
+ } else {
+ // Generate move operation parameters
+ // Single file move
+ if from_mappings.len() == 1 {
+ let from = from_mappings[0].clone();
+ let to = if is_to_pattern_a_dir {
+ // Input is a directory, append the filename
+ format_path(
+ to_path
+ .join(from.strip_prefix(&base_path.display().to_string()).unwrap())
+ .to_path_buf(),
+ )
+ .unwrap()
+ } else {
+ // Input is a filename, use it directly
+ format_path(to_path.to_path_buf()).unwrap()
+ };
+
+ let from: PathBuf = from.into();
+ // If the from path contains to_path, ignore it to avoid duplicate moves
+ if !from.starts_with(to_path) {
+ edit_mapping_args
+ .operations
+ .insert(from, (EditMappingOperations::Move, Some(to.clone())));
+ }
+ } else
+ // Multiple file move
+ if from_mappings.len() > 1 && is_to_pattern_a_dir {
+ let to_path = format_path(to_path).unwrap();
+ for p in &from_mappings {
+ let name = p.strip_prefix(&base_path.display().to_string()).unwrap();
+ let to = format_path(to_path.join(name))
+ .unwrap()
+ .display()
+ .to_string();
+
+ let from: PathBuf = p.into();
+ // If the from path contains to_path, ignore it to avoid duplicate moves
+ if !from.starts_with(to_path.display().to_string()) {
+ edit_mapping_args
+ .operations
+ .insert(from, (EditMappingOperations::Move, Some(to.into())));
+ }
+ }
+ }
+ if from_mappings.len() > 1 && !is_to_pattern_a_dir {
+ eprintln!("{}", md(t!("jv.fail.move.count_doesnt_match")));
+ return;
+ }
+
+ // NOTE
+ // if move_file_mappings.len() < 1 {
+ // This case has already been handled earlier: output Help
+ // }
+ }
+
+ let local_cfg = match precheck().await {
+ Some(config) => config,
+ None => return,
+ };
+
+ let (pool, ctx) = match build_pool_and_ctx(&local_cfg).await {
+ Some(result) => result,
+ None => return,
+ };
+
+ match proc_edit_mapping_action(
+ &pool,
+ ctx,
+ EditMappingActionArguments {
+ operations: edit_mapping_args.operations.clone(),
+ },
+ )
+ .await
+ {
+ Ok(r) => match r {
+ EditMappingActionResult::Success => {
+ println!("{}", md(t!("jv.result.move.success")));
+
+ // If the operation succeeds and only_remote is not enabled,
+ // synchronize local moves
+ if !args.only_remote {
+ let erase_dir = local_dir
+ .join(CLIENT_FOLDER_WORKSPACE_ROOT_NAME)
+ .join(".temp")
+ .join("erased");
+
+ let mut skipped = 0;
+ for (from_relative, (operation, to_relative)) in edit_mapping_args.operations {
+ let from = local_dir.join(&from_relative);
+ let to = match operation {
+ EditMappingOperations::Move => local_dir.join(to_relative.unwrap()),
+ EditMappingOperations::Erase => erase_dir.join(&from_relative),
+ };
+ if let Some(to_dir) = to.parent() {
+ let _ = fs::create_dir_all(to_dir).await;
+ }
+ if let Some(e) = fs::rename(&from, &to).await.err() {
+ eprintln!(
+ "{}",
+ md(t!(
+ "jv.fail.move.rename_failed",
+ from = from.display(),
+ to = to.display(),
+ error = e
+ ))
+ .yellow()
+ );
+ skipped += 1;
+ }
+ }
+ if skipped > 0 {
+ eprintln!("{}", md(t!("jv.fail.move.has_rename_failed")));
+ }
+ }
+ }
+ EditMappingActionResult::AuthorizeFailed(e) => {
+ eprintln!("{}", md(t!("jv.result.common.authroize_failed", err = e)))
+ }
+ EditMappingActionResult::MappingNotFound(path_buf) => {
+ eprintln!(
+ "{}",
+ md(t!(
+ "jv.result.move.mapping_not_found",
+ path = path_buf.display()
+ ))
+ )
+ }
+ EditMappingActionResult::InvalidMove(invalid_move_reason) => {
+ match invalid_move_reason {
+ InvalidMoveReason::MoveOperationButNoTarget(path_buf) => {
+ eprintln!(
+ "{}",
+ md(t!(
+ "jv.result.move.invalid_move.no_target",
+ path = path_buf.display()
+ ))
+ )
+ }
+ InvalidMoveReason::ContainsDuplicateMapping(path_buf) => {
+ eprintln!(
+ "{}",
+ md(t!(
+ "jv.result.move.invalid_move.duplicate_mapping",
+ path = path_buf.display()
+ ))
+ )
+ }
+ }
+ }
+ EditMappingActionResult::Unknown => {
+ eprintln!("{}", md(t!("jv.result.move.unknown")))
+ }
+ },
+ Err(err) => handle_err(err),
+ }
}
async fn jv_export(_args: ExportFileArgs) {
diff --git a/src/bin/jvii.rs b/src/bin/jvii.rs
index 83d6162..168acac 100644
--- a/src/bin/jvii.rs
+++ b/src/bin/jvii.rs
@@ -2,13 +2,13 @@ use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;
-use std::time::{Duration, Instant};
+use std::time::Duration;
use clap::{Parser, command};
use crossterm::{
QueueableCommand,
cursor::MoveTo,
- event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
+ event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
execute,
style::{self, Color, Print, SetForegroundColor},
terminal::{
@@ -470,11 +470,6 @@ impl Editor {
false
}
- #[cfg(not(windows))]
- fn is_duplicate_event(&mut self, _key_event: &KeyEvent) -> bool {
- false
- }
-
#[cfg(windows)]
fn should_skip_ime_event(&mut self, key_event: &KeyEvent) -> bool {
// Check for IME composition markers
@@ -513,11 +508,6 @@ impl Editor {
}
}
- #[cfg(not(windows))]
- fn should_skip_ime_event(&mut self, _key_event: &KeyEvent) -> bool {
- false
- }
-
fn handle_key_event(&mut self, key_event: KeyEvent, stdout: &mut io::Stdout) -> io::Result<()> {
match key_event.code {
KeyCode::Char('s') if key_event.modifiers.contains(KeyModifiers::CONTROL) => {
diff --git a/src/utils/env.rs b/src/utils/env.rs
index c96760b..e08f117 100644
--- a/src/utils/env.rs
+++ b/src/utils/env.rs
@@ -48,6 +48,31 @@ pub fn enable_auto_update() -> bool {
false
}
+/// Gets the auto update expiration time based on environment variables.
+///
+/// The function checks the JV_OUTDATED_MINUTES environment variable.
+/// Requires JV_AUTO_UPDATE to be enabled.
+/// Next time the `jv` command is used, if the content is outdated, `jv update` will be automatically executed.
+///
+/// # Returns
+/// - When the set number is < 0, timeout-based update is disabled
+/// - When the set number = 0, update runs every time (not recommended)
+/// - When the set number > 0, update according to the specified time
+/// - If not set or conversion error occurs, the default is -1
+pub fn auto_update_outdate() -> i64 {
+ if !enable_auto_update() {
+ return -1;
+ }
+
+ match std::env::var("JV_OUTDATED_MINUTES") {
+ Ok(value) => match value.trim().parse::<i64>() {
+ Ok(num) => num,
+ Err(_) => -1,
+ },
+ Err(_) => -1,
+ }
+}
+
/// Gets the default text editor based on environment variables.
///
/// The function checks the JV_TEXT_EDITOR and EDITOR environment variables