diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/codemining/CodeMiningLineHeaderAnnotation.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/codemining/CodeMiningLineHeaderAnnotation.java index cee3843409b..5962be4c8c4 100644 --- a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/codemining/CodeMiningLineHeaderAnnotation.java +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/codemining/CodeMiningLineHeaderAnnotation.java @@ -66,7 +66,7 @@ public class CodeMiningLineHeaderAnnotation extends LineHeaderAnnotation impleme * Code mining annotation constructor. * * @param position the position - * @param viewer the viewer + * @param viewer the viewer */ public CodeMiningLineHeaderAnnotation(Position position, ISourceViewer viewer) { super(position, viewer); @@ -77,7 +77,24 @@ public CodeMiningLineHeaderAnnotation(Position position, ISourceViewer viewer) { @Override public int getHeight() { - return hasAtLeastOneResolvedMiningNotEmpty() ? super.getHeight() : 0; + return hasAtLeastOneResolvedMiningNotEmpty() ? getMultilineHeight() : 0; + } + + private int getMultilineHeight() { + int numLinesOfAllMinings= 0; + for (ICodeMining mining : fMinings) { + String label= mining.getLabel(); + if (label == null) { + continue; + } + int numLines= label.split("\\r?\\n|\\r").length; //$NON-NLS-1$ + if (numLines > 1) { + numLinesOfAllMinings= numLines - 1; + } + } + numLinesOfAllMinings++; + StyledText styledText= super.getTextWidget(); + return numLinesOfAllMinings * (styledText.getLineHeight() + styledText.getLineSpacing()); } /** @@ -138,6 +155,8 @@ public void draw(GC gc, StyledText textWidget, int offset, int length, Color col int separatorWidth= -1; boolean redrawn= false; fBounds.clear(); + int singleLineHeight= super.getHeight(); + int lineSpacing= textWidget.getLineSpacing(); for (int i= 0; i < minings.size(); i++) { ICodeMining mining= minings.get(i); // try to get the last resolved mining. @@ -169,10 +188,17 @@ public void draw(GC gc, StyledText textWidget, int offset, int length, Color col } x+= separatorWidth; } - @SuppressWarnings("null") - Point loc= mining.draw(gc, textWidget, color, x, y); - fBounds.add(new Rectangle(x, y, loc.x, loc.y)); - x+= loc.x; + Point loc= null; + if (mining != null) { + loc= mining.draw(gc, textWidget, color, x, y); + } + if (loc != null) { + fBounds.add(new Rectangle(x, y, loc.x, loc.y)); + x+= loc.x; + if (loc.y > singleLineHeight) { + y+= loc.y + lineSpacing - singleLineHeight; + } + } nbDraw++; } } diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/codemining/LineHeaderCodeMining.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/codemining/LineHeaderCodeMining.java index b15f5490162..3d8a319d9cf 100644 --- a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/codemining/LineHeaderCodeMining.java +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/codemining/LineHeaderCodeMining.java @@ -16,7 +16,11 @@ import java.util.function.Consumer; +import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; @@ -73,4 +77,22 @@ public LineHeaderCodeMining(Position position, ICodeMiningProvider provider, Con super(position, provider, action); } + @Override + public Point draw(GC gc, StyledText textWidget, Color color, int x, int y) { + String title= getLabel() != null ? getLabel() : "no command"; //$NON-NLS-1$ + String[] lines= title.split("\\r?\\n|\\r"); //$NON-NLS-1$ + if (lines.length > 1) { + Point result= new Point(0, 0); + for (String line : lines) { + gc.drawString(line, x, y, true); + Point ext= gc.stringExtent(line); + result.x= Math.max(result.x, ext.x); + result.y+= ext.y; + y+= result.y + textWidget.getLineSpacing(); + } + return result; + } else { + return super.draw(gc, textWidget, color, x, y); + } + } } diff --git a/examples/org.eclipse.jface.text.examples/src/org/eclipse/jface/text/examples/codemining/CodeMiningDemo.java b/examples/org.eclipse.jface.text.examples/src/org/eclipse/jface/text/examples/codemining/CodeMiningDemo.java index 002f72a2143..1cc8a5cf656 100644 --- a/examples/org.eclipse.jface.text.examples/src/org/eclipse/jface/text/examples/codemining/CodeMiningDemo.java +++ b/examples/org.eclipse.jface.text.examples/src/org/eclipse/jface/text/examples/codemining/CodeMiningDemo.java @@ -69,7 +69,9 @@ public static void main(String[] args) throws Exception { + "class 5\n" // + "new 5\n" // + "new 5\n" // - + "new 5\n"), + + "new 5\n" // + + "multiline \n" // + + "multiline \n\n"), new AnnotationModel()); GridDataFactory.fillDefaults().grab(true, true).applyTo(sourceViewer.getTextWidget()); // Add AnnotationPainter (required by CodeMining) @@ -79,6 +81,7 @@ public static void main(String[] args) throws Exception { new ClassReferenceCodeMiningProvider(), // new ClassImplementationsCodeMiningProvider(), // new ToEchoWithHeaderAndInlineCodeMiningProvider("echo"), // + new MultilineCodeMiningProvider(), // new EmptyLineCodeMiningProvider(), // new EchoAtEndOfLineCodeMiningProvider(endOfLineString) }); // Execute codemining in a reconciler diff --git a/examples/org.eclipse.jface.text.examples/src/org/eclipse/jface/text/examples/codemining/MultilineCodeMiningProvider.java b/examples/org.eclipse.jface.text.examples/src/org/eclipse/jface/text/examples/codemining/MultilineCodeMiningProvider.java new file mode 100644 index 00000000000..5f51141d3c0 --- /dev/null +++ b/examples/org.eclipse.jface.text.examples/src/org/eclipse/jface/text/examples/codemining/MultilineCodeMiningProvider.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2024, SAP. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.jface.text.examples.codemining; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.codemining.AbstractCodeMining; +import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; +import org.eclipse.jface.text.codemining.ICodeMining; +import org.eclipse.jface.text.codemining.LineHeaderCodeMining; + +public class MultilineCodeMiningProvider extends AbstractCodeMiningProvider { + + @Override + public CompletableFuture> provideCodeMinings(ITextViewer viewer, + IProgressMonitor monitor) { + String multiLineText = "multiline"; + IDocument document = viewer.getDocument(); + List res = new ArrayList<>(); + int index = 0; + while ((index = document.get().indexOf(multiLineText, index)) != -1) { + index += multiLineText.length(); + res.add(new AbstractCodeMining(new Position(index, 1), this, null) { + @Override + public String getLabel() { + return "multiline first part in same line"; + } + }); + try { + int line = document.getLineOfOffset(index); + String lineDelimiter = document.getLineDelimiter(line); + res.add(new LineHeaderCodeMining(line + 1, document, this) { + @Override + public String getLabel() { + return "multiline second line" + lineDelimiter + "multiline third line"; + } + }); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + return CompletableFuture.completedFuture(res); + } + +}