Skip to content

Commit e238300

Browse files
committed
feat: 实现基于位置信息的编辑器,支持最小化diff编辑
🚀 核心功能: - 新增 PositionAwareEditor - 基于位置信息的智能编辑器 - 实现最小化diff的编辑功能,只修改需要变更的部分 - 完美保留原始格式、注释、空行、缩进等 🔧 技术实现: - 扩展 models.Requirement 添加 PositionInfo 结构体 - 记录每个token的精确位置信息(行号、列号) - 智能版本约束位置检测和精确替换 - 基于原始文本+修改的高效序列化 📊 PositionInfo 结构: - LineNumber: 行号信息 - StartColumn/EndColumn: 整行位置 - VersionStartColumn/VersionEndColumn: 版本约束精确位置 - CommentStartColumn: 注释位置 ✨ 编辑器功能: - UpdatePackageVersion() - 单包版本更新 - BatchUpdateVersions() - 批量版本更新 - GetPackageInfo() - 包信息查询 - ListPackages() - 包列表 - SerializeToString() - 最小化diff序列化 🧪 测试覆盖: - 5个全面的测试用例 - 最小化diff验证 - 复杂格式保持测试 - 位置信息准确性验证 - 错误处理测试 📚 示例演示: - examples/08-position-aware-editor/ - 完整功能演示 - 展示19.0%变化率(4/21行)的精确编辑 - 完美保留extras、markers、注释、VCS、URL等 �� 核心优势: ✅ 最小化diff - 只修改需要变更的部分 ✅ 格式保持 - 完美保留所有原始格式 ✅ 精确编辑 - 基于位置信息的精确替换 ✅ 高性能 - 无需重新解析,直接基于位置修改 ✅ 复杂格式支持 - 支持所有pip规范格式 这解决了用户提出的核心需求:基于Parser的编辑器能够 记住原始文本位置,实现最小变更的编辑方式!
1 parent b62c999 commit e238300

File tree

6 files changed

+715
-0
lines changed

6 files changed

+715
-0
lines changed
2.73 MB
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module position-aware-editor-example
2+
3+
go 1.23
4+
5+
replace github.com/scagogogo/python-requirements-parser => ../..
6+
7+
require github.com/scagogogo/python-requirements-parser v0.0.0-00010101000000-000000000000
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"strings"
7+
8+
"github.com/scagogogo/python-requirements-parser/pkg/editor"
9+
)
10+
11+
func main() {
12+
fmt.Println("=== 位置感知编辑器示例 ===")
13+
fmt.Println("演示最小化diff的编辑功能")
14+
fmt.Println()
15+
16+
// 创建位置感知编辑器
17+
posEditor := editor.NewPositionAwareEditor()
18+
19+
// 示例requirements.txt内容(保持复杂的格式)
20+
originalContent := `# Production dependencies
21+
flask==1.0.0 # Web framework
22+
django>=3.2.0,<4.0.0 # Another web framework
23+
requests>=2.25.0,<3.0.0 # HTTP library
24+
25+
# Development dependencies
26+
pytest>=6.0.0 # Testing framework
27+
black==21.9b0 # Code formatter
28+
29+
# Complex dependencies with extras and markers
30+
uvicorn[standard]>=0.15.0 # ASGI server
31+
pywin32>=1.0; platform_system == "Windows" # Windows specific
32+
django[rest,auth]>=3.2.0 # Web framework with extras
33+
34+
# URL and VCS dependencies (will be preserved as-is)
35+
git+https://github.com/user/project.git#egg=project
36+
https://example.com/package.whl
37+
38+
# File references (will be preserved)
39+
-r dev-requirements.txt
40+
-c constraints.txt`
41+
42+
fmt.Println("原始 requirements.txt 内容:")
43+
fmt.Println(strings.Repeat("=", 50))
44+
fmt.Println(originalContent)
45+
fmt.Println(strings.Repeat("=", 50))
46+
fmt.Println()
47+
48+
// 解析文档
49+
doc, err := posEditor.ParseRequirementsFile(originalContent)
50+
if err != nil {
51+
log.Fatalf("解析失败: %v", err)
52+
}
53+
54+
// 显示解析出的包和位置信息
55+
fmt.Println("=== 解析结果和位置信息 ===")
56+
packages := posEditor.ListPackages(doc)
57+
fmt.Printf("发现 %d 个包依赖:\n", len(packages))
58+
for _, pkg := range packages {
59+
fmt.Printf("📦 %s %s", pkg.Name, pkg.Version)
60+
if len(pkg.Extras) > 0 {
61+
fmt.Printf(" [%s]", strings.Join(pkg.Extras, ","))
62+
}
63+
if pkg.Markers != "" {
64+
fmt.Printf(" ; %s", pkg.Markers)
65+
}
66+
if pkg.Comment != "" {
67+
fmt.Printf(" # %s", pkg.Comment)
68+
}
69+
fmt.Println()
70+
71+
if pkg.PositionInfo != nil {
72+
fmt.Printf(" 📍 位置: 行%d, 版本位置: %d-%d\n",
73+
pkg.PositionInfo.LineNumber,
74+
pkg.PositionInfo.VersionStartColumn,
75+
pkg.PositionInfo.VersionEndColumn)
76+
}
77+
fmt.Println()
78+
}
79+
80+
// 演示单个包版本更新
81+
fmt.Println("=== 单个包版本更新 ===")
82+
fmt.Println("更新 flask 版本: 1.0.0 -> 2.0.1")
83+
err = posEditor.UpdatePackageVersion(doc, "flask", "==2.0.1")
84+
if err != nil {
85+
log.Fatalf("更新flask版本失败: %v", err)
86+
}
87+
88+
// 序列化并显示diff
89+
newContent := posEditor.SerializeToString(doc)
90+
fmt.Println("✅ 更新完成")
91+
fmt.Println()
92+
93+
// 显示diff分析
94+
fmt.Println("=== Diff 分析 ===")
95+
originalLines := strings.Split(originalContent, "\n")
96+
newLines := strings.Split(newContent, "\n")
97+
98+
changedLines := 0
99+
for i := 0; i < len(originalLines) && i < len(newLines); i++ {
100+
if originalLines[i] != newLines[i] {
101+
changedLines++
102+
fmt.Printf("📝 行 %d 变化:\n", i+1)
103+
fmt.Printf(" - %s\n", originalLines[i])
104+
fmt.Printf(" + %s\n", newLines[i])
105+
fmt.Println()
106+
}
107+
}
108+
109+
fmt.Printf("📊 总结: 只有 %d 行发生变化(最小化diff)\n", changedLines)
110+
fmt.Println()
111+
112+
// 演示批量更新
113+
fmt.Println("=== 批量版本更新 ===")
114+
updates := map[string]string{
115+
"django": ">=3.2.13,<4.0.0", // 安全更新
116+
"pytest": ">=7.0.0", // 主要版本升级
117+
"uvicorn": ">=0.18.0", // 新版本
118+
}
119+
120+
fmt.Println("批量更新以下包:")
121+
for pkg, version := range updates {
122+
fmt.Printf(" 📦 %s: %s\n", pkg, version)
123+
}
124+
125+
err = posEditor.BatchUpdateVersions(doc, updates)
126+
if err != nil {
127+
log.Printf("批量更新警告: %v", err)
128+
} else {
129+
fmt.Println("✅ 批量更新完成")
130+
}
131+
fmt.Println()
132+
133+
// 最终结果
134+
finalContent := posEditor.SerializeToString(doc)
135+
136+
fmt.Println("=== 最终结果 ===")
137+
fmt.Println(strings.Repeat("=", 50))
138+
fmt.Println(finalContent)
139+
fmt.Println(strings.Repeat("=", 50))
140+
fmt.Println()
141+
142+
// 最终diff分析
143+
fmt.Println("=== 最终 Diff 分析 ===")
144+
finalLines := strings.Split(finalContent, "\n")
145+
totalChangedLines := 0
146+
147+
for i := 0; i < len(originalLines) && i < len(finalLines); i++ {
148+
if originalLines[i] != finalLines[i] {
149+
totalChangedLines++
150+
fmt.Printf("📝 行 %d 最终变化:\n", i+1)
151+
fmt.Printf(" 原始: %s\n", originalLines[i])
152+
fmt.Printf(" 最终: %s\n", finalLines[i])
153+
fmt.Println()
154+
}
155+
}
156+
157+
fmt.Printf("📊 最终总结: 总共 %d 行发生变化\n", totalChangedLines)
158+
fmt.Printf("📈 变化率: %.1f%% (%d/%d 行)\n",
159+
float64(totalChangedLines)/float64(len(originalLines))*100,
160+
totalChangedLines, len(originalLines))
161+
162+
// 演示位置感知编辑器的优势
163+
fmt.Println()
164+
fmt.Println("=== 位置感知编辑器的优势 ===")
165+
fmt.Println("✅ 最小化diff - 只修改需要变更的部分")
166+
fmt.Println("✅ 保持格式 - 完美保留注释、空行、缩进")
167+
fmt.Println("✅ 精确编辑 - 基于位置信息的精确替换")
168+
fmt.Println("✅ 复杂格式支持 - extras、markers、注释都完美保持")
169+
fmt.Println("✅ 非包行保持 - URL、VCS、文件引用等保持不变")
170+
fmt.Println("✅ 高性能 - 基于位置信息,无需重新解析")
171+
172+
fmt.Println()
173+
fmt.Println("🎉 位置感知编辑器演示完成!")
174+
}

0 commit comments

Comments
 (0)