diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleGutterComponent.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleGutterComponent.java index c6cb0737c857..7fdb62517bf4 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleGutterComponent.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleGutterComponent.java @@ -41,6 +41,8 @@ class ConsoleGutterComponent extends JComponent implements MouseMotionListener { addListeners(); addMouseMotionListener(this); + + setOpaque(true); } private void addListeners() { @@ -68,6 +70,8 @@ class ConsoleGutterComponent extends JComponent implements MouseMotionListener { } private void computeMaxAnnotationWidth() { + gutterContentProvider.beforeUiComponentUpdate(editor); + if (!gutterContentProvider.hasText()) { maxAnnotationWidth = 0; return; @@ -75,7 +79,6 @@ class ConsoleGutterComponent extends JComponent implements MouseMotionListener { FontMetrics fontMetrics = editor.getFontMetrics(Font.PLAIN); int lineCount = editor.getDocument().getLineCount(); - gutterContentProvider.beforeUiComponentUpdate(editor); int gutterSize = 0; for (int i = 0; i < lineCount; i++) { String text = gutterContentProvider.getText(i, editor); @@ -101,9 +104,6 @@ class ConsoleGutterComponent extends JComponent implements MouseMotionListener { return; } - g.setColor(editor.getBackgroundColor()); - g.fillRect(clip.x, clip.y, clip.width, clip.height); - UISettings.setupAntialiasing(g); Graphics2D g2 = (Graphics2D)g; diff --git a/platform/lang-impl/src/com/intellij/execution/console/GutterContentProvider.java b/platform/lang-impl/src/com/intellij/execution/console/GutterContentProvider.java index af7d34c0ea59..c283b1a53fdc 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/GutterContentProvider.java +++ b/platform/lang-impl/src/com/intellij/execution/console/GutterContentProvider.java @@ -27,4 +27,8 @@ public abstract class GutterContentProvider { public abstract void doAction(int line, @NotNull Editor editor); public abstract void drawIcon(int line, @NotNull Graphics g, int y, @NotNull Editor editor); + + public boolean isShowSeparatorLine(int line, @NotNull Editor editor) { + return true; + } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java index bacc247bc7ba..07d97e0fa55c 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java @@ -4,18 +4,26 @@ import com.intellij.execution.process.ProcessHandler; import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.VisualPosition; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.DocumentBulkUpdateListener; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.ex.RangeHighlighterEx; +import com.intellij.openapi.editor.impl.EditorComponentImpl; import com.intellij.openapi.editor.impl.EditorImpl; +import com.intellij.openapi.editor.impl.RangeMarkerImpl; +import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; +import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.components.JBLayeredPane; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -112,40 +120,6 @@ public class LanguageConsoleBuilder { } } - private static class GutteredEditorPanel extends JPanel { - private final EditorEx editor; - - public GutteredEditorPanel(EditorEx editor) { - super(new BorderLayout()); - - this.editor = editor; - } - - @Override - public void paint(Graphics g) { - super.paint(g); - - Rectangle clip = g.getClipBounds(); - if (clip.height < 0) { - return; - } - - int lineHeight = editor.getLineHeight(); - int startLine = clip.y / lineHeight; - int endLine = Math.min(((clip.y + clip.height) / lineHeight) + 1, ((EditorImpl)editor).getVisibleLineCount()); - if (startLine >= endLine) { - return; - } - - int y = ((startLine + 1) * lineHeight); - g.setColor(editor.getColorsScheme().getColor(EditorColors.INDENT_GUIDE_COLOR)); - for (int i = startLine; i < endLine; i++) { - g.drawLine(ConsoleIconGutterComponent.ICON_AREA_WIDTH, y, clip.width, y); - y += lineHeight; - } - } - } - private static class MyLanguageConsole extends LanguageConsoleImpl { @Nullable private GutterContentProvider gutterContentProvider; @@ -155,7 +129,7 @@ public class LanguageConsoleBuilder { } @Override - protected void setupEditorDefault(@NotNull EditorEx editor) { + protected void setupEditorDefault(@NotNull final EditorEx editor) { super.setupEditorDefault(editor); if (editor == getConsoleEditor()) { @@ -163,52 +137,53 @@ public class LanguageConsoleBuilder { editor.getSettings().setAdditionalLinesCount(1); } else if (gutterContentProvider != null) { - JScrollPane scrollPane = editor.getScrollPane(); - JPanel panel = new GutteredEditorPanel(editor); - - final ConsoleIconGutterComponent lineStartGutter = new ConsoleIconGutterComponent(editor, gutterContentProvider); - panel.add(lineStartGutter, BorderLayout.LINE_START); - - panel.add(scrollPane.getViewport().getView(), BorderLayout.CENTER); - - final ConsoleGutterComponent lineEndGutter = new ConsoleGutterComponent(editor, gutterContentProvider); - panel.add(lineEndGutter, BorderLayout.LINE_END); - - scrollPane.setViewportView(panel); - - getProject().getMessageBus().connect(this).subscribe(DocumentBulkUpdateListener.TOPIC, new DocumentBulkUpdateListener.Adapter() { + JLayeredPane layeredPane = new JBLayeredPane() { @Override - public void updateFinished(@NotNull Document document) { - if (document.getTextLength() == 0) { - gutterContentProvider.documentCleared(getHistoryViewer()); - } - lineStartGutter.updateSize(); - lineEndGutter.updateSize(); - } - }); - - editor.getDocument().addDocumentListener(new DocumentAdapter() { - @Override - public void documentChanged(DocumentEvent event) { - EditorEx editor = getHistoryViewer(); - DocumentEx document = editor.getDocument(); - if (document.isInBulkUpdate()) { - return; - } - - if (document.getTextLength() > 0) { - int startDocLine = document.getLineNumber(event.getOffset()); - int endDocLine = document.getLineNumber(event.getOffset() + event.getNewLength()); - if (event.getOldLength() > event.getNewLength() || startDocLine != endDocLine || StringUtil.indexOf(event.getOldFragment(), '\n') != -1) { - lineStartGutter.updateSize(); - lineEndGutter.updateSize(); + public void doLayout() { + EditorComponentImpl editor = null; + Component lineStartGutter = null; + Component lineEndGutter = null; + for (int i = getComponentCount() - 1; i >= 0; i--) { + Component component = getComponent(i); + if (component instanceof EditorComponentImpl) { + editor = (EditorComponentImpl)component; + } + else if (getLayer(component) == JLayeredPane.DEFAULT_LAYER) { + lineStartGutter = component; + } + else { + lineEndGutter = component; } } - else if (event.getOldLength() > 0) { - gutterContentProvider.documentCleared(editor); - } + + assert editor != null && lineStartGutter != null && lineEndGutter != null; + + int w = getWidth(); + int h = getHeight(); + Dimension lineStartGutterDimension = lineStartGutter.getPreferredSize(); + lineStartGutter.setBounds(0, 0, lineStartGutterDimension.width, h); + + editor.setBounds(lineStartGutterDimension.width, 0, w - lineStartGutterDimension.width, h); + + Dimension lineEndGutterDimension = lineEndGutter.getPreferredSize(); + lineEndGutter.setBounds(w - lineEndGutterDimension.width - editor.getEditor().getScrollPane().getVerticalScrollBar().getWidth(), 0, lineEndGutterDimension.width, h); } - }); + }; + + ConsoleIconGutterComponent lineStartGutter = new ConsoleIconGutterComponent(editor, gutterContentProvider); + layeredPane.add(lineStartGutter, JLayeredPane.DEFAULT_LAYER); + + JScrollPane scrollPane = editor.getScrollPane(); + layeredPane.add(scrollPane.getViewport().getView(), JLayeredPane.DEFAULT_LAYER); + + ConsoleGutterComponent lineEndGutter = new ConsoleGutterComponent(editor, gutterContentProvider); + layeredPane.add(lineEndGutter, JLayeredPane.PALETTE_LAYER); + + scrollPane.setViewportView(layeredPane); + + GutterUpdateScheduler gutterUpdateScheduler = new GutterUpdateScheduler(lineStartGutter, lineEndGutter); + getProject().getMessageBus().connect(this).subscribe(DocumentBulkUpdateListener.TOPIC, gutterUpdateScheduler); + editor.getDocument().addDocumentListener(gutterUpdateScheduler); } } @@ -221,5 +196,247 @@ public class LanguageConsoleBuilder { gutterContentProvider.beforeEvaluate(getHistoryViewer()); } } + + private class GutterUpdateScheduler extends DocumentAdapter implements DocumentBulkUpdateListener { + private final ConsoleIconGutterComponent lineStartGutter; + private final ConsoleGutterComponent lineEndGutter; + + private boolean lineSeparatorPainterAdded; + + public GutterUpdateScheduler(@NotNull ConsoleIconGutterComponent lineStartGutter, @NotNull ConsoleGutterComponent lineEndGutter) { + this.lineStartGutter = lineStartGutter; + this.lineEndGutter = lineEndGutter; + } + + private void addLineSeparatorPainterIfNeed() { + if (lineSeparatorPainterAdded) { + return; + } + + lineSeparatorPainterAdded = true; + + EditorEx editor = getHistoryViewer(); + editor.getMarkupModel().addRangeHighlighter(new MyRangeMarkerImpl(editor), 0, getDocument().getTextLength(), false, false, HighlighterLayer.ADDITIONAL_SYNTAX); + } + + private DocumentEx getDocument() { + return getHistoryViewer().getDocument(); + } + + @Override + public void documentChanged(DocumentEvent event) { + DocumentEx document = getDocument(); + if (document.isInBulkUpdate()) { + return; + } + + if (document.getTextLength() > 0) { + addLineSeparatorPainterIfNeed(); + int startDocLine = document.getLineNumber(event.getOffset()); + int endDocLine = document.getLineNumber(event.getOffset() + event.getNewLength()); + if (event.getOldLength() > event.getNewLength() || startDocLine != endDocLine || StringUtil.indexOf(event.getOldFragment(), '\n') != -1) { + lineStartGutter.updateSize(); + lineEndGutter.updateSize(); + } + } + else if (event.getOldLength() > 0) { + assert gutterContentProvider != null; + gutterContentProvider.documentCleared(getHistoryViewer()); + } + } + + @Override + public void updateStarted(@NotNull Document doc) { + } + + @Override + public void updateFinished(@NotNull Document doc) { + if (getDocument().getTextLength() == 0) { + assert gutterContentProvider != null; + gutterContentProvider.documentCleared(getHistoryViewer()); + } + else { + addLineSeparatorPainterIfNeed(); + } + lineStartGutter.updateSize(); + lineEndGutter.updateSize(); + } + } + + private class MyRangeMarkerImpl extends RangeMarkerImpl implements RangeHighlighterEx, Getter { + private final CustomHighlighterRenderer renderer = new CustomHighlighterRenderer() { + @Override + public void paint(@NotNull Editor editor, @NotNull RangeHighlighter highlighter, @NotNull Graphics g) { + Rectangle clip = g.getClipBounds(); + if (clip.height < 0) { + return; + } + + int lineHeight = editor.getLineHeight(); + int startLine = clip.y / lineHeight; + int endLine = Math.min(((clip.y + clip.height) / lineHeight) + 1, ((EditorImpl)editor).getVisibleLineCount()); + if (startLine >= endLine) { + return; + } + + int y = ((startLine + 1) * lineHeight); + g.setColor(editor.getColorsScheme().getColor(EditorColors.INDENT_GUIDE_COLOR)); + assert gutterContentProvider != null; + for (int i = startLine; i < endLine; i++) { + if (gutterContentProvider.isShowSeparatorLine(editor.visualToLogicalPosition(new VisualPosition(i, 0)).line, editor)) { + g.drawLine(0, y, clip.width, y); + } + y += lineHeight; + } + } + }; + + public MyRangeMarkerImpl(@NotNull EditorEx editor) { + super(editor.getDocument(), 0, 1, false); + } + + @Override + protected void changedUpdateImpl(DocumentEvent e) { + setIntervalEnd(myDocument.getTextLength()); + } + + @Override + public boolean isAfterEndOfLine() { + return false; + } + + @Override + public void setAfterEndOfLine(boolean value) { + } + + @Override + public int getAffectedAreaStartOffset() { + return 0; + } + + @Override + public int getAffectedAreaEndOffset() { + return myDocument.getTextLength(); + } + + @Override + public void setTextAttributes(@NotNull TextAttributes textAttributes) { + } + + @NotNull + @Override + public HighlighterTargetArea getTargetArea() { + return HighlighterTargetArea.EXACT_RANGE; + } + + @Nullable + @Override + public TextAttributes getTextAttributes() { + return null; + } + + @Nullable + @Override + public LineMarkerRenderer getLineMarkerRenderer() { + return null; + } + + @Override + public void setLineMarkerRenderer(@Nullable LineMarkerRenderer renderer) { + } + + @Nullable + @Override + public CustomHighlighterRenderer getCustomRenderer() { + return renderer; + } + + @Override + public void setCustomRenderer(CustomHighlighterRenderer renderer) { + } + + @Nullable + @Override + public GutterIconRenderer getGutterIconRenderer() { + return null; + } + + @Override + public void setGutterIconRenderer(@Nullable GutterIconRenderer renderer) { + } + + @Nullable + @Override + public Color getErrorStripeMarkColor() { + return null; + } + + @Override + public void setErrorStripeMarkColor(@Nullable Color color) { + } + + @Nullable + @Override + public Object getErrorStripeTooltip() { + return null; + } + + @Override + public void setErrorStripeTooltip(@Nullable Object tooltipObject) { + } + + @Override + public boolean isThinErrorStripeMark() { + return false; + } + + @Override + public void setThinErrorStripeMark(boolean value) { + } + + @Nullable + @Override + public Color getLineSeparatorColor() { + return null; + } + + @Override + public void setLineSeparatorColor(@Nullable Color color) { + } + + @Override + public void setLineSeparatorRenderer(LineSeparatorRenderer renderer) { + } + + @Override + public LineSeparatorRenderer getLineSeparatorRenderer() { + return null; + } + + @Nullable + @Override + public SeparatorPlacement getLineSeparatorPlacement() { + return null; + } + + @Override + public void setLineSeparatorPlacement(@Nullable SeparatorPlacement placement) { + } + + @Override + public void setEditorFilter(@NotNull MarkupEditorFilter filter) { + } + + @NotNull + @Override + public MarkupEditorFilter getEditorFilter() { + return MarkupEditorFilter.EMPTY; + } + + @Override + public RangeHighlighterEx get() { + return this; + } + } } } \ No newline at end of file