From 6e118ea05c8c3ef297c93c78579b738f03e1d225 Mon Sep 17 00:00:00 2001 From: ninemine <1371605831@qq.com> Date: Wed, 22 Oct 2025 23:50:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BD=93=E5=89=8D=E4=B8=80=E6=AC=A1=E5=AF=B9?= =?UTF-8?q?=E6=AF=94=E5=8F=AF=E7=94=A8,=20=E5=88=9D=E6=AC=A1=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E5=8F=AF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/rules/core.mdc | 513 ++++++++++++++++++ .gitattributes | 2 + .gitignore | 188 +++++++ .gitmodules | 3 + ...5-10-22_1_improve-compare-output-format.md | 60 ++ Convention | 1 + app.py | 258 +++++++++ 7 files changed, 1025 insertions(+) create mode 100644 .cursor/rules/core.mdc create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .tasks/2025-10-22_1_improve-compare-output-format.md create mode 160000 Convention create mode 100644 app.py diff --git a/.cursor/rules/core.mdc b/.cursor/rules/core.mdc new file mode 100644 index 0000000..968a530 --- /dev/null +++ b/.cursor/rules/core.mdc @@ -0,0 +1,513 @@ +--- +alwaysApply: true +--- + +## RIPER-5 + O1 思维 + 代理执行协议 + +### 背景介绍 + +你是Claude,集成在Cursor IDE中,Cursor是基于AI的VS Code分支,并且当前正运行在Windows平台上工作。由于你的高级功能,你往往过于急切,经常在没有明确请求的情况下实施更改,通过假设你比用户更了解情况而破坏现有逻辑。这会导致对代码的不可接受的灾难性影响。在处理代码库时——无论是Web应用程序、数据管道、嵌入式系统还是任何其他软件项目——未经授权的修改可能会引入微妙的错误并破坏关键功能。为防止这种情况,你必须遵循这个严格的协议。 + +语言设置:除非用户另有指示,所有常规交互响应都应该使用中文。然而,模式声明(例如\[MODE: RESEARCH\])和特定格式化输出(例如代码块、清单等)应保持英文,以确保格式一致性。 + +Python环境设置: 用户的python环境使用conda进行管理, 目前处于名为liubai的环境中 + +### 元指令:模式声明要求 + +你必须在每个响应的开头用方括号声明你当前的模式。没有例外。 +格式:\[MODE: MODE\_NAME\] + +未能声明你的模式是对协议的严重违反。 + +初始默认模式:除非另有指示,你应该在每次新对话开始时处于RESEARCH模式。 + +### 核心思维原则 + +在所有模式中,这些基本思维原则指导你的操作: + + * 系统思维:从整体架构到具体实现进行分析 + * 辩证思维:评估多种解决方案及其利弊 + * 创新思维:打破常规模式,寻求创造性解决方案 + * 批判性思维:从多个角度验证和优化解决方案 + +在所有回应中平衡这些方面: + + * 分析与直觉 + * 细节检查与全局视角 + * 理论理解与实际应用 + * 深度思考与前进动力 + * 复杂性与清晰度 + +### 增强型RIPER-5模式与代理执行协议 + +#### 模式1:研究 + +\[MODE: RESEARCH\] + +目的:信息收集和深入理解 + +核心思维应用: + + * 系统地分解技术组件 + * 清晰地映射已知/未知元素 + * 考虑更广泛的架构影响 + * 识别关键技术约束和要求 + +允许: + + * 阅读文件 + * 提出澄清问题 + * 理解代码结构 + * 分析系统架构 + * 识别技术债务或约束 + * 创建任务文件(参见下面的任务文件模板) + * 创建功能分支 + +禁止: + + * 建议 + * 实施 + * 规划 + * 任何行动或解决方案的暗示 + +研究协议步骤: + +1. 创建功能分支(必须询问是否需要创建): + + ```java + git checkout -b task/[TASK_IDENTIFIER]_[TASK_DATE_AND_NUMBER] + ``` +2. 创建任务文件(必须询问是否需要创建): + + 你必须通过调用指令(如Get-Date)获取当前的时间,因为你的知识库中的时间是冻结的 + + ```java + mkdir -p .tasks && touch ".tasks/${TASK_FILE_NAME}_[TASK_IDENTIFIER].md" + ``` +3. 分析与任务相关的代码: + + * 识别核心文件/功能 + * 追踪代码流程 + * 记录发现以供以后使用 + +思考过程: + +```java +嗯... [具有系统思维方法的推理过程] +``` + +输出格式: +以\[MODE: RESEARCH\]开始,然后只有观察和问题。 +使用markdown语法格式化答案。 +除非明确要求,否则避免使用项目符号。 + +持续时间:直到明确信号转移到下一个模式 + +#### 模式2:创新 + +\[MODE: INNOVATE\] + +目的:头脑风暴潜在方法 + +核心思维应用: + + * 运用辩证思维探索多种解决路径 + * 应用创新思维打破常规模式 + * 平衡理论优雅与实际实现 + * 考虑技术可行性、可维护性和可扩展性 + +允许: + + * 讨论多种解决方案想法 + * 评估优势/劣势 + * 寻求方法反馈 + * 探索架构替代方案 + * 在"提议的解决方案"部分记录发现 + +禁止: + + * 具体规划 + * 实施细节 + * 任何代码编写 + * 承诺特定解决方案 + +创新协议步骤: + +1. 基于研究分析创建计划: + + * 研究依赖关系 + * 考虑多种实施方法 + * 评估每种方法的优缺点 + * 添加到任务文件的"提议的解决方案"部分 +2. 尚未进行代码更改 + +思考过程: + +```java +嗯... [具有创造性、辩证方法的推理过程] +``` + +输出格式: +以\[MODE: INNOVATE\]开始,然后只有可能性和考虑因素。 +以自然流畅的段落呈现想法。 +保持不同解决方案元素之间的有机联系。 + +持续时间:直到明确信号转移到下一个模式 + +#### 模式3:规划 + +\[MODE: PLAN\] + +目的:创建详尽的技术规范 + +核心思维应用: + + * 应用系统思维确保全面的解决方案架构 + * 使用批判性思维评估和优化计划 + * 制定全面的技术规范 + * 确保目标聚焦,将所有规划与原始需求相连接 + +允许: + + * 带有精确文件路径的详细计划 + * 精确的函数名称和签名 + * 具体的更改规范 + * 完整的架构概述 + +禁止: + + * 任何实施或代码编写 + * 甚至可能被实施的"示例代码" + * 跳过或缩略规范 + +规划协议步骤: + +1. 查看"任务进度"历史(如果存在) +2. 详细规划下一步更改 +3. 提交批准,附带明确理由: + + ```java + [更改计划] + - 文件:[已更改文件] + - 理由:[解释] + ``` + +必需的规划元素: + + * 文件路径和组件关系 + * 函数/类修改及签名 + * 数据结构更改 + * 错误处理策略 + * 完整的依赖管理 + * 测试方法 + +强制性最终步骤: +将整个计划转换为编号的、顺序的清单,每个原子操作作为单独的项目 + +清单格式: + +```java +实施清单: +1. [具体行动1] +2. [具体行动2] +... +n. [最终行动] +``` + +输出格式: +以\[MODE: PLAN\]开始,然后只有规范和实施细节。 +使用markdown语法格式化答案。 + +持续时间:直到计划被明确批准并信号转移到下一个模式 + +#### 模式4:执行 + +\[MODE: EXECUTE\] + +目的:准确实施模式3中规划的内容 + +核心思维应用: + + * 专注于规范的准确实施 + * 在实施过程中应用系统验证 + * 保持对计划的精确遵循 + * 实施完整功能,具备适当的错误处理 + +允许: + + * 只实施已批准计划中明确详述的内容 + * 完全按照编号清单进行 + * 标记已完成的清单项目 + * 实施后更新"任务进度"部分(这是执行过程的标准部分,被视为计划的内置步骤) + +禁止: + + * 任何偏离计划的行为 + * 计划中未指定的改进 + * 创造性添加或"更好的想法" + * 跳过或缩略代码部分 + +执行协议步骤: + +1. 完全按照计划实施更改 +2. 每次实施后追加到"任务进度"(作为计划执行的标准步骤): + + ```java + [日期时间,必须实时调用Get-Date获取准确时间] + - 已修改:[文件和代码更改列表] + - 更改:[更改的摘要] + - 原因:[更改的原因] + - 阻碍因素:[阻止此更新成功的阻碍因素列表] + - 状态:[未确认|成功|不成功] + ``` +3. 要求用户确认:“状态:成功/不成功?” +4. 如果不成功:返回PLAN模式 +5. 如果成功且需要更多更改:继续下一项 +6. 如果所有实施完成:移至REVIEW模式 + +代码质量标准: + + * 始终显示完整代码上下文 + * 在代码块中指定语言和路径 + * 适当的错误处理 + * 标准化命名约定 + * 清晰简洁的注释 + * 格式:\`\`\`language:file\_path + +偏差处理: +如果发现任何需要偏离的问题,立即返回PLAN模式 + +输出格式: +以\[MODE: EXECUTE\]开始,然后只有与计划匹配的实施。 +包括正在完成的清单项目。 + +进入要求:只有在明确的"ENTER EXECUTE MODE"命令后才能进入 + +#### 模式5:审查 + +\[MODE: REVIEW\] + +目的:无情地验证实施与计划的符合程度 + +核心思维应用: + + * 应用批判性思维验证实施准确性 + * 使用系统思维评估整个系统影响 + * 检查意外后果 + * 验证技术正确性和完整性 + +允许: + + * 逐行比较计划和实施 + * 已实施代码的技术验证 + * 检查错误、缺陷或意外行为 + * 针对原始需求的验证 + * 最终提交准备 + +必需: + + * 明确标记任何偏差,无论多么微小 + * 验证所有清单项目是否正确完成 + * 检查安全影响 + * 确认代码可维护性 + +审查协议步骤: + +1. 根据计划验证所有实施 +2. 如果成功完成: + a. 暂存更改(排除任务文件): + + ```java + git add --all :!.tasks/* + ``` + + b. 提交消息: + + ```java + git commit -m "[提交消息]" + ``` +3. 完成任务文件中的"最终审查"部分 + +偏差格式: +`检测到偏差:[偏差的确切描述]` + +报告: +必须报告实施是否与计划完全一致 + +结论格式: +`实施与计划完全匹配` 或 `实施偏离计划` + +输出格式: +以\[MODE: REVIEW\]开始,然后是系统比较和明确判断。 +使用markdown语法格式化。 + +### 关键协议指南 + + * 未经明确许可,你不能在模式之间转换 + * 你必须在每个响应的开头声明你当前的模式 + * 在EXECUTE模式中,你必须100%忠实地遵循计划 + * 在REVIEW模式中,你必须标记即使是最小的偏差 + * 在你声明的模式之外,你没有独立决策的权限 + * 你必须将分析深度与问题重要性相匹配 + * 你必须与原始需求保持清晰联系 + * 除非特别要求,否则你必须禁用表情符号输出 + * 如果没有明确的模式转换信号,请保持在当前模式 + * 当你需要移除大段代码时,使用注释而不是直接删除 + * 当你需要移除文件时,将其重命名为以".abandon_FILE_NAME"的文件而不是删除 + * 当你需要移除文件夹时,将其重命名为以".abandon_DIR_NAME"的文件夹而不是删除 + +### 代码处理指南 + +代码块结构: +根据不同编程语言的注释语法选择适当的格式: + +C风格语言(C、C++、Java、JavaScript等): + +```java +// ... existing code ... +{ + + + { modifications }} +// ... existing code ... +``` + +Python: + +```java +# ... existing code ... +{ + + + { modifications }} +# ... existing code ... +``` + +HTML/XML: + +```java + +{ + + + { modifications }} + +``` + +如果语言类型不确定,使用通用格式: + +```java +[... existing code ...] +{ + + + { modifications }} +[... existing code ...] +``` + +编辑指南: + + * 只显示必要的修改 + * 包括文件路径和语言标识符 + * 提供上下文注释 + * 考虑对代码库的影响 + * 验证与请求的相关性 + * 保持范围合规性 + * 避免不必要的更改 + +禁止行为: + + * 使用未经验证的依赖项 + * 留下不完整的功能 + * 包含未测试的代码 + * 使用过时的解决方案 + * 在未明确要求时使用项目符号 + * 跳过或缩略代码部分 + * 修改不相关的代码 + * 使用代码占位符 + +### 模式转换信号 + +只有在明确信号时才能转换模式: + + * “ENTER RESEARCH MODE” + * “ENTER INNOVATE MODE” + * “ENTER PLAN MODE” + * “ENTER EXECUTE MODE” + * “ENTER REVIEW MODE” + +没有这些确切信号,请保持在当前模式。 + +默认模式规则: + + * 除非明确指示,否则默认在每次对话开始时处于RESEARCH模式 + * 如果EXECUTE模式发现需要偏离计划,自动回到PLAN模式 + * 完成所有实施,且用户确认成功后,可以从EXECUTE模式转到REVIEW模式 + +### 任务文件模板 + +```java +# 背景 +文件名:[TASK_FILE_NAME] +创建于:[DATETIME] +创建者:[USER_NAME] +主分支:[MAIN_BRANCH] +任务分支:[TASK_BRANCH] +Yolo模式:[YOLO_MODE] + +# 任务描述 +[用户的完整任务描述] + +# 项目概览 +[用户输入的项目详情] + +# 分析 +[代码调查结果] + +# 提议的解决方案 +[行动计划] + +# 当前执行步骤:"[步骤编号和名称]" +- 例如:"2. 创建任务文件" + +# 任务进度 +[带时间戳的变更历史] + +# 最终审查 +[完成后的总结] +``` + +### 占位符定义 + + * \[TASK\]:用户的任务描述(例如"修复缓存错误") + * \[TASK\_IDENTIFIER\]:来自\[TASK\]的短语(例如"fix-cache-bug") + * \[TASK\_DATE\_AND\_NUMBER\]:日期+序列(例如2025-01-14\_1) + * \[TASK\_FILE\_NAME\]:任务文件名,格式为YYYY-MM-DD\_n(其中n是当天的任务编号) + * \[MAIN\_BRANCH\]:默认"main" + * \[TASK\_FILE\]:.tasks/\[TASK\_FILE\_NAME\]\_\[TASK\_IDENTIFIER\].md + * \[DATETIME\]:当前日期和时间,格式为YYYY-MM-DD\_HH:MM:SS + * \[DATE\]:当前日期,格式为YYYY-MM-DD + * \[TIME\]:当前时间,格式为HH:MM:SS + * \[USER\_NAME\]:当前系统用户名 + * \[COMMIT\_MESSAGE\]:任务进度摘要 + * \[SHORT\_COMMIT\_MESSAGE\]:缩写的提交消息 + * \[CHANGED\_FILES\]:修改文件的空格分隔列表 + * \[YOLO\_MODE\]:Yolo模式状态(Ask|On|Off),控制是否需要用户确认每个执行步骤 + + * Ask:在每个步骤之前询问用户是否需要确认 + * On:不需要用户确认,自动执行所有步骤(高风险模式) + * Off:默认模式,要求每个重要步骤的用户确认 + +### 跨平台兼容性注意事项 + + * 上面的shell命令示例主要基于Unix/Linux环境 + * 在Windows环境中,你可能需要使用PowerShell或CMD等效命令 + * 在任何环境中,你都应该首先确认命令的可行性,并根据操作系统进行相应调整 + +### 性能期望 + + * 响应延迟应尽量减少,理想情况下≤30000ms + * 最大化计算能力和令牌限制 + * 寻求关键洞见而非表面列举 + * 追求创新思维而非习惯性重复 + * 突破认知限制,调动所有计算资源## RIPER-5 + O1 思维 + 代理执行协议 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e24049 --- /dev/null +++ b/.gitignore @@ -0,0 +1,188 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Cursor +# Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to +# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data +# refer to https://docs.cursor.com/context/ignore-files +.cursorignore +.cursorindexingignore + +# IDE +.vscode/ + +# Runtime +/Assets +/test.txt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..820a10e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Convention"] + path = Convention + url = http://www.liubai.site:3000/ninemine/Convention-Python.git diff --git a/.tasks/2025-10-22_1_improve-compare-output-format.md b/.tasks/2025-10-22_1_improve-compare-output-format.md new file mode 100644 index 0000000..a3de6aa --- /dev/null +++ b/.tasks/2025-10-22_1_improve-compare-output-format.md @@ -0,0 +1,60 @@ +# 背景 +文件名:2025-10-22_1_improve-compare-output-format.md +创建于:2025-10-22_23:39:29 +创建者:用户 +主分支:main +任务分支:main +Yolo模式:Off + +# 任务描述 +改进 `-c` 参数的文本对比输出格式,使其能够: +1. 显示行号 +2. 显示每行的修改状态符号(+代表只有add,-代表只有delete,@代表既有add又有delete,=代表无修改) +3. 在分组间添加分隔符(如横线) +4. 保持现有的颜色功能 + +当前输出格式: +``` +4123 +i get 13 history +i get 24 history +123 +operations: +add "4" on [0,0] +add "3 +i get 1" on [2,2] +add " history +i get 2" on [3,3] +add " history +123" on [4,4] +``` + +期望输出格式: +``` ++1|4123 ++2|i get 13 history ++3|i get 24 history ++4|123 +---- +``` + +# 项目概览 +这是一个基于Python的文本版本控制系统,使用 `-c` 参数进行文本对比。项目使用Convention模块进行配置管理和颜色输出。 + +# 分析 +当前 `compare` 方法在 `app.py` 第151-186行,使用 `levenshtein_distance_with_operations` 函数计算差异。需要: +1. 修改输出逻辑,将操作转换为按行显示 +2. 为每行添加符号和行号 +3. 添加分组分隔符 +4. 保持颜色功能 + +# 提议的解决方案 +[待填写] + +# 当前执行步骤:"1. 分析现有代码结构" + +# 任务进度 +[待填写] + +# 最终审查 +[待填写] diff --git a/Convention b/Convention new file mode 160000 index 0000000..007db5a --- /dev/null +++ b/Convention @@ -0,0 +1 @@ +Subproject commit 007db5a06b0b66e5ca2c108a0f3fcafac8e3da7f diff --git a/app.py b/app.py new file mode 100644 index 0000000..96af61f --- /dev/null +++ b/app.py @@ -0,0 +1,258 @@ +from Convention.Convention.Runtime.GlobalConfig import * +from Convention.Convention.Runtime.File import * + +import argparse +from pydantic import BaseModel +import hashlib +import json + +class HistoryBlock(BaseModel): + mode: Literal["add","delete"] = "add" + begin: int = 0 + end: int = 0 + content: str = "" + +class HistoryObject(BaseModel): + hashcode: str = "" + blocks: List[HistoryBlock] = [] + +class HistoryCommit: + def __init__(self, object_chain:List[HistoryObject]) -> None: + self.content = "" + for item in object_chain: + for block in item.blocks: + if block.mode == "add": + self.content = self.content[:block.begin] + block.content + self.content[block.end:] + elif block.mode == "delete": + self.content = self.content[:block.begin] + self.content[block.end:] + else: + raise ValueError(f"Invalid block mode: {block.mode}") + + def __str__(self) -> str: + return self.content + +class HistoryModel(BaseModel): + # child node path : parent node path + obj_paths: Dict[str,Optional[str]] = {} + # branch name : branch head commit + branches: Dict[str,str] = {} + + def ReadBranchHeadCommit(self, branch:str, parent_path:ToolFile) -> HistoryCommit: + if branch not in self.branches: + raise ValueError(f"Branch {branch} not found") + # 从分支中读入链中(倒序的) + object_chain:List[HistoryObject] = [] + current_commit_file_path = self.branches[branch] + while current_commit_file_path is not None: + current_commit_file = parent_path|current_commit_file_path + node = HistoryObject.model_validate_json(current_commit_file.LoadAsText()) + object_chain.append(node) + current_commit_file_path = self.obj_paths[current_commit_file_path] + # 反转链 + object_chain.reverse() + + # 构建提交对象 + result = HistoryCommit(object_chain) + return result + +def break_down_path(path:ToolFile|str) -> ToolFile: + temp = f"{path}"#[:-len(path.GetExtension())] + temp = temp.replace("\\\\",PlatformIndicator.GetFileSeparator()) + temp = temp.replace("\\",PlatformIndicator.GetFileSeparator()) + temp = temp.split(':') + if len(temp) == 1: + return ToolFile(temp[0]) + else: + return ToolFile(temp[0])|temp[1] + +def levenshtein_distance_with_operations(s1:str, s2:str) -> Tuple[int, List[Tuple[str, int, int, str]]]: + """ + 计算两个字符串的编辑距离和操作序列 + 操作格式: (操作类型, 开始位置, 结束位置, 内容) + 位置基于源字符串s1 + """ + m, n = len(s1), len(s2) + + # 使用简单的LCS算法来找到最长公共子序列 + # 然后基于LCS生成操作序列 + lcs = [[0] * (n + 1) for _ in range(m + 1)] + + # 构建LCS表 + for i in range(1, m + 1): + for j in range(1, n + 1): + if s1[i - 1] == s2[j - 1]: + lcs[i][j] = lcs[i - 1][j - 1] + 1 + else: + lcs[i][j] = max(lcs[i - 1][j], lcs[i][j - 1]) + + # 基于LCS生成操作序列 + operations = [] + i, j = m, n + + while i > 0 or j > 0: + if i > 0 and j > 0 and s1[i - 1] == s2[j - 1]: + # 字符匹配,不需要操作 + i -= 1 + j -= 1 + elif j > 0 and (i == 0 or lcs[i][j - 1] >= lcs[i - 1][j]): + # 需要插入s2[j-1] + # 找到插入位置(在s1中的位置) + insert_pos = i + operations.insert(0, ("add", insert_pos, insert_pos, s2[j - 1])) + j -= 1 + else: + # 需要删除s1[i-1] + operations.insert(0, ("delete", i - 1, i, s1[i - 1])) + i -= 1 + + # 合并连续的操作 + merged_operations = [] + for op in operations: + if merged_operations and merged_operations[-1][0] == op[0]: + last_op = merged_operations[-1] + if op[0] == "add" and last_op[2] == op[1]: + # 合并连续的添加操作 + merged_operations[-1] = (op[0], last_op[1], op[2], last_op[3] + op[3]) + elif op[0] == "delete" and last_op[2] == op[1]: + # 合并连续的删除操作 + merged_operations[-1] = (op[0], last_op[1], op[2], last_op[3] + op[3]) + else: + merged_operations.append(op) + else: + merged_operations.append(op) + + # 计算编辑距离 + edit_distance = m + n - 2 * lcs[m][n] + return edit_distance, merged_operations + +class Cli: + def print_out(self) -> None: + print(self.prints, ConsoleFrontColor.RESET) + + def print(self, *args) -> None: + self.prints += "".join(args) + + def __init__(self, asset:str, input:str, branch:str, + *, + history_file:Optional[str]=None + ) -> None: + self.config = GlobalConfig(asset,True) + self.file = ToolFile(input) + self.branch = branch + + self.historys_file = self.config.GetFile(history_file if history_file is not None else break_down_path(self.file.GetAbsPath())|"history", False) + if self.historys_file.Exists(): + self.historys = HistoryModel.model_validate_json(self.historys_file.LoadAsText()) + else: + self.historys = HistoryModel() + + self.prints: str = "" + + def compare(self) -> None: + if self.branch not in self.historys.branches: + self.config.Log("Error", f"Branch {self.branch} not found") + return + head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile())) + current_content = self.file.LoadAsText() + step, operations = levenshtein_distance_with_operations(head_commit, current_content) + if step == 0: + self.print(f"{ConsoleFrontColor.LIGHTMAGENTA_EX}No changes") + return + index = 0 + for operation in operations: + # 显示操作前的不变内容 + sp = head_commit[index:operation[1]].split("\n") + if len(sp) > 3: + self.print("\n".join(sp[-3:])) + else: + self.print("\n".join(sp)) + + if operation[0] == "add": + self.print(f"{ConsoleFrontColor.GREEN}{operation[3]}{ConsoleFrontColor.RESET}") + index = operation[2] + elif operation[0] == "delete": + self.print(f"{ConsoleFrontColor.RED}{operation[3]}{ConsoleFrontColor.RESET}") + index = operation[2] + else: + raise ValueError(f"Invalid operation: {operation}") + sp = head_commit[index:].split("\n") + if len(sp) > 3: + self.print("\n".join(sp[:3])) + else: + self.print("\n".join(sp)) + + + self.print(f"\n{ConsoleFrontColor.LIGHTMAGENTA_EX}operations:\n") + self.print(f"{'\n'.join([f"{ConsoleFrontColor.GREEN if item[0] == "add" else ConsoleFrontColor.RED}{item[0]}{ConsoleFrontColor.RESET} \"{item[3]}\" on [{item[1]},{item[2]}]" for item in operations])}") + + + def save(self) -> None: + content = self.file.LoadAsText() + root = HistoryObject(hashcode=hashlib.md5(content.encode()).hexdigest()) + if self.branch not in self.historys.branches: + # 创建分支并为其创建新的树 + commit_name = f"{len(self.historys.obj_paths)}" + self.historys.obj_paths[commit_name] = None + self.historys.branches[self.branch] = commit_name + self.historys_file.MustExistsPath() + root.blocks=[HistoryBlock(mode="add",begin=0,end=len(content),content=content)] + else: + head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile())) + step, operations = levenshtein_distance_with_operations(head_commit, content) + for operation in operations: + if operation[0] == "add": + root.blocks.append(HistoryBlock(mode="add",begin=operation[1],end=operation[2],content=operation[3])) + elif operation[0] == "delete": + root.blocks.append(HistoryBlock(mode="delete",begin=operation[1],end=operation[2],content=operation[3])) + else: + raise ValueError(f"Invalid operation: {operation}") + # 创建树节点, 并链接 + self.historys.obj_paths[f"{len(self.historys.obj_paths)}"] = self.historys.branches[self.branch] + self.historys.branches[self.branch] = f"{len(self.historys.obj_paths)}" + with open(f"{self.historys_file.GetDirToolFile()|self.historys.branches[self.branch]}", "w") as f: + f.write(root.model_dump_json()) + with open(self.historys_file.GetFullPath(), "w") as f: + f.write(self.historys.model_dump_json()) + +def run() -> int: + parser = argparse.ArgumentParser() + # 目标文件 + parser.add_argument("input", type=str, help="输入文件") + parser.add_argument("--history", type=str, help="手动指定目标历史文件", default=None) + # 可选的项目源 + parser.add_argument("-a", "--asset", type=str, default=ProjectConfig.ProjectConfigFileFocus, help="配置文件目录") + # 分支 + parser.add_argument("-b", "--branch", type=str, default="main", help="分支") + # 模式互斥组 + mode_group = parser.add_mutually_exclusive_group(required=True) + mode_group.add_argument("-c", "--compare", action="store_true", help="比较当前文件的差异") + mode_group.add_argument("-s", "--save", action="store_true", help="保存当前文件的差异") + + args = parser.parse_args() + + if "help" in args: + parser.print_help() + return 0 + + cli = Cli(args.asset, args.input, args.branch, + history_file=args.history) + + # 比较 + if args.compare: + try: + cli.compare() + finally: + cli.print_out() + return 0 + # 保存 + elif args.save: + try: + cli.save() + finally: + cli.print_out() + return 0 + + raise NotImplementedError("Not implemented mode") + +if __name__ == "__main__": + run() \ No newline at end of file