Skip to content

Commit fe93192

Browse files
Feature yaml editor (#76)
* added yaml editor * name fix * moved component to one code block * removed logs * fix: yaml editor * fix: reviews * new line * fix: merge fix
1 parent f7722c4 commit fe93192

File tree

3 files changed

+160
-121
lines changed

3 files changed

+160
-121
lines changed

docs/configure-coderabbit.md

-18
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,6 @@ You can add a `.coderabbit.yaml` configuration file to the root of your
3939
repositories. Below is a sample YAML file that can be used as a starting point
4040
and changed as needed:
4141

42-
```yaml
43-
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
44-
language: "en-US"
45-
early_access: false
46-
reviews:
47-
profile: "chill"
48-
request_changes_workflow: false
49-
high_level_summary: true
50-
poem: true
51-
review_status: true
52-
collapse_walkthrough: false
53-
auto_review:
54-
enabled: true
55-
drafts: false
56-
chat:
57-
auto_reply: true
58-
```
59-
6042
Write your configuration file in the below editor to validate:
6143

6244
```mdx-code-block

src/components/YamlEditor/YamlEditor.jsx

-103
This file was deleted.
+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import React, { useState, useEffect } from "react";
2+
3+
import AceEditor from "react-ace";
4+
import "ace-builds/src-noconflict/theme-github";
5+
import "ace-builds/src-noconflict/ext-language_tools";
6+
7+
import "ace-builds/webpack-resolver";
8+
import "ace-builds/src-noconflict/mode-yaml";
9+
10+
import jsYaml from "js-yaml";
11+
12+
import Ajv from "ajv";
13+
const ajv = new Ajv({ allErrors: true });
14+
15+
import Schema from "../../../static/schema/schema.v2.json";
16+
17+
const validate = ajv.compile(Schema.definitions.schema);
18+
const initialValue = `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
19+
language: "en-US"
20+
early_access: false
21+
reviews:
22+
profile: "chill"
23+
request_changes_workflow: false
24+
high_level_summary: true
25+
poem: true
26+
review_status: true
27+
collapse_walkthrough: false
28+
auto_review:
29+
enabled: true
30+
drafts: false
31+
chat:
32+
auto_reply: true
33+
34+
`;
35+
export default function YamlEditor() {
36+
const [value, setValue] = useState(initialValue);
37+
const [annotations, setAnnotations] = useState([]);
38+
39+
useEffect(() => {
40+
setValue(initialValue);
41+
validateAndSetAnnotations(initialValue);
42+
}, []);
43+
44+
function validateAndSetAnnotations(yaml) {
45+
try {
46+
const doc = jsYaml.load(yaml, { strict: true });
47+
const isValid = validate(doc);
48+
49+
if (!isValid && validate.errors) {
50+
setAnnotations(
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+
})
66+
);
67+
} else {
68+
setAnnotations([]);
69+
}
70+
} catch (err) {
71+
const instancePathArr = err?.instancePath?.split("/");
72+
const key =
73+
instancePathArr && instancePathArr[instancePathArr.length - 1];
74+
75+
setAnnotations([
76+
{
77+
row: err.instancePath ? getLineNumber(yaml, err.instancePath) : 0,
78+
column: 0,
79+
text:
80+
`${key}: ${err.message} ${
81+
err?.params?.allowedValues
82+
? `Allowed values: ${err.params.allowedValues.join(", ")}`
83+
: ""
84+
}` || "YAML parsing error",
85+
type: "error",
86+
},
87+
]);
88+
}
89+
}
90+
91+
function getLineNumber(yaml, instancePath) {
92+
const lines = yaml.split("\n");
93+
const pathParts = instancePath.split("/").filter(Boolean);
94+
let currentObj = jsYaml.load(yaml);
95+
let lineNumber = 0;
96+
97+
const lastPathPart = pathParts[pathParts.length - 1];
98+
const lastPathPartIndex = pathParts.length - 1;
99+
100+
for (let i = 0; i < lines.length; i++) {
101+
if (lines[i].trim().startsWith(pathParts[0] + ":")) {
102+
// Found the top-level field
103+
lineNumber = i;
104+
currentObj = currentObj[pathParts[0]];
105+
106+
for (let j = 1; j < lastPathPartIndex; j++) {
107+
// Go through the nested fields
108+
for (let k = lineNumber + 1; k < lines.length; k++) {
109+
if (lines[k].trim().startsWith(pathParts[j] + ":")) {
110+
lineNumber = k;
111+
currentObj = currentObj[pathParts[j]];
112+
break;
113+
}
114+
}
115+
}
116+
117+
// look for the last path part with array syntax as well as object syntax
118+
for (let l = lineNumber + 1; l < lines.length; l++) {
119+
if (lines[l].trim().startsWith(`- ${lastPathPart}:`)) {
120+
lineNumber = l;
121+
break;
122+
} else if (lines[l].trim().startsWith(lastPathPart + ":")) {
123+
lineNumber = l;
124+
break;
125+
}
126+
}
127+
break;
128+
}
129+
}
130+
131+
return lineNumber;
132+
}
133+
134+
function onChange(newValue) {
135+
setValue(newValue);
136+
validateAndSetAnnotations(newValue);
137+
}
138+
139+
return (
140+
<div className="m4">
141+
<AceEditor
142+
mode="yaml"
143+
theme="github"
144+
onChange={onChange}
145+
value={value}
146+
name="yaml-editor"
147+
editorProps={{ $blockScrolling: true }}
148+
setOptions={{
149+
useWorker: false,
150+
showPrintMargin: false,
151+
showGutter: true,
152+
}}
153+
annotations={annotations}
154+
width="100%"
155+
height="400px"
156+
/>
157+
<br />
158+
</div>
159+
);
160+
}

0 commit comments

Comments
 (0)