Skip to content

Commit 20e691f

Browse files
committed
Implemented renaming functionality for usage message strings
1 parent be44581 commit 20e691f

10 files changed

Lines changed: 286 additions & 8 deletions

File tree

.idea/dictionaries/patrick.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/Mathematica_Plugin.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/META-INF/plugin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
<inspectionToolProvider
166166
implementation="de.halirutan.mathematica.codeinsight.inspections.MathematicaInspectionProvider"/>
167167

168+
<psi.referenceContributor language="Mathematica" implementation="de.halirutan.mathematica.parsing.psi.MathematicaReferenceContributor"/>
168169
</extensions>
169170

170171
<actions>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2016 Patrick Scheibe
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to deal
5+
* in the Software without restriction, including without limitation the rights
6+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
* copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
* THE SOFTWARE.
20+
*/
21+
22+
package de.halirutan.mathematica.parsing.psi;
23+
24+
import com.intellij.patterns.PlatformPatterns;
25+
import com.intellij.psi.PsiReferenceContributor;
26+
import com.intellij.psi.PsiReferenceRegistrar;
27+
import de.halirutan.mathematica.parsing.psi.api.string.MString;
28+
import org.jetbrains.annotations.NotNull;
29+
30+
/**
31+
* @author patrick (29.11.16).
32+
*/
33+
public class MathematicaReferenceContributor extends PsiReferenceContributor {
34+
35+
/**
36+
* Gives registrar the chance to do registrar
37+
* @param registrar will work as registrar
38+
*/
39+
@Override
40+
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
41+
registrar.registerReferenceProvider(PlatformPatterns.psiElement(MString.class),
42+
new MathematicaStringReferenceProvider());
43+
}
44+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) 2016 Patrick Scheibe
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to deal
5+
* in the Software without restriction, including without limitation the rights
6+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
* copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
* THE SOFTWARE.
20+
*/
21+
22+
package de.halirutan.mathematica.parsing.psi;
23+
24+
import com.intellij.openapi.util.TextRange;
25+
import com.intellij.psi.PsiElement;
26+
import com.intellij.psi.PsiReference;
27+
import com.intellij.psi.PsiReferenceProvider;
28+
import com.intellij.util.ProcessingContext;
29+
import de.halirutan.mathematica.parsing.psi.api.Expression;
30+
import de.halirutan.mathematica.parsing.psi.api.MessageName;
31+
import de.halirutan.mathematica.parsing.psi.api.StringifiedSymbol;
32+
import de.halirutan.mathematica.parsing.psi.api.Symbol;
33+
import de.halirutan.mathematica.parsing.psi.api.assignment.Set;
34+
import de.halirutan.mathematica.parsing.psi.api.string.MString;
35+
import de.halirutan.mathematica.parsing.psi.api.string.StringJoin;
36+
import de.halirutan.mathematica.parsing.psi.impl.StringUsageReference;
37+
import org.jetbrains.annotations.NotNull;
38+
39+
import java.util.ArrayList;
40+
import java.util.regex.Matcher;
41+
import java.util.regex.Pattern;
42+
43+
/**
44+
* @author patrick (29.11.16).
45+
*/
46+
class MathematicaStringReferenceProvider extends PsiReferenceProvider {
47+
48+
49+
@NotNull
50+
@Override
51+
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
52+
if (!(element instanceof MString)) {
53+
return new PsiReference[0];
54+
}
55+
ArrayList<PsiReference> result = new ArrayList<PsiReference>();
56+
57+
PsiElement setElement = element.getParent();
58+
59+
// Specifically for Szabolcs who likes to <> usage messages
60+
while (setElement instanceof StringJoin) {
61+
setElement = setElement.getParent();
62+
}
63+
64+
if (setElement instanceof Set) {
65+
final PsiElement messageElement = setElement.getFirstChild();
66+
if (messageElement instanceof MessageName) {
67+
final StringifiedSymbol tag = ((MessageName) messageElement).getTag();
68+
if ("usage".equals(tag != null ? tag.getText() : null)) {
69+
final Expression symbol = ((MessageName) messageElement).getSymbol();
70+
if (symbol instanceof Symbol) {
71+
String usageText = element.getText();
72+
final String symbolName = ((Symbol) symbol).getSymbolName();
73+
Pattern symbolNamePattern = StringUsageReference.getSymbolPattern(symbolName);
74+
final Matcher matcher = symbolNamePattern.matcher(usageText);
75+
while (matcher.find()) {
76+
final int start = matcher.start(2);
77+
final int end = matcher.end(2);
78+
result.add(
79+
new StringUsageReference((MString) element, TextRange.create(start, end), symbolName, (Symbol) symbol)
80+
);
81+
}
82+
}
83+
84+
85+
}
86+
}
87+
}
88+
return result.toArray(PsiReference.EMPTY_ARRAY);
89+
}
90+
91+
92+
}

src/de/halirutan/mathematica/parsing/psi/api/string/MString.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121

2222
package de.halirutan.mathematica.parsing.psi.api.string;
2323

24+
import com.intellij.psi.PsiNamedElement;
25+
2426
/**
2527
* @author patrick (4/14/13)
2628
*/
27-
public interface MString {
29+
public interface MString extends PsiNamedElement{
2830
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) 2016 Patrick Scheibe
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to deal
5+
* in the Software without restriction, including without limitation the rights
6+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
* copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
* THE SOFTWARE.
20+
*/
21+
22+
package de.halirutan.mathematica.parsing.psi.impl;
23+
24+
import com.intellij.openapi.util.TextRange;
25+
import com.intellij.psi.PsiElement;
26+
import com.intellij.psi.PsiReferenceBase;
27+
import com.intellij.util.IncorrectOperationException;
28+
import de.halirutan.mathematica.parsing.psi.api.Symbol;
29+
import de.halirutan.mathematica.parsing.psi.api.string.MString;
30+
import org.jetbrains.annotations.NotNull;
31+
import org.jetbrains.annotations.Nullable;
32+
33+
import java.util.regex.Matcher;
34+
import java.util.regex.Pattern;
35+
36+
/**
37+
* @author patrick (29.11.16).
38+
*/
39+
public class StringUsageReference extends PsiReferenceBase<MString> {
40+
41+
private MString myElement1;
42+
private final String mySymbolNameInside;
43+
private final Symbol myTarget;
44+
45+
public StringUsageReference(MString element, TextRange rangeInElement, final String symbolName, Symbol target) {
46+
super(element, rangeInElement, true);
47+
mySymbolNameInside = symbolName;
48+
myElement1 = element;
49+
myTarget = target;
50+
}
51+
52+
@Nullable
53+
@Override
54+
public PsiElement resolve() {
55+
return myTarget;
56+
}
57+
58+
@NotNull
59+
@Override
60+
public Object[] getVariants() {
61+
return new Object[0];
62+
}
63+
64+
@Override
65+
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
66+
final String text = myElement1.getText();
67+
Matcher matcher = getSymbolPattern(mySymbolNameInside).matcher(text);
68+
String newContent = matcher.replaceAll("$1" + newElementName + "$3");
69+
myElement1 = (MString) myElement1.setName(newContent);
70+
return myElement1;
71+
}
72+
73+
@Override
74+
public boolean isReferenceTo(PsiElement element) {
75+
return element.equals(myTarget);
76+
}
77+
78+
/**
79+
* Returns the pattern that matches the usage of a function-name or symbol inside a Mathematica usage string.
80+
* Example: "ref[x] works like a charm. The head of ref[x] is ref."
81+
* We match ref if it is at the beginning of a string or preceded by a whitespace. ref needs to be followed
82+
* by either [, a whitespace, a literal dot, or the end of the string.
83+
* @param symbolName the symbol name you want to match
84+
* @return a compiled pattern to match symbolName inside a usage string
85+
*/
86+
public static Pattern getSymbolPattern(String symbolName) {
87+
return Pattern.compile("(\"|\\s)(" + symbolName + ")(\\[|\\.|\\s|\")");
88+
}
89+
90+
91+
}

src/de/halirutan/mathematica/parsing/psi/impl/SymbolPsiReference.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@
3232
import de.halirutan.mathematica.parsing.psi.api.Symbol;
3333
import de.halirutan.mathematica.parsing.psi.util.GlobalDefinitionResolveProcessor;
3434
import de.halirutan.mathematica.parsing.psi.util.LocalDefinitionResolveProcessor;
35-
import de.halirutan.mathematica.parsing.psi.util.LocalizationConstruct;
35+
import de.halirutan.mathematica.parsing.psi.util.LocalizationConstruct.ConstructType;
3636
import org.jetbrains.annotations.NotNull;
3737
import org.jetbrains.annotations.Nullable;
3838

3939
import java.util.Set;
4040

4141
/**
42-
* Povides functionality to resolve where a certain symbol is defined in code. For this, the SymbolPsiReference class
42+
* Provides functionality to resolve where a certain symbol is defined in code. For this, the SymbolPsiReference class
4343
* uses several processors which scan the local scope and global file scope. Note that GlobalDefinitionResolveProcessor
4444
* does not scan the whole file because this would be too slow. Instead, it expects that global symbol definitions are
4545
* done at file-scope. The class uses caching to speed up the resolve process. Once a definition for a symbol is found,
@@ -52,10 +52,12 @@ public class SymbolPsiReference extends CachingReference implements PsiReference
5252
private static final Set<String> NAMES = SymbolInformationProvider.getSymbolNames().keySet();
5353
private final Symbol myVariable;
5454

55-
public SymbolPsiReference(Symbol element) {
55+
SymbolPsiReference(Symbol element) {
5656
myVariable = element;
5757
}
5858

59+
60+
5961
@Nullable
6062
@Override
6163
public PsiElement resolveInner() {
@@ -64,7 +66,7 @@ public PsiElement resolveInner() {
6466

6567
if (myVariable.cachedResolve()) {
6668
if (myVariable.getSymbolName().equals(myVariable.getResolveElement().getSymbolName()) ||
67-
myVariable.getLocalizationConstruct().equals(LocalizationConstruct.ConstructType.ANONYMOUSFUNCTION)) {
69+
myVariable.getLocalizationConstruct().equals(ConstructType.ANONYMOUSFUNCTION)) {
6870
return myVariable.getResolveElement();
6971
} else {
7072
myVariable.subtreeChanged();
@@ -86,7 +88,7 @@ public PsiElement resolveInner() {
8688

8789
final PsiElement globalDefinition = globalProcessor.getMyReferringSymbol();
8890
if (globalDefinition instanceof Symbol) {
89-
myVariable.setReferringElement((Symbol) globalDefinition, LocalizationConstruct.ConstructType.NULL, null);
91+
myVariable.setReferringElement((Symbol) globalDefinition, ConstructType.NULL, null);
9092
return globalDefinition;
9193
}
9294
return null;

src/de/halirutan/mathematica/parsing/psi/impl/string/StringImpl.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,59 @@
2222
package de.halirutan.mathematica.parsing.psi.impl.string;
2323

2424
import com.intellij.lang.ASTNode;
25+
import com.intellij.psi.PsiElement;
26+
import com.intellij.psi.PsiFileFactory;
27+
import com.intellij.psi.PsiReference;
28+
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
29+
import com.intellij.util.IncorrectOperationException;
30+
import de.halirutan.mathematica.filetypes.MathematicaFileType;
31+
import de.halirutan.mathematica.parsing.MathematicaElementTypes;
2532
import de.halirutan.mathematica.parsing.psi.api.string.MString;
33+
import de.halirutan.mathematica.parsing.psi.impl.MathematicaPsiFileImpl;
2634
import de.halirutan.mathematica.parsing.psi.impl.OperatorNameProviderImpl;
35+
import org.jetbrains.annotations.NonNls;
2736
import org.jetbrains.annotations.NotNull;
2837

2938
/**
3039
*
3140
*/
3241
public class StringImpl extends OperatorNameProviderImpl implements MString {
42+
43+
private PsiReference[] myReferences = null;
44+
3345
public StringImpl(@NotNull ASTNode node) {
3446
super(node);
3547
}
48+
49+
@Override
50+
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
51+
ASTNode literalNode = getNode().findChildByType(MathematicaElementTypes.STRING_LITERAL);
52+
final PsiFileFactory fileFactory = PsiFileFactory.getInstance(getProject());
53+
final MathematicaPsiFileImpl file = (MathematicaPsiFileImpl) fileFactory.createFileFromText("dummy.m", MathematicaFileType.INSTANCE, name);
54+
ASTNode newElm = file.getFirstChild().getNode().findChildByType(MathematicaElementTypes.STRING_LITERAL);
55+
if (literalNode != null && newElm != null) {
56+
getNode().replaceChild(literalNode, newElm);
57+
}
58+
return this;
59+
}
60+
61+
@Override
62+
public PsiReference getReference() {
63+
final PsiReference[] references = getReferences();
64+
return references.length > 0 ? references[0] : null;
65+
}
66+
67+
@Override
68+
public void subtreeChanged() {
69+
myReferences = null;
70+
}
71+
72+
@NotNull
73+
@Override
74+
public PsiReference[] getReferences() {
75+
if (myReferences == null) {
76+
return myReferences = ReferenceProvidersRegistry.getReferencesFromProviders(this);
77+
}
78+
return myReferences;
79+
}
3680
}

src/de/halirutan/mathematica/parsing/psi/impl/string/StringJoinImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@
2222
package de.halirutan.mathematica.parsing.psi.impl.string;
2323

2424
import com.intellij.lang.ASTNode;
25+
import de.halirutan.mathematica.parsing.psi.api.string.StringJoin;
2526
import de.halirutan.mathematica.parsing.psi.impl.OperatorNameProviderImpl;
2627
import org.jetbrains.annotations.NotNull;
2728

2829
/**
2930
* @author patrick (4/14/13)
3031
*/
31-
public class StringJoinImpl extends OperatorNameProviderImpl {
32+
public class StringJoinImpl extends OperatorNameProviderImpl implements StringJoin{
3233
public StringJoinImpl(@NotNull ASTNode node) {
3334
super(node);
3435
}

0 commit comments

Comments
 (0)