maintain smartpointer to each host psi element to survive documentWindow when psi modification leads to he whole host replacement

This commit is contained in:
Alexey Kudravtsev
2012-02-27 13:29:15 +04:00
parent fec608ae34
commit 7944cd56fe
16 changed files with 322 additions and 188 deletions
@@ -36,6 +36,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.PsiCommentImpl;
import com.intellij.psi.util.PsiTreeUtil;
@@ -191,7 +192,8 @@ public class MyTestInjector {
Disposer.register(parent, new Disposable() {
@Override
public void dispose() {
InjectedLanguageManager.getInstance(psiManager.getProject()).unregisterMultiHostInjector(myMultiHostInjector);
boolean b = InjectedLanguageManager.getInstance(psiManager.getProject()).unregisterMultiHostInjector(myMultiHostInjector);
assert b;
}
});
@@ -250,8 +252,32 @@ public class MyTestInjector {
}
}
}
}
if (host instanceof PsiComment && ((PsiComment)host).getTokenType() == JavaTokenType.C_STYLE_COMMENT) {
/* {{{
* js code
* js code
* }}}
*/
String text = host.getText();
String prefix = "/*\n * {{{\n";
String suffix = " }}}\n */";
if (text.startsWith(prefix) && text.endsWith(suffix)) {
String s = StringUtil.trimEnd(StringUtil.trimStart(text, prefix), suffix);
int off = 0;
while (!s.isEmpty()) {
String t = s.trim();
if (t.startsWith("*")) t = t.substring(1).trim();
int i = s.length() - t.length();
placesToInject.addPlace(js, TextRange.from(prefix.length() + off, i), "", "\n");
off += i;
s = s.substring(i);
}
return;
}
}
if (host instanceof PsiCommentImpl) {
String text = host.getText();
if (text.startsWith("/*--{") && text.endsWith("}--*/")) {
@@ -275,6 +301,7 @@ public class MyTestInjector {
return;
}
}
// inject to all string literal initializers of variables named 'ql'
if (host instanceof PsiLiteralExpression && ((PsiLiteralExpression)host).getValue() instanceof String) {
PsiVariable variable = PsiTreeUtil.getParentOfType(host, PsiVariable.class);
@@ -20,7 +20,7 @@
package com.intellij.injected.editor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -45,7 +45,7 @@ public interface DocumentWindow extends Document {
int injectedToHostLine(int line);
@NotNull
RangeMarker[] getHostRanges();
Segment[] getHostRanges();
boolean areRangesEqual(@NotNull DocumentWindow documentWindow);
@@ -16,10 +16,10 @@
package com.intellij.psi;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -49,63 +49,27 @@ public interface PsiLanguageInjectionHost extends PsiElement {
void visit(@NotNull PsiFile injectedPsi, @NotNull List<Shred> places);
}
class Shred {
public PsiLanguageInjectionHost host;
private final RangeMarker relevantRangeInHost;
public final TextRange range; // range in (decoded) PSI
public final String prefix;
public final String suffix;
interface Shred {
@Nullable("returns null when the host document marker is invalid")
Segment getHostRangeMarker();
public Shred(@NotNull PsiLanguageInjectionHost host, @NotNull final RangeMarker relevantRangeInHost, @NotNull String prefix, @NotNull String suffix, @NotNull TextRange range) {
this.host = host;
this.relevantRangeInHost = relevantRangeInHost;
this.prefix = prefix;
this.suffix = suffix;
this.range = range;
assert isValid();
assert relevantRangeInHost.isValid();
}
@NotNull
TextRange getRangeInsideHost();
public RangeMarker getHostRangeMarker() {
return relevantRangeInHost;
}
boolean isValid();
public TextRange getRangeInsideHost() {
TextRange hostTextRange = host.getTextRange();
ProperTextRange textRange = relevantRangeInHost.isValid() ? new ProperTextRange(relevantRangeInHost.getStartOffset(), relevantRangeInHost.getEndOffset()) : null;
textRange = textRange == null ? null : textRange.intersection(hostTextRange);
if (textRange == null) return new ProperTextRange(0, hostTextRange.getLength());
return textRange.shiftRight(-hostTextRange.getStartOffset());
}
void dispose();
@SuppressWarnings({"HardCodedStringLiteral"})
public String toString() {
return "Shred "+ host.getTextRange() + ": "+ host+
" Inhost range: "+(relevantRangeInHost.isValid() ? "" : "!") + "(" + relevantRangeInHost.getStartOffset()+","+relevantRangeInHost.getEndOffset()+");" +
" PSI range: "+range;
}
@Nullable
PsiLanguageInjectionHost getHost();
public boolean isValid() {
return relevantRangeInHost.isValid() && host.isValid();
}
@NotNull
TextRange getRange();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@NotNull
String getPrefix();
Shred shred = (Shred)o;
return host.equals(shred.host) &&
prefix.equals(shred.prefix) &&
suffix.equals(shred.suffix) &&
range.equals(shred.range) &&
TextRange.create(relevantRangeInHost).equals(TextRange.create(shred.relevantRangeInHost));
}
@Override
public int hashCode() {
return range.hashCode();
}
@NotNull
String getSuffix();
}
}
@@ -342,7 +342,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
if (documentWindow == null) return true;
Place places = InjectedLanguageUtil.getShreds(injectedPsi);
for (PsiLanguageInjectionHost.Shred place : places) {
TextRange textRange = place.getRangeInsideHost().shiftRight(place.host.getTextRange().getStartOffset());
TextRange textRange = place.getRangeInsideHost().shiftRight(place.getHost().getTextRange().getStartOffset());
if (textRange.isEmpty()) continue;
String desc = injectedPsi.getLanguage().getDisplayName() + ": " + injectedPsi.getText();
HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.INJECTED_LANGUAGE_BACKGROUND, textRange, null, desc, injectedAttributes);
@@ -457,7 +457,7 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP
// finds the first nearest text range
private static TextRange findNearestTextRange(final DocumentWindow documentWindow, final int startOffset) {
TextRange textRange = null;
for (RangeMarker marker : documentWindow.getHostRanges()) {
for (Segment marker : documentWindow.getHostRanges()) {
TextRange curRange = ProperTextRange.create(marker);
if (curRange.getStartOffset() > startOffset && textRange != null) break;
textRange = curRange;
@@ -27,10 +27,7 @@ import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.EditReadOnlyListener;
import com.intellij.openapi.editor.ex.LineIterator;
import com.intellij.openapi.editor.ex.RangeMarkerEx;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiLanguageInjectionHost;
import com.intellij.psi.impl.source.tree.injected.Place;
@@ -64,8 +61,8 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
synchronized (myLock) {
myShreds = shreds;
}
myPrefixLineCount = Math.max(1, 1 + StringUtil.countNewLines(shreds.get(0).prefix));
mySuffixLineCount = Math.max(1, 1 + StringUtil.countNewLines(shreds.get(shreds.size()- 1).suffix));
myPrefixLineCount = Math.max(1, 1 + StringUtil.countNewLines(shreds.get(0).getPrefix()));
mySuffixLineCount = Math.max(1, 1 + StringUtil.countNewLines(shreds.get(shreds.size()- 1).getSuffix()));
}
@Nullable("null means we were unable to calculate")
@@ -79,8 +76,10 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
for (int i = myShreds.size() - 1; i >= 0; i--) {
PsiLanguageInjectionHost.Shred shred = myShreds.get(i);
if (!shred.isValid()) continue;
int hShredEndOffset = shred.getHostRangeMarker().getEndOffset();
int hShredStartOffset = shred.getHostRangeMarker().getStartOffset();
Segment hostRangeMarker = shred.getHostRangeMarker();
if (hostRangeMarker == null) continue;
int hShredEndOffset = hostRangeMarker.getEndOffset();
int hShredStartOffset = hostRangeMarker.getStartOffset();
int hShredStartLine = myDelegate.getLineNumber(hShredStartOffset);
int hShredEndLine = myDelegate.getLineNumber(hShredEndOffset);
@@ -131,17 +130,17 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
int[] pos = new int[2]; // pos[0] = curLine; pos[1] == offset;
synchronized (myLock) {
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
RangeMarker hostRange = shred.getHostRangeMarker();
if (!hostRange.isValid()) continue;
Segment hostRange = shred.getHostRangeMarker();
if (hostRange == null) continue;
int found = countNewLinesIn(shred.prefix, pos, line);
int found = countNewLinesIn(shred.getPrefix(), pos, line);
if (found != -1) return found;
String text = hostText.substring(hostRange.getStartOffset(), hostRange.getEndOffset());
found = countNewLinesIn(text, pos, line);
if (found != -1) return found;
found = countNewLinesIn(shred.suffix, pos, line);
found = countNewLinesIn(shred.getSuffix(), pos, line);
if (found != -1) return found;
}
}
@@ -187,11 +186,11 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
CharSequence hostText = myDelegate.getCharsSequence();
synchronized (myLock) {
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
RangeMarker hostRange = shred.getHostRangeMarker();
if (hostRange.isValid()) {
text.append(shred.prefix);
Segment hostRange = shred.getHostRangeMarker();
if (hostRange != null) {
text.append(shred.getPrefix());
text.append(hostText, hostRange.getStartOffset(), hostRange.getEndOffset());
text.append(shred.suffix);
text.append(shred.getSuffix());
}
}
}
@@ -221,10 +220,11 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
int length = 0;
synchronized (myLock) {
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
RangeMarker hostRange = shred.getHostRangeMarker();
length += shred.prefix.length();
Segment hostRange = shred.getHostRangeMarker();
if (hostRange == null) continue;
length += shred.getPrefix().length();
length += hostRange.getEndOffset() - hostRange.getStartOffset();
length += shred.suffix.length();
length += shred.getSuffix().length();
}
}
return length;
@@ -236,16 +236,16 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
String hostText = myDelegate.getText();
synchronized (myLock) {
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
String prefix = shred.prefix;
String suffix = shred.suffix;
String prefix = shred.getPrefix();
String suffix = shred.getSuffix();
lineNumber += StringUtil.getLineBreakCount(prefix.substring(0, Math.min(offset, prefix.length())));
if (offset < prefix.length()) {
return lineNumber;
}
offset -= prefix.length();
RangeMarker currentRange = shred.getHostRangeMarker();
if (!currentRange.isValid()) continue;
Segment currentRange = shred.getHostRangeMarker();
if (currentRange == null) continue;
int rangeLength = currentRange.getEndOffset() - currentRange.getStartOffset();
String rangeText = hostText.substring(currentRange.getStartOffset(), currentRange.getEndOffset());
@@ -271,7 +271,8 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
public TextRange getHostRange(int hostOffset) {
synchronized (myLock) {
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
RangeMarker currentRange = shred.getHostRangeMarker();
Segment currentRange = shred.getHostRangeMarker();
if (currentRange == null) continue;
TextRange textRange = ProperTextRange.create(currentRange);
if (textRange.grown(1).contains(hostOffset)) return textRange;
}
@@ -282,8 +283,9 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
@Override
public void insertString(final int offset, @NotNull CharSequence s) {
synchronized (myLock) {
LOG.assertTrue(offset >= myShreds.get(0).prefix.length(), myShreds.get(0).prefix);
LOG.assertTrue(offset <= getTextLength() - myShreds.get(myShreds.size() - 1).suffix.length(), myShreds.get(myShreds.size() - 1).suffix);
LOG.assertTrue(offset >= myShreds.get(0).getPrefix().length(), myShreds.get(0).getPrefix());
LOG.assertTrue(offset <= getTextLength() - myShreds.get(myShreds.size() - 1).getSuffix().length(),
myShreds.get(myShreds.size() - 1).getSuffix());
}
if (isOneLine()) {
s = StringUtil.replace(s.toString(), "\n", "");
@@ -303,11 +305,11 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
int offset = startOffset;
int curRangeStart = 0;
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
curRangeStart += shred.prefix.length();
curRangeStart += shred.getPrefix().length();
if (offset < curRangeStart) offset = curRangeStart;
if (offset >= endOffset) break;
RangeMarker hostRange = shred.getHostRangeMarker();
if (!hostRange.isValid()) continue;
Segment hostRange = shred.getHostRangeMarker();
if (hostRange == null) continue;
int hostRangeLength = hostRange.getEndOffset() - hostRange.getStartOffset();
TextRange range = TextRange.from(curRangeStart, hostRangeLength);
if (range.contains(offset)) {
@@ -316,7 +318,7 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
offset = rangeToDelete.getEndOffset();
}
curRangeStart += hostRangeLength;
curRangeStart += shred.suffix.length();
curRangeStart += shred.getSuffix().length();
}
}
@@ -357,23 +359,23 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
int curRangeStart = 0;
for (int i = 0; i < myShreds.size(); i++) {
PsiLanguageInjectionHost.Shred shred = myShreds.get(i);
curRangeStart += shred.prefix.length();
curRangeStart += shred.getPrefix().length();
if (offset < curRangeStart) offset = curRangeStart;
RangeMarker hostRange = shred.getHostRangeMarker();
if (!hostRange.isValid()) continue;
Segment hostRange = shred.getHostRangeMarker();
if (hostRange == null) continue;
int hostRangeLength = hostRange.getEndOffset() - hostRange.getStartOffset();
TextRange range = TextRange.from(curRangeStart, hostRangeLength);
if (range.contains(offset) || range.getEndOffset() == offset/* in case of inserting at the end*/) {
TextRange rangeToModify = new TextRange(offset, Math.min(range.getEndOffset(), endOffset));
TextRange hostRangeToModify = rangeToModify.shiftRight(hostRange.getStartOffset() - curRangeStart);
CharSequence toReplace = i == myShreds.size() - 1 || range.getEndOffset() + shred.suffix.length() >= endOffset
CharSequence toReplace = i == myShreds.size() - 1 || range.getEndOffset() + shred.getSuffix().length() >= endOffset
? s : s.subSequence(0, Math.min(hostRangeToModify.getLength(), s.length()));
s = toReplace == s ? "" : s.subSequence(toReplace.length(), s.length());
hostRangesToModify.add(Pair.create(hostRangeToModify, toReplace));
offset = rangeToModify.getEndOffset();
}
curRangeStart += hostRangeLength;
curRangeStart += shred.suffix.length();
curRangeStart += shred.getSuffix().length();
if (curRangeStart >= endOffset) break;
}
}
@@ -495,8 +497,8 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
@Override
public void setText(@NotNull CharSequence text) {
synchronized (myLock) {
LOG.assertTrue(text.toString().startsWith(myShreds.get(0).prefix));
LOG.assertTrue(text.toString().endsWith(myShreds.get(myShreds.size() - 1).suffix));
LOG.assertTrue(text.toString().startsWith(myShreds.get(0).getPrefix()));
LOG.assertTrue(text.toString().endsWith(myShreds.get(myShreds.size() - 1).getSuffix()));
if (isOneLine()) {
text = StringUtil.replace(text.toString(), "\n", "");
}
@@ -505,7 +507,8 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
for (int i = 0; i < changes.length; i++) {
String change = changes[i];
if (change != null) {
RangeMarker hostRange = myShreds.get(i).getHostRangeMarker();
Segment hostRange = myShreds.get(i).getHostRangeMarker();
if (hostRange == null) continue;
myDelegate.replaceString(hostRange.getStartOffset(), hostRange.getEndOffset(), change);
}
}
@@ -514,14 +517,16 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
@Override
@NotNull
public RangeMarker[] getHostRanges() {
public Segment[] getHostRanges() {
synchronized (myLock) {
RangeMarker[] markers = new RangeMarker[myShreds.size()];
for (int i = 0; i < myShreds.size(); i++) {
PsiLanguageInjectionHost.Shred shred = myShreds.get(i);
markers[i] = shred.getHostRangeMarker();
List<Segment> markers = new ArrayList<Segment>(myShreds.size());
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
Segment hostMarker = shred.getHostRangeMarker();
if (hostMarker != null) {
markers.add(hostMarker);
}
}
return markers;
return markers.isEmpty() ? Segment.EMPTY_ARRAY : markers.toArray(new Segment[markers.size()]);
}
}
@@ -618,20 +623,20 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
@Override
public int hostToInjected(int hostOffset) {
synchronized (myLock) {
if (hostOffset < myShreds.get(0).getHostRangeMarker().getStartOffset()) return myShreds.get(0).prefix.length();
if (hostOffset < myShreds.get(0).getHostRangeMarker().getStartOffset()) return myShreds.get(0).getPrefix().length();
int offset = 0;
for (int i = 0; i < myShreds.size(); i++) {
offset += myShreds.get(i).prefix.length();
RangeMarker currentRange = myShreds.get(i).getHostRangeMarker();
RangeMarker nextRange = i == myShreds.size() - 1 ? null : myShreds.get(i + 1).getHostRangeMarker();
offset += myShreds.get(i).getPrefix().length();
Segment currentRange = myShreds.get(i).getHostRangeMarker();
Segment nextRange = i == myShreds.size() - 1 ? null : myShreds.get(i + 1).getHostRangeMarker();
if (nextRange == null || hostOffset < nextRange.getStartOffset()) {
if (hostOffset >= currentRange.getEndOffset()) hostOffset = currentRange.getEndOffset();
return offset + hostOffset - currentRange.getStartOffset();
}
offset += currentRange.getEndOffset() - currentRange.getStartOffset();
offset += myShreds.get(i).suffix.length();
offset += myShreds.get(i).getSuffix().length();
}
return getTextLength() - myShreds.get(myShreds.size() - 1).suffix.length();
return getTextLength() - myShreds.get(myShreds.size() - 1).getSuffix().length();
}
}
@@ -655,11 +660,12 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
private int injectedToHost(int offset, boolean preferLeftFragment) {
synchronized (myLock) {
if (offset < myShreds.get(0).prefix.length()) return myShreds.get(0).getHostRangeMarker().getStartOffset();
if (offset < myShreds.get(0).getPrefix().length()) return myShreds.get(0).getHostRangeMarker().getStartOffset();
int prevEnd = 0;
for (int i = 0; i < myShreds.size(); i++) {
RangeMarker currentRange = myShreds.get(i).getHostRangeMarker();
offset -= myShreds.get(i).prefix.length();
Segment currentRange = myShreds.get(i).getHostRangeMarker();
if (currentRange == null) continue;
offset -= myShreds.get(i).getPrefix().length();
int length = currentRange.getEndOffset() - currentRange.getStartOffset();
if (offset < 0) {
return preferLeftFragment ? prevEnd : currentRange.getStartOffset() - 1;
@@ -671,7 +677,7 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
return currentRange.getStartOffset() + offset;
}
offset -= length;
offset -= myShreds.get(i).suffix.length();
offset -= myShreds.get(i).getSuffix().length();
prevEnd = currentRange.getEndOffset();
}
return myShreds.get(myShreds.size() - 1).getHostRangeMarker().getEndOffset();
@@ -712,8 +718,8 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
synchronized (myLock) {
if (end - start > myShreds.get(0).getHostRangeMarker().getEndOffset() - myShreds.get(0).getHostRangeMarker().getStartOffset()) return false;
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
RangeMarker hostRange = shred.getHostRangeMarker();
if (!hostRange.isValid()) continue;
Segment hostRange = shred.getHostRangeMarker();
if (hostRange == null) continue;
TextRange textRange = ProperTextRange.create(hostRange);
if (textRange.contains(new ProperTextRange(start, end))) return true;
}
@@ -730,8 +736,9 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
synchronized (myLock) {
int offset = 0;
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
RangeMarker hostRange = shred.getHostRangeMarker();
offset += shred.prefix.length();
Segment hostRange = shred.getHostRangeMarker();
if (hostRange == null) continue;
offset += shred.getPrefix().length();
int length = hostRange.getEndOffset() - hostRange.getStartOffset();
TextRange intersection = new ProperTextRange(offset, offset + length).intersection(rangeToEdit);
if (intersection != null) {
@@ -741,7 +748,7 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
endOffset = intersection.getEndOffset();
}
offset += length;
offset += shred.suffix.length();
offset += shred.getSuffix().length();
}
}
if (startOffset == -1) return null;
@@ -760,8 +767,8 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
for (int i = 0; i < result.length; i++) {
String change = result[i];
if (change == null) continue;
String prefix = myShreds.get(i).prefix;
String suffix = myShreds.get(i).suffix;
String prefix = myShreds.get(i).getPrefix();
String suffix = myShreds.get(i).getSuffix();
assert change.startsWith(prefix) : change + "/" + prefix;
assert change.endsWith(suffix) : change + "/" + suffix;
result[i] = StringUtil.trimEnd(StringUtil.trimStart(change, prefix), suffix);
@@ -773,9 +780,9 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
private String getRangeText(String hostText, int hostNum) {
synchronized (myLock) {
PsiLanguageInjectionHost.Shred shred = myShreds.get(hostNum);
return shred.prefix +
return shred.getPrefix() +
hostText.substring(shred.getHostRangeMarker().getStartOffset(), shred.getHostRangeMarker().getEndOffset()) +
shred.suffix;
shred.getSuffix();
}
}
private void calculateMinEditSequence(String hostText, String newText, String[] result, int i, int j) {
@@ -797,8 +804,8 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
return;
}
if (i+1 == j) {
String suffix = myShreds.get(i).suffix;
String prefix = myShreds.get(j).prefix;
String suffix = myShreds.get(i).getSuffix();
String prefix = myShreds.get(j).getPrefix();
String separator = suffix + prefix;
if (!separator.isEmpty()) {
int sep = newText.indexOf(separator);
@@ -843,12 +850,12 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
for (int i = 0; i < shreds.size(); i++) {
PsiLanguageInjectionHost.Shred shred = shreds.get(i);
PsiLanguageInjectionHost.Shred otherShred = otherShreds.get(i);
if (!shred.prefix.equals(otherShred.prefix)) return false;
if (!shred.suffix.equals(otherShred.suffix)) return false;
if (!shred.getPrefix().equals(otherShred.getPrefix())) return false;
if (!shred.getSuffix().equals(otherShred.getSuffix())) return false;
RangeMarker hostRange = shred.getHostRangeMarker();
RangeMarker other = otherShred.getHostRangeMarker();
if (hostRange.getStartOffset() != other.getStartOffset()) return false;
Segment hostRange = shred.getHostRangeMarker();
Segment other = otherShred.getHostRangeMarker();
if (hostRange == null || other == null || hostRange.getStartOffset() != other.getStartOffset()) return false;
if (hostRange.getEndOffset() != other.getEndOffset()) return false;
}
return true;
@@ -880,10 +887,7 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
@Override
public void dispose() {
synchronized (myLock) {
for (PsiLanguageInjectionHost.Shred shred : myShreds) {
RangeMarker rangeMarker = shred.getHostRangeMarker();
rangeMarker.dispose();
}
myShreds.dispose();
}
}
@@ -70,8 +70,8 @@ public abstract class InjectedLanguageBlockBuilder {
canProcessFragment(childText.substring(textRange.getEndOffset()), injectionHost))) {
injectedFile[0] = injectedPsi;
injectedRangeInsideHost.set(textRange);
prefixLength.set(shred.prefix != null ? shred.prefix.length() : 0);
suffixLength.set(shred.suffix != null ? shred.suffix.length() : 0);
prefixLength.set(shred.getPrefix() != null ? shred.getPrefix().length() : 0);
suffixLength.set(shred.getSuffix() != null ? shred.getSuffix().length() : 0);
}
}
}
@@ -21,16 +21,15 @@ import com.intellij.injected.editor.DocumentWindowImpl;
import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.lang.Language;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.FreeThreadedFileViewProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -55,7 +54,7 @@ public class InjectedFileViewProvider extends SingleRootFileViewProvider impleme
@NotNull Language language) {
super(psiManager, (VirtualFile)virtualFile, true, language);
myDocumentWindow = documentWindow;
myProject = documentWindow.getShreds().get(0).host.getProject();
myProject = documentWindow.getShreds().get(0).getHost().getProject();
}
@Override
@@ -73,10 +72,11 @@ public class InjectedFileViewProvider extends SingleRootFileViewProvider impleme
String change = changes[i];
if (change != null) {
PsiLanguageInjectionHost.Shred shred = shreds.get(i);
PsiLanguageInjectionHost host = shred.host;
PsiLanguageInjectionHost host = shred.getHost();
TextRange rangeInsideHost = shred.getRangeInsideHost();
String newHostText = StringUtil.replaceSubstring(host.getText(), rangeInsideHost, change);
shred.host = host.updateText(newHostText);
//shred.host =
host.updateText(newHostText);
}
}
}
@@ -89,7 +89,7 @@ public class InjectedFileViewProvider extends SingleRootFileViewProvider impleme
PsiFile hostFile = documentManager.getPsiFile(hostDocument);
final Language hostFileLanguage = getPsi(getBaseLanguage()).getContext().getContainingFile().getLanguage();
PsiFile hostPsiFileCopy = (PsiFile)hostFile.copy();
RangeMarker firstTextRange = oldDocumentWindow.getHostRanges()[0];
Segment firstTextRange = oldDocumentWindow.getHostRanges()[0];
PsiElement elementCopy = hostPsiFileCopy.getViewProvider().findElementAt(firstTextRange.getStartOffset(), hostFileLanguage);
assert elementCopy != null;
final Ref<FileViewProvider> provider = new Ref<FileViewProvider>();
@@ -105,15 +105,6 @@ public class InjectedFileViewProvider extends SingleRootFileViewProvider impleme
return provider.get();
}
@Override
@Nullable
protected PsiFile getPsiInner(Language target) {
// when FileManager rebuilds file map, all files temporarily become invalid, so this check is doomed
PsiFile file = super.getPsiInner(target);
//if (file == null || file.getContext() == null) return null;
return file;
}
// returns true if shreds were set, false if old ones were reused
boolean setShreds(@NotNull Place newShreds, @NotNull Project project) {
synchronized (myLock) {
@@ -32,7 +32,6 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.ExtensionPointListener;
import com.intellij.openapi.extensions.Extensions;
@@ -43,6 +42,7 @@ import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
@@ -58,6 +58,7 @@ import org.jetbrains.annotations.TestOnly;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -128,7 +129,8 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager impleme
final PsiFile hostPsiFile = documentManager.getCachedPsiFile(hostDocument);
if (hostPsiFile == null) return;
final List<DocumentWindow> injected = InjectedLanguageUtil.getCachedInjectedDocuments(hostPsiFile);
final CopyOnWriteArrayList<DocumentWindow> injected =
(CopyOnWriteArrayList<DocumentWindow>)InjectedLanguageUtil.getCachedInjectedDocuments(hostPsiFile);
if (injected.isEmpty()) return;
if (myProgress.isCanceled()) {
@@ -143,8 +145,9 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager impleme
if (indicator != null && indicator.isCanceled()) return false;
if (documentManager.isUncommited(hostDocument) || !hostPsiFile.isValid()) return false; // will be committed later
RangeMarker rangeMarker = documentWindow.getHostRanges()[0];
PsiElement element = rangeMarker.isValid() ? hostPsiFile.findElementAt(rangeMarker.getStartOffset()) : null;
Segment[] ranges = documentWindow.getHostRanges();
Segment rangeMarker = ranges.length > 0 ? ranges[0] : null;
PsiElement element = rangeMarker == null ? null : hostPsiFile.findElementAt(rangeMarker.getStartOffset());
if (element == null) {
synchronized (PsiLock.LOCK) {
injected.remove(documentWindow);
@@ -166,7 +169,7 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager impleme
}
else if (stillInjectedDocument[0] != documentWindow) {
injected.remove(documentWindow);
injected.add(stillInjectedDocument[0]);
injected.addIfAbsent(stillInjectedDocument[0]);
}
}
@@ -334,7 +337,7 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager impleme
int count = 0;
int offset = 0;
for (PsiLanguageInjectionHost.Shred shred : shreds) {
TextRange encodedRange = TextRange.from(offset + shred.prefix.length(), shred.getRangeInsideHost().getLength());
TextRange encodedRange = TextRange.from(offset + shred.getPrefix().length(), shred.getRangeInsideHost().getLength());
TextRange intersection = encodedRange.intersection(rangeToEdit);
if (intersection != null) {
count++;
@@ -364,7 +367,7 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager impleme
((List<TextRange>)result).add(intersection);
}
}
offset += shred.prefix.length() + shred.getRangeInsideHost().getLength() + shred.suffix.length();
offset += shred.getPrefix().length() + shred.getRangeInsideHost().getLength() + shred.getSuffix().length();
}
return count == 0 ? Collections.<TextRange>emptyList() : count == 1 ? Collections.singletonList((TextRange)result) : (List<TextRange>)result;
}
@@ -85,7 +85,7 @@ public class InjectedLanguageUtil {
@Override
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
for (PsiLanguageInjectionHost.Shred place : places) {
if (place.host == inTree) {
if (place.getHost() == inTree) {
result.add(new Pair<PsiElement, TextRange>(injectedPsi, place.getRangeInsideHost()));
}
}
@@ -238,7 +238,7 @@ public class InjectedLanguageUtil {
for (Pair<Place, PsiFile> pair : places) {
Place place = pair.first;
for (PsiLanguageInjectionHost.Shred shred : place) {
if (shred.host.getTextRange().intersects(elementRange)) {
if (shred.getHost().getTextRange().intersects(elementRange)) {
if (place.isValid()) break nextParent;
}
}
@@ -310,7 +310,7 @@ public class InjectedLanguageUtil {
@Override
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
for (PsiLanguageInjectionHost.Shred place : places) {
TextRange hostRange = place.host.getTextRange();
TextRange hostRange = place.getHost().getTextRange();
if (hostRange.cutOut(place.getRangeInsideHost()).grown(1).contains(hostOffset)) {
DocumentWindowImpl document = (DocumentWindowImpl)documentManager.getCachedDocument(injectedPsi);
if (document == null) return;
@@ -74,14 +74,14 @@ class LeafPatcher extends RecursiveTreeElementWalkingVisitor {
private StringBuilder constructTextFromHostPSI(int startOffset, int endOffset) {
PsiLanguageInjectionHost.Shred current = myShreds.get(shredNo);
if (hostText == null) hostText = current.host.getText();
if (hostText == null) hostText = current.getHost().getText();
StringBuilder text = new StringBuilder(endOffset-startOffset);
while (startOffset < endOffset) {
TextRange shredRange = current.range;
String prefix = current.prefix;
TextRange shredRange = current.getRange();
String prefix = current.getPrefix();
if (startOffset >= shredRange.getEndOffset()) {
current = myShreds.get(++shredNo);
hostText = current.host.getText();
hostText = current.getHost().getText();
continue;
}
assert startOffset >= shredRange.getStartOffset();
@@ -93,7 +93,7 @@ class LeafPatcher extends RecursiveTreeElementWalkingVisitor {
continue;
}
String suffix = current.suffix;
String suffix = current.getSuffix();
if (startOffset < shredRange.getEndOffset() - suffix.length()) {
// inside host body, cut out from the host text
int startOffsetInHost = myEscapers.get(shredNo).getOffsetInHost(startOffset - shredRange.getStartOffset() - prefix.length(), current.getRangeInsideHost());
@@ -53,7 +53,6 @@ import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.ArrayUtil;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -183,7 +182,7 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
RangeMarker relevantMarker = myHostDocument.createRangeMarker(relevantRangeInHost);
relevantMarker.setGreedyToLeft(true);
relevantMarker.setGreedyToRight(true);
shreds.add(new PsiLanguageInjectionHost.Shred(host, relevantMarker, prefix, suffix, new ProperTextRange(startOffset, endOffset)));
shreds.add(new ShredImpl(host, myHostPsiFile, relevantMarker, prefix, suffix, new ProperTextRange(startOffset, endOffset)));
return this;
}
@@ -193,8 +192,9 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
if (shreds.isEmpty()) {
throw new IllegalStateException("Seems you haven't called addPlace()");
}
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
assert ArrayUtil.indexOf(documentManager.getUncommittedDocuments(), myHostDocument) == -1 : "document is uncommitted: "+myHostDocument;
PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(myProject);
//todo restore
//assert !documentManager.getUncommittedDocumentsUnsafe().contains(myHostDocument) : "document is uncommitted: "+myHostDocument;
Place place = new Place(shreds);
DocumentWindowImpl documentWindow = new DocumentWindowImpl(myHostDocument, isOneLineEditor, place);
@@ -218,7 +218,7 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
assert parserDefinition != null : "Parser definition for language "+myLanguage+" is null";
PsiFile psiFile = parserDefinition.createFile(viewProvider);
SmartPsiElementPointer<PsiLanguageInjectionHost> pointer = createHostSmartPointer(shreds.get(0).host, myHostPsiFile);
SmartPsiElementPointer<PsiLanguageInjectionHost> pointer = ((ShredImpl)shreds.get(0)).getSmartPointer();
synchronized (PsiLock.LOCK) {
final ASTNode parsedNode = keepTreeFromChameleoningBack(psiFile);
@@ -334,7 +334,7 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
private void assertEverythingIsAllright(PsiDocumentManager documentManager, DocumentWindowImpl documentWindow, PsiFile psiFile) {
boolean isAncestor = false;
for (PsiLanguageInjectionHost.Shred shred : shreds) {
PsiLanguageInjectionHost host = shred.host;
PsiLanguageInjectionHost host = shred.getHost();
isAncestor |= PsiTreeUtil.isAncestor(myContextElement, host, false);
}
assert isAncestor : exceptionContext(myContextElement + " must be the parent of at least one of injection hosts");
@@ -355,12 +355,6 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
result.add(Pair.create(place, psiFile));
}
@NotNull
private static <T extends PsiLanguageInjectionHost> SmartPsiElementPointer<T> createHostSmartPointer(@NotNull T host, @NotNull PsiFile hostPsiFile) {
return hostPsiFile.isPhysical()
? SmartPointerManager.getInstance(host.getProject()).createSmartPsiElementPointer(host, hostPsiFile)
: new IdentitySmartPointer<T>(host, hostPsiFile);
}
private static void patchLeafs(ASTNode parsedNode, List<LiteralTextEscaper<? extends PsiLanguageInjectionHost>> escapers, Place shreds) {
LeafPatcher patcher = new LeafPatcher(shreds, escapers);
@@ -373,7 +367,7 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
"\nLeaf texts concatenated:'"+ patcher.catLeafs +"';" +
"\nFile root: "+parsedNode+
"\nLanguage: "+parsedNode.getPsi().getLanguage()+
"\nHost file: "+shreds.get(0).host.getContainingFile().getVirtualFile()
"\nHost file: "+ shreds.get(0).getHost().getContainingFile().getVirtualFile()
;
for (Map.Entry<LeafElement, String> entry : patcher.newTexts.entrySet()) {
LeafElement leaf = entry.getKey();
@@ -396,7 +390,6 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
DocumentWindowImpl oldDocument = (DocumentWindowImpl)injected.get(i);
final PsiFileImpl oldFile = (PsiFileImpl)documentManager.getCachedPsiFile(oldDocument);
FileViewProvider viewProvider;
if (oldFile == null ||
!oldFile.isValid() ||
!((viewProvider = oldFile.getViewProvider()) instanceof InjectedFileViewProvider) ||
@@ -472,13 +465,13 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
if (range.getStartOffset() >= shredEndOffset) {
hostNum++;
PsiLanguageInjectionHost.Shred shred = shreds.get(hostNum);
shredEndOffset = shred.range.getEndOffset();
shredEndOffset = shred.getRange().getEndOffset();
prevHostEndOffset = range.getStartOffset();
host = shred.host;
host = shred.getHost();
escaper = escapers.get(hostNum);
rangeInsideHost = shred.getRangeInsideHost();
prefixLength = shred.prefix.length();
suffixLength = shred.suffix.length();
prefixLength = shred.getPrefix().length();
suffixLength = shred.getSuffix().length();
}
//in prefix/suffix or spills over to next fragment
if (range.getStartOffset() < prevHostEndOffset + prefixLength) {
@@ -41,7 +41,7 @@ public class Place extends SmartList<PsiLanguageInjectionHost.Shred> {
public void dispose() {
for (PsiLanguageInjectionHost.Shred shred : this) {
shred.getHostRangeMarker().dispose();
shred.dispose();
}
}
}
@@ -0,0 +1,146 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source.tree.injected;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class ShredImpl implements PsiLanguageInjectionHost.Shred {
private final RangeMarker relevantRangeInHost;
private final SmartPsiElementPointer<PsiLanguageInjectionHost> hostElementPointer;
private final TextRange range; // range in (decoded) PSI
private final String prefix;
private final String suffix;
ShredImpl(@NotNull PsiLanguageInjectionHost host,
@NotNull PsiFile hostPsiFile,
@NotNull final RangeMarker relevantRangeInHost,
@NotNull String prefix,
@NotNull String suffix,
@NotNull TextRange range) {
hostElementPointer = createHostSmartPointer(host, hostPsiFile);
this.relevantRangeInHost = relevantRangeInHost;
this.prefix = prefix;
this.suffix = suffix;
this.range = range;
assert isValid();
assert relevantRangeInHost.isValid();
}
public SmartPsiElementPointer<PsiLanguageInjectionHost> getSmartPointer() {
return hostElementPointer;
}
@NotNull
private static <T extends PsiLanguageInjectionHost> SmartPsiElementPointer<T> createHostSmartPointer(@NotNull T host,
@NotNull PsiFile hostPsiFile) {
return hostPsiFile.isPhysical()
? SmartPointerManager.getInstance(hostPsiFile.getProject()).createSmartPsiElementPointer(host, hostPsiFile)
: new IdentitySmartPointer<T>(host, hostPsiFile);
}
@Override
@Nullable("returns null when the host document marker is invalid")
public Segment getHostRangeMarker() {
return relevantRangeInHost;
}
@Override
@NotNull
public TextRange getRangeInsideHost() {
TextRange hostTextRange = getHost().getTextRange();
ProperTextRange textRange =
relevantRangeInHost.isValid() ? new ProperTextRange(relevantRangeInHost.getStartOffset(), relevantRangeInHost.getEndOffset()) : null;
textRange = textRange == null ? null : textRange.intersection(hostTextRange);
if (textRange == null) return new ProperTextRange(0, hostTextRange.getLength());
return textRange.shiftRight(-hostTextRange.getStartOffset());
}
@SuppressWarnings({"HardCodedStringLiteral"})
public String toString() {
return "Shred " + getHost().getTextRange() + ": " + getHost() +
" Inhost range: " +
(relevantRangeInHost.isValid() ? "" : "!") +
"(" +
relevantRangeInHost.getStartOffset() +
"," +
relevantRangeInHost.getEndOffset() +
");" +
" PSI range: " +
range;
}
@Override
public boolean isValid() {
PsiLanguageInjectionHost host = getHost();
return relevantRangeInHost.isValid() && host != null && host.isValid();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PsiLanguageInjectionHost.Shred shred = (PsiLanguageInjectionHost.Shred)o;
PsiLanguageInjectionHost host = getHost();
return host != null &&
host.equals(shred.getHost()) &&
prefix.equals(shred.getPrefix()) &&
suffix.equals(shred.getSuffix()) &&
range.equals(shred.getRange()) &&
TextRange.create(relevantRangeInHost).equals(TextRange.create(shred.getHostRangeMarker()));
}
@Override
public int hashCode() {
return range.hashCode();
}
@Override
public void dispose() {
relevantRangeInHost.dispose();
}
@Override
@Nullable
public PsiLanguageInjectionHost getHost() {
return hostElementPointer.getElement();
}
@NotNull
@Override
public TextRange getRange() {
return range;
}
@NotNull
@Override
public String getPrefix() {
return prefix;
}
@NotNull
@Override
public String getSuffix() {
return suffix;
}
}
@@ -19,6 +19,7 @@ package com.intellij.openapi.util;
* User: cdr
*/
public interface Segment {
Segment[] EMPTY_ARRAY = new Segment[0];
int getStartOffset();
int getEndOffset();
}
@@ -112,6 +112,10 @@ public class TextRange implements Segment, Serializable {
return create(segment.getStartOffset(), segment.getEndOffset());
}
public static boolean areSegmentsEqual(@NotNull Segment segment1, @NotNull Segment segment2) {
return segment1.getStartOffset() == segment2.getStartOffset()
&& segment1.getEndOffset() == segment2.getEndOffset();
}
@NotNull
public String replace(@NotNull String original, @NotNull String replacement) {
String beginning = original.substring(0, getStartOffset());
@@ -176,7 +176,7 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction {
final String text = InjectedLanguageManager.getInstance(project).getUnescapedText(injectedFile);
final String newFileName =
StringUtil.notNullize(language.getDisplayName(), "Injected") + " Fragment " + "(" +
origFile.getName() + ":" + shreds.get(0).host.getTextRange().getStartOffset() + ")" + "." + fileType.getDefaultExtension();
origFile.getName() + ":" + shreds.get(0).getHost().getTextRange().getStartOffset() + ")" + "." + fileType.getDefaultExtension();
myNewFile = factory.createFileFromText(newFileName, language, text, true, true);
myNewVirtualFile = (LightVirtualFile)myNewFile.getVirtualFile();
assert myNewVirtualFile != null;
@@ -296,11 +296,12 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction {
final SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject);
for (PsiLanguageInjectionHost.Shred shred : shreds) {
final RangeMarker rangeMarker = myNewDocument
.createRangeMarker(shred.range.getStartOffset() + shred.prefix.length(), shred.range.getEndOffset() - shred.suffix.length());
.createRangeMarker(
shred.getRange().getStartOffset() + shred.getPrefix().length(), shred.getRange().getEndOffset() - shred.getSuffix().length());
final TextRange rangeInsideHost = shred.getRangeInsideHost();
final RangeMarker origMarker =
myOrigDocument.createRangeMarker(rangeInsideHost.shiftRight(shred.host.getTextRange().getStartOffset()));
myMarkers.put(smartPointerManager.createSmartPsiElementPointer(shred.host), Pair.create(origMarker, rangeMarker));
myOrigDocument.createRangeMarker(rangeInsideHost.shiftRight(shred.getHost().getTextRange().getStartOffset()));
myMarkers.put(smartPointerManager.createSmartPsiElementPointer(shred.getHost()), Pair.create(origMarker, rangeMarker));
}
for (Pair<RangeMarker, RangeMarker> markers : myMarkers.values()) {
markers.first.setGreedyToLeft(true);