Skip to content

Commit

Permalink
feat: external annotator for OpenFGA files
Browse files Browse the repository at this point in the history
  • Loading branch information
d-jeffery committed May 22, 2024
1 parent fb531fc commit ab7a709
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 1 deletion.
8 changes: 7 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ repositories {
}

dependencies {
implementation("org.antlr:antlr4:4.13.1")
implementation("dev.openfga:openfga-sdk:0.4.2")
implementation("org.dmfs:oauth2-essentials:0.22.1")
implementation("org.dmfs:httpurlconnection-executor:1.22.1")

implementation(files("libs/java-0.0.1.jar"))

testImplementation("junit:junit:4.13.2")
}

Expand All @@ -28,7 +31,10 @@ intellij {
version.set("2023.3")
type.set("IC") // Target IDE Platform

plugins.set(listOf("org.intellij.intelliLang", "org.jetbrains.plugins.yaml"))
plugins.set(listOf(
"org.intellij.intelliLang",
"org.jetbrains.plugins.yaml",
"com.intellij.java"))
}

grammarKit {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package dev.openfga.intellijplugin.language;

import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.ExternalAnnotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiFile;
import dev.openfga.language.errors.DslErrorsException;
import dev.openfga.language.errors.ParsingError;
import dev.openfga.language.errors.StartEnd;
import dev.openfga.language.validation.DslValidator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.util.List;

public class OpenFGAAnnotator extends ExternalAnnotator<String, List<? extends ParsingError>> {

@Override
public @Nullable String collectInformation(@NotNull PsiFile file) {
return file.getText();
}

@Override
public @Nullable List<? extends ParsingError> doAnnotate(@NotNull final String collectedInfo) {
try {
DslValidator.validate(collectedInfo);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (DslErrorsException e) {
return e.getErrors();
}

return null;
}

@Override
public void apply(@NotNull final PsiFile file,
final List<? extends ParsingError> annotationResult,
@NotNull final AnnotationHolder holder) {

final String fileContents = file.getText();

for (ParsingError error : annotationResult) {
final StartEnd startEndLine = error.getLine();
final StartEnd startEndColumn = error.getColumn();

int offsetStart = getOffsetFromRange(fileContents, startEndLine.getStart(), startEndColumn.getStart());
int offsetEnd = getOffsetFromRange(fileContents, startEndLine.getEnd(), startEndColumn.getEnd());

holder.newAnnotation(HighlightSeverity.ERROR, error.getFullMessage())
.range(new TextRange(offsetStart, offsetEnd))
.create();
}
}

private static int getOffsetFromRange(@NotNull final String doc, int line, int character) {
int offset = 0;

final String[] lines = doc.split("\n");

for (int i = 0; i < line; i++) {
offset += lines[i].length() + 1;
}

offset += character;

return offset;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package dev.openfga.intellijplugin.language;

import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.ExternalAnnotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import dev.openfga.language.errors.DslErrorsException;
import dev.openfga.language.errors.ParsingError;
import dev.openfga.language.errors.StartEnd;
import dev.openfga.language.validation.DslValidator;
import org.apache.commons.lang3.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.yaml.YAMLUtil;
import org.jetbrains.yaml.psi.YAMLFile;

import java.io.IOException;
import java.util.List;

public class OpenFGAStoreAnnotator extends ExternalAnnotator<String, List<? extends ParsingError>> {

@Override
public @Nullable String collectInformation(@NotNull final PsiFile file) {

final Pair<PsiElement, String> modelField = getModelField(file);

if (ObjectUtils.isNotEmpty(modelField) && ObjectUtils.isNotEmpty(modelField.getSecond())) {
return modelField.getSecond();
}

return null;
}

@Override
public @Nullable List<? extends ParsingError> doAnnotate(@NotNull final String collectedInfo) {
try {
DslValidator.validate(collectedInfo);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (DslErrorsException e) {
return e.getErrors();
}

return null;
}

// Parsing is difficult: both the trimmed string (model) and the string retaining whitespace (originalString)
// The model is the clean string, whereas the originalString is that from the YAML with extra whitespace
// First the clean string is validated, then the original string is used to determine correct offsets
@Override
public void apply(@NotNull final PsiFile file,
final List<? extends ParsingError> annotationResult,
@NotNull final AnnotationHolder holder) {

final @Nullable Pair<PsiElement, String> fileContents = getModelField(file);

final PsiElement key = fileContents.getFirst();
final String originalString = key.getText().split("\n", 2)[1];
// Key offset and newline
int offset = key.getFirstChild().getTextRange().getEndOffset() + 1;

for (ParsingError error : annotationResult) {
final StartEnd startEndLine = error.getLine();
final StartEnd startEndColumn = error.getColumn();

int offsetStart = getOffsetFromRange(originalString, startEndLine.getStart(), startEndColumn.getStart(), offset) + 2;
int offsetEnd = getOffsetFromRange(originalString, startEndLine.getEnd(), startEndColumn.getEnd(), offset) + 2;

holder.newAnnotation(HighlightSeverity.ERROR, error.getMessage())
.range(new TextRange(offsetStart, offsetEnd))
.create();
}
}

private static int getOffsetFromRange(@NotNull final String doc, int line, int character, int offset) {
final String[] lines = doc.split("\n");

for (int i = 0; i < line; i++) {
final String replacementLine = lines[i].replaceFirst("^ ", "");
offset += replacementLine.length() + (lines[i].length() - replacementLine.length()) + 1;
}

offset += character;

return offset;
}

private static @Nullable Pair<PsiElement, String> getModelField(@NotNull final PsiFile file) {
final Pair<PsiElement, String> field = YAMLUtil.getValue((YAMLFile) file, "model");

if (ObjectUtils.isNotEmpty(field)) {
return field;
}
return null;
}
}
8 changes: 8 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@

<highlightVisitor implementation="dev.openfga.intellijplugin.OpenFGAHighlightVisitor"/>

<externalAnnotator
language="OpenFGA"
implementationClass="dev.openfga.intellijplugin.language.OpenFGAAnnotator"/>

<externalAnnotator
language="yaml"
implementationClass="dev.openfga.intellijplugin.language.OpenFGAStoreAnnotator"/>

<colorSettingsPage
implementation="dev.openfga.intellijplugin.OpenFGAColorSettingsPage"/>

Expand Down

0 comments on commit ab7a709

Please sign in to comment.