diff --git a/Convention b/Convention index 61df366..59dfd08 160000 --- a/Convention +++ b/Convention @@ -1 +1 @@ -Subproject commit 61df36626cc80afc9e75aa3caa1f9469bb121564 +Subproject commit 59dfd08c54507866a773fbcfafaa65c93535fa42 diff --git a/app.py b/app.py index d4d148e..8f45fef 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,11 @@ from Convention.Convention.Runtime.GlobalConfig import * from Convention.Convention.Runtime.File import * -from Convention.Convention.Runtime.String import GetEditorDistanceAndOperations, FillString +from Convention.Convention.Runtime.String import GetDiffOperations, FillString import argparse from pydantic import BaseModel import hashlib -import json +import pickle class HistoryBlock(BaseModel): mode: Literal["add","delete"] = "add" @@ -13,6 +13,14 @@ class HistoryBlock(BaseModel): end: int = 0 content: str = "" + def ToOperation(self) -> Tuple[Literal["add","delete"], int, int, str]: + return (self.mode, self.begin, self.end, self.content) + def FromOperation(self, operation:Tuple[Literal["add","delete"], int, int, str]) -> None: + self.mode = operation[0] + self.begin = operation[1] + self.end = operation[2] + self.content = operation[3] + class HistoryObject(BaseModel): hashcode: str = "" blocks: List[HistoryBlock] = [] @@ -21,7 +29,8 @@ class HistoryCommit: def __init__(self, object_chain:List[HistoryObject]) -> None: self.content = "" for item in object_chain: - for block in item.blocks: + # 从后往前应用操作,因为操作使用的是静态坐标系(基于原始字符串) + for block in reversed(item.blocks): if block.mode == "add": self.content = self.content[:block.begin] + block.content + self.content[block.end:] elif block.mode == "delete": @@ -54,7 +63,7 @@ class HistoryModel(BaseModel): current_commit_file_path = commit_name 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()) + node = pickle.loads(current_commit_file.LoadAsBinary()) object_chain.append(node) current_commit_file_path = self.obj_paths[current_commit_file_path] # 反转链 @@ -139,22 +148,13 @@ class Cli: 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()) + self.historys = pickle.loads(self.historys_file.LoadAsBinary()) 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 = GetEditorDistanceAndOperations(head_commit, current_content) - if step == 0: - PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "No changes") - return + def show_compare_result(self, head_commit:str, operations:List[Tuple[Literal["add","delete"], int, int, str]]) -> None: index = 0 for operation in operations: # 显示操作前的不变内容 @@ -162,14 +162,14 @@ class Cli: if operation[0] == "add": color = ConsoleFrontColor.GREEN - eline = "[>>>]" - etab = "[>>]" - ewrite = "[>]" + eline = f"{ConsoleBackgroundColor.LIGHTGREEN_EX} {ConsoleBackgroundColor.RESET}" + etab = f"{ConsoleBackgroundColor.GREEN}\t{ConsoleBackgroundColor.RESET}" + ewrite = f"{ConsoleBackgroundColor.GREEN} {ConsoleBackgroundColor.RESET}" elif operation[0] == "delete": color = ConsoleFrontColor.RED - eline = "[<<<]" - etab = "[<<]" - ewrite = "[<]" + eline = f"{ConsoleBackgroundColor.LIGHTRED_EX} {ConsoleBackgroundColor.RESET}" + etab = f"{ConsoleBackgroundColor.RED}\t{ConsoleBackgroundColor.RESET}" + ewrite = f"{ConsoleBackgroundColor.RED} {ConsoleBackgroundColor.RESET}" else: raise ValueError(f"Invalid operation: {operation}") @@ -187,6 +187,20 @@ class Cli: index = operation[2] self.print(head_commit[index:]) self.print_out() + + + 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() + operations = GetDiffOperations(head_commit, current_content) + if len(operations) == 0: + PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "No changes") + return + + self.show_compare_result(head_commit, operations) if self.verbose: PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "\noperations:") @@ -206,7 +220,7 @@ class Cli: PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "\nAll content is new") else: head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile())) - step, operations = GetEditorDistanceAndOperations(head_commit, content) + operations = GetDiffOperations(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])) @@ -221,8 +235,8 @@ class Cli: # 创建树节点, 并链接 self.historys.obj_paths[commit_name] = self.historys.branches[self.branch] self.historys.branches[self.branch] = commit_name - (self.historys_file.GetDirToolFile()|commit_name).SaveAsText(root.model_dump_json()) - (self.historys_file).SaveAsText(self.historys.model_dump_json()) + (self.historys_file.GetDirToolFile()|commit_name).SaveAsBinary(pickle.dumps(root)) + (self.historys_file).SaveAsBinary(pickle.dumps(self.historys)) def view(self, commit_name:Optional[str]=None) -> None: if commit_name is None or commit_name == "": @@ -235,10 +249,15 @@ class Cli: def restore(self) -> None: head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile())) self.file.SaveAsText(head_commit) + PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, f"\nRestored {self.file.GetAbsPath()}") - def take(self, commit_name:str) -> None: - commit = self.historys.ReadCommit(commit_name, self.historys_file.GetDirToolFile()) - self.file.SaveAsText(commit.content) + def take(self, commit_name:Optional[str]=None) -> None: + if commit_name is None or commit_name == "": + head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile())) + else: + head_commit = str(self.historys.ReadCommit(commit_name, self.historys_file.GetDirToolFile())) + self.file.SaveAsText(head_commit) + PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, f"\nTaken {self.file} to commit {"head" if commit_name is None or commit_name == "" else commit_name}") def run() -> int: parser = argparse.ArgumentParser() @@ -278,7 +297,7 @@ def run() -> int: cli.save() return 0 # 查看记录内容 - elif "view" in args: + elif args.view is not None: cli.view(args.view) return 0 # 恢复 @@ -286,7 +305,7 @@ def run() -> int: cli.restore() return 0 # 获取指定提交的文件 - elif "take" in args: + elif args.take is not None: cli.take(args.take) return 0