-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #35 from davelopez/custom_validators
Add support for custom validators
- Loading branch information
Showing
14 changed files
with
972 additions
and
3,635 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { ASTNode } from "./languageTypes"; | ||
|
||
export function getPathSegments(path: string): string[] | null { | ||
const segments = path.split(/[/.]/); | ||
// Skip leading `/` or `.` | ||
if (!segments[0]) return segments.slice(1); | ||
return segments; | ||
} | ||
|
||
export function getPropertyNodeFromPath(root: ASTNode, path: string): ASTNode | null { | ||
let segments = getPathSegments(path); | ||
if (!segments) return null; | ||
if (segments.length === 1 && !segments[0]) return null; | ||
let currentNode = root; | ||
while (segments.length) { | ||
const segment = segments[0]; | ||
segments = segments?.slice(1); | ||
const isLast = !segments.length; | ||
if (currentNode.type == "object") { | ||
const property = currentNode.properties.find((p) => p.keyNode.value == segment); | ||
if (property && isLast) return property; | ||
if (!property?.valueNode) return null; | ||
if (property.valueNode.type == "object") { | ||
currentNode = property.valueNode; | ||
} else if (property.valueNode.type == "array") { | ||
currentNode = property.valueNode; | ||
} else { | ||
return null; | ||
} | ||
} else if (currentNode.type == "array") { | ||
const index = Number(segment); | ||
const itemAtIndex = currentNode.items.at(index); | ||
if (itemAtIndex) { | ||
currentNode = itemAtIndex; | ||
} else { | ||
return null; | ||
} | ||
} | ||
} | ||
return currentNode; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { JSONDocument } from "vscode-json-languageservice"; | ||
import { getPropertyNodeFromPath } from "../jsonUtils"; | ||
import { TextDocument, Range, Position, ASTNode, WorkflowDocument } from "../languageTypes"; | ||
|
||
/** | ||
* This class contains information about Native workflow semantics. | ||
*/ | ||
export class NativeWorkflowDocument extends WorkflowDocument { | ||
private _jsonDocument: JSONDocument; | ||
|
||
constructor(textDocument: TextDocument, jsonDocument: JSONDocument) { | ||
super(textDocument); | ||
this._jsonDocument = jsonDocument; | ||
} | ||
|
||
public get jsonDocument(): JSONDocument { | ||
return this._jsonDocument; | ||
} | ||
|
||
public get rootNode(): ASTNode | undefined { | ||
return this._jsonDocument.root; | ||
} | ||
|
||
public override getNodeAtPosition(position: Position): ASTNode | undefined { | ||
const offset = this.textDocument.offsetAt(position); | ||
return this.jsonDocument.getNodeFromOffset(offset); | ||
} | ||
|
||
public override getDocumentRange(): Range { | ||
const root = this.jsonDocument.root; | ||
if (root) { | ||
return Range.create(this.textDocument.positionAt(root.offset), this.textDocument.positionAt(root.length)); | ||
} | ||
return Range.create(this.textDocument.positionAt(0), this.textDocument.positionAt(1)); | ||
} | ||
|
||
public getNodeRange(node: ASTNode): Range { | ||
return Range.create( | ||
this.textDocument.positionAt(node.offset), | ||
this.textDocument.positionAt(node.offset + node.length) | ||
); | ||
} | ||
|
||
public getNodeRangeAtPosition(position: Position): Range { | ||
const node = this.getNodeAtPosition(position); | ||
return node ? this.getNodeRange(node) : this.getDefaultRangeAtPosition(position); | ||
} | ||
|
||
public isLastNodeInParent(node: ASTNode): boolean { | ||
const parent = node.parent; | ||
if (!parent || !parent.children) { | ||
return true; // Must be root | ||
} | ||
const lastNode = parent.children[parent.children.length - 1]; | ||
return node === lastNode; | ||
} | ||
|
||
public getPreviousSiblingNode(node: ASTNode): ASTNode | null { | ||
const parent = node.parent; | ||
if (!parent || !parent.children) { | ||
return null; | ||
} | ||
const previousNodeIndex = parent.children.indexOf(node) - 1; | ||
if (previousNodeIndex < 0) { | ||
return null; | ||
} | ||
return parent.children[previousNodeIndex]; | ||
} | ||
|
||
public override getNodeFromPath(path: string): ASTNode | null { | ||
const root = this._jsonDocument.root; | ||
if (!root) return null; | ||
return getPropertyNodeFromPath(root, path); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
server/src/providers/validation/MissingPropertyValidation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Diagnostic, DiagnosticSeverity } from "vscode-languageserver-types"; | ||
import { ValidationContributor, WorkflowDocument } from "../../languageTypes"; | ||
|
||
export class MissingPropertyValidationRule implements ValidationContributor { | ||
constructor(readonly nodePath: string, readonly severity?: DiagnosticSeverity | undefined) {} | ||
|
||
validate(workflowDocument: WorkflowDocument): Promise<Diagnostic[]> { | ||
const result: Diagnostic[] = []; | ||
const targetNode = workflowDocument.getNodeFromPath(this.nodePath); | ||
if (!targetNode) { | ||
result.push({ | ||
message: `Property '${this.nodePath}' is missing`, | ||
range: workflowDocument.getDefaultRange(), | ||
severity: this.severity, | ||
}); | ||
} | ||
return Promise.resolve(result); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.