Files
dingtalk-workspace-cli/internal/helpers/doc/jsonml_diag.go

232 lines
8.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package doc
import (
"fmt"
"strings"
)
// ──────────────────────────────────────────────────────────────────────────────
// Position / Range — 源文本定位,对齐 LSP Diagnostic 规范。
// ──────────────────────────────────────────────────────────────────────────────
// Position 表示源文本中的一个位置点。
type Position struct {
Line int // 1-based 行号
Col int // 1-based 列号 (UTF-8 byte offset within line)
Offset int64 // 0-based 字节偏移 (相对于整个输入)
}
// Range 表示源文本中的一个区间 [Start, End)。
type Range struct {
Start Position
End Position
}
// ──────────────────────────────────────────────────────────────────────────────
// Severity — 诊断严重程度。
// ──────────────────────────────────────────────────────────────────────────────
// Severity 诊断严重程度,值对齐 LSP Diagnostic (1=Error, 2=Warning, 3=Info, 4=Hint)。
type Severity int
const (
SeverityError Severity = 1
SeverityWarning Severity = 2
SeverityInformation Severity = 3
SeverityHint Severity = 4
)
func (s Severity) String() string {
switch s {
case SeverityError:
return "error"
case SeverityWarning:
return "warning"
case SeverityInformation:
return "info"
case SeverityHint:
return "hint"
default:
return fmt.Sprintf("severity(%d)", int(s))
}
}
// ──────────────────────────────────────────────────────────────────────────────
// Diagnostic — 单条诊断信息。
// ──────────────────────────────────────────────────────────────────────────────
// Diagnostic 表示一条诊断信息(错误/警告/提示)。
type Diagnostic struct {
Range Range // 源位置
Severity Severity // 严重程度
Code string // 错误码 (见 error code 常量)
Source string // "json-syntax" | "jsonml-grammar" | "jsonml-schema"
Message string // 人可读描述
Suggestion string // 可粘贴的最小合法片段 (可选)
Context *DiagnosticContext // schema 上下文 (可选, Layer 2 填充)
}
// DiagnosticContext 附加的 schema 上下文信息。
type DiagnosticContext struct {
TagName string // 当前/期望 tag
Description string // P1: tag description from schema
AllowedChildren []string // 父节点合法子节点全集
AllValidTags []string // schema 全量 tag 列表(unknown tag 时填充)
AllValidAttrs []string // 当前 tag 全量 attr 列表(unknown attr 时填充)
AttrSpec *TypeSpec // 当前属性的 schema 定义
}
// String 返回单条诊断的格式化输出。
// 格式: "L{line}:{col} {severity}[{Code}]: {Message}"
func (d Diagnostic) String() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("L%d:%d %s[%s]: %s",
d.Range.Start.Line, d.Range.Start.Col,
d.Severity, d.Code, d.Message))
if d.Suggestion != "" {
sb.WriteString(". Suggestion: ")
sb.WriteString(d.Suggestion)
}
return sb.String()
}
// ──────────────────────────────────────────────────────────────────────────────
// DiagnosticList — 诊断列表,支持聚合操作。
// ──────────────────────────────────────────────────────────────────────────────
// DiagnosticList 诊断列表。
type DiagnosticList []Diagnostic
// HasErrors 返回列表中是否包含 Error 级别诊断。
func (dl DiagnosticList) HasErrors() bool {
for i := range dl {
if dl[i].Severity == SeverityError {
return true
}
}
return false
}
// ErrorCount 返回 Error 级别诊断数量。
func (dl DiagnosticList) ErrorCount() int {
n := 0
for i := range dl {
if dl[i].Severity == SeverityError {
n++
}
}
return n
}
// WarningCount 返回 Warning 级别诊断数量。
func (dl DiagnosticList) WarningCount() int {
n := 0
for i := range dl {
if dl[i].Severity == SeverityWarning {
n++
}
}
return n
}
// Filter 按 Source 过滤诊断。
func (dl DiagnosticList) Filter(source string) DiagnosticList {
var result DiagnosticList
for i := range dl {
if dl[i].Source == source {
result = append(result, dl[i])
}
}
return result
}
// Summary 返回人可读的诊断报告。
func (dl DiagnosticList) Summary() string {
if len(dl) == 0 {
return ""
}
errCount := dl.ErrorCount()
warnCount := dl.WarningCount()
var sb strings.Builder
if errCount > 0 {
sb.WriteString(fmt.Sprintf("JSONML 校验失败(%d 个错误", errCount))
if warnCount > 0 {
sb.WriteString(fmt.Sprintf(", %d 个警告", warnCount))
}
sb.WriteString("):\n")
} else if warnCount > 0 {
sb.WriteString(fmt.Sprintf("JSONML 校验警告(%d 个):\n", warnCount))
}
for i, d := range dl {
sb.WriteString(fmt.Sprintf(" %d. %s\n", i+1, d.String()))
}
return sb.String()
}
// ToLegacyResult 将 DiagnosticList 转为旧 JsonMLValidationResult 格式,保持对外兼容。
func (dl DiagnosticList) ToLegacyResult() *JsonMLValidationResult {
r := &JsonMLValidationResult{}
for _, d := range dl {
line := d.String()
switch d.Severity {
case SeverityError:
r.Errors = append(r.Errors, line)
default:
r.Warnings = append(r.Warnings, line)
}
}
return r
}
// ──────────────────────────────────────────────────────────────────────────────
// Error Code 常量 — 按 Layer 分组。
// ──────────────────────────────────────────────────────────────────────────────
// Source 常量。
const (
SourceJSONSyntax = "json-syntax"
SourceJSONMLGrammar = "jsonml-grammar"
SourceJSONMLSchema = "jsonml-schema"
)
// Layer 0: JSON Syntax errors (reported by jsontext.Decoder).
const (
CodeJSONSyntaxUnexpectedToken = "JSON_SYNTAX_UNEXPECTED_TOKEN"
CodeJSONSyntaxUnterminatedStr = "JSON_SYNTAX_UNTERMINATED_STRING"
CodeJSONSyntaxInvalidEscape = "JSON_SYNTAX_INVALID_ESCAPE"
CodeJSONSyntaxTrailingData = "JSON_SYNTAX_TRAILING_DATA"
)
// Layer 1: JSONML Grammar errors (structural violations).
const (
CodeJSONMLSyntaxNotArray = "JSONML_SYNTAX_NOT_ARRAY"
CodeJSONMLSyntaxEmptyElement = "JSONML_SYNTAX_EMPTY_ELEMENT"
CodeJSONMLSyntaxTagNotString = "JSONML_SYNTAX_TAG_NOT_STRING"
CodeJSONMLSyntaxInvalidContent = "JSONML_SYNTAX_INVALID_CONTENT"
CodeJSONMLSyntaxUnexpectedEOF = "JSONML_SYNTAX_UNEXPECTED_EOF"
CodeJSONMLSyntaxMaxDepth = "JSONML_SYNTAX_MAX_DEPTH"
)
// Layer 2: JSONML Schema errors (semantic violations).
const (
CodeJSONMLSchemaUnknownTag = "JSONML_SCHEMA_UNKNOWN_TAG"
CodeJSONMLSchemaUnknownAttr = "JSONML_SCHEMA_UNKNOWN_ATTR"
CodeJSONMLSchemaAttrAlias = "JSONML_SCHEMA_ATTR_ALIAS"
CodeJSONMLSchemaTypeMismatch = "JSONML_SCHEMA_TYPE_MISMATCH"
CodeJSONMLSchemaRequiredMissing = "JSONML_SCHEMA_REQUIRED_MISSING"
CodeJSONMLSchemaChildNotAllowed = "JSONML_SCHEMA_CHILD_NOT_ALLOWED"
CodeJSONMLSchemaEnumInvalid = "JSONML_SCHEMA_ENUM_INVALID"
CodeJSONMLSchemaDeprecatedAttr = "JSONML_SCHEMA_DEPRECATED_ATTR"
CodeJSONMLSchemaNumberRange = "JSONML_SCHEMA_NUMBER_RANGE"
)
// Layer 2: CustomValidator-specific codes.
const (
CodeJSONMLSchemaTableMissingColsWidth = "JSONML_SCHEMA_TABLE_MISSING_COLSWIDTH"
CodeJSONMLSchemaTableColsWidthMismatch = "JSONML_SCHEMA_TABLE_COLSWIDTH_MISMATCH"
CodeJSONMLSchemaListStartInvalid = "JSONML_SCHEMA_LIST_START_INVALID"
CodeJSONMLSchemaTocContentShape = "JSONML_SCHEMA_TOC_CONTENT_SHAPE"
)