PY-17265 Extracted PyBaseMoveDialog to later create new dialog for the refactoring
[idea/community.git] / python / psi-api / src / com / jetbrains / python / psi / impl / PyPsiUtils.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.psi.impl;
17
18 import com.google.common.base.Preconditions;
19 import com.intellij.lang.ASTNode;
20 import com.intellij.lang.injection.InjectedLanguageManager;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.module.Module;
23 import com.intellij.openapi.util.Couple;
24 import com.intellij.openapi.util.io.FileUtil;
25 import com.intellij.openapi.util.text.StringUtil;
26 import com.intellij.openapi.vfs.VirtualFile;
27 import com.intellij.psi.*;
28 import com.intellij.psi.stubs.StubElement;
29 import com.intellij.psi.tree.IElementType;
30 import com.intellij.psi.tree.TokenSet;
31 import com.intellij.psi.util.PsiTreeUtil;
32 import com.intellij.psi.util.PsiUtilCore;
33 import com.intellij.psi.util.QualifiedName;
34 import com.intellij.util.containers.ContainerUtil;
35 import com.jetbrains.python.PyTokenTypes;
36 import com.jetbrains.python.psi.*;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39
40 import java.lang.reflect.Array;
41 import java.util.ArrayList;
42 import java.util.LinkedList;
43 import java.util.List;
44
45 /**
46  * @author max
47  */
48 public class PyPsiUtils {
49
50   private static final Logger LOG = Logger.getInstance(PyPsiUtils.class.getName());
51
52   private PyPsiUtils() {
53   }
54
55   @NotNull
56   protected static <T extends PyElement> T[] nodesToPsi(ASTNode[] nodes, T[] array) {
57     T[] psiElements = (T[])Array.newInstance(array.getClass().getComponentType(), nodes.length);
58     for (int i = 0; i < nodes.length; i++) {
59       //noinspection unchecked
60       psiElements[i] = (T)nodes[i].getPsi();
61     }
62     return psiElements;
63   }
64
65   /**
66    * Finds the closest comma after the element skipping any whitespaces in-between.
67    */
68   @Nullable
69   public static PsiElement getPrevComma(@NotNull PsiElement element) {
70     final PsiElement prevNode = getPrevNonWhitespaceSibling(element);
71     return prevNode != null && prevNode.getNode().getElementType() == PyTokenTypes.COMMA ? prevNode : null;
72   }
73
74   /**
75    * Finds first non-whitespace sibling before given PSI element.
76    */
77   @Nullable
78   public static PsiElement getPrevNonWhitespaceSibling(@Nullable PsiElement element) {
79     return PsiTreeUtil.skipSiblingsBackward(element, PsiWhiteSpace.class);
80   }
81
82   /**
83    * Finds first non-whitespace sibling before given AST node.
84    */
85   @Nullable
86   public static ASTNode getPrevNonWhitespaceSibling(@NotNull ASTNode node) {
87     return skipSiblingsBackward(node, TokenSet.create(TokenType.WHITE_SPACE));
88   }
89
90   /**
91    * Finds first sibling that is neither comment, nor whitespace before given element.
92    * @param strict prohibit returning element itself
93    */
94   @Nullable
95   public static PsiElement getPrevNonCommentSibling(@Nullable PsiElement start, boolean strict) {
96     if (!strict && !(start instanceof PsiWhiteSpace || start instanceof PsiComment)) {
97       return start;
98     }
99     return PsiTreeUtil.skipSiblingsBackward(start, PsiWhiteSpace.class, PsiComment.class);
100   }
101
102   /**
103    * Finds the closest comma after the element skipping any whitespaces in-between.
104    */
105   @Nullable
106   public static PsiElement getNextComma(@NotNull PsiElement element) {
107     final PsiElement nextNode = getNextNonWhitespaceSibling(element);
108     return nextNode != null && nextNode.getNode().getElementType() == PyTokenTypes.COMMA ? nextNode : null;
109   }
110
111   /**
112    * Finds first non-whitespace sibling after given PSI element.
113    */
114   @Nullable
115   public static PsiElement getNextNonWhitespaceSibling(@Nullable PsiElement element) {
116     return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace.class);
117   }
118
119   /**
120    * Finds first non-whitespace sibling after given PSI element but stops at first whitespace containing line feed.  
121    */
122   @Nullable
123   public static PsiElement getNextNonWhitespaceSiblingOnSameLine(@NotNull PsiElement element) {
124     PsiElement cur = element.getNextSibling();
125     while (cur != null) {
126       if (!(cur instanceof PsiWhiteSpace)) {
127         return cur;
128       }
129       else if (cur.textContains('\n')) {
130         break;
131       }
132       cur = cur.getNextSibling();
133     }
134     return null;
135   }
136   
137   /**
138    * Finds first non-whitespace sibling after given AST node.
139    */
140   @Nullable
141   public static ASTNode getNextNonWhitespaceSibling(@NotNull ASTNode after) {
142     return skipSiblingsForward(after, TokenSet.create(TokenType.WHITE_SPACE));
143   }
144
145   /**
146    * Finds first sibling that is neither comment, nor whitespace after given element.
147    * @param strict prohibit returning element itself
148    */
149   @Nullable
150   public static PsiElement getNextNonCommentSibling(@Nullable PsiElement start, boolean strict) {
151     if (!strict && !(start instanceof PsiWhiteSpace || start instanceof PsiComment)) {
152       return start;
153     }
154     return PsiTreeUtil.skipSiblingsForward(start, PsiWhiteSpace.class, PsiComment.class);
155   }
156
157   /**
158    * Finds first token after given element that doesn't consist solely of spaces and is not empty (e.g. error marker).
159    * @param ignoreComments ignore commentaries as well
160    */
161   @Nullable
162   public static PsiElement getNextSignificantLeaf(@Nullable PsiElement element, boolean ignoreComments) {
163     while (element != null && StringUtil.isEmptyOrSpaces(element.getText()) || ignoreComments && element instanceof PsiComment) {
164       element = PsiTreeUtil.nextLeaf(element);
165     }
166     return element;
167   }
168
169   /**
170    * Finds first token before given element that doesn't consist solely of spaces and is not empty (e.g. error marker).
171    * @param ignoreComments ignore commentaries as well
172    */
173   @Nullable
174   public static PsiElement getPrevSignificantLeaf(@Nullable PsiElement element, boolean ignoreComments) {
175     while (element != null && StringUtil.isEmptyOrSpaces(element.getText()) || ignoreComments && element instanceof PsiComment) {
176       element = PsiTreeUtil.prevLeaf(element);
177     }
178     return element;
179   }
180
181   /**
182    * Finds the closest comma looking for the next comma first and then for the preceding one.
183    */
184   @Nullable
185   public static PsiElement getAdjacentComma(@NotNull PsiElement element) {
186     final PsiElement nextComma = getNextComma(element);
187     return nextComma != null ? nextComma : getPrevComma(element);
188   }
189
190   /**
191    * Works similarly to {@link PsiTreeUtil#skipSiblingsForward(PsiElement, Class[])}, but for AST nodes.
192    */
193   @Nullable
194   public static ASTNode skipSiblingsForward(@Nullable ASTNode node, @NotNull TokenSet types) {
195     if (node == null) {
196       return null;
197     }
198     for (ASTNode next = node.getTreeNext(); next != null; next = next.getTreeNext()) {
199       if (!types.contains(next.getElementType())) {
200         return next;
201       }
202     }
203     return null;
204   }
205
206   /**
207    * Works similarly to {@link PsiTreeUtil#skipSiblingsBackward(PsiElement, Class[])}, but for AST nodes.
208    */
209   @Nullable
210   public static ASTNode skipSiblingsBackward(@Nullable ASTNode node, @NotNull TokenSet types) {
211     if (node == null) {
212       return null;
213     }
214     for (ASTNode prev = node.getTreePrev(); prev != null; prev = prev.getTreePrev()) {
215       if (!types.contains(prev.getElementType())) {
216         return prev;
217       }
218     }
219     return null;
220   }
221
222     /**
223    * Returns first child psi element with specified element type or {@code null} if no such element exists.
224    * Semantically it's the same as {@code getChildByFilter(element, TokenSet.create(type), 0)}.
225    *
226    * @param element tree parent node
227    * @param type    element type expected
228    * @return child element described
229    */
230   @Nullable
231   public static PsiElement getFirstChildOfType(@NotNull final PsiElement element, @NotNull PyElementType type) {
232     final ASTNode child = element.getNode().findChildByType(type);
233     return child != null ? child.getPsi() : null;
234   }
235
236   /**
237    * Returns child element in the psi tree
238    *
239    * @param filter  Types of expected child
240    * @param number  number
241    * @param element tree parent node
242    * @return PsiElement - child psiElement
243    */
244   @Nullable
245   public static PsiElement getChildByFilter(@NotNull PsiElement element, @NotNull TokenSet filter, int number) {
246     final ASTNode node = element.getNode();
247     if (node != null) {
248       final ASTNode[] children = node.getChildren(filter);
249       return (0 <= number && number < children.length) ? children[number].getPsi() : null;
250     }
251     return null;
252   }
253
254   public static void addBeforeInParent(@NotNull final PsiElement anchor, @NotNull final PsiElement... newElements) {
255     final ASTNode anchorNode = anchor.getNode();
256     LOG.assertTrue(anchorNode != null);
257     for (PsiElement newElement : newElements) {
258       anchorNode.getTreeParent().addChild(newElement.getNode(), anchorNode);
259     }
260   }
261
262   public static void removeElements(@NotNull final PsiElement... elements) {
263     final ASTNode parentNode = elements[0].getParent().getNode();
264     LOG.assertTrue(parentNode != null);
265     for (PsiElement element : elements) {
266       //noinspection ConstantConditions
267       parentNode.removeChild(element.getNode());
268     }
269   }
270
271   @Nullable
272   public static PsiElement getStatement(@NotNull final PsiElement element) {
273     final PyElement compStatement = getStatementList(element);
274     if (compStatement == null) {
275       return null;
276     }
277     return getParentRightBefore(element, compStatement);
278   }
279
280   public static PyElement getStatementList(final PsiElement element) {
281     //noinspection ConstantConditions
282     return element instanceof PyFile || element instanceof PyStatementList
283            ? (PyElement)element
284            : PsiTreeUtil.getParentOfType(element, PyFile.class, PyStatementList.class);
285   }
286
287   /**
288    * Returns ancestor of the element that is also direct child of the given super parent.
289    *
290    * @param element     element to start search from
291    * @param superParent direct parent of the desired ancestor
292    * @return described element or {@code null} if it doesn't exist
293    */
294   @Nullable
295   public static PsiElement getParentRightBefore(@NotNull PsiElement element, @NotNull final PsiElement superParent) {
296     return PsiTreeUtil.findFirstParent(element, false, element1 -> element1.getParent() == superParent);
297   }
298
299   public static List<PsiElement> collectElements(final PsiElement statement1, final PsiElement statement2) {
300     // Process ASTNodes here to handle all the nodes
301     final ASTNode node1 = statement1.getNode();
302     final ASTNode node2 = statement2.getNode();
303     final ASTNode parentNode = node1.getTreeParent();
304
305     boolean insideRange = false;
306     final List<PsiElement> result = new ArrayList<>();
307     for (ASTNode node : parentNode.getChildren(null)) {
308       // start
309       if (node1 == node) {
310         insideRange = true;
311       }
312       if (insideRange) {
313         result.add(node.getPsi());
314       }
315       // stop
316       if (node == node2) {
317         break;
318       }
319     }
320     return result;
321   }
322
323   public static int getElementIndentation(final PsiElement element) {
324     final PsiElement compStatement = getStatementList(element);
325     final PsiElement statement = getParentRightBefore(element, compStatement);
326     if (statement == null) {
327       return 0;
328     }
329     PsiElement sibling = statement.getPrevSibling();
330     if (sibling == null) {
331       sibling = compStatement.getPrevSibling();
332     }
333     final String whitespace = sibling instanceof PsiWhiteSpace ? sibling.getText() : "";
334     final int i = whitespace.lastIndexOf("\n");
335     return i != -1 ? whitespace.length() - i - 1 : 0;
336   }
337
338   public static void removeRedundantPass(final PyStatementList statementList) {
339     final PyStatement[] statements = statementList.getStatements();
340     if (statements.length > 1) {
341       for (PyStatement statement : statements) {
342         if (statement instanceof PyPassStatement) {
343           statement.delete();
344         }
345       }
346     }
347   }
348
349   public static boolean isMethodContext(final PsiElement element) {
350     final PsiNamedElement parent = PsiTreeUtil.getParentOfType(element, PyFile.class, PyFunction.class, PyClass.class);
351     // In case if element is inside method which is inside class
352     if (parent instanceof PyFunction && PsiTreeUtil.getParentOfType(parent, PyFile.class, PyClass.class) instanceof PyClass) {
353       return true;
354     }
355     return false;
356   }
357
358
359   @NotNull
360   public static PsiElement getRealContext(@NotNull final PsiElement element) {
361     assertValid(element);
362     final PsiFile file = element.getContainingFile();
363     if (file instanceof PyExpressionCodeFragment) {
364       final PsiElement context = file.getContext();
365       if (LOG.isDebugEnabled()) {
366         LOG.debug("PyPsiUtil.getRealContext(" + element + ") is called. Returned " + context + ". Element inside code fragment");
367       }
368       return context != null ? context : element;
369     }
370     else {
371       if (LOG.isDebugEnabled()) {
372         LOG.debug("PyPsiUtil.getRealContext(" + element + ") is called. Returned " + element + ".");
373       }
374       return element;
375     }
376   }
377
378   /**
379    * Removes comma closest to the given child node along with any whitespaces around. First following comma is checked and only
380    * then, if it doesn't exists, preceding one.
381    *
382    * @param element parent node
383    * @param child   child node comma should be adjacent to
384    * @see #getAdjacentComma(PsiElement)
385    */
386   public static void deleteAdjacentCommaWithWhitespaces(@NotNull PsiElement element, @NotNull PsiElement child) {
387     final PsiElement commaNode = getAdjacentComma(child);
388     if (commaNode != null) {
389       final PsiElement nextNonWhitespace = getNextNonWhitespaceSibling(commaNode);
390       final PsiElement last = nextNonWhitespace == null ? element.getLastChild() : nextNonWhitespace.getPrevSibling();
391       final PsiElement prevNonWhitespace = getPrevNonWhitespaceSibling(commaNode);
392       final PsiElement first = prevNonWhitespace == null ? element.getFirstChild() : prevNonWhitespace.getNextSibling();
393       element.deleteChildRange(first, last);
394     }
395   }
396
397   /**
398    * Returns comments preceding given elements as pair of the first and the last such comments. Comments should not be
399    * separated by any empty line.
400    * @param element element comments should be adjacent to
401    * @return described range or {@code null} if there are no such comments
402    */
403   @Nullable
404   public static Couple<PsiComment> getPrecedingComments(@NotNull PsiElement element) {
405     PsiComment firstComment = null, lastComment = null;
406     overComments:
407     while (true) {
408       int newLinesCount = 0;
409       for (element = element.getPrevSibling(); element instanceof PsiWhiteSpace; element = element.getPrevSibling()) {
410         newLinesCount += StringUtil.getLineBreakCount(element.getText());
411         if (newLinesCount > 1) {
412           break overComments;
413         }
414       }
415       if (element instanceof PsiComment) {
416         if (lastComment == null) {
417           lastComment = (PsiComment)element;
418         }
419         firstComment = (PsiComment)element;
420       }
421       else {
422         break;
423       }
424     }
425     return lastComment == null ? null : Couple.of(firstComment, lastComment);
426   }
427
428   @NotNull
429   static <T, U extends PsiElement> List<T> collectStubChildren(U e,
430                                                                final StubElement<U> stub, final IElementType elementType,
431                                                                final Class<T> itemClass) {
432     final List<T> result = new ArrayList<>();
433     if (stub != null) {
434       final List<StubElement> children = stub.getChildrenStubs();
435       for (StubElement child : children) {
436         if (child.getStubType() == elementType) {
437           //noinspection unchecked
438           result.add((T)child.getPsi());
439         }
440       }
441     }
442     else {
443       e.acceptChildren(new TopLevelVisitor() {
444         @Override
445         protected void checkAddElement(PsiElement node) {
446           if (node.getNode().getElementType() == elementType) {
447             //noinspection unchecked
448             result.add((T)node);
449           }
450         }
451       });
452     }
453     return result;
454   }
455
456   static List<PsiElement> collectAllStubChildren(PsiElement e, StubElement stub) {
457     final List<PsiElement> result = new ArrayList<>();
458     if (stub != null) {
459       final List<StubElement> children = stub.getChildrenStubs();
460       for (StubElement child : children) {
461         //noinspection unchecked
462         result.add(child.getPsi());
463       }
464     }
465     else {
466       e.acceptChildren(new TopLevelVisitor() {
467         @Override
468         protected void checkAddElement(PsiElement node) {
469           result.add(node);
470         }
471       });
472     }
473     return result;
474   }
475
476   public static int findArgumentIndex(PyCallExpression call, PsiElement argument) {
477     final PyExpression[] args = call.getArguments();
478     for (int i = 0; i < args.length; i++) {
479       PyExpression expression = args[i];
480       if (expression instanceof PyKeywordArgument) {
481         expression = ((PyKeywordArgument)expression).getValueExpression();
482       }
483       expression = flattenParens(expression);
484       if (expression == argument) {
485         return i;
486       }
487     }
488     return -1;
489   }
490
491   @Nullable
492   public static PyTargetExpression getAttribute(@NotNull final PyFile file, @NotNull final String name) {
493     PyTargetExpression attr = file.findTopLevelAttribute(name);
494     if (attr == null) {
495       for (PyFromImportStatement element : file.getFromImports()) {
496         PyReferenceExpression expression = element.getImportSource();
497         if (expression == null) continue;
498         final PsiElement resolved = expression.getReference().resolve();
499         if (resolved instanceof PyFile && resolved != file) {
500           return ((PyFile)resolved).findTopLevelAttribute(name);
501         }
502       }
503     }
504     return attr;
505   }
506
507   public static List<PyExpression> getAttributeValuesFromFile(@NotNull PyFile file, @NotNull String name) {
508     List<PyExpression> result = ContainerUtil.newArrayList();
509     final PyTargetExpression attr = file.findTopLevelAttribute(name);
510     if (attr != null) {
511       sequenceToList(result, attr.findAssignedValue());
512     }
513     return result;
514   }
515
516   public static void sequenceToList(List<PyExpression> result, PyExpression value) {
517     value = flattenParens(value);
518     if (value instanceof PySequenceExpression) {
519       result.addAll(ContainerUtil.newArrayList(((PySequenceExpression)value).getElements()));
520     }
521     else {
522       result.add(value);
523     }
524   }
525
526   public static List<String> getStringValues(PyExpression[] elements) {
527     List<String> results = ContainerUtil.newArrayList();
528     for (PyExpression element : elements) {
529       if (element instanceof PyStringLiteralExpression) {
530         results.add(((PyStringLiteralExpression)element).getStringValue());
531       }
532     }
533     return results;
534   }
535
536   @Nullable
537   public static PyExpression flattenParens(@Nullable PyExpression expr) {
538     while (expr instanceof PyParenthesizedExpression) {
539       expr = ((PyParenthesizedExpression)expr).getContainedExpression();
540     }
541     return expr;
542   }
543
544   @Nullable
545   public static String strValue(@Nullable PyExpression expression) {
546     return expression instanceof PyStringLiteralExpression ? ((PyStringLiteralExpression)expression).getStringValue() : null;
547   }
548
549   public static boolean isBefore(@NotNull final PsiElement element, @NotNull final PsiElement element2) {
550     // TODO: From RubyPsiUtil, should be moved to PsiTreeUtil
551     return element.getTextOffset() <= element2.getTextOffset();
552   }
553
554   @Nullable
555   public static QualifiedName asQualifiedName(@Nullable PyExpression expr) {
556     return expr instanceof PyQualifiedExpression ? ((PyQualifiedExpression)expr).asQualifiedName() : null;
557   }
558
559   @Nullable
560   public static PyExpression getFirstQualifier(@NotNull PyQualifiedExpression expr) {
561     final List<PyExpression> expressions = unwindQualifiers(expr);
562     if (!expressions.isEmpty()) {
563       return expressions.get(0);
564     }
565     return null;
566   }
567
568   @NotNull
569   public static String toPath(@Nullable PyQualifiedExpression expr) {
570     if (expr != null) {
571       final QualifiedName qName = expr.asQualifiedName();
572       if (qName != null) {
573         return qName.toString();
574       }
575       final String name = expr.getName();
576       if (name != null) {
577         return name;
578       }
579     }
580     return "";
581   }
582
583   @Nullable
584   protected static QualifiedName asQualifiedName(@NotNull PyQualifiedExpression expr) {
585     return fromReferenceChain(unwindQualifiers(expr));
586   }
587
588   @NotNull
589   private static List<PyExpression> unwindQualifiers(@NotNull final PyQualifiedExpression expr) {
590     final List<PyExpression> path = new LinkedList<>();
591     PyQualifiedExpression e = expr;
592     while (e != null) {
593       path.add(0, e);
594       final PyExpression q = e.getQualifier();
595       e = q instanceof PyQualifiedExpression ? (PyQualifiedExpression)q : null;
596     }
597     return path;
598   }
599
600   @Nullable
601   private static QualifiedName fromReferenceChain(@NotNull List<PyExpression> components) {
602     final List<String> componentNames = new ArrayList<>(components.size());
603     for (PyExpression component : components) {
604       final String refName = (component instanceof PyQualifiedExpression) ? ((PyQualifiedExpression)component).getReferencedName() : null;
605       if (refName == null) {
606         return null;
607       }
608       componentNames.add(refName);
609     }
610     return QualifiedName.fromComponents(componentNames);
611   }
612
613   /**
614    * Wrapper for {@link PsiUtilCore#ensureValid(PsiElement)} that skips nulls
615    */
616   public static void assertValid(@Nullable final PsiElement element) {
617     if (element == null) {
618       return;
619     }
620     PsiUtilCore.ensureValid(element);
621   }
622
623   public static void assertValid(@NotNull final Module module) {
624     Preconditions.checkArgument(!module.isDisposed(), String.format("Module %s is disposed", module));
625   }
626
627   @NotNull
628   public static PsiFileSystemItem getFileSystemItem(@NotNull PsiElement element) {
629     if (element instanceof PsiFileSystemItem) {
630       return (PsiFileSystemItem)element;
631     }
632     return element.getContainingFile();
633   }
634
635   @Nullable
636   public static String getContainingFilePath(@NotNull PsiElement element) {
637     final VirtualFile file;
638     if (element instanceof PsiFileSystemItem) {
639       file = ((PsiFileSystemItem)element).getVirtualFile();
640     }
641     else {
642       file = element.getContainingFile().getVirtualFile();
643     }
644     if (file != null) {
645       return FileUtil.toSystemDependentName(file.getPath());
646     }
647     return null;
648   }
649
650   private static abstract class TopLevelVisitor extends PyRecursiveElementVisitor {
651     public void visitPyElement(final PyElement node) {
652       super.visitPyElement(node);
653       checkAddElement(node);
654     }
655
656     public void visitPyClass(final PyClass node) {
657       checkAddElement(node);  // do not recurse into functions
658     }
659
660     public void visitPyFunction(final PyFunction node) {
661       checkAddElement(node);  // do not recurse into classes
662     }
663
664     protected abstract void checkAddElement(PsiElement node);
665   }
666
667   /**
668    * Returns text of the given PSI element. Unlike obvious {@link PsiElement#getText()} this method unescapes text of the element if latter
669    * belongs to injected code fragment using {@link InjectedLanguageManager#getUnescapedText(PsiElement)}.
670    *
671    * @param element PSI element which text is needed
672    * @return text of the element with any host escaping removed
673    */
674   @NotNull
675   public static String getElementTextWithoutHostEscaping(@NotNull PsiElement element) {
676     final InjectedLanguageManager manager = InjectedLanguageManager.getInstance(element.getProject());
677     if (manager.isInjectedFragment(element.getContainingFile())) {
678       return manager.getUnescapedText(element);
679     }
680     else {
681       return element.getText();
682     }
683   }
684 }