Skip to content

Commit 238ffdf

Browse files
[DQL] Detecting DQL Expression files (#132)
* [DQL] Detecting DQL Expression files In cases where a DQL expression is written in a DQL file, the inspection will suggest renaming it into the `.dqlexpr` file. * Resolving code review issues
1 parent e9d7975 commit 238ffdf

File tree

8 files changed

+111
-3
lines changed

8 files changed

+111
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
- Added missing quick fixes for inspections:
4646
- Dropping DQL commands and functions for unknown & experimental expressions
4747
- Renaming the DQL file to `.dqlpart` when an invalid command context is detected (only for `.dql` files)
48+
- Renaming the DQL file to `.dqlexpr` when the DQL file contains an expression without the command context
4849
- Adding a Dynatrace Query Console view that allows the user to execute DQL queries without opening a DQL file.
4950
You can open the console via the `Tools` -> `Services` -> `Dynatrace Query Console` menu or by clicking the dedicated
5051
button in the `Services` tab.

src/main/java/pl/thedeem/intellij/dql/completion/DQLExternalCompletionContributor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import pl.thedeem.intellij.dql.DQLUtil;
88
import pl.thedeem.intellij.dql.completion.engines.DQLDynatraceAutocomplete;
99
import pl.thedeem.intellij.dql.settings.DQLSettings;
10+
import pl.thedeem.intellij.dqlexpr.DQLExprFileType;
1011

1112
public class DQLExternalCompletionContributor extends CompletionContributor {
1213

@@ -15,7 +16,9 @@ public DQLExternalCompletionContributor() {
1516
new CompletionProvider<>() {
1617
public void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet resultSet) {
1718
PsiFile file = parameters.getOriginalFile();
18-
if (DQLUtil.isPartialFile(file) || !DQLSettings.getInstance().isUseDynatraceAutocompleteEnabled()) {
19+
if (!DQLSettings.getInstance().isUseDynatraceAutocompleteEnabled()
20+
|| DQLUtil.isPartialFile(file)
21+
|| DQLExprFileType.INSTANCE.equals(file.getFileType())) {
1922
return;
2023
}
2124
new DQLDynatraceAutocomplete().autocomplete(parameters, resultSet);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package pl.thedeem.intellij.dql.inspections.commands;
2+
3+
import com.intellij.codeInspection.LocalInspectionTool;
4+
import com.intellij.codeInspection.ProblemHighlightType;
5+
import com.intellij.codeInspection.ProblemsHolder;
6+
import com.intellij.psi.*;
7+
import com.intellij.psi.util.PsiTreeUtil;
8+
import org.jetbrains.annotations.NotNull;
9+
import org.jetbrains.annotations.Nullable;
10+
import pl.thedeem.intellij.dql.DQLBundle;
11+
import pl.thedeem.intellij.dql.DQLFile;
12+
import pl.thedeem.intellij.dql.inspections.fixes.RenameFileQuickFix;
13+
import pl.thedeem.intellij.dql.psi.DQLCommand;
14+
import pl.thedeem.intellij.dql.psi.DQLQuery;
15+
import pl.thedeem.intellij.dqlexpr.DQLExprLanguage;
16+
17+
public class PotentialExpressionFileInspection extends LocalInspectionTool {
18+
@Override
19+
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
20+
return new PsiElementVisitor() {
21+
@Override
22+
public void visitFile(@NotNull PsiFile file) {
23+
super.visitFile(file);
24+
25+
if (!(file instanceof DQLFile)) {
26+
return;
27+
}
28+
29+
PsiElement content = findPotentialDQLExpressionContent(file);
30+
if (content != null && isValidExpressionFile(file)) {
31+
holder.registerProblem(
32+
content,
33+
DQLBundle.message("inspection.potentialExpressionFile.detected"),
34+
ProblemHighlightType.WARNING,
35+
new RenameFileQuickFix(file.getName().replaceFirst("(?i)\\.dql$", ".dqlexpr"))
36+
);
37+
}
38+
}
39+
};
40+
}
41+
42+
private @Nullable PsiElement findPotentialDQLExpressionContent(@NotNull PsiFile file) {
43+
DQLQuery query = PsiTreeUtil.getChildOfType(file, DQLQuery.class);
44+
if (query == null) {
45+
return PsiTreeUtil.getChildOfType(file, PsiErrorElement.class);
46+
}
47+
DQLCommand[] commands = PsiTreeUtil.getChildrenOfType(query, DQLCommand.class);
48+
if (commands == null || commands.length != 1) {
49+
return null;
50+
}
51+
DQLCommand command = commands[0];
52+
if (command.getDefinition() != null) {
53+
return null;
54+
}
55+
56+
return command;
57+
}
58+
59+
private boolean isValidExpressionFile(@NotNull PsiFile file) {
60+
PsiFile dqlExprFile = PsiFileFactory.getInstance(file.getProject())
61+
.createFileFromText("temp.dqlexpr", DQLExprLanguage.INSTANCE, file.getText());
62+
PsiErrorElement[] errors = PsiTreeUtil.getChildrenOfType(dqlExprFile, PsiErrorElement.class);
63+
return errors == null || errors.length == 0;
64+
}
65+
}
66+

src/main/java/pl/thedeem/intellij/dql/inspections/external/DQLVerificationAnnotator.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import pl.thedeem.intellij.dql.settings.DQLSettings;
3131
import pl.thedeem.intellij.dql.settings.tenants.DynatraceTenant;
3232
import pl.thedeem.intellij.dql.settings.tenants.DynatraceTenantsService;
33+
import pl.thedeem.intellij.dqlexpr.DQLExprFileType;
3334

3435
import java.io.IOException;
3536
import java.net.ConnectException;
@@ -129,6 +130,7 @@ private TextRange getTextRange(DQLVerifyResponse.DQLVerifyNotification notificat
129130
private boolean canPerformExternalValidation(Input input) {
130131
// We cannot do any annotations on partial files, as they will not be valid either way
131132
return !DQLUtil.isPartialFile(input.file)
133+
&& !DQLExprFileType.INSTANCE.equals(input.file.getFileType())
132134
&& DQLSettings.getInstance().isPerformingLiveValidationEnabled();
133135
}
134136

src/main/java/pl/thedeem/intellij/dql/inspections/fixes/RenameFileQuickFix.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class RenameFileQuickFix implements LocalQuickFix {
1717
public RenameFileQuickFix(@NotNull String proposedName) {
1818
this.proposedName = proposedName;
1919
}
20-
20+
2121
@Override
2222
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
2323
return IntentionPreviewInfo.EMPTY;
@@ -28,6 +28,11 @@ public RenameFileQuickFix(@NotNull String proposedName) {
2828
return DQLBundle.message("inspection.command.context.fixes.renameFile", proposedName);
2929
}
3030

31+
@Override
32+
public boolean startInWriteAction() {
33+
return false;
34+
}
35+
3136
@Override
3237
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
3338
PsiElement element = descriptor.getPsiElement();

src/main/resources/META-INF/plugin.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@
163163
groupKey="inspection.groups.dql" key="inspection.function.duplicatedParameters.display.name"
164164
implementationClass="pl.thedeem.intellij.dql.inspections.functions.DuplicatedFunctionParameterInspection"/>
165165
<!-- Command validations -->
166+
<localInspection language="DQL" bundle="messages.DQLBundle" shortName="DQLPotentialExpressionFile"
167+
enabledByDefault="true"
168+
level="WARNING"
169+
groupKey="inspection.groups.dql" key="inspection.potentialExpressionFile.display.name"
170+
implementationClass="pl.thedeem.intellij.dql.inspections.commands.PotentialExpressionFileInspection"/>
166171
<localInspection language="DQL" bundle="messages.DQLBundle" shortName="DQLUnknownCommand"
167172
enabledByDefault="true"
168173
level="ERROR"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<html lang="en">
2+
<body>
3+
Detects when a DQL file contains expressions that don't parse as valid DQL queries.
4+
5+
<p>
6+
This inspection identifies files where the content fails to parse as a valid DQL query,
7+
but would be valid as a DQL expression. Such cases can be properly parsed as a separate language, which this
8+
plugin does support for files with the <code>.dqlexpr</code> extension.
9+
</p>
10+
11+
<p>
12+
The inspection validates that the file content would parse correctly as a DQL expression before suggesting
13+
the conversion. This ensures that converting the file will resolve the parsing errors.
14+
</p>
15+
<pre><code>
16+
not matchesValue(field, "something") and matchesPhrase(field2, "somethingelse")
17+
</code></pre>
18+
19+
<p>
20+
The inspection will suggest converting the file to use the <code>.dqlexpr</code> extension
21+
to properly support expression-only syntax.
22+
</p>
23+
</body>
24+
</html>

src/main/resources/messages/DQLBundle.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,9 @@ inspection.command.context.invalidStartingCommand=The command {0} cannot be used
253253
inspection.command.context.pipeNotAllowed=DQL query cannot start with "|" symbol. If this is a partial DQL, rename the file to "*.partial.dql"
254254
inspection.command.context.invalidExtensionCommand=The command {0} cannot be used as a DQL query extension
255255
inspection.command.context.missingPipe=DQL statements must be separated by the "|" symbol
256-
inspection.command.context.fixes.renameFile=Move to a partial file: {0}
256+
inspection.command.context.fixes.renameFile=Rename the file to {0}
257+
inspection.potentialExpressionFile.display.name=Potential DQL expression file
258+
inspection.potentialExpressionFile.detected=This file does not contain a valid DQL query. If it contains only a DQL expression without the command context, it should be a DQL Expression File.
257259
inspection.command.body.display.name=Missing command parameters
258260
inspection.command.body.missingRequiredParameters=The command {0} is missing the required parameters: {1}
259261
inspection.command.duplicatedParameters.display.name=Duplicated command parameter

0 commit comments

Comments
 (0)