mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
js repl: draw line separators using range marker highlighter
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+294
-77
@@ -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<RangeHighlighterEx> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user