-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide refactoring capabilities specific to maven properties. Inline…
… maven property. #383
- Loading branch information
Showing
10 changed files
with
419 additions
and
8 deletions.
There are no files selected for viewing
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
124 changes: 124 additions & 0 deletions
124
...rg/eclipse/lemminx/extensions/maven/participants/codeaction/InlinePropertyCodeAction.java
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,124 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 Red Hat Inc. and others. | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*******************************************************************************/ | ||
package org.eclipse.lemminx.extensions.maven.participants.codeaction; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.CancellationException; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
import org.apache.maven.project.MavenProject; | ||
import org.eclipse.lemminx.commons.BadLocationException; | ||
import org.eclipse.lemminx.commons.CodeActionFactory; | ||
import org.eclipse.lemminx.commons.TextDocument; | ||
import org.eclipse.lemminx.dom.DOMDocument; | ||
import org.eclipse.lemminx.dom.DOMElement; | ||
import org.eclipse.lemminx.extensions.maven.MavenLemminxExtension; | ||
import org.eclipse.lemminx.extensions.maven.utils.DOMUtils; | ||
import org.eclipse.lemminx.extensions.maven.utils.ParticipantUtils; | ||
import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionParticipant; | ||
import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionRequest; | ||
import org.eclipse.lsp4j.CodeAction; | ||
import org.eclipse.lsp4j.Range; | ||
import org.eclipse.lsp4j.TextEdit; | ||
import org.eclipse.lsp4j.jsonrpc.CancelChecker; | ||
|
||
public class InlinePropertyCodeAction implements ICodeActionParticipant { | ||
private static final Logger LOGGER = Logger.getLogger(InlinePropertyCodeAction.class.getName()); | ||
private final MavenLemminxExtension plugin; | ||
|
||
public InlinePropertyCodeAction(MavenLemminxExtension plugin) { | ||
this.plugin = plugin; | ||
} | ||
|
||
@Override | ||
public void doCodeActionUnconditional(ICodeActionRequest request, List<CodeAction> codeActions, CancelChecker cancelChecker) throws CancellationException { | ||
Range range = request.getRange(); | ||
if (range == null) { | ||
return; | ||
} | ||
|
||
cancelChecker.checkCanceled(); | ||
try { | ||
DOMDocument document = request.getDocument(); | ||
int startOffset = document.offsetAt(range.getStart()); | ||
Map.Entry<Range, String> mavenProperty = ParticipantUtils.getMavenProperty(document.findNodeAt(startOffset), startOffset); | ||
if (mavenProperty == null) { | ||
return; | ||
} | ||
|
||
cancelChecker.checkCanceled(); | ||
MavenProject project = plugin.getProjectCache().getLastSuccessfulMavenProject(document); | ||
if (project != null) { | ||
cancelChecker.checkCanceled(); | ||
Map<String, String> properties = ParticipantUtils.getMavenProjectProperties(project); | ||
String value = properties.get(mavenProperty.getValue()); | ||
if (value != null) { | ||
cancelChecker.checkCanceled(); | ||
// Replace the property with its value only in current node | ||
codeActions.add(CodeActionFactory.replace( | ||
"Replace property with its value", | ||
range, value, document.getTextDocument(), null)); | ||
|
||
// Replace the property with its value in entire document | ||
List<TextEdit> textEdits = new ArrayList<>(); | ||
collectInlinePropertyTextEdits(document.getDocumentElement(), | ||
"${" + mavenProperty.getValue() + "}", | ||
value, textEdits, cancelChecker); | ||
if (textEdits.size() > 1) { | ||
codeActions.add(CodeActionFactory.replace( | ||
"Replace all property entries with its value", | ||
textEdits, document.getTextDocument(), null)); | ||
} | ||
} | ||
} | ||
} catch (CancellationException e) { | ||
throw e; | ||
} catch (Exception e) { | ||
LOGGER.log(Level.SEVERE, e.getMessage(), e); | ||
} | ||
} | ||
|
||
void collectInlinePropertyTextEdits(DOMElement rootElement, String property, String value, | ||
List<TextEdit> textEditss, CancelChecker cancelChecker) throws CancellationException { | ||
cancelChecker.checkCanceled(); | ||
|
||
// Check this element's text | ||
collectInElementTextEdits(rootElement, property, value, textEditss, cancelChecker); | ||
|
||
// collect in this element's children | ||
rootElement.getChildren().stream().filter(DOMElement.class::isInstance).map(DOMElement.class::cast) | ||
.forEach(child -> collectInlinePropertyTextEdits(child, property, value, textEditss, cancelChecker)); | ||
} | ||
|
||
void collectInElementTextEdits(DOMElement element, String property, String value, | ||
List<TextEdit> textEditss, CancelChecker cancelChecker) throws CancellationException { | ||
TextDocument textDocument = element.getOwnerDocument().getTextDocument(); | ||
DOMUtils.findElementTextChildren(element).stream() | ||
.filter(text -> text.getData().contains(property)) | ||
.forEach(text -> { | ||
String data = text.getData(); | ||
int index = 0; | ||
for (index = data.indexOf(property); index != -1; index = data.indexOf(property, index + property.length())) { | ||
cancelChecker.checkCanceled(); | ||
try { | ||
int propertyUseStart = text.getStart() + index; | ||
Range range = new Range(textDocument.positionAt(propertyUseStart), | ||
textDocument.positionAt(propertyUseStart + property.length())); | ||
textEditss.add(new TextEdit(range, value)); | ||
} catch (BadLocationException e) { | ||
LOGGER.log(Level.SEVERE, e.getMessage(), e); | ||
return; | ||
} | ||
} | ||
}); | ||
} | ||
} |
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
109 changes: 109 additions & 0 deletions
109
...minx/extensions/maven/participants/codeaction/MavenCodeActionPropertyRefactoringTest.java
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,109 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 Red Hat Inc. and others. | ||
* This program and the accompanying materials are made | ||
* available under the terms of the Eclipse Public License 2.0 | ||
* which is available at https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
*******************************************************************************/ | ||
package org.eclipse.lemminx.extensions.maven.participants.codeaction; | ||
|
||
import static org.eclipse.lemminx.XMLAssert.ca; | ||
import static org.eclipse.lemminx.XMLAssert.te; | ||
import static org.eclipse.lemminx.XMLAssert.teOp; | ||
import static org.eclipse.lemminx.XMLAssert.testCodeActionsFor; | ||
import static org.eclipse.lemminx.extensions.maven.utils.MavenLemminxTestsUtils.createDOMDocument; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
import static org.junit.jupiter.api.Assertions.fail; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
import org.eclipse.lemminx.commons.BadLocationException; | ||
import org.eclipse.lemminx.commons.TextDocument; | ||
import org.eclipse.lemminx.dom.DOMDocument; | ||
import org.eclipse.lemminx.extensions.maven.NoMavenCentralExtension; | ||
import org.eclipse.lemminx.services.XMLLanguageService; | ||
import org.eclipse.lemminx.settings.SharedSettings; | ||
import org.eclipse.lsp4j.CodeAction; | ||
import org.eclipse.lsp4j.Position; | ||
import org.eclipse.lsp4j.Range; | ||
import org.eclipse.lsp4j.TextEdit; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
|
||
@ExtendWith(NoMavenCentralExtension.class) | ||
public class MavenCodeActionPropertyRefactoringTest { | ||
private XMLLanguageService xmlLanguageService = new XMLLanguageService(); | ||
private SharedSettings sharedSettings = new SharedSettings(); | ||
|
||
@Test | ||
public void testCodeActionsInlineSinglePropertyUse() throws Exception { | ||
DOMDocument xmlDocument = createDOMDocument("/property-refactoring/pom-with-property.xml", xmlLanguageService); | ||
String text = xmlDocument.getText(); | ||
TextDocument document = xmlDocument.getTextDocument(); | ||
|
||
// ${myPropertyGroupId} | ||
String propertyUse = "${myPropertyGroupId}"; | ||
int propertyUseStart = text.indexOf(propertyUse); | ||
int propertyUseEnd = propertyUseStart + propertyUse.length(); | ||
|
||
Position positionStart = document.positionAt(propertyUseStart); | ||
Position positionEnd = document.positionAt(propertyUseEnd); | ||
|
||
CodeAction expectedCodeAction = ca(null, | ||
teOp(xmlDocument.getDocumentURI(), positionStart.getLine(), positionStart.getCharacter(), | ||
positionEnd.getLine(), positionEnd.getCharacter(), "my-property-group-value")); | ||
// Test for expected code actions returned | ||
testCodeActionsFor(xmlDocument.getText(), null, new Range(positionStart, positionEnd), | ||
(String) null, xmlDocument.getDocumentURI(), sharedSettings, xmlLanguageService, -1, | ||
expectedCodeAction); | ||
} | ||
|
||
@Test | ||
public void testCodeActionsInlineMultiplePropertyUses() throws Exception { | ||
DOMDocument xmlDocument = createDOMDocument("/property-refactoring/pom-with-multiple-property-uses.xml", xmlLanguageService); | ||
String text = xmlDocument.getText(); | ||
TextDocument document = xmlDocument.getTextDocument(); | ||
|
||
// ${myPropertyGroupId} | ||
String propertyUse = "${myPropertyGroupId}"; | ||
List<Range> ranges = new ArrayList<>(); | ||
|
||
int index = 0; | ||
for (index = text.indexOf(propertyUse); index != -1; index = text.indexOf(propertyUse, index + propertyUse.length())) { | ||
try { | ||
int propertyUseStart = index; | ||
int propertyUseEnd = propertyUseStart + propertyUse.length(); | ||
|
||
Position positionStart = document.positionAt(propertyUseStart); | ||
Position positionEnd = document.positionAt(propertyUseEnd); | ||
ranges.add(new Range(positionStart, positionEnd)); | ||
} catch (BadLocationException e) { | ||
fail("Cannot find all property uses in test data", e); | ||
} | ||
} | ||
assertTrue(ranges.size() > 1, "There should be more than one prperty use"); | ||
|
||
// Inline for the selected single property use | ||
CodeAction expectedCodeAction_1 = ca(null, | ||
teOp(xmlDocument.getDocumentURI(), | ||
ranges.get(0).getStart().getLine(), ranges.get(0).getStart().getCharacter(), | ||
ranges.get(0).getEnd().getLine(), ranges.get(0).getEnd().getCharacter(), | ||
"my-property-group-value")); | ||
|
||
// Inline for all the property uses in pom.xml | ||
List<TextEdit> textEdits = ranges.stream().map(range -> | ||
te(range.getStart().getLine(), range.getStart().getCharacter(), | ||
range.getEnd().getLine(), range.getEnd().getCharacter(), | ||
"my-property-group-value")).collect(Collectors.toList()) ; | ||
assertTrue(textEdits.size() == ranges.size(), "The TextEdits size should be equl to the size of ranges"); | ||
CodeAction expectedCodeAction_2 = ca(null, teOp(xmlDocument.getDocumentURI(), textEdits.toArray(new TextEdit[textEdits.size()]))); | ||
|
||
// Test for expected code actions returned | ||
testCodeActionsFor(xmlDocument.getText(), null, ranges.get(0), | ||
(String) null, xmlDocument.getDocumentURI(), sharedSettings, xmlLanguageService, -1, | ||
expectedCodeAction_1, expectedCodeAction_2); | ||
} | ||
} |
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.