Files
cursor-byok/cursor-proto/extractor/messages.go
T
2026-08-13 22:01:11 +08:00

344 lines
9.5 KiB
Go

// messages.go 解析消息声明、字段数组和字段类型信息。
package main
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)
// extractMessages 从多种 bundle 语法中提取消息声明。
func extractMessages(text string, moduleStarts []int) []Message {
var messages []Message
messageExists := func(typeName, varName string) bool {
for _, existing := range messages {
if existing.TypeName == typeName && existing.VarName == varName {
return true
}
}
return false
}
// 形式一:变量引用继承基类并在类体中声明 typeName 和 fields。
// 先找所有 "变量名 = class 内部类名" 定义
// JS 变量名可以包含 $ 符号,如 B$e, qg 等
// 需要同时捕获外部变量名和内部类名,因为字段引用可能用任一个
classDefRe := regexp.MustCompile(`([\w$]+)\s*=\s*class\s+([\w$]+)\s+extends\s+[\w$.]+\s*\{`)
classMatches := classDefRe.FindAllStringSubmatchIndex(text, -1)
// 从任意包的 this.typeName 字段读取完整类型名。
typeNameRe := regexp.MustCompile(`this\.typeName\s*=\s*"([\w.]+)"`)
// 从 this.fields 的 newFieldList 回调读取字段数组。
fieldsRe := regexp.MustCompile(`this\.fields\s*=\s*\w+(?:\.proto3)?\.util\.newFieldList\s*\(\s*\(\s*\)\s*=>\s*\[`)
for _, classMatch := range classMatches {
varName := text[classMatch[2]:classMatch[3]]
internalName := text[classMatch[4]:classMatch[5]]
classStart := classMatch[0]
// 找到类的结束位置(匹配大括号)
classEnd := findClassEnd(text, classMatch[1]-1)
if classEnd == -1 {
continue
}
classBody := text[classStart:classEnd]
// 在类体内查找 typeName
typeMatch := typeNameRe.FindStringSubmatch(classBody)
if typeMatch == nil {
continue
}
typeName := typeMatch[1]
// 在类体内查找 fields
fieldsMatch := fieldsRe.FindStringIndex(classBody)
if fieldsMatch == nil {
continue
}
// 找到 fields 数组的开始位置
bracketPos := classStart + fieldsMatch[1] - 1
fields := extractFieldArray(text, bracketPos)
pkg, shortName := parseTypeName(typeName)
msg := Message{
TypeName: typeName,
VarName: varName,
InternalName: internalName,
Fields: fields,
Package: pkg,
ShortName: shortName,
Pos: classStart,
ModuleStart: moduleStartForPos(moduleStarts, classStart),
}
messages = append(messages, msg)
}
// 形式二:匹配转译或压缩 bundle 中连续赋值的消息声明。
// 例如 i.runtime=n.proto3,i.typeName="agent.v1.McpArgs",i.fields=n.proto3.util.newFieldList(()=>[{...}])。
assignmentRe := regexp.MustCompile(`([\w$]+)\.typeName\s*=\s*"([\w.]+)"\s*,\s*[\w$]+\.fields\s*=\s*\w+(?:\.\w+)*\.util\.newFieldList\s*\(\s*\(\s*\)\s*=>\s*\[`)
assignmentMatches := assignmentRe.FindAllStringSubmatchIndex(text, -1)
for _, m := range assignmentMatches {
varName := text[m[2]:m[3]]
typeName := text[m[4]:m[5]]
// 跳过已经由类体形式提取的重复消息。
if messageExists(typeName, varName) {
continue
}
// 正则停在左方括号之前,从匹配尾部定位数组起点。
start := m[1] - 1
if start < 0 || start >= len(text) || text[start] != '[' {
continue
}
fields := extractFieldArray(text, start)
pkg, shortName := parseTypeName(typeName)
messages = append(messages, Message{
TypeName: typeName,
VarName: varName,
InternalName: "",
Fields: fields,
Package: pkg,
ShortName: shortName,
Pos: m[0],
ModuleStart: moduleStartForPos(moduleStarts, m[0]),
})
}
// 形式三:匹配现代 @bufbuild/protobuf 工厂调用。
// 例如 Req=A.makeMessageType("aiserver.v1.HasSeenAdRequest",()=>[{...}])。
messageFactoryRe := regexp.MustCompile(`([\w$]+)\s*=\s*[\w$.]+\.makeMessageType\s*\(\s*["']([\w.]+)["']\s*,\s*\(\s*\)\s*=>\s*\[`)
factoryMatches := messageFactoryRe.FindAllStringSubmatchIndex(text, -1)
for _, m := range factoryMatches {
varName := text[m[2]:m[3]]
typeName := text[m[4]:m[5]]
if messageExists(typeName, varName) {
continue
}
bracketStart := m[1] - 1
if bracketStart < 0 || bracketStart >= len(text) || text[bracketStart] != '[' {
continue
}
pkg, shortName := parseTypeName(typeName)
messages = append(messages, Message{
TypeName: typeName,
VarName: varName,
Fields: extractFieldArray(text, bracketStart),
Package: pkg,
ShortName: shortName,
Pos: m[0],
ModuleStart: moduleStartForPos(moduleStarts, m[0]),
})
}
// 空消息直接传字段数组,不使用延迟回调。
// 例如 Res=A.makeMessageType("aiserver.v1.MarkAdAsSeenResponse",[])。
emptyMessageFactoryRe := regexp.MustCompile(`([\w$]+)\s*=\s*[\w$.]+\.makeMessageType\s*\(\s*["']([\w.]+)["']\s*,\s*\[`)
emptyFactoryMatches := emptyMessageFactoryRe.FindAllStringSubmatchIndex(text, -1)
for _, m := range emptyFactoryMatches {
varName := text[m[2]:m[3]]
typeName := text[m[4]:m[5]]
if messageExists(typeName, varName) {
continue
}
bracketStart := m[1] - 1
if bracketStart < 0 || bracketStart >= len(text) || text[bracketStart] != '[' {
continue
}
pkg, shortName := parseTypeName(typeName)
messages = append(messages, Message{
TypeName: typeName,
VarName: varName,
Fields: extractFieldArray(text, bracketStart),
Package: pkg,
ShortName: shortName,
Pos: m[0],
ModuleStart: moduleStartForPos(moduleStarts, m[0]),
})
}
return messages
}
// findClassEnd 查找类定义的配对右花括号。
func findClassEnd(text string, openBrace int) int {
depth := 0
for i := openBrace; i < len(text); i++ {
if text[i] == '{' {
depth++
} else if text[i] == '}' {
depth--
if depth == 0 {
return i + 1
}
}
}
return -1
}
// extractFieldArray 从左方括号位置解析完整字段数组。
func extractFieldArray(text string, start int) []Field {
// 查找字段数组的配对右方括号。
depth := 0
end := start
for i := start; i < len(text); i++ {
if text[i] == '[' {
depth++
} else if text[i] == ']' {
depth--
if depth == 0 {
end = i + 1
break
}
}
}
arrayText := text[start:end]
// 按每个花括号块解析独立字段对象。
var fields []Field
// 依次查找字段对象。
fieldObjects := extractFieldObjects(arrayText)
for _, fieldObj := range fieldObjects {
field, parseErr := parseFieldObject(fieldObj)
if parseErr != nil {
activeDiagnostics.addSkippedField(fieldObj, parseErr)
continue
}
activeDiagnostics.addParsedField()
fields = append(fields, *field)
}
return fields
}
// extractFieldObjects 从数组文本中提取独立字段对象。
func extractFieldObjects(arrayText string) []string {
var objects []string
depth := 0
start := -1
for i := 0; i < len(arrayText); i++ {
if arrayText[i] == '{' {
if depth == 0 {
start = i
}
depth++
} else if arrayText[i] == '}' {
depth--
if depth == 0 && start >= 0 {
objects = append(objects, arrayText[start:i+1])
start = -1
}
}
}
return objects
}
// parseFieldObject 解析包含编号、名称、类型和修饰符的单个字段对象。
func parseFieldObject(obj string) (*Field, error) {
// 提取字段编号。
noMatch := noRe.FindStringSubmatch(obj)
if noMatch == nil {
return nil, errors.New("missing field no")
}
no, _ := strconv.Atoi(noMatch[1])
// 提取字段名称。
nameMatch := nameRe.FindStringSubmatch(obj)
if nameMatch == nil {
return nil, errors.New("missing field name")
}
name := strings.TrimSpace(nameMatch[1])
if !fieldNameRe.MatchString(name) {
return nil, fmt.Errorf("invalid field name: %s", name)
}
// 提取字段类别。
kindMatch := kindRe.FindStringSubmatch(obj)
if kindMatch == nil {
return nil, errors.New("missing field kind")
}
kind := strings.TrimSpace(kindMatch[1])
field := &Field{
No: no,
Name: name,
Kind: kind,
}
// 类型 T 可以是标量编号、变量名或 getEnumType 枚举调用。
// 枚举优先匹配 getEnumType 调用。
if enumMatch := enumTypeRe.FindStringSubmatch(obj); enumMatch != nil {
field.T = enumMatch[1]
} else {
// 其余类型匹配普通 T 属性值。
if tMatch := tRe.FindStringSubmatch(obj); tMatch != nil {
if t, err := strconv.Atoi(tMatch[1]); err == nil {
field.T = t
} else {
field.T = tMatch[1]
}
} else if shorthandTRe.MatchString(obj) {
field.T = "T"
}
}
// 仅在当前字段对象内检查 oneof 分组。
if oneofMatch := oneofRe.FindStringSubmatch(obj); oneofMatch != nil {
candidate := strings.TrimSpace(oneofMatch[1])
if oneofNameRe.MatchString(candidate) {
field.Oneof = candidate
}
}
// 仅在当前字段对象内检查 repeated;压缩 JS 中 !0 表示真。
if repeatedRe.MatchString(obj) {
field.Repeated = true
}
// 仅在当前字段对象内检查 optional。
if optRe.MatchString(obj) {
field.Opt = true
}
// map 字段通过 K 键类型和 V 值描述共同表示。
if field.Kind == "map" {
// 提取 map 键类型。
if keyMatch := keyRe.FindStringSubmatch(obj); keyMatch != nil {
field.MapKey, _ = strconv.Atoi(keyMatch[1])
}
// 提取 map 值类型,兼容属性顺序变化。
if valueMatch := mapValueRe.FindStringSubmatch(obj); valueMatch != nil {
valueObj := valueMatch[1]
if kindMatch := mapValueKRe.FindStringSubmatch(valueObj); kindMatch != nil {
field.MapValueKind = kindMatch[1]
}
if tMatch := mapValueTRe.FindStringSubmatch(valueObj); tMatch != nil {
if t, err := strconv.Atoi(tMatch[1]); err == nil {
field.MapValueT = t
} else {
field.MapValueT = tMatch[1]
}
}
}
}
return field, nil
}