232 lines
8.7 KiB
Go
232 lines
8.7 KiB
Go
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"
|
||
)
|