1
- import { React , useState } from "react" ;
1
+ import { React , useState , useEffect } from "react" ;
2
2
3
3
import AceEditor from "react-ace" ;
4
4
import "ace-builds/src-noconflict/theme-github" ;
@@ -14,65 +14,70 @@ const ajv = new Ajv({ allErrors: true });
14
14
15
15
import Schema from "../../../static/schema/schema.v2.json" ;
16
16
17
+ const validate = ajv . compile ( Schema . definitions . schema ) ;
18
+
17
19
export default function YamlEditor ( ) {
20
+ const [ value , setValue ] = useState ( "" ) ;
18
21
const [ annotations , setAnnotations ] = useState ( [ ] ) ;
19
- const [ value , setValue ] = useState (
20
- "# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json\n"
21
- ) ;
22
- const validate = ajv . compile ( Schema . definitions . schema ) ;
23
- function getRowFromPath ( path ) {
24
- // Convert path to row number (0-based)
25
- return path . split ( "/" ) . length - 1 ;
26
- }
27
- function getLineNumber ( yaml , path ) {
28
- const lines = yaml . split ( "\n" ) ;
29
- const pathParts = path . split ( "/" ) . filter ( Boolean ) ;
30
- let currentObj = jsYaml . load ( yaml ) ;
31
- let lineNumber = 0 ;
32
22
33
- for ( const part of pathParts ) {
34
- for ( let i = lineNumber ; i < lines . length ; i ++ ) {
35
- if ( lines [ i ] . trim ( ) . startsWith ( part + ":" ) ) {
36
- lineNumber = i ;
37
- break ;
38
- }
39
- }
40
- currentObj = currentObj [ part ] ;
41
- }
23
+ useEffect ( ( ) => {
24
+ const initialValue = `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
25
+ language: "en-US"
26
+ early_access: false
27
+ reviews:
28
+ profile: "chill"
29
+ request_changes_workflow: false
30
+ high_level_summary: true
31
+ poem: true
32
+ review_status: true
33
+ collapse_walkthrough: false
34
+ auto_review:
35
+ enabled: true
36
+ drafts: false
37
+ chat:
38
+ auto_reply: true
42
39
43
- return lineNumber ;
44
- }
45
- function onChange ( newValue ) {
46
- setValue ( newValue ) ;
40
+ ` ;
41
+ setValue ( initialValue ) ;
42
+ validateAndSetAnnotations ( initialValue ) ;
43
+ } , [ ] ) ;
44
+ function validateAndSetAnnotations ( yaml ) {
47
45
try {
48
- const doc = jsYaml . load ( newValue , { strict : true } ) ;
49
- const valid = validate ( doc ) ;
46
+ const doc = jsYaml . load ( yaml , { strict : true } ) ;
47
+ const isValid = validate ( doc ) ;
50
48
51
- if ( ! valid && validate . errors ) {
49
+ if ( ! isValid && validate . errors ) {
52
50
setAnnotations (
53
- validate . errors . map ( ( err ) => ( {
54
- row : err . instancePath
55
- ? getLineNumber ( newValue , err . instancePath )
56
- : 0 ,
57
- column : 0 ,
58
- text : `${ err . keyword } : ${ err . message } ${
59
- err ?. params ?. allowedValues
60
- ? `Allowed values: ${ err . params . allowedValues . join ( ", " ) } `
61
- : ""
62
- } `,
63
- type : "error" ,
64
- } ) )
51
+ validate . errors . map ( ( err ) => {
52
+ const instancePathArr = err ?. instancePath ?. split ( "/" ) ;
53
+ const key =
54
+ instancePathArr && instancePathArr [ instancePathArr . length - 1 ] ;
55
+ return {
56
+ row : err . instancePath ? getLineNumber ( yaml , err . instancePath ) : 0 ,
57
+ column : 0 ,
58
+ text : `${ key } : ${ err . message } ${
59
+ err ?. params ?. allowedValues
60
+ ? `Allowed values: ${ err . params . allowedValues . join ( ", " ) } `
61
+ : ""
62
+ } `,
63
+ type : "error" ,
64
+ } ;
65
+ } )
65
66
) ;
66
67
} else {
67
68
setAnnotations ( [ ] ) ;
68
69
}
69
70
} catch ( err ) {
71
+ const instancePathArr = err ?. instancePath ?. split ( "/" ) ;
72
+ const key =
73
+ instancePathArr && instancePathArr [ instancePathArr . length - 1 ] ;
74
+
70
75
setAnnotations ( [
71
76
{
72
- row : err . instancePath ? getLineNumber ( newValue , err . instancePath ) : 0 ,
77
+ row : err . instancePath ? getLineNumber ( yaml , err . instancePath ) : 0 ,
73
78
column : 0 ,
74
79
text :
75
- `${ err . keyword } : ${ err . message } ${
80
+ `${ key } : ${ err . message } ${
76
81
err ?. params ?. allowedValues
77
82
? `Allowed values: ${ err . params . allowedValues . join ( ", " ) } `
78
83
: ""
@@ -82,6 +87,53 @@ export default function YamlEditor() {
82
87
] ) ;
83
88
}
84
89
}
90
+ function getLineNumber ( yaml , instancePath ) {
91
+ const lines = yaml . split ( "\n" ) ;
92
+ const pathParts = instancePath . split ( "/" ) . filter ( Boolean ) ;
93
+ let currentObj = jsYaml . load ( yaml ) ;
94
+ let lineNumber = 0 ;
95
+
96
+ const lastPathPart = pathParts [ pathParts . length - 1 ] ;
97
+ const lastPathPartIndex = pathParts . length - 1 ;
98
+
99
+ for ( let i = 0 ; i < lines . length ; i ++ ) {
100
+ if ( lines [ i ] . trim ( ) . startsWith ( pathParts [ 0 ] + ":" ) ) {
101
+ // Found the top-level field
102
+ lineNumber = i ;
103
+ currentObj = currentObj [ pathParts [ 0 ] ] ;
104
+
105
+ for ( let j = 1 ; j < lastPathPartIndex ; j ++ ) {
106
+ // Go through the nested fields
107
+ for ( let k = lineNumber + 1 ; k < lines . length ; k ++ ) {
108
+ if ( lines [ k ] . trim ( ) . startsWith ( pathParts [ j ] + ":" ) ) {
109
+ lineNumber = k ;
110
+ currentObj = currentObj [ pathParts [ j ] ] ;
111
+ break ;
112
+ }
113
+ }
114
+ }
115
+
116
+ // look for the last path part with array syntax as well as object syntax
117
+ for ( let l = lineNumber + 1 ; l < lines . length ; l ++ ) {
118
+ if ( lines [ l ] . trim ( ) . startsWith ( `- ${ lastPathPart } :` ) ) {
119
+ lineNumber = l ;
120
+ break ;
121
+ } else if ( lines [ l ] . trim ( ) . startsWith ( lastPathPart + ":" ) ) {
122
+ lineNumber = l ;
123
+ break ;
124
+ }
125
+ }
126
+ break ;
127
+ }
128
+ }
129
+
130
+ return lineNumber ;
131
+ }
132
+ function onChange ( newValue ) {
133
+ setValue ( newValue ) ;
134
+ validateAndSetAnnotations ( newValue ) ;
135
+ }
136
+
85
137
return (
86
138
< AceEditor
87
139
mode = "yaml"
0 commit comments