1- import { React , useState } from "react" ;
1+ import { React , useState , useEffect } from "react" ;
22
33import AceEditor from "react-ace" ;
44import "ace-builds/src-noconflict/theme-github" ;
@@ -14,65 +14,70 @@ const ajv = new Ajv({ allErrors: true });
1414
1515import Schema from "../../../static/schema/schema.v2.json" ;
1616
17+ const validate = ajv . compile ( Schema . definitions . schema ) ;
18+
1719export default function YamlEditor ( ) {
20+ const [ value , setValue ] = useState ( "" ) ;
1821const [ 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 ;
3222
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
4239
43- return lineNumber ;
44- }
45- function onChange ( newValue ) {
46- setValue ( newValue ) ;
40+ ` ;
41+ setValue ( initialValue ) ;
42+ validateAndSetAnnotations ( initialValue ) ;
43+ } , [ ] ) ;
44+ function validateAndSetAnnotations ( yaml ) {
4745try {
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 ) ;
5048
51- if ( ! valid && validate . errors ) {
49+ if ( ! isValid && validate . errors ) {
5250setAnnotations (
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+ } )
6566) ;
6667} else {
6768setAnnotations ( [ ] ) ;
6869}
6970} catch ( err ) {
71+ const instancePathArr = err ?. instancePath ?. split ( "/" ) ;
72+ const key =
73+ instancePathArr && instancePathArr [ instancePathArr . length - 1 ] ;
74+
7075setAnnotations ( [
7176{
72- row :err . instancePath ?getLineNumber ( newValue , err . instancePath ) :0 ,
77+ row :err . instancePath ?getLineNumber ( yaml , err . instancePath ) :0 ,
7378column :0 ,
7479text :
75- `${ err . keyword } :${ err . message } ${
80+ `${ key } :${ err . message } ${
7681err ?. params ?. allowedValues
7782 ?`Allowed values:${ err . params . allowedValues . join ( ", " ) } `
7883 :""
@@ -82,6 +87,53 @@ export default function YamlEditor() {
8287] ) ;
8388}
8489}
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+
85137return (
86138< AceEditor
87139mode = "yaml"