static void setOrRefreshErrorStripeRenderer( @NotNull EditorMarkupModel editorMarkupModel, @NotNull Project project, @NotNull Document document, PsiFile file) { ApplicationManager.getApplication().assertIsDispatchThread(); if (!editorMarkupModel.isErrorStripeVisible() || !DaemonCodeAnalyzer.getInstance(project).isHighlightingAvailable(file)) { return; } ErrorStripeRenderer renderer = editorMarkupModel.getErrorStripeRenderer(); if (renderer instanceof TrafficLightRenderer) { TrafficLightRenderer tlr = (TrafficLightRenderer) renderer; tlr.refresh(); ((EditorMarkupModelImpl) editorMarkupModel).repaintVerticalScrollBar(); if (tlr.myFile == null || tlr.myFile.isValid()) return; Disposer.dispose(tlr); } EditorImpl editor = (EditorImpl) editorMarkupModel.getEditor(); if (!editor.isDisposed()) { renderer = new TrafficLightRenderer(project, document, file); Disposer.register(editor.getDisposable(), (Disposable) renderer); editorMarkupModel.setErrorStripeRenderer(renderer); } }
int getVisualLineWidth( VisualLinesIterator visualLinesIterator, @Nullable Runnable quickEvaluationListener) { assert !visualLinesIterator.atEnd(); int visualLine = visualLinesIterator.getVisualLine(); FoldRegion[] topLevelRegions = myEditor.getFoldingModel().fetchTopLevel(); if (quickEvaluationListener != null && (topLevelRegions == null || topLevelRegions.length == 0) && myEditor.getSoftWrapModel().getRegisteredSoftWraps().isEmpty() && !myView.getTextLayoutCache().hasCachedLayoutFor(visualLine)) { // fast path - speeds up editor opening quickEvaluationListener.run(); return myView .getLogicalPositionCache() .offsetToLogicalColumn( visualLine, myDocument.getLineEndOffset(visualLine) - myDocument.getLineStartOffset(visualLine)) * myView.getMaxCharWidth(); } float x = 0; int maxOffset = visualLinesIterator.getVisualLineStartOffset(); for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLinesIterator, quickEvaluationListener)) { x = fragment.getEndX(); maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); } if (myEditor.getSoftWrapModel().getSoftWrap(maxOffset) != null) { x += myEditor .getSoftWrapModel() .getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); } return (int) x; }
private boolean paintPlaceholderText(Graphics2D g) { CharSequence hintText = myEditor.getPlaceholder(); EditorComponentImpl editorComponent = myEditor.getContentComponent(); if (myDocument.getTextLength() > 0 || hintText == null || hintText.length() == 0 || KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == editorComponent && !myEditor.getShowPlaceholderWhenFocused()) { return false; } hintText = SwingUtilities.layoutCompoundLabel( g.getFontMetrics(), hintText.toString(), null, 0, 0, 0, 0, editorComponent.getBounds(), new Rectangle(), new Rectangle(), 0); g.setColor(myEditor.getFoldingModel().getPlaceholderAttributes().getForegroundColor()); g.setFont(myEditor.getColorsScheme().getFont(EditorFontType.PLAIN)); g.drawString(hintText.toString(), 0, myView.getAscent()); return true; }
private void paintSelectionOnFirstSoftWrapLineIfNecessary( Graphics2D g, int columnStart, float xStart, float xEnd, int y) { VisualPosition selectionStartPosition = myEditor.getSelectionModel().getSelectionStartPosition(); VisualPosition selectionEndPosition = myEditor.getSelectionModel().getSelectionEndPosition(); int visualLine = myView.yToVisualLine(y); if (selectionStartPosition.equals(selectionEndPosition) || visualLine < selectionStartPosition.line || visualLine > selectionEndPosition.line || visualLine == selectionEndPosition.line && selectionEndPosition.column <= columnStart) { return; } float startX = selectionStartPosition.line == visualLine && selectionStartPosition.column > columnStart ? myView.visualPositionToXY(selectionStartPosition).x : xStart; float endX = selectionEndPosition.line == visualLine ? myView.visualPositionToXY(selectionEndPosition).x : xEnd; paintBackground( g, myEditor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR), startX, y, endX - startX); }
int getPreferredHeight() { int lineHeight = myView.getLineHeight(); if (myEditor.isOneLineMode()) return lineHeight; // Preferred height of less than a single line height doesn't make sense: // at least a single line with a blinking caret on it is to be displayed int size = Math.max(myEditor.getVisibleLineCount(), 1) * lineHeight; EditorSettings settings = myEditor.getSettings(); if (settings.isAdditionalPageAtBottom()) { int visibleAreaHeight = myEditor.getScrollingModel().getVisibleArea().height; // There is a possible case that user with 'show additional page at bottom' scrolls to that // virtual page; switched to another // editor (another tab); and then returns to the previously used editor (the one scrolled to // virtual page). We want to preserve // correct view size then because viewport position is set to the end of the original text // otherwise. if (visibleAreaHeight > 0 || myVirtualPageHeight <= 0) { myVirtualPageHeight = Math.max(visibleAreaHeight - 2 * lineHeight, lineHeight); } size += Math.max(myVirtualPageHeight, 0); } else { size += settings.getAdditionalLinesCount() * lineHeight; } Insets insets = myView.getInsets(); return size + insets.top + insets.bottom; }
@Override public int calcColumnNumber( @NotNull final CharSequence text, final int start, final int offset, final int tabSize) { int hostStart = myDocumentWindow.injectedToHost(start); int hostOffset = myDocumentWindow.injectedToHost(offset); return myDelegate.calcColumnNumber( myDelegate.getDocument().getText(), hostStart, hostOffset, tabSize); }
EditorSizeManager(EditorView view) { myView = view; myEditor = view.getEditor(); myDocument = myEditor.getDocument(); myDocument.addDocumentListener(this, this); myEditor.getFoldingModel().addListener(this, this); myEditor.getSoftWrapModel().getApplianceManager().addListener(mySoftWrapChangeListener); }
private void assertValidState() { if (myDocument.isInBulkUpdate() || myDirty) return; if (myLineWidths.size() != myEditor.getVisibleLineCount()) { LOG.error("Inconsistent state", new Attachment("editor.txt", myEditor.dumpState())); reset(); } assert myLineWidths.size() == myEditor.getVisibleLineCount(); }
private void paintBackground(Graphics2D g, Color color, float x, int y, float width) { if (width <= 0 || color == null || color.equals(myEditor.getColorsScheme().getDefaultBackground()) || color.equals(myEditor.getBackgroundColor())) return; g.setColor(color); g.fillRect((int) x, y, (int) width, myView.getLineHeight()); }
private void paintRightMargin(Graphics g, Rectangle clip) { EditorSettings settings = myEditor.getSettings(); Color rightMargin = myEditor.getColorsScheme().getColor(EditorColors.RIGHT_MARGIN_COLOR); if (!settings.isRightMarginShown() || rightMargin == null) return; int x = settings.getRightMargin(myEditor.getProject()) * myView.getPlainSpaceWidth(); g.setColor(rightMargin); UIUtil.drawLine(g, x, clip.y, x, clip.y + clip.height); }
private void paintCaret(Graphics2D g_) { EditorImpl.CaretRectangle[] locations = myEditor.getCaretLocations(true); if (locations == null) return; Graphics2D g = IdeBackgroundUtil.getOriginalGraphics(g_); int lineHeight = myView.getLineHeight(); EditorSettings settings = myEditor.getSettings(); Color caretColor = myEditor.getColorsScheme().getColor(EditorColors.CARET_COLOR); if (caretColor == null) caretColor = new JBColor(CARET_DARK, CARET_LIGHT); g.setColor(caretColor); for (EditorImpl.CaretRectangle location : locations) { int x = location.myPoint.x; int y = location.myPoint.y; Caret caret = location.myCaret; boolean isRtl = location.myIsRtl; if (myEditor.isInsertMode() != settings.isBlockCursor()) { int lineWidth = JBUI.scale(settings.getLineCursorWidth()); g.fillRect(x, y, lineWidth, lineHeight); if (myDocument.getTextLength() > 0 && caret != null && !myView.getLineLayout(caret.getLogicalPosition().line).isLtr()) { g.fillPolygon( new int[] { isRtl ? x + lineWidth : x, isRtl ? x + lineWidth - CARET_DIRECTION_MARK_SIZE : x + CARET_DIRECTION_MARK_SIZE, isRtl ? x + lineWidth : x }, new int[] {y, y, y + CARET_DIRECTION_MARK_SIZE}, 3); } } else { int width = location.myWidth; int startX = Math.max(0, isRtl ? x - width : x); g.fillRect(startX, y, width, lineHeight - 1); if (myDocument.getTextLength() > 0 && caret != null) { int targetVisualColumn = caret.getVisualPosition().column; for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, caret.getVisualLineStart(), false)) { int startVisualColumn = fragment.getStartVisualColumn(); int endVisualColumn = fragment.getEndVisualColumn(); if (startVisualColumn < targetVisualColumn && endVisualColumn > targetVisualColumn || startVisualColumn == targetVisualColumn && !isRtl || endVisualColumn == targetVisualColumn && isRtl) { g.setColor(ColorUtil.isDark(caretColor) ? CARET_LIGHT : CARET_DARK); fragment.draw( g, startX, y + myView.getAscent(), targetVisualColumn - startVisualColumn - (isRtl ? 1 : 0), targetVisualColumn - startVisualColumn + (isRtl ? 0 : 1)); break; } } } } } }
void repaintCarets() { EditorImpl.CaretRectangle[] locations = myEditor.getCaretLocations(false); if (locations == null) return; int lineHeight = myView.getLineHeight(); for (EditorImpl.CaretRectangle location : locations) { int x = location.myPoint.x; int y = location.myPoint.y; int width = Math.max(location.myWidth, CARET_DIRECTION_MARK_SIZE); myEditor.getContentComponent().repaintEditorComponent(x - width, y, width * 2, lineHeight); } }
Dimension getPreferredSize() { int width = getPreferredWidth(); if (!myDocument.isInBulkUpdate()) { for (Caret caret : myEditor.getCaretModel().getAllCarets()) { if (caret.isUpToDate()) { int caretX = myView.visualPositionToXY(caret.getVisualPosition()).x; width = Math.max(caretX, width); } } } width += myEditor.getSettings().getAdditionalColumnsCount() * myView.getPlainSpaceWidth(); return new Dimension(width, myEditor.getPreferredHeight()); }
private EditorWindow( @NotNull DocumentWindowImpl documentWindow, @NotNull final EditorImpl delegate, @NotNull PsiFile injectedFile, boolean oneLine) { myDocumentWindow = documentWindow; myDelegate = delegate; myInjectedFile = injectedFile; myOneLine = oneLine; myCaretModelDelegate = new CaretModelWindow(myDelegate.getCaretModel(), this); mySelectionModelDelegate = new SelectionModelWindow(myDelegate, myDocumentWindow, this); myMarkupModelDelegate = new MarkupModelWindow((MarkupModelEx) myDelegate.getMarkupModel(), myDocumentWindow); myFoldingModelWindow = new FoldingModelWindow(delegate.getFoldingModel(), documentWindow, this); }
@Override public void removeEditorMouseMotionListener(@NotNull final EditorMouseMotionListener listener) { EditorMouseMotionListener wrapper = myEditorMouseMotionListeners.removeWrapper(listener); if (wrapper != null) { myDelegate.removeEditorMouseMotionListener(wrapper); } }
private void dispose() { assert !myDisposed; myCaretModelDelegate.disposeModel(); for (EditorMouseListener wrapper : myEditorMouseListeners.wrappers()) { myDelegate.removeEditorMouseListener(wrapper); } myEditorMouseListeners.clear(); for (EditorMouseMotionListener wrapper : myEditorMouseMotionListeners.wrappers()) { myDelegate.removeEditorMouseMotionListener(wrapper); } myEditorMouseMotionListeners.clear(); myDisposed = true; Disposer.dispose(myDocumentWindow); }
private void validateMaxLineWithExtension() { if (myMaxLineWithExtensionWidth > 0) { Project project = myEditor.getProject(); VirtualFile virtualFile = myEditor.getVirtualFile(); if (project != null && virtualFile != null) { for (EditorLinePainter painter : EditorLinePainter.EP_NAME.getExtensions()) { Collection<LineExtensionInfo> extensions = painter.getLineExtensions(project, virtualFile, myWidestLineWithExtension); if (extensions != null && !extensions.isEmpty()) { return; } } } myMaxLineWithExtensionWidth = 0; } }
@Override @NotNull public LogicalPosition xyToLogicalPosition(@NotNull final Point p) { assert isValid(); LogicalPosition hostPos = myDelegate.xyToLogicalPosition(p); return hostToInjected(hostPos); }
private static List<BidiRun> createRuns(EditorImpl editor, char[] text, int startOffsetInEditor) { int textLength = text.length; if (editor.myDisableRtl) return Collections.singletonList(new BidiRun((byte) 0, 0, textLength)); List<BidiRun> runs = new ArrayList<BidiRun>(); if (startOffsetInEditor >= 0) { // running bidi algorithm separately for text fragments corresponding to different lexer // tokens int lastOffset = startOffsetInEditor; IElementType lastToken = null; HighlighterIterator iterator = editor.getHighlighter().createIterator(startOffsetInEditor); int endOffsetInEditor = startOffsetInEditor + textLength; while (!iterator.atEnd() && iterator.getStart() < endOffsetInEditor) { IElementType currentToken = iterator.getTokenType(); if (distinctTokens(lastToken, currentToken)) { int tokenStart = Math.max(iterator.getStart(), startOffsetInEditor); addRuns(runs, text, lastOffset - startOffsetInEditor, tokenStart - startOffsetInEditor); lastToken = currentToken; lastOffset = tokenStart; } iterator.advance(); } addRuns( runs, text, lastOffset - startOffsetInEditor, endOffsetInEditor - startOffsetInEditor); } else { addRuns(runs, text, 0, textLength); } return runs; }
private void paintVirtualSelectionIfNecessary( Graphics2D g, Map<Integer, Couple<Integer>> virtualSelectionMap, int columnStart, float xStart, float xEnd, int y) { int visualLine = myView.yToVisualLine(y); Couple<Integer> selectionRange = virtualSelectionMap.get(visualLine); if (selectionRange == null || selectionRange.second <= columnStart) return; float startX = selectionRange.first <= columnStart ? xStart : myView.visualPositionToXY(new VisualPosition(visualLine, selectionRange.first)).x; float endX = Math.min( xEnd, myView.visualPositionToXY(new VisualPosition(visualLine, selectionRange.second)).x); paintBackground( g, myEditor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR), startX, y, endX - startX); }
@Override protected void revertStateOnFinish() { final Editor editor = InjectedLanguageUtil.getTopLevelEditor(myEditor); if (editor == FileEditorManager.getInstance(myProject).getSelectedTextEditor()) { ((EditorImpl) editor).startDumb(); } revertState(); }
void textLayoutPerformed(int startOffset, int endOffset) { if (myDocument.isInBulkUpdate()) return; if (myEditor.getFoldingModel().isInBatchFoldingOperation()) { myDeferredRanges.add(new TextRange(startOffset, endOffset)); } else { onTextLayoutPerformed(startOffset, endOffset); } }
private boolean checkDirty() { if (myEditor.getSoftWrapModel().isDirty()) { myDirty = true; return true; } if (myDirty) { int visibleLineCount = myEditor.getVisibleLineCount(); int lineDiff = visibleLineCount - myLineWidths.size(); if (lineDiff > 0) myLineWidths.add(new int[lineDiff]); else if (lineDiff < 0) myLineWidths.remove(visibleLineCount, -lineDiff); for (int i = 0; i < visibleLineCount; i++) { myLineWidths.set(i, UNKNOWN_WIDTH); } myDirty = false; } return false; }
@Override public void removeEditorMouseListener(@NotNull final EditorMouseListener listener) { EditorMouseListener wrapper = myEditorMouseListeners.removeWrapper(listener); // HintManager might have an old editor instance if (wrapper != null) { myDelegate.removeEditorMouseListener(wrapper); } }
Dimension getPreferredSize() { int widthWithoutCaret = getPreferredWidth(); int width = widthWithoutCaret; if (!myDocument.isInBulkUpdate()) { for (Caret caret : myEditor.getCaretModel().getAllCarets()) { if (caret.isUpToDate()) { int caretX = myView.visualPositionToXY(caret.getVisualPosition()).x; width = Math.max(caretX, width); } } } if (shouldRespectAdditionalColumns(widthWithoutCaret)) { width += myEditor.getSettings().getAdditionalColumnsCount() * myView.getPlainSpaceWidth(); } Insets insets = myView.getInsets(); return new Dimension(width + insets.left + insets.right, getPreferredHeight()); }
private void onTextLayoutPerformed(int startOffset, int endOffset) { if (checkDirty()) return; boolean purePaintingMode = myEditor.isPurePaintingMode(); boolean foldingEnabled = myEditor.getFoldingModel().isFoldingEnabled(); myEditor.setPurePaintingMode(false); myEditor.getFoldingModel().setFoldingEnabled(true); try { int startVisualLine = myView.offsetToVisualLine(startOffset, false); int endVisualLine = myView.offsetToVisualLine(endOffset, true); boolean sizeInvalidated = false; for (int i = startVisualLine; i <= endVisualLine; i++) { if (myLineWidths.get(i) < 0) { myLineWidths.set(i, UNKNOWN_WIDTH); sizeInvalidated = true; } } if (sizeInvalidated) { myWidthInPixels = -1; myEditor.getContentComponent().revalidate(); } } finally { myEditor.setPurePaintingMode(purePaintingMode); myEditor.getFoldingModel().setFoldingEnabled(foldingEnabled); } }
public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final EditorWindow that = (EditorWindow) o; DocumentWindow thatWindow = that.getDocument(); return myDelegate.equals(that.myDelegate) && myDocumentWindow.equals(thatWindow); }
public void testLeadingTabWithShiftedWidth() throws IOException { // Inspired by IDEA-76353. The point is that we need to consider cached information about tab // symbols width during logical // position to offset mapping String text = "\t test"; init(15, text); ((EditorImpl) myEditor).setPrefixTextAndAttributes(" ", new TextAttributes()); myEditor.getCaretModel().moveToOffset(text.length()); }
private void paintBackground( Graphics2D g, Rectangle clip, int startVisualLine, int endVisualLine) { int lineCount = myEditor.getVisibleLineCount(); final Map<Integer, Couple<Integer>> virtualSelectionMap = createVirtualSelectionMap(startVisualLine, endVisualLine); for (int visualLine = startVisualLine; visualLine <= endVisualLine; visualLine++) { int y = myView.visualLineToY(visualLine); LineLayout prefixLayout = myView.getPrefixLayout(); if (visualLine == 0 && prefixLayout != null) { paintBackground(g, myView.getPrefixAttributes(), 0, y, prefixLayout.getWidth()); } if (visualLine >= lineCount) break; paintLineFragments( g, clip, visualLine, y, new LineFragmentPainter() { @Override public void paintBeforeLineStart( Graphics2D g, TextAttributes attributes, int columnEnd, float xEnd, int y) { paintBackground(g, attributes, 0, y, xEnd); paintSelectionOnSecondSoftWrapLineIfNecessary(g, columnEnd, xEnd, y); } @Override public void paint( Graphics2D g, VisualLineFragmentsIterator.Fragment fragment, int start, int end, TextAttributes attributes, float xStart, float xEnd, int y) { paintBackground(g, attributes, xStart, y, xEnd - xStart); } @Override public void paintAfterLineEnd( Graphics2D g, Rectangle clip, IterationState it, int columnStart, float x, int y) { paintBackground( g, it.getPastLineEndBackgroundAttributes(), x, y, clip.x + clip.width - x); int offset = it.getEndOffset(); SoftWrap softWrap = myEditor.getSoftWrapModel().getSoftWrap(offset); if (softWrap == null) { paintVirtualSelectionIfNecessary( g, virtualSelectionMap, columnStart, x, clip.x + clip.width, y); } else { paintSelectionOnFirstSoftWrapLineIfNecessary( g, columnStart, x, clip.x + clip.width, y); } } }); } }
private void paintTextEffect( Graphics2D g, float xFrom, float xTo, int y, Color effectColor, EffectType effectType) { int xStart = (int) xFrom; int xEnd = (int) xTo; g.setColor(effectColor); if (effectType == EffectType.LINE_UNDERSCORE) { UIUtil.drawLine(g, xStart, y + 1, xEnd, y + 1); } else if (effectType == EffectType.BOLD_LINE_UNDERSCORE) { int height = JBUI.scale(Registry.intValue("editor.bold.underline.height", 2)); g.fillRect(xStart, y, xEnd - xStart, height); } else if (effectType == EffectType.STRIKEOUT) { int y1 = y - myView.getCharHeight() / 2; UIUtil.drawLine(g, xStart, y1, xEnd, y1); } else if (effectType == EffectType.WAVE_UNDERSCORE) { UIUtil.drawWave(g, new Rectangle(xStart, y + 1, xEnd - xStart, myView.getDescent() - 1)); } else if (effectType == EffectType.BOLD_DOTTED_LINE) { UIUtil.drawBoldDottedLine( g, xStart, xEnd, SystemInfo.isMac ? y : y + 1, myEditor.getBackgroundColor(), g.getColor(), false); } }