Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature yaml editor #76

Merged
merged 9 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 4 additions & 16 deletions docs/configure-coderabbit.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,10 @@ You can add a `.coderabbit.yaml` configuration file to the root of your
repositories. Below is a sample YAML file that can be used as a starting point
and changed as needed:

```yaml
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: "en-US"
early_access: false
reviews:
profile: "chill"
request_changes_workflow: false
high_level_summary: true
poem: true
review_status: true
collapse_walkthrough: false
auto_review:
enabled: true
drafts: false
chat:
auto_reply: true
Write your configuration file in the below editor to validate:
Spikatrix marked this conversation as resolved.
Show resolved Hide resolved

```mdx-code-block
<YamlEditor />
```

Write your configuration file in the below editor to validate:
Expand Down
140 changes: 96 additions & 44 deletions src/components/YamlEditor/YamlEditor.jsx
Spikatrix marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { React, useState } from "react";
import { React, useState, useEffect } from "react";

import AceEditor from "react-ace";
import "ace-builds/src-noconflict/theme-github";
Expand All @@ -14,65 +14,70 @@ const ajv = new Ajv({ allErrors: true });

import Schema from "../../../static/schema/schema.v2.json";

const validate = ajv.compile(Schema.definitions.schema);

export default function YamlEditor() {
const [value, setValue] = useState("");
Spikatrix marked this conversation as resolved.
Show resolved Hide resolved
const [annotations, setAnnotations] = useState([]);
const [value, setValue] = useState(
"# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json\n"
);
const validate = ajv.compile(Schema.definitions.schema);
function getRowFromPath(path) {
// Convert path to row number (0-based)
return path.split("/").length - 1;
}
function getLineNumber(yaml, path) {
const lines = yaml.split("\n");
const pathParts = path.split("/").filter(Boolean);
let currentObj = jsYaml.load(yaml);
let lineNumber = 0;

for (const part of pathParts) {
for (let i = lineNumber; i < lines.length; i++) {
if (lines[i].trim().startsWith(part + ":")) {
lineNumber = i;
break;
}
}
currentObj = currentObj[part];
}
useEffect(() => {
const initialValue = `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
Spikatrix marked this conversation as resolved.
Show resolved Hide resolved
language: "en-US"
early_access: false
reviews:
profile: "chill"
request_changes_workflow: false
high_level_summary: true
poem: true
review_status: true
collapse_walkthrough: false
auto_review:
enabled: true
drafts: false
chat:
auto_reply: true

return lineNumber;
}
function onChange(newValue) {
setValue(newValue);
`;
setValue(initialValue);
validateAndSetAnnotations(initialValue);
}, []);
function validateAndSetAnnotations(yaml) {
Spikatrix marked this conversation as resolved.
Show resolved Hide resolved
try {
const doc = jsYaml.load(newValue, { strict: true });
const valid = validate(doc);
const doc = jsYaml.load(yaml, { strict: true });
const isValid = validate(doc);

if (!valid && validate.errors) {
if (!isValid && validate.errors) {
setAnnotations(
validate.errors.map((err) => ({
row: err.instancePath
? getLineNumber(newValue, err.instancePath)
: 0,
column: 0,
text: `${err.keyword}: ${err.message} ${
err?.params?.allowedValues
? `Allowed values: ${err.params.allowedValues.join(", ")}`
: ""
}`,
type: "error",
}))
validate.errors.map((err) => {
const instancePathArr = err?.instancePath?.split("/");
const key =
instancePathArr && instancePathArr[instancePathArr.length - 1];
return {
row: err.instancePath ? getLineNumber(yaml, err.instancePath) : 0,
column: 0,
text: `${key}: ${err.message} ${
err?.params?.allowedValues
? `Allowed values: ${err.params.allowedValues.join(", ")}`
: ""
}`,
type: "error",
};
})
);
} else {
setAnnotations([]);
}
} catch (err) {
const instancePathArr = err?.instancePath?.split("/");
const key =
instancePathArr && instancePathArr[instancePathArr.length - 1];

setAnnotations([
{
row: err.instancePath ? getLineNumber(newValue, err.instancePath) : 0,
row: err.instancePath ? getLineNumber(yaml, err.instancePath) : 0,
column: 0,
text:
`${err.keyword}: ${err.message} ${
`${key}: ${err.message} ${
err?.params?.allowedValues
? `Allowed values: ${err.params.allowedValues.join(", ")}`
: ""
Expand All @@ -82,6 +87,53 @@ export default function YamlEditor() {
]);
}
}
function getLineNumber(yaml, instancePath) {
const lines = yaml.split("\n");
const pathParts = instancePath.split("/").filter(Boolean);
let currentObj = jsYaml.load(yaml);
let lineNumber = 0;

const lastPathPart = pathParts[pathParts.length - 1];
const lastPathPartIndex = pathParts.length - 1;

for (let i = 0; i < lines.length; i++) {
if (lines[i].trim().startsWith(pathParts[0] + ":")) {
// Found the top-level field
lineNumber = i;
currentObj = currentObj[pathParts[0]];

for (let j = 1; j < lastPathPartIndex; j++) {
// Go through the nested fields
for (let k = lineNumber + 1; k < lines.length; k++) {
if (lines[k].trim().startsWith(pathParts[j] + ":")) {
lineNumber = k;
currentObj = currentObj[pathParts[j]];
break;
}
}
}

// look for the last path part with array syntax as well as object syntax
for (let l = lineNumber + 1; l < lines.length; l++) {
if (lines[l].trim().startsWith(`- ${lastPathPart}:`)) {
lineNumber = l;
break;
} else if (lines[l].trim().startsWith(lastPathPart + ":")) {
lineNumber = l;
break;
}
}
break;
}
}

return lineNumber;
}
function onChange(newValue) {
setValue(newValue);
validateAndSetAnnotations(newValue);
}

return (
<AceEditor
mode="yaml"
Expand Down