diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/commons/CodeActionFactory.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/commons/CodeActionFactory.java index 90271990f..c53a0e104 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/commons/CodeActionFactory.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/commons/CodeActionFactory.java @@ -106,6 +106,16 @@ public static CodeAction replace(String title, Range range, String replaceText, return replace(title, Collections.singletonList(replace), document, diagnostic); } + @SuppressWarnings("null") + public static CodeAction replace(String title, List ranges, String replaceText, TextDocumentItem document, + Diagnostic diagnostic) { + List edits = null; + for (Range range : ranges) { + edits.add(new TextEdit(range, replaceText)); + } + return replace(title, edits, document, diagnostic); + } + public static CodeAction replace(String title, List replace, TextDocumentItem document, Diagnostic diagnostic) { diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCodeActions.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCodeActions.java index 52faba10e..5c97fdd05 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCodeActions.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCodeActions.java @@ -50,7 +50,7 @@ /** * Qute code actions support. - * + * * @author Angelo ZERR * */ @@ -72,6 +72,8 @@ class QuteCodeActions { private static final String EXCLUDED_VALIDATION_TITLE = "Exclude this file from validation."; + private static final String QUTE_DEPRICATED_WITH_SECTION = "Replace `with` with `let`."; + public CompletableFuture> doCodeActions(Template template, CodeActionContext context, Range range, SharedSettings sharedSettings) { List codeActions = new ArrayList<>(); @@ -109,6 +111,15 @@ public CompletableFuture> doCodeActions(Template template, Code // Create `undefinedTag`" doCodeActionsForUndefinedSectionTag(template, diagnostic, codeActions); break; + case NotRecommendedWithSection: + // The following Qute template: + // {#with } + // + // will provide a quickfix like: + // + // Replace `with` with `let`. + doCodeActionsForDeprecatedWithSection(template, diagnostic, codeActions); + break; default: break; } @@ -186,15 +197,42 @@ private static void doCodeActionToDisableValidation(Template template, List codeActions) { + Range withSectionRange = diagnostic.getRange(); + try { + int withSectionStart = template.offsetAt(withSectionRange.getStart()); + int withSectionEnd = template.offsetAt(withSectionRange.getEnd()); + String withSectionText = template.getText(withSectionStart, withSectionEnd); + int withClosingSectionIndex = withSectionText.lastIndexOf("/with"); + String replacement = withSectionText.replaceFirst("#with", "#let").substring(0, withClosingSectionIndex) + + "/let"; + CodeAction replaceWithSection = CodeActionFactory.replace(QUTE_DEPRICATED_WITH_SECTION, + diagnostic.getRange(), replacement, template.getTextDocument(), diagnostic); + codeActions.add(replaceWithSection); + } catch (BadLocationException e) { + return; + } + } + /** * Create the configuration update (done on client side) quick fix. - * + * * @param title the displayed name of the QuickFix. * @param sectionName the section name of the settings to update. * @param item the section value of the settings to update. * @param editType the configuration edit type. * @param diagnostic the diagnostic list that this CodeAction will fix. - * + * * @return the configuration update (done on client side) quick fix. */ private static CodeAction createConfigurationUpdateCodeAction(String title, String scopeUri, String sectionName, diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java index 59a1b0b51..401b55396 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java @@ -54,6 +54,7 @@ import com.redhat.qute.parser.template.Template; import com.redhat.qute.parser.template.sections.IncludeSection; import com.redhat.qute.parser.template.sections.LoopSection; +import com.redhat.qute.parser.template.sections.WithSection; import com.redhat.qute.project.JavaMemberResult; import com.redhat.qute.project.QuteProject; import com.redhat.qute.project.datamodel.JavaDataModelCache; @@ -246,6 +247,9 @@ private void validateDataModel(Node parent, Template template, ResolvingJavaType case INCLUDE: validateIncludeSection((IncludeSection) section, diagnostics); break; + case WITH: + validateWithSection((WithSection) section, diagnostics); + break; default: validateSectionTag(section, template, resolvingJavaTypeContext, diagnostics); } @@ -345,6 +349,22 @@ private static void validateIncludeSection(IncludeSection includeSection, List diagnostics) { +// List tags = Collections.singletonList(DiagnosticTag.Deprecated); + Range range = QutePositionUtility.createRange(withSection); +// Diagnostic diagnostic = createDiagnosticWithTags(range, DiagnosticSeverity.Warning, +// QuteErrorCode.DeprecatedWithSection, tags); + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Warning, + QuteErrorCode.NotRecommendedWithSection); + diagnostics.add(diagnostic); + } + private ResolvedJavaTypeInfo validateExpression(Expression expression, Section ownerSection, Template template, ResolutionContext resolutionContext, ResolvingJavaTypeContext resolvingJavaTypeContext, List diagnostics) { @@ -391,7 +411,7 @@ private ResolvedJavaTypeInfo validateExpressionParts(Parts parts, Section ownerS ResolvedJavaTypeInfo resolvedJavaType = null; String namespace = null; for (int i = 0; i < parts.getChildCount(); i++) { - Part current = ((Part) parts.getChild(i)); + Part current = (parts.getChild(i)); if (current.isLast()) { // It's the last part, check if it is not ended with '.' @@ -586,7 +606,7 @@ private ResolvedJavaTypeInfo validateObjectPart(ObjectPart objectPart, Section o /** * Validate the given property, method part. - * + * * @param part the property, method part to validate. * @param ownerSection the owner section and null otherwise. * @param template the template. @@ -596,7 +616,7 @@ private ResolvedJavaTypeInfo validateObjectPart(ObjectPart objectPart, Section o * @param iterableOfType the iterable of type. * @param diagnostics the diagnostic list to fill. * @param resolvingJavaTypeContext the resolving Java type context. - * + * * @return the Java type returned by the member part and null otherwise. */ private ResolvedJavaTypeInfo validateMemberPart(Part part, Section ownerSection, Template template, @@ -617,7 +637,7 @@ private ResolvedJavaTypeInfo validateMemberPart(Part part, Section ownerSection, /** * Validate the given property part. - * + * * @param part the property part to validate. * @param ownerSection the owner section and null otherwise. * @param template the template. @@ -627,7 +647,7 @@ private ResolvedJavaTypeInfo validateMemberPart(Part part, Section ownerSection, * @param iterableOfType the iterable of type. * @param diagnostics the diagnostic list to fill. * @param resolvingJavaTypeContext the resolving Java type context. - * + * * @return the Java type returned by the member part and null otherwise. */ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section ownerSection, Template template, @@ -650,7 +670,7 @@ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section own /** * Validate the given method part. - * + * * @param part the method part to validate. * @param ownerSection the owner section and null otherwise. * @param template the template. @@ -660,7 +680,7 @@ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section own * @param iterableOfType the iterable of type. * @param diagnostics the diagnostic list to fill. * @param resolvingJavaTypeContext the resolving Java type context. - * + * * @return the Java type returned by the member part and null otherwise. */ private ResolvedJavaTypeInfo validateMethodPart(MethodPart methodPart, Section ownerSection, Template template, diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/DiagnosticDataFactory.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/DiagnosticDataFactory.java index b0d16a59c..3d7c95d8e 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/DiagnosticDataFactory.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/DiagnosticDataFactory.java @@ -16,8 +16,11 @@ import static com.redhat.qute.services.diagnostics.QuteDiagnosticContants.DIAGNOSTIC_DATA_TAG; import static com.redhat.qute.services.diagnostics.QuteDiagnosticContants.QUTE_SOURCE; +import java.util.List; + import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.DiagnosticTag; import org.eclipse.lsp4j.Range; import com.google.gson.JsonObject; @@ -55,4 +58,13 @@ public static Diagnostic createDiagnostic(Range range, String message, Diagnosti errorCode != null ? errorCode.getCode() : null); return diagnostic; } + + public static Diagnostic createDiagnosticWithTags(Range range, DiagnosticSeverity severity, + IQuteErrorCode errorCode, List tags, Object... arguments) { + String message = errorCode.getMessage(arguments); + Diagnostic diagnostic = new Diagnostic(range, message, severity, QUTE_SOURCE, + errorCode != null ? errorCode.getCode() : null); + diagnostic.setTags(tags); + return diagnostic; + } } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java index 2838d4d03..dd07affa2 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java @@ -45,7 +45,10 @@ public enum QuteErrorCode implements IQuteErrorCode { UndefinedSectionTag("No section helper found for `{0}`."), // - SyntaxError("Syntax error: `{0}`."); + SyntaxError("Syntax error: `{0}`."), + + // Error code for deprecated #with section + NotRecommendedWithSection("`with` is not recommended. Use `let` instead."); private final String rawMessage; diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInExpressionWithWithSectionTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInExpressionWithWithSectionTest.java index e40f3b4d1..afd51b948 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInExpressionWithWithSectionTest.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInExpressionWithWithSectionTest.java @@ -23,7 +23,7 @@ /** * Test with #with section - * + * * @author Angelo ZERR * */ @@ -34,23 +34,28 @@ public void undefinedObject() throws Exception { String template = "{#with item}\r\n" + // "{/with}"; - Diagnostic d = d(0, 7, 0, 11, QuteErrorCode.UndefinedVariable, "`item` cannot be resolved to a variable.", + Diagnostic d1 = d(0, 7, 0, 11, QuteErrorCode.UndefinedVariable, "`item` cannot be resolved to a variable.", DiagnosticSeverity.Warning); - d.setData(DiagnosticDataFactory.createUndefinedVariableData("item", false)); + d1.setData(DiagnosticDataFactory.createUndefinedVariableData("item", false)); - testDiagnosticsFor(template, d); - testCodeActionsFor(template, d, // - ca(d, te(0, 0, 0, 0, "{@java.lang.String item}\r\n"))); + Diagnostic d2 = d(0, 0, 1, 7, QuteErrorCode.NotRecommendedWithSection, + "`with` is not recommended. Use `let` instead.", DiagnosticSeverity.Warning); + + testDiagnosticsFor(template, d1, d2); + testCodeActionsFor(template, d1, // + ca(d1, te(0, 0, 0, 0, "{@java.lang.String item}\r\n"))); } @Test - public void noError() throws Exception { + public void singleSection() throws Exception { String template = "{@org.acme.Item item}\r\n" + // "{#with item}\r\n" + // "

{name}

\r\n" + // "

{price}

\r\n" + // "{/with}"; - testDiagnosticsFor(template); + Diagnostic d = d(1, 0, 4, 7, QuteErrorCode.NotRecommendedWithSection, + "`with` is not recommended. Use `let` instead.", DiagnosticSeverity.Warning); + testDiagnosticsFor(template, d); } @Test @@ -68,13 +73,19 @@ public void nested() throws Exception { " {/with}\r\n" + // "{/with}"; - Diagnostic d = d(6, 5, 6, 12, QuteErrorCode.UndefinedVariable, "`average` cannot be resolved to a variable.", + Diagnostic d1 = d(1, 0, 11, 7, QuteErrorCode.NotRecommendedWithSection, + "`with` is not recommended. Use `let` instead.", DiagnosticSeverity.Warning); + + Diagnostic d2 = d(4, 2, 10, 9, QuteErrorCode.NotRecommendedWithSection, + "`with` is not recommended. Use `let` instead.", DiagnosticSeverity.Warning); + + Diagnostic d3 = d(6, 5, 6, 12, QuteErrorCode.UndefinedVariable, "`average` cannot be resolved to a variable.", DiagnosticSeverity.Warning); - d.setData(DiagnosticDataFactory.createUndefinedVariableData("average", false)); + d3.setData(DiagnosticDataFactory.createUndefinedVariableData("average", false)); - testDiagnosticsFor(template, d); - testCodeActionsFor(template, d, // - ca(d, te(0, 0, 0, 0, "{@java.lang.String average}\r\n"))); + testDiagnosticsFor(template, d1, d2, d3); + testCodeActionsFor(template, d3, // + ca(d3, te(0, 0, 0, 0, "{@java.lang.String average}\r\n"))); } }