@Override @Nullable public PsiElement restoreElement() { long typeAndId = myStubElementTypeAndId; int stubId = (int) typeAndId; if (stubId != -1) { PsiFile file = restoreFile(); if (!(file instanceof PsiFileWithStubSupport)) return null; short index = (short) (typeAndId >> 32); IStubElementType stubElementType = (IStubElementType) IElementType.find(index); return PsiAnchor.restoreFromStubIndex( (PsiFileWithStubSupport) file, stubId, stubElementType, false); } Segment psiRange = getPsiRange(); if (psiRange == null) return null; PsiFile file = restoreFile(); if (file == null) return null; PsiElement anchor = file.findElementAt(psiRange.getStartOffset()); if (anchor == null) return null; TextRange range = anchor.getTextRange(); if (range.getStartOffset() != psiRange.getStartOffset() || range.getEndOffset() != psiRange.getEndOffset()) return null; for (SmartPointerAnchorProvider provider : SmartPointerAnchorProvider.EP_NAME.getExtensions()) { final PsiElement element = provider.restoreElement(anchor); if (element != null) return element; } return null; }
public int hostToInjectedUnescaped(int hostOffset) { synchronized (myLock) { Segment hostRangeMarker = myShreds.get(0).getHostRangeMarker(); if (hostRangeMarker == null || hostOffset < hostRangeMarker.getStartOffset()) return myShreds.get(0).getPrefix().length(); int offset = 0; for (int i = 0; i < myShreds.size(); i++) { offset += myShreds.get(i).getPrefix().length(); Segment currentRange = myShreds.get(i).getHostRangeMarker(); if (currentRange == null) continue; Segment nextRange = i == myShreds.size() - 1 ? null : myShreds.get(i + 1).getHostRangeMarker(); if (nextRange == null || hostOffset < nextRange.getStartOffset()) { if (hostOffset >= currentRange.getEndOffset()) { offset += myShreds.get(i).getRange().getLength(); } else { // todo use escaper to convert host-range delta into injected space offset += hostOffset - currentRange.getStartOffset(); } return offset; } offset += myShreds.get(i).getRange().getLength(); offset += myShreds.get(i).getSuffix().length(); } return getTextLength() - myShreds.get(myShreds.size() - 1).getSuffix().length(); } }
private int injectedToHost(int offset, boolean preferLeftFragment) { synchronized (myLock) { if (offset < myShreds.get(0).getPrefix().length()) { Segment hostRangeMarker = myShreds.get(0).getHostRangeMarker(); return hostRangeMarker == null ? 0 : hostRangeMarker.getStartOffset(); } int prevEnd = 0; for (int i = 0; i < myShreds.size(); i++) { 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; } if (offset == 0) { return preferLeftFragment && i != 0 ? prevEnd : currentRange.getStartOffset(); } if (offset < length || offset == length && preferLeftFragment) { return currentRange.getStartOffset() + offset; } offset -= length; offset -= myShreds.get(i).getSuffix().length(); prevEnd = currentRange.getEndOffset(); } Segment hostRangeMarker = myShreds.get(myShreds.size() - 1).getHostRangeMarker(); return hostRangeMarker == null ? 0 : hostRangeMarker.getEndOffset(); } }
private void doReplaceString(int startOffset, int endOffset, CharSequence s) { assert intersectWithEditable(new TextRange(startOffset, startOffset)) != null; assert intersectWithEditable(new TextRange(endOffset, endOffset)) != null; List<Pair<TextRange, CharSequence>> hostRangesToModify; synchronized (myLock) { hostRangesToModify = new ArrayList<Pair<TextRange, CharSequence>>(myShreds.size()); int offset = startOffset; int curRangeStart = 0; for (int i = 0; i < myShreds.size(); i++) { PsiLanguageInjectionHost.Shred shred = myShreds.get(i); curRangeStart += shred.getPrefix().length(); if (offset < curRangeStart) offset = curRangeStart; 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.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.getSuffix().length(); if (curRangeStart > endOffset) break; } } int delta = 0; for (Pair<TextRange, CharSequence> pair : hostRangesToModify) { TextRange hostRange = pair.getFirst(); CharSequence replace = pair.getSecond(); myDelegate.replaceString( hostRange.getStartOffset() + delta, hostRange.getEndOffset() + delta, replace); delta -= hostRange.getLength() - replace.length(); } }
@Nullable("null means we were unable to calculate") LogicalPosition hostToInjectedInVirtualSpace(@NotNull LogicalPosition hPos) { // beware the virtual space int hLineStartOffset = hPos.line >= myDelegate.getLineCount() ? myDelegate.getTextLength() : myDelegate.getLineStartOffset(hPos.line); int iLineStartOffset = hostToInjected(hLineStartOffset); int iLine = getLineNumber(iLineStartOffset); synchronized (myLock) { for (int i = myShreds.size() - 1; i >= 0; i--) { PsiLanguageInjectionHost.Shred shred = myShreds.get(i); if (!shred.isValid()) continue; 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); if (hShredStartLine <= hPos.line && hPos.line <= hShredEndLine) { int hColumnOfShredEnd = hShredEndOffset - hLineStartOffset; int iColumnOfShredEnd = hostToInjected(hShredEndOffset) - iLineStartOffset; int iColumn = iColumnOfShredEnd + hPos.column - hColumnOfShredEnd; return new LogicalPosition(iLine, iColumn); } } } return null; }
@Override @Deprecated @Nullable public TextRange intersectWithEditable(@NotNull TextRange rangeToEdit) { int startOffset = -1; int endOffset = -1; synchronized (myLock) { int offset = 0; for (PsiLanguageInjectionHost.Shred shred : myShreds) { 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) { if (startOffset == -1) { startOffset = intersection.getStartOffset(); } endOffset = intersection.getEndOffset(); } offset += length; offset += shred.getSuffix().length(); } } if (startOffset == -1) return null; return new ProperTextRange(startOffset, endOffset); }
@Override public boolean pointsToTheSameElementAs(@NotNull final SmartPointerElementInfo other) { if (other instanceof SelfElementInfo) { SelfElementInfo otherInfo = (SelfElementInfo) other; if (!getVirtualFile().equals(other.getVirtualFile()) || myType != otherInfo.myType) return false; Segment range1 = getPsiRange(); Segment range2 = otherInfo.getPsiRange(); return range1 != null && range2 != null && range1.getStartOffset() == range2.getStartOffset() && range1.getEndOffset() == range2.getEndOffset(); } return areRestoredElementsEqual(other); }
private void initGuardedBlocks(Place shreds) { int origOffset = -1; int curOffset = 0; for (PsiLanguageInjectionHost.Shred shred : shreds) { Segment hostRangeMarker = shred.getHostRangeMarker(); int start = shred.getRange().getStartOffset() + shred.getPrefix().length(); int end = shred.getRange().getEndOffset() - shred.getSuffix().length(); if (curOffset < start) { RangeMarker guard = myNewDocument.createGuardedBlock(curOffset, start); if (curOffset == 0 && shred == shreds.get(0)) guard.setGreedyToLeft(true); String padding = origOffset < 0 ? "" : myOrigDocument.getText().substring(origOffset, hostRangeMarker.getStartOffset()); guard.putUserData(REPLACEMENT_KEY, fixQuotes(padding)); } curOffset = end; origOffset = hostRangeMarker.getEndOffset(); } if (curOffset < myNewDocument.getTextLength()) { RangeMarker guard = myNewDocument.createGuardedBlock(curOffset, myNewDocument.getTextLength()); guard.setGreedyToRight(true); guard.putUserData(REPLACEMENT_KEY, ""); } }
@Override public void selectInEditor() { if (!isValid()) return; Editor editor = openTextEditor(true); Segment marker = getFirstSegment(); editor.getSelectionModel().setSelection(marker.getStartOffset(), marker.getEndOffset()); }
@Override public int getLineStartOffset(int line) { LOG.assertTrue(line >= 0, line); if (line == 0) return 0; String hostText = myDelegate.getText(); int[] pos = new int[2]; // pos[0] = curLine; pos[1] == offset; synchronized (myLock) { for (PsiLanguageInjectionHost.Shred shred : myShreds) { Segment hostRange = shred.getHostRangeMarker(); if (hostRange == null) continue; int found = countNewLinesIn(shred.getPrefix(), pos, line); if (found != -1) return found; CharSequence text = hostText.subSequence(hostRange.getStartOffset(), hostRange.getEndOffset()); found = countNewLinesIn(text, pos, line); if (found != -1) return found; found = countNewLinesIn(shred.getSuffix(), pos, line); if (found != -1) return found; } } return pos[1]; }
@Override public void highlightInEditor() { if (!isValid()) return; Segment marker = getFirstSegment(); SelectInEditorManager.getInstance(getProject()) .selectInEditor(getFile(), marker.getStartOffset(), marker.getEndOffset(), false, false); }
void setRange(@Nullable Segment range) { if (range != null) { myStartOffset = range.getStartOffset(); myEndOffset = range.getEndOffset(); } else { myStartOffset = -1; myEndOffset = -1; } }
@Override public PsiElement restoreElement() { Segment segment = getPsiRange(); if (segment == null) return null; PsiFile file = restoreFile(); if (file == null || !file.isValid()) return null; return findElementInside(file, segment.getStartOffset(), segment.getEndOffset(), myType); }
@Override @Nullable public FileEditorLocation getLocation() { VirtualFile virtualFile = getFile(); if (virtualFile == null) return null; FileEditor editor = FileEditorManager.getInstance(getProject()).getSelectedEditor(virtualFile); if (!(editor instanceof TextEditor)) return null; Segment segment = getUsageInfo().getSegment(); if (segment == null) return null; return new TextEditorLocation(segment.getStartOffset(), (TextEditor) editor); }
private String getRangeText(@NotNull String hostText, int hostNum) { synchronized (myLock) { PsiLanguageInjectionHost.Shred shred = myShreds.get(hostNum); Segment hostRangeMarker = shred.getHostRangeMarker(); return shred.getPrefix() + (hostRangeMarker == null ? "" : hostText.substring( hostRangeMarker.getStartOffset(), hostRangeMarker.getEndOffset())) + shred.getSuffix(); } }
@Override public boolean areRangesEqual(@NotNull DocumentWindow otherd) { DocumentWindowImpl window = (DocumentWindowImpl) otherd; Place shreds = getShreds(); Place otherShreds = window.getShreds(); if (shreds.size() != otherShreds.size()) return false; for (int i = 0; i < shreds.size(); i++) { PsiLanguageInjectionHost.Shred shred = shreds.get(i); PsiLanguageInjectionHost.Shred otherShred = otherShreds.get(i); if (!shred.getPrefix().equals(otherShred.getPrefix())) return false; if (!shred.getSuffix().equals(otherShred.getSuffix())) 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; }
@Override public int getLineNumber(int offset) { int lineNumber = 0; String hostText = myDelegate.getText(); synchronized (myLock) { for (PsiLanguageInjectionHost.Shred shred : myShreds) { 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(); Segment currentRange = shred.getHostRangeMarker(); if (currentRange == null) continue; int rangeLength = currentRange.getEndOffset() - currentRange.getStartOffset(); CharSequence rangeText = hostText.subSequence(currentRange.getStartOffset(), currentRange.getEndOffset()); lineNumber += StringUtil.getLineBreakCount(rangeText.subSequence(0, Math.min(offset, rangeLength))); if (offset < rangeLength) { return lineNumber; } offset -= rangeLength; lineNumber += StringUtil.getLineBreakCount(suffix.substring(0, Math.min(offset, suffix.length()))); if (offset < suffix.length()) { return lineNumber; } offset -= suffix.length(); } } lineNumber = getLineCount() - 1; return lineNumber < 0 ? 0 : lineNumber; }
@Override public Element export( @NotNull RefEntity refEntity, @NotNull final Element element, final int actualLine) { refEntity = getRefinedElement(refEntity); Element problem = new Element("problem"); if (refEntity instanceof RefElement) { final RefElement refElement = (RefElement) refEntity; final SmartPsiElementPointer pointer = refElement.getPointer(); PsiFile psiFile = pointer.getContainingFile(); if (psiFile == null) return null; Element fileElement = new Element("file"); Element lineElement = new Element("line"); final VirtualFile virtualFile = psiFile.getVirtualFile(); LOG.assertTrue(virtualFile != null); fileElement.addContent(virtualFile.getUrl()); if (actualLine == -1) { final Document document = PsiDocumentManager.getInstance(pointer.getProject()).getDocument(psiFile); LOG.assertTrue(document != null); final Segment range = pointer.getRange(); lineElement.addContent( String.valueOf( range != null ? document.getLineNumber(range.getStartOffset()) + 1 : -1)); } else { lineElement.addContent(String.valueOf(actualLine)); } problem.addContent(fileElement); problem.addContent(lineElement); appendModule(problem, refElement.getModule()); } else if (refEntity instanceof RefModule) { final RefModule refModule = (RefModule) refEntity; final VirtualFile moduleFile = refModule.getModule().getModuleFile(); final Element fileElement = new Element("file"); fileElement.addContent(moduleFile != null ? moduleFile.getUrl() : refEntity.getName()); problem.addContent(fileElement); appendModule(problem, refModule); } for (RefManagerExtension extension : myExtensions.values()) { extension.export(refEntity, problem); } new SmartRefElementPointerImpl(refEntity, true).writeExternal(problem); element.addContent(problem); return problem; }
@Override public void deleteString(final int startOffset, final int endOffset) { assert intersectWithEditable(new TextRange(startOffset, startOffset)) != null; assert intersectWithEditable(new TextRange(endOffset, endOffset)) != null; List<TextRange> hostRangesToDelete; synchronized (myLock) { hostRangesToDelete = new ArrayList<TextRange>(myShreds.size()); int offset = startOffset; int curRangeStart = 0; for (PsiLanguageInjectionHost.Shred shred : myShreds) { curRangeStart += shred.getPrefix().length(); if (offset < curRangeStart) offset = curRangeStart; if (offset >= endOffset) break; Segment hostRange = shred.getHostRangeMarker(); if (hostRange == null) continue; int hostRangeLength = hostRange.getEndOffset() - hostRange.getStartOffset(); TextRange range = TextRange.from(curRangeStart, hostRangeLength); if (range.contains(offset)) { TextRange rangeToDelete = new TextRange(offset, Math.min(range.getEndOffset(), endOffset)); hostRangesToDelete.add( rangeToDelete.shiftRight(hostRange.getStartOffset() - curRangeStart)); offset = rangeToDelete.getEndOffset(); } curRangeStart += hostRangeLength; curRangeStart += shred.getSuffix().length(); } } int delta = 0; for (TextRange hostRangeToDelete : hostRangesToDelete) { myDelegate.deleteString( hostRangeToDelete.getStartOffset() + delta, hostRangeToDelete.getEndOffset() + delta); delta -= hostRangeToDelete.getLength(); } }
// by start offset @Override public int compareTo(final UsageInfo2UsageAdapter o) { VirtualFile containingFile = getFile(); int shift1 = 0; if (containingFile instanceof VirtualFileWindow) { shift1 = ((VirtualFileWindow) containingFile).getDocumentWindow().injectedToHost(0); containingFile = ((VirtualFileWindow) containingFile).getDelegate(); } VirtualFile oContainingFile = o.getFile(); int shift2 = 0; if (oContainingFile instanceof VirtualFileWindow) { shift2 = ((VirtualFileWindow) oContainingFile).getDocumentWindow().injectedToHost(0); oContainingFile = ((VirtualFileWindow) oContainingFile).getDelegate(); } if (containingFile == null && oContainingFile == null || !Comparing.equal(containingFile, oContainingFile)) { return 0; } Segment s1 = getFirstSegment(); Segment s2 = o.getFirstSegment(); if (s1 == null || s2 == null) return 0; return s1.getStartOffset() + shift1 - s2.getStartOffset() - shift2; }
@Override public int hostToInjected(int hostOffset) { synchronized (myLock) { Segment hostRangeMarker = myShreds.get(0).getHostRangeMarker(); if (hostRangeMarker == null || hostOffset < hostRangeMarker.getStartOffset()) return myShreds.get(0).getPrefix().length(); int offset = 0; for (int i = 0; i < myShreds.size(); i++) { offset += myShreds.get(i).getPrefix().length(); Segment currentRange = myShreds.get(i).getHostRangeMarker(); if (currentRange == null) continue; 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).getSuffix().length(); } return getTextLength() - myShreds.get(myShreds.size() - 1).getSuffix().length(); } }
@Override public int getTextLength() { int length = 0; synchronized (myLock) { for (PsiLanguageInjectionHost.Shred shred : myShreds) { Segment hostRange = shred.getHostRangeMarker(); if (hostRange == null) continue; length += shred.getPrefix().length(); length += hostRange.getEndOffset() - hostRange.getStartOffset(); length += shred.getSuffix().length(); } } return length; }
@NotNull private String calcText() { StringBuilder text = new StringBuilder(); CharSequence hostText = myDelegate.getCharsSequence(); synchronized (myLock) { for (PsiLanguageInjectionHost.Shred shred : myShreds) { Segment hostRange = shred.getHostRangeMarker(); if (hostRange != null) { text.append(shred.getPrefix()); text.append(hostText, hostRange.getStartOffset(), hostRange.getEndOffset()); text.append(shred.getSuffix()); } } } return text.toString(); }
@NotNull @Override public List<TextRange> getNonEditableFragments(@NotNull DocumentWindow window) { List<TextRange> result = ContainerUtil.newArrayList(); int offset = 0; for (PsiLanguageInjectionHost.Shred shred : ((DocumentWindowImpl) window).getShreds()) { Segment hostRange = shred.getHostRangeMarker(); if (hostRange == null) continue; offset = appendRange(result, offset, shred.getPrefix().length()); offset += hostRange.getEndOffset() - hostRange.getStartOffset(); offset = appendRange(result, offset, shred.getSuffix().length()); } return result; }
public static void renameNonCodeUsages( @NotNull Project project, @NotNull NonCodeUsageInfo[] usages) { PsiDocumentManager.getInstance(project).commitAllDocuments(); Map<Document, List<UsageOffset>> docsToOffsetsMap = new HashMap<Document, List<UsageOffset>>(); final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project); for (NonCodeUsageInfo usage : usages) { PsiElement element = usage.getElement(); if (element == null) continue; element = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(element, true); if (element == null) continue; final ProperTextRange rangeInElement = usage.getRangeInElement(); if (rangeInElement == null) continue; final PsiFile containingFile = element.getContainingFile(); final Document document = psiDocumentManager.getDocument(containingFile); final Segment segment = usage.getSegment(); LOG.assertTrue(segment != null); int fileOffset = segment.getStartOffset(); List<UsageOffset> list = docsToOffsetsMap.get(document); if (list == null) { list = new ArrayList<UsageOffset>(); docsToOffsetsMap.put(document, list); } list.add(new UsageOffset(fileOffset, fileOffset + rangeInElement.getLength(), usage.newText)); } for (Document document : docsToOffsetsMap.keySet()) { List<UsageOffset> list = docsToOffsetsMap.get(document); LOG.assertTrue(list != null, document); UsageOffset[] offsets = list.toArray(new UsageOffset[list.size()]); Arrays.sort(offsets); for (int i = offsets.length - 1; i >= 0; i--) { UsageOffset usageOffset = offsets[i]; document.replaceString(usageOffset.startOffset, usageOffset.endOffset, usageOffset.newText); } PsiDocumentManager.getInstance(project).commitDocument(document); } PsiDocumentManager.getInstance(project).commitAllDocuments(); }
@Override public int injectedToHostLine(int line) { if (line < myPrefixLineCount) { synchronized (myLock) { Segment hostRangeMarker = myShreds.get(0).getHostRangeMarker(); return hostRangeMarker == null ? 0 : myDelegate.getLineNumber(hostRangeMarker.getStartOffset()); } } int lineCount = getLineCount(); if (line > lineCount - mySuffixLineCount) { return lineCount; } int offset = getLineStartOffset(line); int hostOffset = injectedToHost(offset); return myDelegate.getLineNumber(hostOffset); }
@Override public void setText(@NotNull CharSequence text) { synchronized (myLock) { 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", ""); } String[] changes = calculateMinEditSequence(text.toString()); assert changes.length == myShreds.size(); for (int i = 0; i < changes.length; i++) { String change = changes[i]; if (change != null) { Segment hostRange = myShreds.get(i).getHostRangeMarker(); if (hostRange == null) continue; myDelegate.replaceString(hostRange.getStartOffset(), hostRange.getEndOffset(), change); } } } }
public int hashCode() { synchronized (myLock) { Segment hostRangeMarker = myShreds.get(0).getHostRangeMarker(); return hostRangeMarker == null ? -1 : hostRangeMarker.getStartOffset(); } }