正在进行编辑距离函数优化
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
---
|
---
|
||||||
alwaysApply: true
|
alwaysApply: true
|
||||||
---
|
---
|
||||||
|
|
||||||
## RIPER-5 + O1 思维 + 代理执行协议
|
## RIPER-5 + O1 思维 + 代理执行协议
|
||||||
|
|
||||||
### 背景介绍
|
### 背景介绍
|
||||||
|
|||||||
1
.gitmodules
vendored
1
.gitmodules
vendored
@@ -1,3 +1,4 @@
|
|||||||
[submodule "Convention"]
|
[submodule "Convention"]
|
||||||
path = Convention
|
path = Convention
|
||||||
url = http://www.liubai.site:3000/ninemine/Convention-Python.git
|
url = http://www.liubai.site:3000/ninemine/Convention-Python.git
|
||||||
|
branch = main
|
||||||
|
|||||||
Submodule Convention updated: 007db5a06b...61df36626c
219
app.py
219
app.py
@@ -1,5 +1,6 @@
|
|||||||
from Convention.Convention.Runtime.GlobalConfig import *
|
from Convention.Convention.Runtime.GlobalConfig import *
|
||||||
from Convention.Convention.Runtime.File import *
|
from Convention.Convention.Runtime.File import *
|
||||||
|
from Convention.Convention.Runtime.String import GetEditorDistanceAndOperations, FillString
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -37,12 +38,20 @@ class HistoryModel(BaseModel):
|
|||||||
# branch name : branch head commit
|
# branch name : branch head commit
|
||||||
branches: Dict[str,str] = {}
|
branches: Dict[str,str] = {}
|
||||||
|
|
||||||
def ReadBranchHeadCommit(self, branch:str, parent_path:ToolFile) -> HistoryCommit:
|
def GetParentCommit(self, commit_name:str) -> Optional[str]:
|
||||||
|
if commit_name not in self.obj_paths:
|
||||||
|
raise ValueError(f"Commit {commit_name} not found")
|
||||||
|
return self.obj_paths[commit_name]
|
||||||
|
|
||||||
|
def GetBranchHeadCommit(self, branch:str) -> str:
|
||||||
if branch not in self.branches:
|
if branch not in self.branches:
|
||||||
raise ValueError(f"Branch {branch} not found")
|
raise ValueError(f"Branch {branch} not found")
|
||||||
|
return self.branches[branch]
|
||||||
|
|
||||||
|
def ReadCommit(self, commit_name:str, parent_path:ToolFile) -> HistoryCommit:
|
||||||
# 从分支中读入链中(倒序的)
|
# 从分支中读入链中(倒序的)
|
||||||
object_chain:List[HistoryObject] = []
|
object_chain:List[HistoryObject] = []
|
||||||
current_commit_file_path = self.branches[branch]
|
current_commit_file_path = commit_name
|
||||||
while current_commit_file_path is not None:
|
while current_commit_file_path is not None:
|
||||||
current_commit_file = parent_path|current_commit_file_path
|
current_commit_file = parent_path|current_commit_file_path
|
||||||
node = HistoryObject.model_validate_json(current_commit_file.LoadAsText())
|
node = HistoryObject.model_validate_json(current_commit_file.LoadAsText())
|
||||||
@@ -55,6 +64,9 @@ class HistoryModel(BaseModel):
|
|||||||
result = HistoryCommit(object_chain)
|
result = HistoryCommit(object_chain)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def ReadBranchHeadCommit(self, branch:str, parent_path:ToolFile) -> HistoryCommit:
|
||||||
|
return self.ReadCommit(self.GetBranchHeadCommit(branch), parent_path)
|
||||||
|
|
||||||
def break_down_path(path:ToolFile|str) -> ToolFile:
|
def break_down_path(path:ToolFile|str) -> ToolFile:
|
||||||
temp = f"{path}"#[:-len(path.GetExtension())]
|
temp = f"{path}"#[:-len(path.GetExtension())]
|
||||||
temp = temp.replace("\\\\",PlatformIndicator.GetFileSeparator())
|
temp = temp.replace("\\\\",PlatformIndicator.GetFileSeparator())
|
||||||
@@ -65,81 +77,50 @@ def break_down_path(path:ToolFile|str) -> ToolFile:
|
|||||||
else:
|
else:
|
||||||
return ToolFile(temp[0])|temp[1]
|
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:
|
class Cli:
|
||||||
def print_out(self) -> None:
|
def print_out(self, is_show_all = False) -> None:
|
||||||
sp = self.prints.split("\n")
|
sp = self.prints.split("\n")
|
||||||
status_len = len(str(len(sp)))
|
status_len = len(str(len(sp)))
|
||||||
for line_index, line in enumerate(sp):
|
if is_show_all:
|
||||||
if ConsoleFrontColor.GREEN in line and ConsoleFrontColor.RED in line:
|
for line_index, line in enumerate(sp):
|
||||||
perfix = f"{ConsoleFrontColor.YELLOW}@"
|
if ConsoleFrontColor.GREEN in line and ConsoleFrontColor.RED in line:
|
||||||
elif ConsoleFrontColor.GREEN in line:
|
perfix = f"{ConsoleFrontColor.YELLOW}@"
|
||||||
perfix = f"{ConsoleFrontColor.GREEN}+"
|
elif ConsoleFrontColor.GREEN in line:
|
||||||
elif ConsoleFrontColor.RED in line:
|
perfix = f"{ConsoleFrontColor.GREEN}+"
|
||||||
perfix = f"{ConsoleFrontColor.RED}-"
|
elif ConsoleFrontColor.RED in line:
|
||||||
else:
|
perfix = f"{ConsoleFrontColor.RED}-"
|
||||||
perfix = "="
|
else:
|
||||||
|
perfix = "="
|
||||||
|
|
||||||
print(f"{perfix}{line_index}{" "*max(0,status_len-len(str(line_index)))}{ConsoleFrontColor.RESET} | {line}")
|
print(f"{perfix}{FillString(line_index+1, max_length=status_len, side = "left")}{ConsoleFrontColor.RESET} | {line}")
|
||||||
|
else:
|
||||||
|
layer = 0
|
||||||
|
for line_index, line in enumerate(sp):
|
||||||
|
layer -= 1
|
||||||
|
|
||||||
|
forward = sp[min(line_index+self.group_size, len(sp)-1)]
|
||||||
|
if ConsoleFrontColor.GREEN in forward and ConsoleFrontColor.RED in forward:
|
||||||
|
layer = self.group_size
|
||||||
|
elif ConsoleFrontColor.GREEN in forward:
|
||||||
|
layer = self.group_size
|
||||||
|
elif ConsoleFrontColor.RED in forward:
|
||||||
|
layer = self.group_size
|
||||||
|
|
||||||
|
if ConsoleFrontColor.GREEN in line and ConsoleFrontColor.RED in line:
|
||||||
|
perfix = f"{ConsoleFrontColor.YELLOW}@"
|
||||||
|
layer = self.group_size
|
||||||
|
elif ConsoleFrontColor.GREEN in line:
|
||||||
|
perfix = f"{ConsoleFrontColor.GREEN}+"
|
||||||
|
layer = self.group_size
|
||||||
|
elif ConsoleFrontColor.RED in line:
|
||||||
|
perfix = f"{ConsoleFrontColor.RED}-"
|
||||||
|
layer = self.group_size
|
||||||
|
else:
|
||||||
|
perfix = "="
|
||||||
|
|
||||||
|
if layer <= 0:
|
||||||
|
continue
|
||||||
|
print(f"{perfix}{FillString(line_index+1, max_length=status_len, side = "left")}{ConsoleFrontColor.RESET} | {line}")
|
||||||
print(ConsoleFrontColor.RESET)
|
print(ConsoleFrontColor.RESET)
|
||||||
|
|
||||||
def print(self, *args) -> None:
|
def print(self, *args) -> None:
|
||||||
@@ -147,11 +128,14 @@ class Cli:
|
|||||||
|
|
||||||
def __init__(self, asset:str, input:str, branch:str,
|
def __init__(self, asset:str, input:str, branch:str,
|
||||||
*,
|
*,
|
||||||
history_file:Optional[str]=None
|
history_file:Optional[str]=None,
|
||||||
|
verbose:bool=False
|
||||||
) -> None:
|
) -> None:
|
||||||
self.config = GlobalConfig(asset,True)
|
self.config = GlobalConfig(asset,True)
|
||||||
self.file = ToolFile(input)
|
self.file = ToolFile(input)
|
||||||
self.branch = branch
|
self.branch = branch
|
||||||
|
self.verbose = verbose or self.config.FindItem("verbose", False)
|
||||||
|
self.group_size = self.config.FindItem("group_size", 3)
|
||||||
|
|
||||||
self.historys_file = self.config.GetFile(history_file if history_file is not None else break_down_path(self.file.GetAbsPath())|"history", False)
|
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():
|
if self.historys_file.Exists():
|
||||||
@@ -167,7 +151,7 @@ class Cli:
|
|||||||
return
|
return
|
||||||
head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile()))
|
head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile()))
|
||||||
current_content = self.file.LoadAsText()
|
current_content = self.file.LoadAsText()
|
||||||
step, operations = levenshtein_distance_with_operations(head_commit, current_content)
|
step, operations = GetEditorDistanceAndOperations(head_commit, current_content)
|
||||||
if step == 0:
|
if step == 0:
|
||||||
PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "No changes")
|
PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "No changes")
|
||||||
return
|
return
|
||||||
@@ -178,42 +162,51 @@ class Cli:
|
|||||||
|
|
||||||
if operation[0] == "add":
|
if operation[0] == "add":
|
||||||
color = ConsoleFrontColor.GREEN
|
color = ConsoleFrontColor.GREEN
|
||||||
|
eline = "[>>>]"
|
||||||
|
etab = "[>>]"
|
||||||
|
ewrite = "[>]"
|
||||||
elif operation[0] == "delete":
|
elif operation[0] == "delete":
|
||||||
color = ConsoleFrontColor.RED
|
color = ConsoleFrontColor.RED
|
||||||
|
eline = "[<<<]"
|
||||||
|
etab = "[<<]"
|
||||||
|
ewrite = "[<]"
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Invalid operation: {operation}")
|
raise ValueError(f"Invalid operation: {operation}")
|
||||||
|
|
||||||
sp = operation[3].split("\n")
|
self.print(color)
|
||||||
if operation[3][0] == "\n":
|
for ch in operation[3]:
|
||||||
self.print("\n")
|
if ch == '\n':
|
||||||
for line_index, line in enumerate(sp):
|
self.print(f"{eline}\n{color}")
|
||||||
self.print(f"{color}{line}{ConsoleFrontColor.RESET}")
|
elif ch == '\t':
|
||||||
if line_index == len(sp) - 1:
|
self.print(etab)
|
||||||
if operation[3][-1] == "\n":
|
elif ch == ' ':
|
||||||
self.print("\n")
|
self.print(ewrite)
|
||||||
else:
|
else:
|
||||||
self.print("\n")
|
self.print(ch)
|
||||||
|
self.print(ConsoleFrontColor.RESET)
|
||||||
index = operation[2]
|
index = operation[2]
|
||||||
self.print(head_commit[index:])
|
self.print(head_commit[index:])
|
||||||
self.print_out()
|
self.print_out()
|
||||||
|
|
||||||
PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "\noperations:")
|
if self.verbose:
|
||||||
print(f"{'\n'.join([f"{ConsoleFrontColor.GREEN if item[0] == "add" else ConsoleFrontColor.RED}{item[0]}{ConsoleFrontColor.RESET} \"{ConsoleFrontColor.YELLOW}{item[3]}{ConsoleFrontColor.RESET}\" on [{item[1]},{item[2]}]" for item in operations])}")
|
PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "\noperations:")
|
||||||
|
print(f"{'\n'.join([f"{ConsoleFrontColor.GREEN if item[0] == "add" else ConsoleFrontColor.RED}{item[0]}{ConsoleFrontColor.RESET} \"{ConsoleFrontColor.YELLOW}{item[3]}{ConsoleFrontColor.RESET}\" on [{item[1]},{item[2]}]" for item in operations])}")
|
||||||
|
|
||||||
|
|
||||||
def save(self) -> None:
|
def save(self) -> None:
|
||||||
content = self.file.LoadAsText()
|
content = self.file.LoadAsText()
|
||||||
root = HistoryObject(hashcode=hashlib.md5(content.encode()).hexdigest())
|
root = HistoryObject(hashcode=hashlib.md5(content.encode()).hexdigest())
|
||||||
|
commit_name = f"{len(self.historys.obj_paths)}"
|
||||||
if self.branch not in self.historys.branches:
|
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.obj_paths[commit_name] = None
|
||||||
self.historys.branches[self.branch] = commit_name
|
self.historys.branches[self.branch] = commit_name
|
||||||
self.historys_file.MustExistsPath()
|
self.historys_file.MustExistsPath()
|
||||||
root.blocks=[HistoryBlock(mode="add",begin=0,end=len(content),content=content)]
|
root.blocks=[HistoryBlock(mode="add",begin=0,end=len(content),content=content)]
|
||||||
|
PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "\nAll content is new")
|
||||||
else:
|
else:
|
||||||
head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile()))
|
head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile()))
|
||||||
step, operations = levenshtein_distance_with_operations(head_commit, content)
|
step, operations = GetEditorDistanceAndOperations(head_commit, content)
|
||||||
for operation in operations:
|
for operation in operations:
|
||||||
if operation[0] == "add":
|
if operation[0] == "add":
|
||||||
root.blocks.append(HistoryBlock(mode="add",begin=operation[1],end=operation[2],content=operation[3]))
|
root.blocks.append(HistoryBlock(mode="add",begin=operation[1],end=operation[2],content=operation[3]))
|
||||||
@@ -221,13 +214,31 @@ class Cli:
|
|||||||
root.blocks.append(HistoryBlock(mode="delete",begin=operation[1],end=operation[2],content=operation[3]))
|
root.blocks.append(HistoryBlock(mode="delete",begin=operation[1],end=operation[2],content=operation[3]))
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Invalid operation: {operation}")
|
raise ValueError(f"Invalid operation: {operation}")
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
PrintColorful(ConsoleFrontColor.LIGHTMAGENTA_EX, "\noperations:")
|
||||||
|
print(f"{'\n'.join([f"{ConsoleFrontColor.GREEN if item[0] == "add" else ConsoleFrontColor.RED}{item[0]}{ConsoleFrontColor.RESET} \"{ConsoleFrontColor.YELLOW}{item[3]}{ConsoleFrontColor.RESET}\" on [{item[1]},{item[2]}]" for item in operations])}")
|
||||||
# 创建树节点, 并链接
|
# 创建树节点, 并链接
|
||||||
self.historys.obj_paths[f"{len(self.historys.obj_paths)}"] = self.historys.branches[self.branch]
|
self.historys.obj_paths[commit_name] = self.historys.branches[self.branch]
|
||||||
self.historys.branches[self.branch] = f"{len(self.historys.obj_paths)}"
|
self.historys.branches[self.branch] = commit_name
|
||||||
with open(f"{self.historys_file.GetDirToolFile()|self.historys.branches[self.branch]}", "w") as f:
|
(self.historys_file.GetDirToolFile()|commit_name).SaveAsText(root.model_dump_json())
|
||||||
f.write(root.model_dump_json())
|
(self.historys_file).SaveAsText(self.historys.model_dump_json())
|
||||||
with open(self.historys_file.GetFullPath(), "w") as f:
|
|
||||||
f.write(self.historys.model_dump_json())
|
def view(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.print(head_commit)
|
||||||
|
self.print_out(True)
|
||||||
|
|
||||||
|
def restore(self) -> None:
|
||||||
|
head_commit = str(self.historys.ReadBranchHeadCommit(self.branch, self.historys_file.GetDirToolFile()))
|
||||||
|
self.file.SaveAsText(head_commit)
|
||||||
|
|
||||||
|
def take(self, commit_name:str) -> None:
|
||||||
|
commit = self.historys.ReadCommit(commit_name, self.historys_file.GetDirToolFile())
|
||||||
|
self.file.SaveAsText(commit.content)
|
||||||
|
|
||||||
def run() -> int:
|
def run() -> int:
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@@ -238,10 +249,15 @@ def run() -> int:
|
|||||||
parser.add_argument("-a", "--asset", type=str, default=ProjectConfig.ProjectConfigFileFocus, help="配置文件目录")
|
parser.add_argument("-a", "--asset", type=str, default=ProjectConfig.ProjectConfigFileFocus, help="配置文件目录")
|
||||||
# 分支
|
# 分支
|
||||||
parser.add_argument("-b", "--branch", type=str, default="main", help="分支")
|
parser.add_argument("-b", "--branch", type=str, default="main", help="分支")
|
||||||
|
# 是否详细信息
|
||||||
|
parser.add_argument("--verbose", action="store_true", help="是否详细信息")
|
||||||
# 模式互斥组
|
# 模式互斥组
|
||||||
mode_group = parser.add_mutually_exclusive_group(required=True)
|
mode_group = parser.add_mutually_exclusive_group(required=True)
|
||||||
mode_group.add_argument("-c", "--compare", action="store_true", help="比较当前文件的差异")
|
mode_group.add_argument("-c", "--compare", action="store_true", help="比较当前文件的差异")
|
||||||
mode_group.add_argument("-s", "--save", action="store_true", help="保存当前文件的差异")
|
mode_group.add_argument("-s", "--save", action="store_true", help="保存当前文件的差异")
|
||||||
|
mode_group.add_argument("-v", "--view", type=str,default=None, help="查看记录内容")
|
||||||
|
mode_group.add_argument("-r", "--restore", action="store_true", help="恢复当前文件")
|
||||||
|
mode_group.add_argument("-t", "--take", type=str,default=None, help="获取指定提交的文件")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -250,7 +266,8 @@ def run() -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
cli = Cli(args.asset, args.input, args.branch,
|
cli = Cli(args.asset, args.input, args.branch,
|
||||||
history_file=args.history)
|
history_file=args.history,
|
||||||
|
verbose=args.verbose)
|
||||||
|
|
||||||
# 比较
|
# 比较
|
||||||
if args.compare:
|
if args.compare:
|
||||||
@@ -260,6 +277,18 @@ def run() -> int:
|
|||||||
elif args.save:
|
elif args.save:
|
||||||
cli.save()
|
cli.save()
|
||||||
return 0
|
return 0
|
||||||
|
# 查看记录内容
|
||||||
|
elif "view" in args:
|
||||||
|
cli.view(args.view)
|
||||||
|
return 0
|
||||||
|
# 恢复
|
||||||
|
elif args.restore:
|
||||||
|
cli.restore()
|
||||||
|
return 0
|
||||||
|
# 获取指定提交的文件
|
||||||
|
elif "take" in args:
|
||||||
|
cli.take(args.take)
|
||||||
|
return 0
|
||||||
|
|
||||||
raise NotImplementedError("Not implemented mode")
|
raise NotImplementedError("Not implemented mode")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user