Merge remote branch 'origin/master'

This commit is contained in:
irengrig
2012-07-27 19:19:54 +04:00
39 changed files with 3925 additions and 247 deletions
@@ -0,0 +1,14 @@
package com.intellij.find.findUsages;
import com.intellij.psi.PsiElement;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
/**
* User: Andrey.Vokin
* Date: 7/26/12
*/
public interface CustomJavaFindUsagesHandler {
public void processElementUsages(@NotNull final PsiElement element, @NotNull final Processor<UsageInfo> processor, @NotNull final FindUsagesOptions options);
}
@@ -0,0 +1,13 @@
package com.intellij.find.findUsages;
import com.intellij.openapi.extensions.ExtensionPointName;
/**
* User: Andrey.Vokin
* Date: 7/26/12
*/
public abstract class CustomJavaFindUsagesHandlerProvider {
public static final ExtensionPointName<CustomJavaFindUsagesHandlerProvider> EP_NAME = ExtensionPointName.create("com.intellij.findUsages.java.customJavaFindUsagesHandlerProvider");
public abstract CustomJavaFindUsagesHandler getHandler();
}
@@ -384,6 +384,11 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{
// todo add to fastTrack
processUsagesInText(element, processor, (GlobalSearchScope)options.searchScope);
}
final CustomJavaFindUsagesHandlerProvider[] customJavaFindUsagesHandlerProviders = Extensions.getExtensions(CustomJavaFindUsagesHandlerProvider.EP_NAME);
for (CustomJavaFindUsagesHandlerProvider provider : customJavaFindUsagesHandlerProviders) {
provider.getHandler().processElementUsages(element, processor, options);
}
}
private static void addAliasingUsages(PomTarget pomTarget, final Processor<UsageInfo> processor, final FindUsagesOptions options) {
@@ -1,6 +1,8 @@
import java.io.File;
public class Foo {
void foo(boolean flag) {
if ('\\' == File.separach<caret>)
if ('\\' == File.separaCh<caret>)
System.out.println();
}
}
@@ -45,10 +45,7 @@ public abstract class TemplateLanguageErrorFilter extends HighlightErrorFilter {
if (!(viewProvider.getClass() == myTemplateFileViewProviderClass)) {
return true;
}
final Language parentLanguage = element.getParent().getLanguage();
final Language javaScript = Language.findLanguageByID("JavaScript");
final Language css = Language.findLanguageByID("CSS");
if (javaScript != null && parentLanguage.is(javaScript) || css != null && parentLanguage.is(css)) {
if (isKnownSubLanguage(element.getParent().getLanguage())) {
final PsiElement next = viewProvider.findElementAt(element.getTextOffset() + 1, viewProvider.getBaseLanguage());
if (next != null && myTemplateExpressionStartTokens.contains(next.getNode().getElementType())) {
return false;
@@ -56,4 +53,10 @@ public abstract class TemplateLanguageErrorFilter extends HighlightErrorFilter {
}
return true;
}
protected boolean isKnownSubLanguage(final @NotNull Language language) {
final Language javaScript = Language.findLanguageByID("JavaScript");
final Language css = Language.findLanguageByID("CSS");
return javaScript != null && language.is(javaScript) || css != null && language.is(css);
}
}
@@ -16,6 +16,7 @@
package com.intellij.execution.runners;
import com.intellij.execution.console.LanguageConsoleImpl;
import com.intellij.execution.process.BaseOSProcessHandler;
import com.intellij.execution.process.ConsoleHistoryModel;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.openapi.command.impl.UndoManagerImpl;
@@ -28,6 +29,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* @author traff
@@ -91,14 +93,14 @@ public class ConsoleExecuteActionHandler {
}
public void sendText(String line) {
//final Charset charset = myProcessHandler.getCharset();
final Charset charset = myProcessHandler instanceof BaseOSProcessHandler ?
((BaseOSProcessHandler)myProcessHandler).getCharset() : null;
final ProcessHandler handler = getProcessHandler();
assert handler != null : "process handler is null";
final OutputStream outputStream = handler.getProcessInput();
assert outputStream != null : "output stream is null";
try {
//byte[] bytes = (line + "\n").getBytes(charset.name());
byte[] bytes = line.getBytes();
byte[] bytes = charset != null ? (line + "\n").getBytes(charset) : line.getBytes();
outputStream.write(bytes);
outputStream.flush();
}
@@ -6,6 +6,7 @@ import com.intellij.lang.PsiParser;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author oleg
@@ -38,7 +39,7 @@ public abstract class IndentationParser implements PsiParser {
final PsiBuilder.Marker documentMarker = builder.mark();
final Stack<BlockInfo> stack = new Stack<BlockInfo>();
stack.push(new BlockInfo(0, builder.mark()));
stack.push(new BlockInfo(0, builder.mark(), builder.getTokenType()));
PsiBuilder.Marker startLineMarker = null;
int currentIndent = 0;
@@ -71,17 +72,19 @@ public abstract class IndentationParser implements PsiParser {
}
// Close indentation blocks
while (!stack.isEmpty() && currentIndent < stack.peek().getIndent()) {
closeBlock(builder, stack.pop().getMarker());
final BlockInfo blockInfo = stack.pop();
closeBlock(builder, blockInfo.getMarker(), blockInfo.getStartTokenType());
}
if (!stack.isEmpty()) {
final BlockInfo blockInfo = stack.peek();
if (currentIndent >= blockInfo.getIndent()) {
if (currentIndent == blockInfo.getIndent()) {
closeBlock(builder, stack.pop().getMarker());
final BlockInfo info = stack.pop();
closeBlock(builder, info.getMarker(), info.getStartTokenType());
}
passEOLsAndIndents(builder);
stack.push(new BlockInfo(currentIndent, builder.mark()));
stack.push(new BlockInfo(currentIndent, builder.mark(), type));
}
}
eolSeen = false;
@@ -97,7 +100,8 @@ public abstract class IndentationParser implements PsiParser {
startLineMarker.drop();
}
while (!stack.isEmpty()){
closeBlock(builder, stack.pop().getMarker());
final BlockInfo blockInfo = stack.pop();
closeBlock(builder, blockInfo.getMarker(), blockInfo.getStartTokenType());
}
documentMarker.done(myDocumentType);
@@ -105,7 +109,10 @@ public abstract class IndentationParser implements PsiParser {
return builder.getTreeBuilt();
}
protected void closeBlock(@NotNull PsiBuilder builder, @NotNull PsiBuilder.Marker marker) {
protected void closeBlock(final @NotNull PsiBuilder builder,
final @NotNull PsiBuilder.Marker marker,
final @Nullable IElementType startTokenType)
{
marker.done(myBlockElementType);
}
@@ -125,10 +132,13 @@ public abstract class IndentationParser implements PsiParser {
private final int myIndent;
@NotNull
private final PsiBuilder.Marker myMarker;
@Nullable
private final IElementType myStartTokenType;
private BlockInfo(final int indent, final @NotNull PsiBuilder.Marker marker) {
this.myIndent = indent;
this.myMarker = marker;
private BlockInfo(final int indent, final @NotNull PsiBuilder.Marker marker, final @Nullable IElementType type) {
myIndent = indent;
myMarker = marker;
myStartTokenType = type;
}
public int getIndent() {
@@ -139,5 +149,10 @@ public abstract class IndentationParser implements PsiParser {
public PsiBuilder.Marker getMarker() {
return myMarker;
}
@Nullable
public IElementType getStartTokenType() {
return myStartTokenType;
}
}
}
@@ -35,7 +35,6 @@ public class ColoredProcessHandler extends OSProcessHandler {
private static final String TEXT_ATTRS_PATTERN = "m" + TEXT_ATTRS_PREFIX_CH + "\\[";
private Key myCurrentColor;
@Nullable private final Charset myCharset;
public static TextAttributes getByKey(final TextAttributesKey key){
return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(key);
@@ -56,17 +55,7 @@ public class ColoredProcessHandler extends OSProcessHandler {
public ColoredProcessHandler(final Process process,
final String commandLine,
@Nullable final Charset charset) {
super(process, commandLine);
myCharset = charset;
}
@Override
public Charset getCharset() {
if (myCharset != null) {
return myCharset;
}
// Charset wasn't specified - use default one
return super.getCharset();
super(process, commandLine, charset);
}
public final void notifyTextAvailable(final String text, final Key outputType) {
@@ -35,10 +35,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -101,6 +98,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
private boolean myForcePressed = false;
private PropertyChangeListener myButtonSynchronizer;
private boolean myMouseInside = false;
private JBPopup myPopup;
public ComboBoxButton(Presentation presentation) {
myPresentation = presentation;
@@ -143,7 +141,56 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
myMouseInside = false;
repaint();
}
@Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
e.consume();
doClick();
}
}
@Override
public void mouseReleased(MouseEvent e) {
dispatchEventToPopup(e);
}
});
addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(MouseEvent e) {
mouseMoved(new MouseEvent(e.getComponent(),
MouseEvent.MOUSE_MOVED,
e.getWhen(),
e.getModifiers(),
e.getX(),
e.getY(),
e.getClickCount(),
e.isPopupTrigger(),
e.getButton()));
}
@Override
public void mouseMoved(MouseEvent e) {
dispatchEventToPopup(e);
}
});
}
// Event forwarding. We need it if user does press-and-drag gesture for opening popup and choosing item there.
// It works in JComboBox, here we provide the same behavior
private void dispatchEventToPopup(MouseEvent e) {
if (myPopup != null && myPopup.isVisible()) {
JComponent content = myPopup.getContent();
Rectangle rectangle = content.getBounds();
Point location = rectangle.getLocation();
SwingUtilities.convertPointToScreen(location, content);
Point eventPoint = e.getLocationOnScreen();
rectangle.setLocation(location);
if (rectangle.contains(eventPoint)) {
MouseEvent event = SwingUtilities.convertMouseEvent(e.getComponent(), e, myPopup.getContent());
Component component = SwingUtilities.getDeepestComponentAt(content, event.getX(), event.getY());
component.dispatchEvent(event);
}
}
}
public void showPopup() {
@@ -156,15 +203,15 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
UIUtil.invokeLaterIfNeeded(new Runnable() {
public void run() {
myForcePressed = false;
myPopup = null;
}
});
repaint();
}
};
JBPopup popup = createPopup(onDispose);
popup.show(new RelativePoint(this, new Point(0, this.getHeight() - 1)));
myPopup = createPopup(onDispose);
myPopup.show(new RelativePoint(this, new Point(0, this.getHeight() - 1)));
}
@Nullable
@@ -372,10 +419,6 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
g.setPaintMode();
}
private boolean isGlowSupported() {
return false;
}
protected void updateButtonSize() {
invalidate();
repaint();
@@ -96,7 +96,7 @@ public class Tree extends JTree implements ComponentWithEmptyText, ComponentWith
public void setUI(final TreeUI ui) {
TreeUI actualUI = ui;
if (!isCustomUI()) {
if (!(ui instanceof WideSelectionTreeUI) && isWideSelection()) {
if (!(ui instanceof WideSelectionTreeUI) && isWideSelection() && !UIUtil.isUnderGTKLookAndFeel()) {
actualUI = new WideSelectionTreeUI(isWideSelection(), !SystemInfo.isMac);
}
}
@@ -487,7 +487,9 @@ public final class LafManagerImpl extends LafManager implements ApplicationCompo
uiDefaults.put("TextField.selectionBackground", bg);
uiDefaults.put("RadioButtonMenuItem.selectionBackground", bg);
uiDefaults.put("CheckBoxMenuItem.selectionBackground", bg);
} if (UIUtil.isUnderMetalLookAndFeel()) {
}
if (UIUtil.isUnderMetalLookAndFeel()) {
uiDefaults.put("Tree.hash", new ColorUIResource(117, 117, 117));
}
}
@@ -226,6 +226,12 @@ public class NameUtilTest extends UsefulTestCase {
assertMatches("text*:sh", "textField:shouldChangeCharactersInRange:replacementString:");
}
public void testFileStructure() {
assertMatches("hint", "height:int");
assertDoesntMatch("Hint", "Height:int");
assertMatches("getColor", "getBackground():Color");
}
public void testMiddleMatchingMinimumTwoConsecutiveLettersInWordMiddle() {
assertMatches("*fo", "reformat");
assertMatches("*f", "reFormat");
@@ -254,7 +260,7 @@ public class NameUtilTest extends UsefulTestCase {
assertMatches("*Git", "BlaGitBla");
assertFalse(firstLetterMatcher("*Git").matches("BlagitBla"));
assertMatches("*git", "BlagitBla");
assertMatches("*Git*", "AtpGenerationItem");
assertDoesntMatch("*Git*", "AtpGenerationItem");
assertMatches("Collec*Util*", "CollectionUtils");
assertMatches("Collec*Util*", "CollectionUtilsTest");
assertTrue(caseInsensitiveMatcher("*us").matches("usage"));
@@ -307,7 +313,7 @@ public class NameUtilTest extends UsefulTestCase {
assertMatches("foo", "fxOo");
assertMatches("foo", "fXOo");
assertMatches("fOo", "foo");
assertMatches("fOo", "FaOaOaXXXX");
assertDoesntMatch("fOo", "FaOaOaXXXX");
assertMatches("ncdfoe", "NoClassDefFoundException");
assertMatches("fob", "FOO_BAR");
assertMatches("fo_b", "FOO_BAR");
@@ -330,7 +336,7 @@ public class NameUtilTest extends UsefulTestCase {
public void testFinalSpace() {
assertMatches("GrDebT ", "GroovyDebuggerTest");
assertMatches("grdebT ", "GroovyDebuggerTest");
assertDoesntMatch("grdebT ", "GroovyDebuggerTest");
assertDoesntMatch("grdebt ", "GroovyDebuggerTest");
assertMatches("Foo ", "Foo");
assertDoesntMatch("Foo ", "FooBar");
@@ -441,6 +447,7 @@ public class NameUtilTest extends UsefulTestCase {
assertPreference("*ap", "add_profile", "application", NameUtil.MatchingCaseSensitivity.NONE);
assertPreference("*les", "configureByFiles", "getLookupElementStrings", NameUtil.MatchingCaseSensitivity.FIRST_LETTER);
assertPreference("*les", "configureByFiles", "getLookupElementStrings", NameUtil.MatchingCaseSensitivity.NONE);
assertPreference("*ea", "LEADING", "NORTH_EAST", NameUtil.MatchingCaseSensitivity.NONE);
}
public void testPreferEarlyMatching() {
@@ -1,9 +1,7 @@
package com.intellij.testFramework;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NonNls;
@@ -14,12 +12,6 @@ import org.jetbrains.annotations.NonNls;
public class CodeInsightTestCase extends LightPlatformCodeInsightFixtureTestCase {
@NonNls protected static final String CARET_STR = "<caret>";
protected CodeInsightTestFixture myFixture;
protected Project getProject() {
return myFixture.getProject();
}
protected int findOffsetBySignature(String signature) {
return findOffsetBySignature(signature, myFixture.getFile());
}
@@ -33,10 +33,19 @@ import static com.intellij.psi.codeStyle.NameUtil.MatchingCaseSensitivity.*;
public class MinusculeMatcher implements Matcher {
private final char[] myPattern;
private final NameUtil.MatchingCaseSensitivity myOptions;
private final boolean myHasHumps;
public MinusculeMatcher(String pattern, NameUtil.MatchingCaseSensitivity options) {
myOptions = options;
myPattern = StringUtil.trimEnd(pattern, "* ").toCharArray();
for (int i = 0; i < myPattern.length; i++) {
char c = myPattern[i];
if (Character.isUpperCase(c)) {
myHasHumps = true;
return;
}
}
myHasHumps = false;
}
private static FList<TextRange> prependRange(FList<TextRange> ranges, int from, int length) {
@@ -92,9 +101,9 @@ public class MinusculeMatcher implements Matcher {
int startIndex = first.getStartOffset();
boolean prefixMatching = isStartMatch(name, startIndex);
boolean middleWordStart = !prefixMatching && NameUtil.isWordStart(name, first.getStartOffset());
boolean middleWordStart = !prefixMatching && startIndex > 0 && NameUtil.isWordStart(name, startIndex) && !NameUtil.isWordStart(name, startIndex - 1);
return -fragmentCount + matchingCase * 20 + commonStart * 30 - startIndex + (prefixMatching ? 2 : middleWordStart ? 1 : 0) * 100 - integral;
return -fragmentCount + matchingCase * 20 + commonStart * 30 - startIndex + (prefixMatching ? 2 : middleWordStart ? 1 : 0) * 1000 - integral;
}
public boolean isStartMatch(String name) {
@@ -159,7 +168,7 @@ public class MinusculeMatcher implements Matcher {
char p = myPattern[patternIndex];
while (true) {
nameIndex = space ? indexOfWordStart(name, p, nameIndex) : StringUtil.indexOfIgnoreCase(name, p, nameIndex + 1);
nameIndex = space ? indexOfWordStart(name, patternIndex, nameIndex) : StringUtil.indexOfIgnoreCase(name, p, nameIndex + 1);
if (nameIndex < 0) {
return null;
}
@@ -188,7 +197,7 @@ public class MinusculeMatcher implements Matcher {
return null;
}
if (myPattern[patternIndex + i] != name.charAt(nameIndex + i)) {
int nextWordStart = indexOfWordStart(name, myPattern[patternIndex + i], nameIndex + i);
int nextWordStart = indexOfWordStart(name, patternIndex + i, nameIndex + i);
FList<TextRange> ranges = matchWildcards(name, patternIndex + i, nextWordStart);
if (ranges != null) {
return prependRange(ranges, nameIndex, i);
@@ -202,7 +211,7 @@ public class MinusculeMatcher implements Matcher {
return i >= minFragment ? FList.<TextRange>emptyList().prepend(TextRange.from(nameIndex, i)) : null;
}
while (i >= minFragment) {
int nextWordStart = isWildcard(patternIndex + i) ? nameIndex + i : indexOfWordStart(name, myPattern[patternIndex + i], nameIndex + i);
int nextWordStart = isWildcard(patternIndex + i) ? nameIndex + i : indexOfWordStart(name, patternIndex + i, nameIndex + i);
FList<TextRange> ranges = matchWildcards(name, patternIndex + i, nextWordStart);
if (ranges != null) {
return prependRange(ranges, nameIndex, i);
@@ -225,11 +234,13 @@ public class MinusculeMatcher implements Matcher {
return myPattern[patternIndex] == c;
}
private static int indexOfWordStart(String name, char p, int startFrom) {
private int indexOfWordStart(String name, int patternIndex, int startFrom) {
char p = myPattern[patternIndex];
if (p == '.' || NameUtil.isWordSeparator(p)) {
return StringUtil.indexOfIgnoreCase(name, p, startFrom + 1);
}
if (startFrom >= name.length()) {
if (startFrom >= name.length() ||
myHasHumps && Character.isLowerCase(p) && !(patternIndex > 0 && NameUtil.isWordSeparator(myPattern[patternIndex - 1]))) {
return -1;
}
int nextWordStart = startFrom;
@@ -947,7 +947,7 @@ public class UIUtil {
}
public static boolean isFullRowSelectionLAF() {
return isUnderQuaquaLookAndFeel();
return isUnderGTKLookAndFeel() || isUnderQuaquaLookAndFeel();
}
public static boolean isUnderNativeMacLookAndFeel() {
@@ -40,17 +40,19 @@ public class WideSelectionTreeUI extends BasicTreeUI {
@NonNls public static final String SOURCE_LIST_CLIENT_PROPERTY = "mac.ui.source.list";
@NonNls public static final String STRIPED_CLIENT_PROPERTY = "mac.ui.striped";
private static final Icon TREE_COLLAPSED_ICON = UIUtil.getTreeCollapsedIcon();
private static final Icon TREE_EXPANDED_ICON = UIUtil.getTreeExpandedIcon();
private static final Icon TREE_SELECTED_COLLAPSED_ICON = UIUtil.isUnderAquaBasedLookAndFeel() || UIUtil.isUnderNimbusLookAndFeel()
? AllIcons.Mac.Tree_white_right_arrow : TREE_COLLAPSED_ICON;
private static final Icon TREE_SELECTED_EXPANDED_ICON = UIUtil.isUnderAquaBasedLookAndFeel() || UIUtil.isUnderNimbusLookAndFeel()
? AllIcons.Mac.Tree_white_down_arrow : TREE_EXPANDED_ICON;
private static Icon getTreeSelectedCollapsedIcon() {
return UIUtil.isUnderAquaBasedLookAndFeel() || UIUtil.isUnderNimbusLookAndFeel() || UIUtil.isUnderGTKLookAndFeel()
? AllIcons.Mac.Tree_white_right_arrow : UIUtil.getTreeCollapsedIcon();
}
private static final Border LIST_BACKGROUND_PAINTER = (Border)UIManager.get("List.sourceListBackgroundPainter");
private static final Border LIST_SELECTION_BACKGROUND_PAINTER = (Border)UIManager.get("List.sourceListSelectionBackgroundPainter");
private static final Border LIST_FOCUSED_SELECTION_BACKGROUND_PAINTER =
(Border)UIManager.get("List.sourceListFocusedSelectionBackgroundPainter");
private static Icon getTreeSelectedExpandedIcon() {
return UIUtil.isUnderAquaBasedLookAndFeel() || UIUtil.isUnderNimbusLookAndFeel() || UIUtil.isUnderGTKLookAndFeel()
? AllIcons.Mac.Tree_white_down_arrow : UIUtil.getTreeExpandedIcon();
}
private static final Border LIST_BACKGROUND_PAINTER = UIManager.getBorder("List.sourceListBackgroundPainter");
private static final Border LIST_SELECTION_BACKGROUND_PAINTER = UIManager.getBorder("List.sourceListSelectionBackgroundPainter");
private static final Border LIST_FOCUSED_SELECTION_BACKGROUND_PAINTER = UIManager.getBorder("List.sourceListFocusedSelectionBackgroundPainter");
private boolean myWideSelection;
private boolean myAlwaysPaintRowBackground;
@@ -106,9 +108,6 @@ public class WideSelectionTreeUI extends BasicTreeUI {
tree.setShowsRootHandles(true);
tree.addMouseListener(mySelectionListener);
//final InputMap inputMap = tree.getInputMap(JComponent.WHEN_FOCUSED);
//inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "clearSelection");
}
@Override
@@ -407,10 +406,10 @@ public class WideSelectionTreeUI extends BasicTreeUI {
boolean isPathSelected = tree.getSelectionModel().isPathSelected(path);
boolean dark = UIUtil.isUnderDarcula();
Icon expandIcon = (isPathSelected && tree.hasFocus()) || dark ? TREE_SELECTED_EXPANDED_ICON
: TREE_EXPANDED_ICON;
Icon collapseIcon = (isPathSelected && tree.hasFocus()) || dark? TREE_SELECTED_COLLAPSED_ICON
: TREE_COLLAPSED_ICON;
Icon expandIcon = (isPathSelected && tree.hasFocus()) || dark ? getTreeSelectedExpandedIcon()
: UIUtil.getTreeExpandedIcon();
Icon collapseIcon = (isPathSelected && tree.hasFocus()) || dark? getTreeSelectedCollapsedIcon()
: UIUtil.getTreeCollapsedIcon();
if (!isLeaf(row)) {
@@ -26,6 +26,7 @@ import com.intellij.psi.xml.*;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomManager;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.wrappers.LazyValueResourceElementWrapper;
import org.jetbrains.android.dom.wrappers.ResourceElementWrapper;
import org.jetbrains.android.dom.wrappers.ValueResourceElementWrapper;
import org.jetbrains.android.facet.AndroidFacet;
@@ -45,21 +46,26 @@ import java.util.List;
public class AndroidFindUsagesHandlerFactory extends FindUsagesHandlerFactory {
@Override
public boolean canFindUsages(@NotNull PsiElement element) {
if (element instanceof XmlAttributeValue) {
XmlAttributeValue value = (XmlAttributeValue)element;
PsiElement element1 = LazyValueResourceElementWrapper.computeLazyElement(element);
if (element1 == null) {
return false;
}
if (element1 instanceof XmlAttributeValue) {
XmlAttributeValue value = (XmlAttributeValue)element1;
if (AndroidResourceUtil.findIdFields(value).length > 0) {
return true;
}
}
element = correctResourceElement(element);
if (element instanceof PsiField) {
return AndroidResourceUtil.isResourceField((PsiField)element);
element1 = correctResourceElement(element1);
if (element1 instanceof PsiField) {
return AndroidResourceUtil.isResourceField((PsiField)element1);
}
else if (element instanceof PsiFile) {
return AndroidResourceUtil.findResourceFieldsForFileResource((PsiFile)element, true).length > 0;
else if (element1 instanceof PsiFile) {
return AndroidResourceUtil.findResourceFieldsForFileResource((PsiFile)element1, true).length > 0;
}
else if (element instanceof XmlTag) {
return AndroidResourceUtil.findResourceFieldsForValueResource((XmlTag)element, true).length > 0;
else if (element1 instanceof XmlTag) {
return AndroidResourceUtil.findResourceFieldsForValueResource((XmlTag)element1, true).length > 0;
}
return false;
}
@@ -101,7 +107,11 @@ public class AndroidFindUsagesHandlerFactory extends FindUsagesHandlerFactory {
@Override
public FindUsagesHandler createFindUsagesHandler(@NotNull PsiElement element, boolean forHighlightUsages) {
PsiElement e = element;
PsiElement e = LazyValueResourceElementWrapper.computeLazyElement(element);
if (e == null) {
return null;
}
AndroidFacet facet = AndroidFacet.getInstance(e);
assert facet != null;
if (e instanceof XmlAttributeValue) {
@@ -41,6 +41,7 @@ import com.intellij.util.IncorrectOperationException;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomManager;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.wrappers.LazyValueResourceElementWrapper;
import org.jetbrains.android.dom.wrappers.ValueResourceElementWrapper;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
@@ -67,23 +68,28 @@ public class AndroidRenameResourceProcessor extends RenamePsiElementProcessor {
public boolean canProcessElement(@NotNull final PsiElement element) {
return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
public Boolean compute() {
if (element instanceof PsiFile) {
return AndroidFacet.getInstance(element) != null && AndroidResourceUtil.isInResourceSubdirectory((PsiFile)element, null);
final PsiElement element1 = LazyValueResourceElementWrapper.computeLazyElement(element);
if (element1 == null) {
return false;
}
else if (element instanceof PsiField) {
PsiField field = (PsiField)element;
if (element1 instanceof PsiFile) {
return AndroidFacet.getInstance(element1) != null && AndroidResourceUtil.isInResourceSubdirectory((PsiFile)element1, null);
}
else if (element1 instanceof PsiField) {
PsiField field = (PsiField)element1;
if (AndroidResourceUtil.isResourceField(field)) {
return AndroidResourceUtil.findResourcesByField(field).size() > 0;
}
}
else if (element instanceof XmlAttributeValue) {
LocalResourceManager manager = LocalResourceManager.getInstance(element);
else if (element1 instanceof XmlAttributeValue) {
LocalResourceManager manager = LocalResourceManager.getInstance(element1);
if (manager != null) {
if (AndroidResourceUtil.isIdDeclaration((XmlAttributeValue)element)) {
if (AndroidResourceUtil.isIdDeclaration((XmlAttributeValue)element1)) {
return true;
}
// then it is value resource
XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
XmlTag tag = PsiTreeUtil.getParentOfType(element1, XmlTag.class);
return tag != null && manager.getValueResourceType(tag) != null;
}
}
@@ -94,22 +100,29 @@ public class AndroidRenameResourceProcessor extends RenamePsiElementProcessor {
@Override
public void prepareRenaming(PsiElement element, String newName, Map<PsiElement, String> allRenames) {
AndroidFacet facet = AndroidFacet.getInstance(element);
assert facet != null;
if (element instanceof PsiFile) {
prepareResourceFileRenaming((PsiFile)element, newName, allRenames, facet);
final PsiElement element1 = LazyValueResourceElementWrapper.computeLazyElement(element);
if (element1 == null) {
return;
}
else if (element instanceof XmlAttributeValue) {
XmlAttributeValue value = (XmlAttributeValue)element;
// todo: support renaming alternative value resources
AndroidFacet facet = AndroidFacet.getInstance(element1);
assert facet != null;
if (element1 instanceof PsiFile) {
prepareResourceFileRenaming((PsiFile)element1, newName, allRenames, facet);
}
else if (element1 instanceof XmlAttributeValue) {
XmlAttributeValue value = (XmlAttributeValue)element1;
if (AndroidResourceUtil.isIdDeclaration(value)) {
prepareIdRenaming(value, newName, allRenames, facet);
}
else {
prepareValueResourceRenaming(element, newName, allRenames, facet);
prepareValueResourceRenaming(element1, newName, allRenames, facet);
}
}
else if (element instanceof PsiField) {
prepareResourceFieldRenaming((PsiField)element, newName, allRenames);
else if (element1 instanceof PsiField) {
prepareResourceFieldRenaming((PsiField)element1, newName, allRenames);
}
}
@@ -1,9 +1,14 @@
package org.jetbrains.android;
import com.android.resources.ResourceType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.psi.PsiFile;
import com.intellij.psi.XmlRecursiveElementVisitor;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import com.intellij.util.indexing.*;
@@ -11,8 +16,9 @@ import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.xml.NanoXmlUtil;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.ResourceEntry;
import org.jetbrains.android.util.ValueResourcesFileParser;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -26,8 +32,12 @@ import java.util.Set;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidValueResourcesIndex extends FileBasedIndexExtension<ResourceEntry, Set<ResourceEntry>> {
public static final ID<ResourceEntry, Set<ResourceEntry>> INDEX_ID = ID.create("android.value.resources.index");
public class AndroidValueResourcesIndex extends FileBasedIndexExtension<ResourceEntry, Set<AndroidValueResourcesIndex.MyResourceInfo>> {
public static final ID<ResourceEntry, Set<MyResourceInfo>> INDEX_ID = ID.create("android.value.resources.index");
@NonNls private static final String RESOURCES_ROOT_TAG = "resources";
@NonNls private static final String NAME_ATTRIBUTE_VALUE = "name";
@NonNls private static final String TYPE_ATTRIBUTE_VALUE = "type";
private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() {
@Override
@@ -37,28 +47,54 @@ public class AndroidValueResourcesIndex extends FileBasedIndexExtension<Resource
}
};
private final DataIndexer<ResourceEntry, Set<ResourceEntry>, FileContent> myIndexer =
new DataIndexer<ResourceEntry, Set<ResourceEntry>, FileContent>() {
private final DataIndexer<ResourceEntry, Set<MyResourceInfo>, FileContent> myIndexer =
new DataIndexer<ResourceEntry, Set<MyResourceInfo>, FileContent>() {
@Override
@NotNull
public Map<ResourceEntry, Set<ResourceEntry>> map(FileContent inputData) {
if (CharArrayUtil.indexOf(inputData.getContentAsText(), "<resources", 0) < 0) {
public Map<ResourceEntry, Set<MyResourceInfo>> map(FileContent inputData) {
if (!isSimilarFile(inputData)) {
return Collections.emptyMap();
}
final Map<ResourceEntry, Set<ResourceEntry>> result = new HashMap<ResourceEntry, Set<ResourceEntry>>();
final PsiFile file = inputData.getPsiFile();
NanoXmlUtil.parse(CharArrayUtil.readerFromCharSequence(inputData.getContentAsText()), new ValueResourcesFileParser() {
@Override
protected void stop() {
throw new NanoXmlUtil.ParserStoppedException();
}
if (!(file instanceof XmlFile)) {
return Collections.emptyMap();
}
final Map<ResourceEntry, Set<MyResourceInfo>> result = new HashMap<ResourceEntry, Set<MyResourceInfo>>();
file.accept(new XmlRecursiveElementVisitor() {
@Override
protected void process(@NotNull ResourceEntry entry) {
result.put(createKey(entry), Collections.<ResourceEntry>emptySet());
addEntryToMap(entry, createTypeMarkerKey(entry.getType()), result);
addEntryToMap(entry, createTypeNameMarkerKey(entry.getType(), entry.getName()), result);
public void visitXmlTag(XmlTag tag) {
super.visitXmlTag(tag);
final String resName = tag.getAttributeValue(NAME_ATTRIBUTE_VALUE);
if (resName == null) {
return;
}
final String tagName = tag.getName();
final String resTypeStr;
if ("item".equals(tagName)) {
resTypeStr = tag.getAttributeValue(TYPE_ATTRIBUTE_VALUE);
}
else {
resTypeStr = AndroidCommonUtils.getResourceTypeByTagName(tagName);
}
final ResourceType resType = resTypeStr != null ? ResourceType.getEnum(resTypeStr) : null;
if (resType == null) {
return;
}
final int offset = tag.getTextRange().getStartOffset();
if (resType == ResourceType.ATTR) {
final XmlTag parentTag = tag.getParentTag();
final String contextName = parentTag != null ? parentTag.getAttributeValue(NAME_ATTRIBUTE_VALUE) : null;
processResourceEntry(new ResourceEntry(resTypeStr, resName, contextName != null ? contextName : ""), result, offset);
}
else {
processResourceEntry(new ResourceEntry(resTypeStr, resName, ""), result, offset);
}
}
});
@@ -66,14 +102,40 @@ public class AndroidValueResourcesIndex extends FileBasedIndexExtension<Resource
}
};
private static void addEntryToMap(ResourceEntry entry, ResourceEntry marker, Map<ResourceEntry, Set<ResourceEntry>> result) {
Set<ResourceEntry> set = result.get(marker);
private static boolean isSimilarFile(FileContent inputData) {
if (CharArrayUtil.indexOf(inputData.getContentAsText(), "<" + RESOURCES_ROOT_TAG, 0) < 0) {
return false;
}
final boolean[] ourRootTag = {false};
NanoXmlUtil.parse(CharArrayUtil.readerFromCharSequence(inputData.getContentAsText()), new NanoXmlUtil.IXMLBuilderAdapter() {
@Override
public void startElement(String name, String nsPrefix, String nsURI, String systemID, int lineNr)
throws Exception {
ourRootTag[0] = RESOURCES_ROOT_TAG.equals(name) && nsPrefix == null;
stop();
}
});
return ourRootTag[0];
}
private static void processResourceEntry(@NotNull ResourceEntry entry,
@NotNull Map<ResourceEntry, Set<MyResourceInfo>> result,
int offset) {
final MyResourceInfo info = new MyResourceInfo(entry, offset);
result.put(entry, Collections.singleton(info));
addEntryToMap(info, createTypeMarkerKey(entry.getType()), result);
addEntryToMap(info, createTypeNameMarkerKey(entry.getType(), entry.getName()), result);
}
private static void addEntryToMap(MyResourceInfo info, ResourceEntry marker, Map<ResourceEntry, Set<MyResourceInfo>> result) {
Set<MyResourceInfo> set = result.get(marker);
if (set == null) {
set = new HashSet<ResourceEntry>();
set = new HashSet<MyResourceInfo>();
result.put(marker, set);
}
set.add(entry);
set.add(info);
}
@NotNull
@@ -83,17 +145,7 @@ public class AndroidValueResourcesIndex extends FileBasedIndexExtension<Resource
@NotNull
public static ResourceEntry createTypeNameMarkerKey(String type, String name) {
return createKey(type, name, "TYPE_MARKER_CONTEXT");
}
@NotNull
public static ResourceEntry createKey(ResourceEntry entry) {
return createKey(entry.getType(), entry.getName(), entry.getContext());
}
@NotNull
public static ResourceEntry createKey(String type, String name, String context) {
return new ResourceEntry(type, normalizeDelimiters(name), context);
return new ResourceEntry(type, normalizeDelimiters(name), "TYPE_MARKER_CONTEXT");
}
private static String normalizeDelimiters(String s) {
@@ -138,33 +190,35 @@ public class AndroidValueResourcesIndex extends FileBasedIndexExtension<Resource
}
};
private final DataExternalizer<Set<ResourceEntry>> myValueExternalizer = new DataExternalizer<Set<ResourceEntry>>() {
private final DataExternalizer<Set<MyResourceInfo>> myValueExternalizer = new DataExternalizer<Set<MyResourceInfo>>() {
@Override
public void save(DataOutput out, Set<ResourceEntry> value) throws IOException {
public void save(DataOutput out, Set<MyResourceInfo> value) throws IOException {
out.writeInt(value.size());
for (ResourceEntry entry : value) {
out.writeUTF(entry.getType());
out.writeUTF(entry.getName());
out.writeUTF(entry.getContext());
for (MyResourceInfo entry : value) {
out.writeUTF(entry.getResourceEntry().getType());
out.writeUTF(entry.getResourceEntry().getName());
out.writeUTF(entry.getResourceEntry().getContext());
out.writeInt(entry.getOffset());
}
}
@Nullable
@Override
public Set<ResourceEntry> read(DataInput in) throws IOException {
public Set<MyResourceInfo> read(DataInput in) throws IOException {
final int size = in.readInt();
if (size == 0) {
return Collections.emptySet();
}
final Set<ResourceEntry> result = new HashSet<ResourceEntry>(size);
final Set<MyResourceInfo> result = new HashSet<MyResourceInfo>(size);
for (int i = 0; i < size; i++) {
final String type = in.readUTF();
final String name = in.readUTF();
final String context = in.readUTF();
result.add(new ResourceEntry(type, name, context));
final int offset = in.readInt();
result.add(new MyResourceInfo(new ResourceEntry(type, name, context), offset));
}
return result;
}
@@ -172,13 +226,13 @@ public class AndroidValueResourcesIndex extends FileBasedIndexExtension<Resource
@NotNull
@Override
public ID<ResourceEntry, Set<ResourceEntry>> getName() {
public ID<ResourceEntry, Set<MyResourceInfo>> getName() {
return INDEX_ID;
}
@NotNull
@Override
public DataIndexer<ResourceEntry, Set<ResourceEntry>, FileContent> getIndexer() {
public DataIndexer<ResourceEntry, Set<MyResourceInfo>, FileContent> getIndexer() {
return myIndexer;
}
@@ -188,7 +242,7 @@ public class AndroidValueResourcesIndex extends FileBasedIndexExtension<Resource
}
@Override
public DataExternalizer<Set<ResourceEntry>> getValueExternalizer() {
public DataExternalizer<Set<MyResourceInfo>> getValueExternalizer() {
return myValueExternalizer;
}
@@ -204,6 +258,53 @@ public class AndroidValueResourcesIndex extends FileBasedIndexExtension<Resource
@Override
public int getVersion() {
return 3;
return 5;
}
public static class MyResourceInfo {
private final ResourceEntry myResourceEntry;
private final int myOffset;
private MyResourceInfo(@NotNull ResourceEntry resourceEntry, int offset) {
myResourceEntry = resourceEntry;
myOffset = offset;
}
@NotNull
public ResourceEntry getResourceEntry() {
return myResourceEntry;
}
public int getOffset() {
return myOffset;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MyResourceInfo info = (MyResourceInfo)o;
if (myOffset != info.myOffset) {
return false;
}
if (!myResourceEntry.equals(info.myResourceEntry)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = myResourceEntry.hashCode();
result = 31 * result + myOffset;
return result;
}
}
}
@@ -15,7 +15,7 @@
*/
package org.jetbrains.android.dom.converters;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.xml.XmlAttributeValue;
@@ -26,12 +26,14 @@ import com.intellij.util.xml.*;
import com.intellij.util.xml.impl.ConvertContextImpl;
import com.intellij.util.xml.impl.DomCompletionContributor;
import org.jetbrains.android.dom.AndroidDomUtil;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.dom.resources.ResourceValue;
import org.jetbrains.android.dom.wrappers.FileResourceElementWrapper;
import org.jetbrains.android.dom.wrappers.ValueResourceElementWrapper;
import org.jetbrains.android.dom.wrappers.LazyValueResourceElementWrapper;
import org.jetbrains.android.dom.wrappers.ResourceElementWrapper;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.resourceManagers.ValueResourceInfo;
import org.jetbrains.android.resourceManagers.ValueResourceInfoImpl;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.annotations.NotNull;
@@ -102,6 +104,29 @@ public class AndroidResourceReference extends PsiReferenceBase.Poly<XmlElement>
return null;
}
@NotNull
public PsiElement[] computeTargetElements() {
final ResolveResult[] resolveResults = multiResolve(false);
final List<PsiElement> results = new ArrayList<PsiElement>();
for (ResolveResult result : resolveResults) {
PsiElement element = result.getElement();
if (element instanceof LazyValueResourceElementWrapper) {
element = ((LazyValueResourceElementWrapper)element).computeElement();
}
if (element instanceof ResourceElementWrapper) {
element = ((ResourceElementWrapper)element).getWrappee();
}
if (element != null) {
results.add(element);
}
}
return results.toArray(new PsiElement[results.size()]);
}
@NotNull
@Override
public ResolveResult[] multiResolve(boolean incompleteCode) {
@@ -122,7 +147,7 @@ public class AndroidResourceReference extends PsiReferenceBase.Poly<XmlElement>
collectTargets(myFacet, myResourceValue, elements, files);
final List<ResolveResult> result = new ArrayList<ResolveResult>();
for (PsiFile target : files) {
if (target != null) {
final PsiFile e = new FileResourceElementWrapper(target);
@@ -131,15 +156,12 @@ public class AndroidResourceReference extends PsiReferenceBase.Poly<XmlElement>
}
for (PsiElement target : elements) {
final PsiElement e = target instanceof NavigationItem && target instanceof XmlAttributeValue
? new ValueResourceElementWrapper((XmlAttributeValue)target)
: target;
result.add(new PsiElementResolveResult(e));
result.add(new PsiElementResolveResult(target));
}
return result.toArray(new ResolveResult[result.size()]);
}
private static void collectTargets(AndroidFacet facet, ResourceValue resValue, List<PsiElement> elements, List<PsiFile> files) {
private void collectTargets(AndroidFacet facet, ResourceValue resValue, List<PsiElement> elements, List<PsiFile> files) {
String resType = resValue.getResourceType();
if (resType == null) {
return;
@@ -148,15 +170,13 @@ public class AndroidResourceReference extends PsiReferenceBase.Poly<XmlElement>
if (manager != null) {
String resName = resValue.getResourceName();
if (resName != null) {
List<ResourceElement> valueResources = manager.findValueResources(resType, resName, false);
for (ResourceElement resource : valueResources) {
elements.add(resource.getName().getXmlAttributeValue());
List<ValueResourceInfoImpl> valueResources = manager.findValueResourceInfos(resType, resName, false);
for (final ValueResourceInfo resource : valueResources) {
elements.add(new LazyValueResourceElementWrapper(resource, myElement));
}
if (resType.equals("id")) {
List<PsiElement> idAttrs = manager.findIdDeclarations(resName);
if (idAttrs != null) {
elements.addAll(idAttrs);
}
elements.addAll(manager.findIdDeclarations(resName));
}
if (elements.size() == 0) {
files.addAll(manager.findResourceFiles(resType, resName, false));
@@ -164,4 +184,32 @@ public class AndroidResourceReference extends PsiReferenceBase.Poly<XmlElement>
}
}
}
@Override
public boolean isReferenceTo(PsiElement element) {
final ResolveResult[] results = multiResolve(false);
final PsiFile psiFile = element.getContainingFile();
final VirtualFile vFile = psiFile != null ? psiFile.getVirtualFile() : null;
for (ResolveResult result : results) {
final PsiElement target = result.getElement();
if (element.getManager().areElementsEquivalent(target, element)) {
return true;
}
if (target instanceof LazyValueResourceElementWrapper && vFile != null) {
final ValueResourceInfo info = ((LazyValueResourceElementWrapper)target).getResourceInfo();
if (info.getContainingFile().equals(vFile)) {
final XmlAttributeValue realTarget = info.computeXmlElement();
if (element.getManager().areElementsEquivalent(realTarget, element)) {
return true;
}
}
}
}
return false;
}
}
@@ -68,7 +68,7 @@ public class StyleItemNameConverter extends ResolvingConverter<String> {
@Override
public String fromString(@Nullable @NonNls String s, ConvertContext context) {
if (s == null) return null;
String[] strs = s.split(s);
String[] strs = s.split(":");
if (strs.length < 2 || !"android".equals(strs[0])) {
return s;
}
@@ -0,0 +1,103 @@
package org.jetbrains.android.dom.wrappers;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiTarget;
import com.intellij.psi.impl.RenameableFakePsiElement;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.android.resourceManagers.ValueResourceInfo;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author Eugene.Kudelevsky
*/
public class LazyValueResourceElementWrapper extends RenameableFakePsiElement implements PsiTarget {
private final ValueResourceInfo myResourceInfo;
private final PsiElement myParent;
public LazyValueResourceElementWrapper(@NotNull ValueResourceInfo resourceInfo, @NotNull PsiElement parent) {
super(parent);
myParent = parent;
myResourceInfo = resourceInfo;
}
@Override
public String getName() {
return myResourceInfo.getName();
}
@Nullable
@Override
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
final XmlAttributeValue element = computeElement();
if (element == null) {
throw new IncorrectOperationException(
"Cannot find resource '" + myResourceInfo.getName() + "' in file " + myResourceInfo.getContainingFile().getPath());
}
return new ValueResourceElementWrapper(element).setName(name);
}
@Override
public ItemPresentation getPresentation() {
return new ItemPresentation() {
@Nullable
public String getPresentableText() {
final String name = myResourceInfo.getName();
final VirtualFile resDir = myResourceInfo.getContainingFile().getParent();
if (resDir == null) {
return name;
}
return name + " (" + resDir.getName() + ')';
}
public String getLocationString() {
return null;
}
public Icon getIcon(boolean open) {
return null;
}
};
}
@Nullable
public XmlAttributeValue computeElement() {
return myResourceInfo.computeXmlElement();
}
@NotNull
@Override
public PsiElement getNavigationElement() {
final XmlAttributeValue element = myResourceInfo.computeXmlElement();
return element != null ? element : myParent;
}
@NotNull
public ValueResourceInfo getResourceInfo() {
return myResourceInfo;
}
@Override
public String getTypeName() {
return "Android Value Resource";
}
@Override
public Icon getIcon() {
return null;
}
@Nullable
public static PsiElement computeLazyElement(PsiElement element) {
if (element instanceof LazyValueResourceElementWrapper) {
element = ((LazyValueResourceElementWrapper)element).computeElement();
}
return element;
}
}
@@ -0,0 +1,53 @@
package org.jetbrains.android.resourceManagers;
import com.android.resources.ResourceType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.XmlRecursiveElementVisitor;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene.Kudelevsky
*/
public class IdResourceInfo extends ValueResourceInfoBase {
private final Project myProject;
IdResourceInfo(@NotNull String name, @NotNull VirtualFile file, @NotNull Project project) {
super(name, ResourceType.ID, file);
myProject = project;
}
@Override
public XmlAttributeValue computeXmlElement() {
final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(myFile);
if (!(psiFile instanceof XmlFile)) {
return null;
}
final Ref<XmlAttributeValue> result = Ref.create();
psiFile.accept(new XmlRecursiveElementVisitor() {
@Override
public void visitXmlAttributeValue(XmlAttributeValue attributeValue) {
if (!result.isNull()) {
return;
}
if (AndroidResourceUtil.isIdDeclaration(attributeValue)) {
final String idInAttr = AndroidResourceUtil.getResourceNameByReferenceText(attributeValue.getValue());
if (myName.equals(idInAttr)) {
result.set(attributeValue);
}
}
}
});
return result.get();
}
}
@@ -141,22 +141,21 @@ public class LocalResourceManager extends ResourceManager {
for (ResourceType resourceType : AndroidResourceUtil.ALL_VALUE_RESOURCE_TYPES) {
final ResourceEntry typeMarkerEntry = AndroidValueResourcesIndex.createTypeMarkerKey(resourceType.getName());
for (Set<ResourceEntry> entrySet : index.getValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, scope)) {
for (ResourceEntry entry : entrySet) {
final Collection<VirtualFile> files =
index.getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, AndroidValueResourcesIndex.createKey(entry), scope);
for (VirtualFile file : files) {
index.processValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, null, new FileBasedIndex.ValueProcessor<Set<AndroidValueResourcesIndex.MyResourceInfo>>() {
@Override
public boolean process(VirtualFile file, Set<AndroidValueResourcesIndex.MyResourceInfo> infos) {
for (AndroidValueResourcesIndex.MyResourceInfo info : infos) {
Set<String> resourcesInFile = file2Types.get(file);
if (resourcesInFile == null) {
resourcesInFile = new HashSet<String>();
file2Types.put(file, resourcesInFile);
}
resourcesInFile.add(entry.getType());
resourcesInFile.add(info.getResourceEntry().getType());
}
return true;
}
}
}, scope);
}
final Set<String> result = new HashSet<String>();
@@ -279,28 +279,28 @@ public abstract class ResourceManager {
if (type == null) {
return Collections.emptyList();
}
final FileBasedIndex index = FileBasedIndex.getInstance();
final ResourceEntry typeMarkerEntry = AndroidValueResourcesIndex.createTypeMarkerKey(resourceType);
final GlobalSearchScope scope = GlobalSearchScope.allScope(myModule.getProject());
final Map<VirtualFile, Set<ResourceEntry>> file2resourceSet = new HashMap<VirtualFile, Set<ResourceEntry>>();
for (Set<ResourceEntry> entrySet : index.getValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, scope)) {
for (ResourceEntry entry : entrySet) {
final Collection<VirtualFile> files =
index.getContainingFiles(AndroidValueResourcesIndex.INDEX_ID, AndroidValueResourcesIndex.createKey(entry), scope);
for (VirtualFile file : files) {
index.processValues(AndroidValueResourcesIndex.INDEX_ID, typeMarkerEntry, null, new FileBasedIndex.ValueProcessor<Set<AndroidValueResourcesIndex.MyResourceInfo>>() {
@Override
public boolean process(VirtualFile file, Set<AndroidValueResourcesIndex.MyResourceInfo> infos) {
for (AndroidValueResourcesIndex.MyResourceInfo info : infos) {
Set<ResourceEntry> resourcesInFile = file2resourceSet.get(file);
if (resourcesInFile == null) {
resourcesInFile = new HashSet<ResourceEntry>();
file2resourceSet.put(file, resourcesInFile);
}
resourcesInFile.add(entry);
resourcesInFile.add(info.getResourceEntry());
}
return true;
}
}
}, scope);
final List<ResourceEntry> result = new ArrayList<ResourceEntry>();
for (VirtualFile file : getAllValueResourceFiles()) {
@@ -332,7 +332,7 @@ public abstract class ResourceManager {
public abstract AttributeDefinitions getAttributeDefinitions();
// searches only declarations such as "@+id/..."
@Nullable
@NotNull
public List<PsiElement> findIdDeclarations(@NotNull final String id) {
if (!isResourcePublic(ResourceType.ID.getName(), id)) {
return Collections.emptyList();
@@ -428,33 +428,56 @@ public abstract class ResourceManager {
return findValueResources(resType, resName, true);
}
// not recommended to use, because it is too slow
@NotNull
public List<ResourceElement> findValueResources(@NotNull String resourceType,
@NotNull String resourceName,
boolean distinguishDelimetersInName) {
final ResourceType type = ResourceType.getEnum(resourceType);
if (type == null) {
return Collections.emptyList();
}
final Collection<VirtualFile> files = FileBasedIndex.getInstance()
.getContainingFiles(AndroidValueResourcesIndex.INDEX_ID,
AndroidValueResourcesIndex.createTypeNameMarkerKey(resourceType, resourceName),
GlobalSearchScope.allScope(myModule.getProject()));
if (files.size() == 0) {
return Collections.emptyList();
}
final Set<VirtualFile> fileSet = new HashSet<VirtualFile>(files);
boolean distinguishDelimitersInName) {
final List<ValueResourceInfoImpl> resources = findValueResourceInfos(resourceType, resourceName, distinguishDelimitersInName);
final List<ResourceElement> result = new ArrayList<ResourceElement>();
for (ResourceElement element : getValueResources(resourceType, fileSet)) {
final String name = element.getName().getValue();
for (ValueResourceInfoImpl resource : resources) {
final ResourceElement domElement = resource.computeDomElement();
if (AndroidUtils.equal(resourceName, name, distinguishDelimetersInName)) {
result.add(element);
if (domElement != null) {
result.add(domElement);
}
}
return result;
}
@NotNull
public List<ValueResourceInfoImpl> findValueResourceInfos(@NotNull String resourceType,
@NotNull final String resourceName,
final boolean distinguishDelimetersInName) {
final ResourceType type = ResourceType.getEnum(resourceType);
if (type == null || !AndroidResourceUtil.VALUE_RESOURCE_TYPES.contains(type)) {
// do not search for styleables and atts here because we don't want to resolve references to it
return Collections.emptyList();
}
new HashMap<VirtualFile, Set<AndroidValueResourcesIndex.MyResourceInfo>>();
final GlobalSearchScope scope = GlobalSearchScope.allScope(myModule.getProject());
final List<ValueResourceInfoImpl> result = new ArrayList<ValueResourceInfoImpl>();
final Set<VirtualFile> valueResourceFiles = getAllValueResourceFiles();
FileBasedIndex.getInstance()
.processValues(AndroidValueResourcesIndex.INDEX_ID, AndroidValueResourcesIndex.createTypeNameMarkerKey(resourceType, resourceName),
null, new FileBasedIndex.ValueProcessor<Set<AndroidValueResourcesIndex.MyResourceInfo>>() {
@Override
public boolean process(VirtualFile file, Set<AndroidValueResourcesIndex.MyResourceInfo> infos) {
for (AndroidValueResourcesIndex.MyResourceInfo info : infos) {
final String name = info.getResourceEntry().getName();
if (AndroidUtils.equal(resourceName, name, distinguishDelimetersInName)) {
if (valueResourceFiles.contains(file)) {
result.add(new ValueResourceInfoImpl(info.getResourceEntry().getName(), type, file, myModule, info.getOffset()));
}
}
}
return true;
}
}, scope);
return result;
}
}
@@ -0,0 +1,25 @@
package org.jetbrains.android.resourceManagers;
import com.android.resources.ResourceType;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.xml.XmlAttributeValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public interface ValueResourceInfo {
@Nullable
XmlAttributeValue computeXmlElement();
@NotNull
VirtualFile getContainingFile();
@NotNull
String getName();
@NotNull
ResourceType getType();
}
@@ -0,0 +1,43 @@
package org.jetbrains.android.resourceManagers;
import com.android.resources.ResourceType;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
/**
* @author Eugene.Kudelevsky
*/
abstract class ValueResourceInfoBase implements ValueResourceInfo {
protected final String myName;
protected final ResourceType myType;
protected final VirtualFile myFile;
protected ValueResourceInfoBase(@NotNull String name, @NotNull ResourceType type, @NotNull VirtualFile file) {
myName = name;
myType = type;
myFile = file;
}
@NotNull
@Override
public VirtualFile getContainingFile() {
return myFile;
}
@NotNull
@Override
public String getName() {
return myName;
}
@NotNull
@Override
public ResourceType getType() {
return myType;
}
@Override
public String toString() {
return "ANDROID_RESOURCE: " + myType + ", " + myName + ", " + myFile.getPath() + "]";
}
}
@@ -0,0 +1,68 @@
package org.jetbrains.android.resourceManagers;
import com.android.resources.ResourceType;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomManager;
import org.jetbrains.android.dom.resources.Item;
import org.jetbrains.android.dom.resources.ResourceElement;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public class ValueResourceInfoImpl extends ValueResourceInfoBase {
private final Module myModule;
private final int myOffset;
ValueResourceInfoImpl(@NotNull String name, @NotNull ResourceType type, @NotNull VirtualFile file, @NotNull Module module, int offset) {
super(name, type, file);
myModule = module;
myOffset = offset;
}
@Override
public XmlAttributeValue computeXmlElement() {
final ResourceElement resDomElement = computeDomElement();
return resDomElement != null ? resDomElement.getName().getXmlAttributeValue() : null;
}
@Nullable
public ResourceElement computeDomElement() {
final Project project = myModule.getProject();
final PsiFile file = PsiManager.getInstance(project).findFile(myFile);
if (!(file instanceof XmlFile)) {
return null;
}
final XmlTag tag = PsiTreeUtil.findElementOfClassAtOffset(file, myOffset, XmlTag.class, true);
if (tag == null) {
return null;
}
final DomElement domElement = DomManager.getDomManager(project).getDomElement(tag);
if (!(domElement instanceof ResourceElement)) {
return null;
}
final String resType = domElement instanceof Item
? ((Item)domElement).getType().getStringValue()
: AndroidCommonUtils.getResourceTypeByTagName(tag.getName());
if (!myType.getName().equals(resType)) {
return null;
}
final ResourceElement resDomElement = (ResourceElement)domElement;
final String resName = ((ResourceElement)domElement).getName().getStringValue();
return myName.equals(resName) ? resDomElement : null;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,35 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/str1"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/str2"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/str100"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/str1000"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/str2500"
/>
</LinearLayout>
@@ -0,0 +1,11 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@strin<caret>g/str1000"
/>
</LinearLayout>
@@ -2,5 +2,7 @@
<resources>
<style name="TextAppearance.Theme.PlainText">
<item name="android:textStyle">normal|bold</item>
<item name="<error>android:unknown</error>">normal|bold</item>
<item name="unknown">normal|bold</item>
</style>
</resources>
@@ -12,6 +12,9 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.util.ThrowableRunnable;
import org.jetbrains.android.inspections.CreateFileResourceQuickFix;
import org.jetbrains.android.inspections.CreateValueResourceQuickFix;
@@ -405,26 +408,67 @@ public class AndroidLayoutDomTest extends AndroidDomTest {
toTestCompletion(getTestName(true) + ".xml", getTestName(true) + "_after.xml");
}
/*public void testCustomAttrsPerformance() throws Throwable {
public void testCustomAttrsPerformance() throws Throwable {
myFixture.copyFileToProject("dom/resources/bigfile.xml", "res/values/bigfile.xml");
myFixture.copyFileToProject("dom/resources/bigattrs.xml", "res/values/bigattrs.xml");
myFixture.copyFileToProject("dom/resources/bigattrs.xml", "res/values/bigattrs1.xml");
myFixture.copyFileToProject("dom/resources/bigattrs.xml", "res/values/bigattrs2.xml");
myFixture.copyFileToProject("dom/resources/bigattrs.xml", "res/values/bigattrs3.xml");
String path = copyFileToProject("bigfile.xml");
VirtualFile f = myFixture.findFileInTempDir(path);
VirtualFile f = copyFileToProject("bigfile.xml");
myFixture.configureFromExistingVirtualFile(f);
IdeaTestUtil.assertTiming("", 800, new Runnable() {
PlatformTestUtil.startPerformanceTest("android custom attrs highlighting is slow", 800, new ThrowableRunnable() {
@Override
public void run() {
try {
myFixture.doHighlighting();
}
catch (Exception e) {
public void run() throws Throwable {
myFixture.doHighlighting();
}
}).attempts(2).cpuBound().usesAllCPUCores().assertTiming();
}
public void testResourceHighlightingPerformance() throws Throwable {
doCopyManyStrings();
final VirtualFile f = copyFileToProject(getTestName(true) + ".xml");
myFixture.configureFromExistingVirtualFile(f);
PlatformTestUtil.startPerformanceTest("android highlighting is slow", 400, new ThrowableRunnable() {
@Override
public void run() throws Throwable {
myFixture.doHighlighting();
}
}).attempts(2).cpuBound().usesAllCPUCores().assertTiming();
}
public void testResourceNavigationPerformance() throws Throwable {
doCopyManyStrings();
final VirtualFile f = copyFileToProject(getTestName(true) + ".xml");
myFixture.configureFromExistingVirtualFile(f);
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
final List<PsiElement> navElements = new ArrayList<PsiElement>();
// warm
myFixture.doHighlighting();
PlatformTestUtil.startPerformanceTest("android highlighting is slow", 7000, new ThrowableRunnable() {
@SuppressWarnings("ConstantConditions")
@Override
public void run() throws Throwable {
final PsiReference reference = TargetElementUtilBase.findReference(myFixture.getEditor(), myFixture.getCaretOffset());
final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false);
for (ResolveResult result : results) {
final PsiElement navElement = result.getElement().getNavigationElement();
assertInstanceOf(navElement, XmlAttributeValue.class);
navElements.add(navElement);
}
}
});
}*/
}).attempts(1).cpuBound().usesAllCPUCores().assertTiming();
assertEquals(31, navElements.size());
}
private void doCopyManyStrings() {
myFixture.copyFileToProject(testFolder + "/many_strings.xml", "res/values/strings.xml");
for (int i = 0; i < 30; i++) {
myFixture.copyFileToProject(testFolder + "/many_strings.xml", "res/values-" + Integer.toString(i) + "/strings.xml");
}
}
public void testViewClassReference() throws Throwable {
VirtualFile file = myFixture.copyFileToProject(testFolder + "/vcr.xml", getPathToCopy("vcr.xml"));
@@ -30,9 +30,9 @@ import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.refactoring.actions.InlineAction;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import org.jetbrains.android.dom.wrappers.LazyValueResourceElementWrapper;
import org.jetbrains.android.inspections.CreateValueResourceQuickFix;
import java.util.ArrayList;
@@ -252,7 +252,8 @@ public class AndroidValueResourcesTest extends AndroidDomTest {
PsiReference rootReference = psiFile.findReferenceAt(rootOffset);
assertNotNull(rootReference);
PsiElement element = rootReference.resolve();
assertTrue("Must be PsiClass reference", element instanceof XmlAttributeValue);
assertInstanceOf(element, LazyValueResourceElementWrapper.class);
assertNotNull(((LazyValueResourceElementWrapper)element).computeElement());
}
// see getPathToCopy()
-1
View File
@@ -19,7 +19,6 @@
<orderEntry type="module" module-name="spellchecker" />
<orderEntry type="module" module-name="resources" scope="TEST" />
<orderEntry type="module" module-name="xml" />
<orderEntry type="module" module-name="testFramework-java" scope="TEST" />
</component>
</module>
@@ -15,12 +15,12 @@
*/
package com.intellij.lang.properties;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
/**
* @author Rustam Vishnyakov
*/
public class PropertiesFoldingTest extends LightCodeInsightFixtureTestCase {
public class PropertiesFoldingTest extends LightPlatformCodeInsightFixtureTestCase {
private static final String BASE_PATH = "/plugins/properties/testData/propertiesFile/folding/";
+1 -1
View File
@@ -153,7 +153,7 @@
<extensionPoint name="generation.topLevelFactory" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
<extensionPoint name="codeInsight.createFieldFromUsageHelper" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
<extensionPoint name="findUsages.java.customJavaFindUsagesHandlerProvider" interface="com.intellij.find.findUsages.CustomJavaFindUsagesHandlerProvider"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
@@ -83,7 +83,7 @@ public class XmlDoctypeImpl extends XmlElementImpl implements XmlDoctype {
@Nullable
public String getDtdUri() {
final XmlElement dtdUrlElement = getDtdUrlElement();
final PsiElement dtdUrlElement = getDtdUrlElement();
if (dtdUrlElement == null || dtdUrlElement.getTextLength() == 0) return null;
return extractValue(dtdUrlElement);
}
@@ -120,7 +120,7 @@ public class XmlDoctypeImpl extends XmlElementImpl implements XmlDoctype {
}
@Nullable
public XmlElement getDtdUrlElement() {
public PsiElement getDtdUrlElement() {
PsiElement docTypePublic = findChildByRoleAsPsiElement(XmlChildRole.XML_DOCTYPE_PUBLIC);
if (docTypePublic != null){
@@ -216,33 +216,32 @@ public class XmlDoctypeImpl extends XmlElementImpl implements XmlDoctype {
@NotNull
public PsiReference[] getReferences() {
final XmlElement dtdUrlElement = getDtdUrlElement();
final PsiElement docTypePublic = findChildByRoleAsPsiElement(XmlChildRole.XML_DOCTYPE_PUBLIC);
PsiReference[] uriRefs = null;
final PsiElement dtdUrlElement = getDtdUrlElement();
PsiReference uriRef = null;
if (dtdUrlElement != null) {
uriRefs = new PsiReference[1];
uriRefs[0] = new URLReference(XmlDoctypeImpl.this) {
@NotNull
public Object[] getVariants() {
return docTypePublic != null ?
super.getVariants(): EMPTY_ARRAY;
}
@NotNull
public String getCanonicalText() {
return extractValue(dtdUrlElement);
}
public TextRange getRangeInElement() {
return TextRange.from(dtdUrlElement.getTextRange().getStartOffset() - getTextRange().getStartOffset() + 1, Math.max(dtdUrlElement.getTextRange().getLength() - 2, 0));
}
};
uriRef = createUrlReference(dtdUrlElement);
}
final PsiReference[] referencesFromProviders = ReferenceProvidersRegistry.getReferencesFromProviders(this);
final PsiReference[] refs = ReferenceProvidersRegistry.getReferencesFromProviders(this);
return ArrayUtil.mergeArrays(
uriRefs != null? uriRefs: PsiReference.EMPTY_ARRAY,
referencesFromProviders
);
return uriRef == null ? refs : ArrayUtil.mergeArrays(new PsiReference[] {uriRef}, refs);
}
protected PsiReference createUrlReference(final PsiElement dtdUrlElement) {
return new URLReference(XmlDoctypeImpl.this) {
@NotNull
public Object[] getVariants() {
return findChildByRoleAsPsiElement(XmlChildRole.XML_DOCTYPE_PUBLIC) != null ?
super.getVariants(): EMPTY_ARRAY;
}
@NotNull
public String getCanonicalText() {
return extractValue(dtdUrlElement);
}
public TextRange getRangeInElement() {
return TextRange.from(dtdUrlElement.getTextRange().getStartOffset() - getTextRange().getStartOffset() + 1, Math.max(dtdUrlElement.getTextRange().getLength() - 2, 0));
}
};
}
}
@@ -15,13 +15,15 @@
*/
package com.intellij.psi.xml;
import com.intellij.psi.PsiElement;
/**
* @author Mike
*/
public interface XmlDoctype extends XmlElement {
XmlElement getNameElement();
String getDtdUri();
XmlElement getDtdUrlElement();
PsiElement getDtdUrlElement();
XmlMarkupDecl getMarkupDecl();
String getPublicId();
String getSystemId();