@NotNull private Dimension expandIfNecessary(@NotNull Dimension base) { if (base.width >= myMinWidth && base.height >= myMinHeight) { return base; } return new Dimension(Math.max(myMinWidth, base.width), Math.max(myMinHeight, base.height)); }
private Pair<MutableTextRange, StringBuffer> getFragmentByRange(int start, final int length) { final StringBuffer fragmentBuffer = new StringBuffer(); int end = start + length; // restoring buffer and remove all subfragments from the list int documentOffset = 0; int effectiveOffset = 0; Iterator<Pair<MutableTextRange, StringBuffer>> iterator = myAffectedFragments.iterator(); while (iterator.hasNext() && effectiveOffset <= end) { final Pair<MutableTextRange, StringBuffer> pair = iterator.next(); final MutableTextRange range = pair.getFirst(); final StringBuffer buffer = pair.getSecond(); int effectiveFragmentEnd = range.getStartOffset() + buffer.length(); if (range.getStartOffset() <= start && effectiveFragmentEnd >= end) return pair; if (effectiveFragmentEnd >= start) { final int effectiveStart = Math.max(effectiveOffset, start); if (range.getStartOffset() > start) { fragmentBuffer.append( myDocument.getCharsSequence(), effectiveStart - effectiveOffset + documentOffset, Math.min(range.getStartOffset(), end) - effectiveOffset + documentOffset); } if (end >= range.getStartOffset()) { fragmentBuffer.append(buffer); end = end > effectiveFragmentEnd ? end - (buffer.length() - range.getLength()) : range.getEndOffset(); effectiveFragmentEnd = range.getEndOffset(); start = Math.min(start, range.getStartOffset()); iterator.remove(); } } documentOffset += range.getEndOffset() - effectiveOffset; effectiveOffset = effectiveFragmentEnd; } if (effectiveOffset < end) { final int effectiveStart = Math.max(effectiveOffset, start); fragmentBuffer.append( myDocument.getCharsSequence(), effectiveStart - effectiveOffset + documentOffset, end - effectiveOffset + documentOffset); } MutableTextRange newRange = new MutableTextRange(start, end); final Pair<MutableTextRange, StringBuffer> pair = new Pair<MutableTextRange, StringBuffer>(newRange, fragmentBuffer); for (Pair<MutableTextRange, StringBuffer> affectedFragment : myAffectedFragments) { MutableTextRange range = affectedFragment.getFirst(); assert end <= range.getStartOffset() || range.getEndOffset() <= start : "Range :" + range + "; Added: " + newRange; } myAffectedFragments.add(pair); return pair; }
/** * Returns the original comment info of the specified element or null * * @param element the specified element * @return text chunk */ @Nullable private static String getOrigCommentInfo(PsiDocCommentOwner element) { StringBuilder sb = new StringBuilder(); PsiElement e = element.getFirstChild(); if (!(e instanceof PsiComment)) { // no comments for this element return null; } else { boolean first = true; while (true) { if (e instanceof PsiDocComment) { PsiComment cm = (PsiComment) e; String text = cm.getText(); if (text.startsWith("//")) { if (!first) sb.append('\n'); sb.append(text.substring(2).trim()); } else if (text.startsWith("/*")) { if (text.charAt(2) == '*') { text = text.substring(3, Math.max(3, text.length() - 2)); } else { text = text.substring(2, Math.max(2, text.length() - 2)); } sb.append(text); } } else if (!(e instanceof PsiWhiteSpace) && !(e instanceof PsiComment)) { break; } first = false; e = e.getNextSibling(); } return sb.toString(); } }
public static boolean filterEquals(ClassFilter[] filters1, ClassFilter[] filters2) { if (filters1.length != filters2.length) { return false; } final Set<ClassFilter> f1 = new HashSet<ClassFilter>(Math.max((int) (filters1.length / .75f) + 1, 16)); final Set<ClassFilter> f2 = new HashSet<ClassFilter>(Math.max((int) (filters2.length / .75f) + 1, 16)); Collections.addAll(f1, filters1); Collections.addAll(f2, filters2); return f2.equals(f1); }
public static void initOffsets(final PsiFile file, final OffsetMap offsetMap) { int offset = Math.max( offsetMap.getOffset(CompletionInitializationContext.SELECTION_END_OFFSET), offsetMap.getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET)); PsiElement element = file.findElementAt(offset); if (element instanceof PsiWhiteSpace && (!element.textContains('\n') || CodeStyleSettingsManager.getSettings(file.getProject()) .getCommonSettings(JavaLanguage.INSTANCE) .METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE)) { element = file.findElementAt(element.getTextRange().getEndOffset()); } if (element == null) return; if (LEFT_PAREN.accepts(element)) { offsetMap.addOffset(LPAREN_OFFSET, element.getTextRange().getStartOffset()); PsiElement list = element.getParent(); PsiElement last = list.getLastChild(); if (last instanceof PsiJavaToken && ((PsiJavaToken) last).getTokenType() == JavaTokenType.RPARENTH) { offsetMap.addOffset(RPAREN_OFFSET, last.getTextRange().getStartOffset()); } offsetMap.addOffset(ARG_LIST_END_OFFSET, list.getTextRange().getEndOffset()); } }
private void enterScope(PsiElement element) { scopeEntered.add(element); m_currentDepth++; if ((m_maximumDepth = Math.max(m_maximumDepth, m_currentDepth)) > myLimit) { stopWalking(); } }
public static boolean filterEquals(ClassFilter[] filters1, ClassFilter[] filters2) { if (filters1.length != filters2.length) { return false; } final Set<ClassFilter> f1 = new HashSet<ClassFilter>(Math.max((int) (filters1.length / .75f) + 1, 16)); final Set<ClassFilter> f2 = new HashSet<ClassFilter>(Math.max((int) (filters2.length / .75f) + 1, 16)); for (ClassFilter filter : filters1) { f1.add(filter); } for (ClassFilter aFilters2 : filters2) { f2.add(aFilters2); } return f2.equals(f1); }
@Override @NotNull public String getPlainText() { int startOffset = getNavigationOffset(); final PsiElement element = getElement(); if (element != null && startOffset != -1) { final Document document = getDocument(); if (document != null) { int lineNumber = document.getLineNumber(startOffset); int lineStart = document.getLineStartOffset(lineNumber); int lineEnd = document.getLineEndOffset(lineNumber); String prefixSuffix = null; if (lineEnd - lineStart > ChunkExtractor.MAX_LINE_LENGTH_TO_SHOW) { prefixSuffix = "..."; lineStart = Math.max( startOffset - ChunkExtractor.OFFSET_BEFORE_TO_SHOW_WHEN_LONG_LINE, lineStart); lineEnd = Math.min(startOffset + ChunkExtractor.OFFSET_AFTER_TO_SHOW_WHEN_LONG_LINE, lineEnd); } String s = document.getCharsSequence().subSequence(lineStart, lineEnd).toString(); if (prefixSuffix != null) s = prefixSuffix + s + prefixSuffix; return s; } } return UsageViewBundle.message("node.invalid"); }
private static int getQuickFixType(PsiVariable variable) { PsiElement outerCodeBlock = PsiUtil.getVariableCodeBlock(variable, null); if (outerCodeBlock == null) return -1; List<PsiReferenceExpression> outerReferences = new ArrayList<PsiReferenceExpression>(); collectReferences(outerCodeBlock, variable, outerReferences); int type = MAKE_FINAL; for (PsiReferenceExpression expression : outerReferences) { // if it happens that variable referenced from another inner class, make sure it can be make // final from there PsiClass innerClass = HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, expression); if (innerClass != null) { int thisType = MAKE_FINAL; if (writtenInside(variable, innerClass)) { // cannot make parameter array if (variable instanceof PsiParameter) return -1; thisType = MAKE_ARRAY; } if (thisType == MAKE_FINAL && !canBeFinal(variable, outerReferences)) { thisType = COPY_TO_FINAL; } type = Math.max(type, thisType); } } return type; }
private static LineRange expandLineRangeToCoverPsiElements( final LineRange range, Editor editor, final PsiFile file) { Pair<PsiElement, PsiElement> psiRange = getElementRange(editor, file, range); if (psiRange == null) return null; final PsiElement parent = PsiTreeUtil.findCommonParent(psiRange.getFirst(), psiRange.getSecond()); Pair<PsiElement, PsiElement> elementRange = getElementRange(parent, psiRange.getFirst(), psiRange.getSecond()); if (elementRange == null) return null; int endOffset = elementRange.getSecond().getTextRange().getEndOffset(); Document document = editor.getDocument(); if (endOffset > document.getTextLength()) { LOG.assertTrue(!PsiDocumentManager.getInstance(file.getProject()).isUncommited(document)); LOG.assertTrue(PsiDocumentManagerImpl.checkConsistency(file, document)); } int endLine; if (endOffset == document.getTextLength()) { endLine = document.getLineCount(); } else { endLine = editor.offsetToLogicalPosition(endOffset).line + 1; endLine = Math.min(endLine, document.getLineCount()); } int startLine = Math.min( range.startLine, editor.offsetToLogicalPosition(elementRange.getFirst().getTextOffset()).line); endLine = Math.max(endLine, range.endLine); return new LineRange(startLine, endLine); }
private int getTypesMaxLength() { int len = 0; for (ParameterTableModelItemBase<ParameterInfoImpl> item : myParametersTableModel.getItems()) { final String text = item.typeCodeFragment == null ? null : item.typeCodeFragment.getText(); len = Math.max(len, text == null ? 0 : text.length()); } return len; }
private int getNamesMaxLength() { int len = 0; for (ParameterTableModelItemBase<ParameterInfoImpl> item : myParametersTableModel.getItems()) { final String text = item.parameter.getName(); len = Math.max(len, text == null ? 0 : text.length()); } return len; }
QuickDocInfoPane( @NotNull PsiElement documentationAnchor, @NotNull PsiElement elementUnderMouse, @NotNull JComponent baseDocControl) { myBaseDocControl = baseDocControl; PresentationFactory presentationFactory = new PresentationFactory(); for (AbstractDocumentationTooltipAction action : ourTooltipActions) { Icon icon = action.getTemplatePresentation().getIcon(); Dimension minSize = new Dimension(icon.getIconWidth(), icon.getIconHeight()); myButtons.add( new ActionButton( action, presentationFactory.getPresentation(action), IdeTooltipManager.IDE_TOOLTIP_PLACE, minSize)); action.setDocInfo(documentationAnchor, elementUnderMouse); } Collections.reverse(myButtons); setPreferredSize(baseDocControl.getPreferredSize()); setMaximumSize(baseDocControl.getMaximumSize()); setMinimumSize(baseDocControl.getMinimumSize()); setBackground(baseDocControl.getBackground()); add(baseDocControl, Integer.valueOf(0)); int minWidth = 0; int minHeight = 0; int buttonWidth = 0; for (JComponent button : myButtons) { button.setBorder(null); button.setBackground(baseDocControl.getBackground()); add(button, Integer.valueOf(1)); button.setVisible(false); Dimension preferredSize = button.getPreferredSize(); minWidth += preferredSize.width; minHeight = Math.max(minHeight, preferredSize.height); buttonWidth = Math.max(buttonWidth, preferredSize.width); } myButtonWidth = buttonWidth; int margin = 2; myMinWidth = minWidth + margin * 2 + (myButtons.size() - 1) * BUTTON_HGAP; myMinHeight = minHeight + margin * 2; }
private String getText(final int start, final int end) { int currentOldDocumentOffset = 0; int currentNewDocumentOffset = 0; StringBuilder text = new StringBuilder(); Iterator<Pair<MutableTextRange, StringBuffer>> iterator = myAffectedFragments.iterator(); while (iterator.hasNext() && currentNewDocumentOffset < end) { final Pair<MutableTextRange, StringBuffer> pair = iterator.next(); final MutableTextRange range = pair.getFirst(); final StringBuffer buffer = pair.getSecond(); final int fragmentEndInNewDocument = range.getStartOffset() + buffer.length(); if (range.getStartOffset() <= start && fragmentEndInNewDocument >= end) { return buffer.substring(start - range.getStartOffset(), end - range.getStartOffset()); } if (range.getStartOffset() >= start) { final int effectiveStart = Math.max(currentNewDocumentOffset, start); text.append( myDocument.getCharsSequence(), effectiveStart - currentNewDocumentOffset + currentOldDocumentOffset, Math.min(range.getStartOffset(), end) - currentNewDocumentOffset + currentOldDocumentOffset); if (end > range.getStartOffset()) { text.append( buffer.substring(0, Math.min(end - range.getStartOffset(), buffer.length()))); } } currentOldDocumentOffset += range.getEndOffset() - currentNewDocumentOffset; currentNewDocumentOffset = fragmentEndInNewDocument; } if (currentNewDocumentOffset < end) { final int effectiveStart = Math.max(currentNewDocumentOffset, start); text.append( myDocument.getCharsSequence(), effectiveStart - currentNewDocumentOffset + currentOldDocumentOffset, end - currentNewDocumentOffset + currentOldDocumentOffset); } return text.toString(); }
int getNavigationOffset() { Document document = getDocument(); if (document == null) return -1; int offset = getUsageInfo().getNavigationOffset(); if (offset == -1) offset = myOffset; if (offset >= document.getTextLength()) { int line = Math.max(0, Math.min(myLineNumber, document.getLineCount() - 1)); offset = document.getLineStartOffset(line); } return offset; }
private void commitToOriginalInner() { final String text = myNewDocument.getText(); final Map< PsiLanguageInjectionHost, Set<Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer>>> map = ContainerUtil.classify( myMarkers.iterator(), new Convertor< Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer>, PsiLanguageInjectionHost>() { @Override public PsiLanguageInjectionHost convert( final Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> o) { final PsiElement element = o.third.getElement(); return (PsiLanguageInjectionHost) element; } }); PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); documentManager.commitDocument(myOrigDocument); // commit here and after each manipulator update int localInsideFileCursor = 0; for (PsiLanguageInjectionHost host : map.keySet()) { if (host == null) continue; String hostText = host.getText(); ProperTextRange insideHost = null; StringBuilder sb = new StringBuilder(); for (Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> entry : map.get(host)) { RangeMarker origMarker = entry.first; // check for validity? int hostOffset = host.getTextRange().getStartOffset(); ProperTextRange localInsideHost = new ProperTextRange( origMarker.getStartOffset() - hostOffset, origMarker.getEndOffset() - hostOffset); RangeMarker rangeMarker = entry.second; ProperTextRange localInsideFile = new ProperTextRange( Math.max(localInsideFileCursor, rangeMarker.getStartOffset()), rangeMarker.getEndOffset()); if (insideHost != null) { // append unchanged inter-markers fragment sb.append( hostText.substring(insideHost.getEndOffset(), localInsideHost.getStartOffset())); } sb.append( localInsideFile.getEndOffset() <= text.length() && !localInsideFile.isEmpty() ? localInsideFile.substring(text) : ""); localInsideFileCursor = localInsideFile.getEndOffset(); insideHost = insideHost == null ? localInsideHost : insideHost.union(localInsideHost); } assert insideHost != null; ElementManipulators.getManipulator(host).handleContentChange(host, insideHost, sb.toString()); documentManager.commitDocument(myOrigDocument); } }
private void updatedMovedIntoEnd( final Document document, @NotNull final MoveInfo info, final int offset) { if (offset + 1 < document.getTextLength()) { final int line = document.getLineNumber(offset + 1); final LineRange toMove2 = info.toMove2; if (toMove2 == null) return; info.toMove2 = new LineRange( toMove2.startLine, Math.min(Math.max(line, toMove2.endLine), document.getLineCount() - 1)); } }
private boolean isWhiteSpaceOrComment(@NotNull PsiElement element, @NotNull TextRange range) { final TextRange textRange = element.getTextRange(); TextRange intersection = range.intersection(textRange); if (intersection == null) { return false; } intersection = TextRange.create( Math.max(intersection.getStartOffset() - textRange.getStartOffset(), 0), Math.min( intersection.getEndOffset() - textRange.getStartOffset(), textRange.getLength())); return isWhiteSpaceOrComment(element) || intersection.substring(element.getText()).trim().length() == 0; }
private static void addNamesFromStatistics( Set<String> names, VariableKind variableKind, @Nullable String propertyName, @Nullable PsiType type) { String[] allNames = JavaStatisticsManager.getAllVariableNamesUsed(variableKind, propertyName, type); int maxFrequency = 0; for (String name : allNames) { int count = JavaStatisticsManager.getVariableNameUseCount(name, variableKind, propertyName, type); maxFrequency = Math.max(maxFrequency, count); } int frequencyLimit = Math.max(5, maxFrequency / 2); for (String name : allNames) { if (names.contains(name)) { continue; } int count = JavaStatisticsManager.getVariableNameUseCount(name, variableKind, propertyName, type); if (LOG.isDebugEnabled()) { LOG.debug("new name:" + name + " count:" + count); LOG.debug("frequencyLimit:" + frequencyLimit); } if (count >= frequencyLimit) { names.add(name); } } if (propertyName != null && type != null) { addNamesFromStatistics(names, variableKind, propertyName, null); addNamesFromStatistics(names, variableKind, null, type); } }
private static int calcMatch(final String expectedName, final List<String> words, int max) { if (expectedName == null) return max; String[] expectedWords = NameUtil.nameToWords(expectedName); int limit = Math.min(words.size(), expectedWords.length); for (int i = 0; i < limit; i++) { String word = words.get(words.size() - i - 1); String expectedWord = expectedWords[expectedWords.length - i - 1]; if (word.equalsIgnoreCase(expectedWord)) { max = Math.max(max, i + 1); } else { break; } } return max; }
private static void adjustIndentationInRange( final PsiFile file, final Document document, final TextRange[] indents, final int indentAdjustment) { final CharSequence charsSequence = document.getCharsSequence(); for (final TextRange indent : indents) { final String oldIndentStr = charsSequence.subSequence(indent.getStartOffset() + 1, indent.getEndOffset()).toString(); final int oldIndent = IndentHelperImpl.getIndent(file.getProject(), file.getFileType(), oldIndentStr, true); final String newIndentStr = IndentHelperImpl.fillIndent( file.getProject(), file.getFileType(), Math.max(oldIndent + indentAdjustment, 0)); document.replaceString(indent.getStartOffset() + 1, indent.getEndOffset(), newIndentStr); } }
@Override public void exchangeRows(int row, int targetRow) { if (row < 0 || row >= getVariableData().length) return; if (targetRow < 0 || targetRow >= getVariableData().length) return; final VariableData currentItem = getVariableData()[row]; getVariableData()[row] = getVariableData()[targetRow]; getVariableData()[targetRow] = currentItem; TypeSelector currentSelector = myParameterTypeSelectors[row]; myParameterTypeSelectors[row] = myParameterTypeSelectors[targetRow]; myParameterTypeSelectors[targetRow] = currentSelector; myTypeRendererCombo.setModel(new DefaultComboBoxModel(getVariableData())); myTableModel.fireTableRowsUpdated(Math.min(targetRow, row), Math.max(targetRow, row)); myTable.getSelectionModel().setSelectionInterval(targetRow, targetRow); updateSignature(); }
/** * It's possible that we need to expand quick doc control's width in order to provide better * visual representation (see http://youtrack.jetbrains.com/issue/IDEA-101425). This method * calculates that width expand. * * @param buttonWidth icon button's width * @param updatedText text which will be should at the quick doc control * @return width increase to apply to the target quick doc control (zero if no additional width * increase is required) */ private static int calculateWidthIncrease(int buttonWidth, String updatedText) { int maxLineWidth = 0; TIntArrayList lineWidths = new TIntArrayList(); for (String lineText : StringUtil.split(updatedText, "<br/>")) { String html = HintUtil.prepareHintText(lineText, HintUtil.getInformationHint()); int width = new JLabel(html).getPreferredSize().width; maxLineWidth = Math.max(maxLineWidth, width); lineWidths.add(width); } if (!lineWidths.isEmpty()) { int firstLineAvailableTrailingWidth = maxLineWidth - lineWidths.get(0); if (firstLineAvailableTrailingWidth >= buttonWidth) { return 0; } else { return buttonWidth - firstLineAvailableTrailingWidth; } } return 0; }
private int updateMovedRegionEnd( final Document document, int movedLineStart, final int valueStart, @NotNull final MoveInfo info, final boolean down) { final int line = document.getLineNumber(valueStart); final LineRange toMove = info.toMove; int delta = line - toMove.endLine; info.toMove = new LineRange(toMove.startLine, Math.max(line, toMove.endLine)); // update moved range if (delta > 0 && down) { final LineRange toMove2 = info.toMove2; info.toMove2 = new LineRange( toMove2.startLine + delta, Math.min(toMove2.endLine + delta, document.getLineCount() - 1)); movedLineStart = document.getLineStartOffset(toMove.startLine); } return movedLineStart; }
protected static void checkAccessStaticLevels( @NotNull List<CandidateInfo> conflicts, boolean checkAccessible) { int conflictsCount = conflicts.size(); int maxCheckLevel = -1; int[] checkLevels = new int[conflictsCount]; int index = 0; for (final CandidateInfo conflict : conflicts) { ProgressManager.checkCanceled(); final MethodCandidateInfo method = (MethodCandidateInfo) conflict; final int level = checkAccessible ? getCheckAccessLevel(method) : getCheckStaticLevel(method); checkLevels[index++] = level; maxCheckLevel = Math.max(maxCheckLevel, level); } for (int i = conflictsCount - 1; i >= 0; i--) { // check for level if (checkLevels[i] < maxCheckLevel) { conflicts.remove(i); } } }
@NotNull @Override public Comparable weigh(@NotNull LookupElement element) { final Object object = element.getObject(); final String name = getLookupObjectName(object); if (name != null) { int max = 0; final List<String> wordsNoDigits = NameUtil.nameToWordsLowerCase(truncDigits(name)); for (ExpectedTypeInfo myExpectedInfo : myExpectedTypes) { String expectedName = ((ExpectedTypeInfoImpl) myExpectedInfo).getExpectedName().compute(); if (expectedName != null) { final THashSet<String> set = new THashSet<String>(NameUtil.nameToWordsLowerCase(truncDigits(expectedName))); set.retainAll(wordsNoDigits); max = Math.max(max, set.size()); } } return -max; } return 0; }
public int getEmptyLinesBetween( @NotNull PsiImportStatementBase statement1, @NotNull PsiImportStatementBase statement2) { int index1 = findEntryIndex(statement1); int index2 = findEntryIndex(statement2); if (index1 == index2) return 0; if (index1 > index2) { int t = index1; index1 = index2; index2 = t; } Entry[] entries = mySettings.IMPORT_LAYOUT_TABLE.getEntries(); int maxSpace = 0; for (int i = index1 + 1; i < index2; i++) { if (entries[i] instanceof EmptyLineEntry) { int space = 0; do { space++; } while (entries[++i] instanceof EmptyLineEntry); maxSpace = Math.max(maxSpace, space); } } return maxSpace; }
/** * Returns number of different parameters in i18n message. For example, for string <i>Class {0} * info: Class {0} extends class {1} and implements interface {2}</i> number of parameters is 3. * * @param expression i18n literal * @return number of parameters */ public static int getPropertyValueParamsMaxCount(final PsiLiteralExpression expression) { int maxCount = -1; for (PsiReference reference : expression.getReferences()) { if (reference instanceof PsiPolyVariantReference) { for (ResolveResult result : ((PsiPolyVariantReference) reference).multiResolve(false)) { if (result.isValidResult() && result.getElement() instanceof IProperty) { String value = ((IProperty) result.getElement()).getValue(); MessageFormat format; try { format = new MessageFormat(value); } catch (Exception e) { continue; // ignore syntax error } try { int count = format.getFormatsByArgumentIndex().length; maxCount = Math.max(maxCount, count); } catch (IllegalArgumentException ignored) { } } } } } return maxCount; }
/** * This method searches ast node that could be reparsed incrementally and returns pair of target * reparseable node and new replacement node. Returns null if there is no any chance to make * incremental parsing. */ @Nullable public Couple<ASTNode> findReparseableRoots( @NotNull PsiFileImpl file, @NotNull TextRange changedPsiRange, @NotNull CharSequence newFileText) { Project project = file.getProject(); final FileElement fileElement = file.getTreeElement(); final CharTable charTable = fileElement.getCharTable(); int lengthShift = newFileText.length() - fileElement.getTextLength(); if (fileElement.getElementType() instanceof ITemplateDataElementType || isTooDeep(file)) { // unable to perform incremental reparse for template data in JSP, or in exceptionally deep // trees return null; } final ASTNode leafAtStart = fileElement.findLeafElementAt(Math.max(0, changedPsiRange.getStartOffset() - 1)); final ASTNode leafAtEnd = fileElement.findLeafElementAt( Math.min(changedPsiRange.getEndOffset(), fileElement.getTextLength() - 1)); ASTNode node = leafAtStart != null && leafAtEnd != null ? TreeUtil.findCommonParent(leafAtStart, leafAtEnd) : fileElement; Language baseLanguage = file.getViewProvider().getBaseLanguage(); while (node != null && !(node instanceof FileElement)) { IElementType elementType = node.getElementType(); if (elementType instanceof IReparseableElementType) { final TextRange textRange = node.getTextRange(); final IReparseableElementType reparseable = (IReparseableElementType) elementType; if (baseLanguage.isKindOf(reparseable.getLanguage()) && textRange.getLength() + lengthShift > 0) { final int start = textRange.getStartOffset(); final int end = start + textRange.getLength() + lengthShift; if (end > newFileText.length()) { reportInconsistentLength(file, newFileText, node, start, end); break; } CharSequence newTextStr = newFileText.subSequence(start, end); if (reparseable.isParsable(node.getTreeParent(), newTextStr, baseLanguage, project)) { ASTNode chameleon = reparseable.createNode(newTextStr); if (chameleon != null) { DummyHolder holder = DummyHolderFactory.createHolder( file.getManager(), null, node.getPsi(), charTable); holder.getTreeElement().rawAddChildren((TreeElement) chameleon); if (holder.getTextLength() != newTextStr.length()) { String details = ApplicationManager.getApplication().isInternal() ? "text=" + newTextStr + "; treeText=" + holder.getText() + ";" : ""; LOG.error("Inconsistent reparse: " + details + " type=" + elementType); } return Couple.of(node, chameleon); } } } } node = node.getTreeParent(); } return null; }
private List<PostponedAction> normalizeAndReorderPostponedActions( final TreeSet<PostprocessFormattingTask> rangesToProcess, Document document) { final List<PostprocessFormattingTask> freeFormatingActions = new ArrayList<PostprocessFormattingTask>(); final List<ReindentTask> indentActions = new ArrayList<ReindentTask>(); PostprocessFormattingTask accumulatedTask = null; Iterator<PostprocessFormattingTask> iterator = rangesToProcess.iterator(); while (iterator.hasNext()) { final PostprocessFormattingTask currentTask = iterator.next(); if (accumulatedTask == null) { accumulatedTask = currentTask; iterator.remove(); } else if (accumulatedTask.getStartOffset() > currentTask.getEndOffset() || accumulatedTask.getStartOffset() == currentTask.getEndOffset() && !canStickActionsTogether(accumulatedTask, currentTask)) { // action can be pushed if (accumulatedTask instanceof ReindentTask) { indentActions.add((ReindentTask) accumulatedTask); } else { freeFormatingActions.add(accumulatedTask); } accumulatedTask = currentTask; iterator.remove(); } else if (accumulatedTask instanceof ReformatTask && currentTask instanceof ReindentTask) { // split accumulated reformat range into two if (accumulatedTask.getStartOffset() < currentTask.getStartOffset()) { final RangeMarker endOfRange = document.createRangeMarker( accumulatedTask.getStartOffset(), currentTask.getStartOffset()); // add heading reformat part rangesToProcess.add(new ReformatTask(endOfRange)); // and manage heading whitespace because formatter does not edit it in previous action iterator = rangesToProcess.iterator(); //noinspection StatementWithEmptyBody while (iterator.next().getRange() != currentTask.getRange()) ; } final RangeMarker rangeToProcess = document.createRangeMarker(currentTask.getEndOffset(), accumulatedTask.getEndOffset()); freeFormatingActions.add(new ReformatWithHeadingWhitespaceTask(rangeToProcess)); accumulatedTask = currentTask; iterator.remove(); } else { if (!(accumulatedTask instanceof ReindentTask)) { iterator.remove(); boolean withLeadingWhitespace = accumulatedTask instanceof ReformatWithHeadingWhitespaceTask; if (accumulatedTask instanceof ReformatTask && currentTask instanceof ReformatWithHeadingWhitespaceTask && accumulatedTask.getStartOffset() == currentTask.getStartOffset()) { withLeadingWhitespace = true; } else if (accumulatedTask instanceof ReformatWithHeadingWhitespaceTask && currentTask instanceof ReformatTask && accumulatedTask.getStartOffset() < currentTask.getStartOffset()) { withLeadingWhitespace = false; } int newStart = Math.min(accumulatedTask.getStartOffset(), currentTask.getStartOffset()); int newEnd = Math.max(accumulatedTask.getEndOffset(), currentTask.getEndOffset()); RangeMarker rangeMarker; if (accumulatedTask.getStartOffset() == newStart && accumulatedTask.getEndOffset() == newEnd) { rangeMarker = accumulatedTask.getRange(); } else if (currentTask.getStartOffset() == newStart && currentTask.getEndOffset() == newEnd) { rangeMarker = currentTask.getRange(); } else { rangeMarker = document.createRangeMarker(newStart, newEnd); } if (withLeadingWhitespace) { accumulatedTask = new ReformatWithHeadingWhitespaceTask(rangeMarker); } else { accumulatedTask = new ReformatTask(rangeMarker); } } else if (currentTask instanceof ReindentTask) { iterator.remove(); } // TODO[ik]: need to be fixed to correctly process indent inside indent } } if (accumulatedTask != null) { if (accumulatedTask instanceof ReindentTask) { indentActions.add((ReindentTask) accumulatedTask); } else { freeFormatingActions.add(accumulatedTask); } } final List<PostponedAction> result = new ArrayList<PostponedAction>(); Collections.reverse(freeFormatingActions); Collections.reverse(indentActions); if (!freeFormatingActions.isEmpty()) { FormatTextRanges ranges = new FormatTextRanges(); for (PostprocessFormattingTask action : freeFormatingActions) { TextRange range = TextRange.create(action); ranges.add(range, action instanceof ReformatWithHeadingWhitespaceTask); } result.add(new ReformatRangesAction(ranges)); } if (!indentActions.isEmpty()) { ReindentRangesAction reindentRangesAction = new ReindentRangesAction(); for (ReindentTask action : indentActions) { reindentRangesAction.add(action.getRange(), action.getOldIndent()); } result.add(reindentRangesAction); } return result; }