Skip to content

Commit

Permalink
Don't compute tooltips based on maxWidth (let the viewport clip it). …
Browse files Browse the repository at this point in the history
…Fixes #PyDev-1233: Eclipse IDE freezes when using a variable with a very long base64 value
  • Loading branch information
fabioz committed Sep 3, 2023
1 parent 7222122 commit ba5f4f0
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 198 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.swt.graphics.Drawable;
import org.eclipse.swt.widgets.Display;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;

/**
Expand Down Expand Up @@ -40,21 +39,7 @@ public String updatePresentation(Display display, String hoverInfo, TextPresenta
* the ones existing and add the ones dependent on the platform
*/
protected String correctLineDelimiters(String str) {
FastStringBuffer buf = new FastStringBuffer();
for (String s : StringUtils.splitInLines(str)) {

boolean found = false;
while (s.endsWith("\r") || s.endsWith("\n")) {
found = true;
s = s.substring(0, s.length() - 1);
}
buf.append(s);
if (found) {
buf.append(LINE_DELIM);
}
}
str = buf.toString();
return str;
return StringUtils.replaceNewLines(str, LINE_DELIM);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jface.resource.JFaceColors;
Expand All @@ -34,7 +33,6 @@
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TypedListener;
import org.python.pydev.ast.item_pointer.ItemPointer;
import org.python.pydev.core.docutils.PyStringUtils;
import org.python.pydev.editor.actions.PyOpenAction;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_ui.tooltips.presenter.AbstractInformationPresenter;
Expand Down Expand Up @@ -76,19 +74,8 @@ public boolean similarTo(StyleRange style) {
}
}

private int fCounter;
private boolean fEnforceUpperLineLimit;
private ControlListener resizeCallback;

public PyInformationPresenter(boolean enforceUpperLineLimit) {
super();
fEnforceUpperLineLimit = enforceUpperLineLimit;
}

public PyInformationPresenter() {
this(true);
}

private TypedListener resizeListener = new TypedListener(new ControlListener() {

@Override
Expand All @@ -111,7 +98,7 @@ public void controlResized(ControlEvent e) {
* Creates the reader and properly puts the presentation into place.
*/
public Reader createReader(String hoverInfo, TextPresentation presentation) {
String str = PyStringUtils.removeWhitespaceColumnsToLeft(hoverInfo);
String str = hoverInfo;

str = correctLineDelimiters(str);

Expand Down Expand Up @@ -192,63 +179,23 @@ private String handleLinks(List<PyStyleRange> lst, String str, FastStringBuffer
return newString;
}

protected void adaptTextPresentation(TextPresentation presentation, int offset, int insertLength) {

int yoursStart = offset;
int yoursEnd = offset + insertLength - 1;
yoursEnd = Math.max(yoursStart, yoursEnd);

Iterator<StyleRange> e = presentation.getAllStyleRangeIterator();
while (e.hasNext()) {

StyleRange range = e.next();

int myStart = range.start;
int myEnd = range.start + range.length - 1;
myEnd = Math.max(myStart, myEnd);

if (myEnd < yoursStart) {
continue;
}

if (myStart < yoursStart) {
range.length += insertLength;
} else {
range.start += insertLength;
}
}
}

private void append(FastStringBuffer buffer, String string, TextPresentation presentation) {

int length = string.length();
private void append(FastStringBuffer buffer, String string) {
buffer.append(string);

if (presentation != null) {
adaptTextPresentation(presentation, fCounter, length);
}

fCounter += length;
}

private String getIndent(String line) {
int length = line.length();

int i = 0;
while (i < length && Character.isWhitespace(line.charAt(i))) {
++i;
}

return (i == length ? line : line.substring(0, i)) + " "; //$NON-NLS-1$
}

/*
* @see IHoverInformationPresenterExtension#updatePresentation(Drawable drawable, String, TextPresentation, int, int)
* @see DefaultInformationControl.IHoverInformationPresenterExtension#updatePresentation(Drawable drawable, String, TextPresentation, int, int)
* @since 3.2
*/
@Override
public String updatePresentation(Drawable drawable, String hoverInfo, TextPresentation presentation, int maxWidth,
int maxHeight) {
// Note: we don't actually use nor respect maxWidth.
// In practice maxWidth could be used to clip/wrap based on the current viewport, but it's not really helpful
// as the current viewport can clip it already even if we send more information and spending compute
// time for the width can be slow (and when the user actually focuses the tooltip scrolls will be
// properly shown as it'll be requested with a big width/height).
// So, we just clip based on the height as that's faster/easier than calculating based on the width.
if (drawable instanceof StyledText) {
final StyledText styledText = (StyledText) drawable;
styledText.addMouseListener(new MouseAdapter() {
Expand All @@ -257,7 +204,7 @@ public String updatePresentation(Drawable drawable, String hoverInfo, TextPresen
public void mouseDown(MouseEvent e) {
int offset;
try {
offset = styledText.getOffsetAtLocation(new Point(e.x, e.y));
offset = styledText.getOffsetAtPoint(new Point(e.x, e.y));
} catch (IllegalArgumentException e1) {
return; //invalid location
}
Expand Down Expand Up @@ -296,57 +243,35 @@ public void mouseDown(MouseEvent e) {
FastStringBuffer buffer = new FastStringBuffer();
int maxNumberOfLines = Math.round((float) maxHeight / (float) gc.getFontMetrics().getHeight());

fCounter = 0;
PyLineBreakReader reader = new PyLineBreakReader(createReader(hoverInfo, presentation), gc, maxWidth);

boolean lastLineFormatted = false;
String lastLineIndent = null;
PyLineBreakReader reader = new PyLineBreakReader(createReader(hoverInfo, presentation));

String line = reader.readLine();
boolean lineFormatted = reader.isFormattedLine();
boolean firstLineProcessed = false;

while (line != null) {

if (fEnforceUpperLineLimit && maxNumberOfLines <= 0) {
if (maxNumberOfLines <= 0) {
break;
}

if (firstLineProcessed) {
if (!lastLineFormatted) {
append(buffer, LINE_DELIM, null);
} else {
append(buffer, LINE_DELIM, presentation);
if (lastLineIndent != null) {
append(buffer, lastLineIndent, presentation);
}
}
append(buffer, LINE_DELIM);
}

append(buffer, line, null);
append(buffer, line);
firstLineProcessed = true;

lastLineFormatted = lineFormatted;
if (!lineFormatted) {
lastLineIndent = null;
} else if (lastLineIndent == null) {
lastLineIndent = getIndent(line);
}

line = reader.readLine();
lineFormatted = reader.isFormattedLine();

maxNumberOfLines--;
}

if (line != null) {
append(buffer, LINE_DELIM, lineFormatted ? presentation : null);
append(buffer, LINE_DELIM);
}

return trim(buffer, presentation);

} catch (IOException e) {

// ignore TODO do something else?
return null;

Expand All @@ -356,7 +281,6 @@ public void mouseDown(MouseEvent e) {
}

private String trim(FastStringBuffer buffer, TextPresentation presentation) {

int length = buffer.length();

int end = length - 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,115 +9,30 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.text.BreakIterator;

import org.eclipse.swt.graphics.GC;

public class PyLineBreakReader {

private BufferedReader fReader;
private GC fGC;
private int fMaxWidth;

private String fLine;
private int fOffset;

private BreakIterator fLineBreakIterator;
private boolean fBreakWords;

/**
* Creates a reader that breaks an input text to fit in a given width.
*
*
* @param reader Reader of the input text
* @param gc The graphic context that defines the currently used font sizes
* @param maxLineWidth The max width (pixels) where the text has to fit in
*/
public PyLineBreakReader(Reader reader, GC gc, int maxLineWidth) {
public PyLineBreakReader(Reader reader) {
fReader = new BufferedReader(reader);
fGC = gc;
fMaxWidth = maxLineWidth;
fOffset = 0;
fLine = null;
fLineBreakIterator = BreakIterator.getLineInstance();
fBreakWords = true;
}

public boolean isFormattedLine() {
return fLine != null;
}

/**
* Reads the next line. The lengths of the line will not exceed the given maximum
* width.
*
* @return the next line
* @throws IOException
*
* @return the next line
* @throws IOException
*/
public String readLine() throws IOException {
if (fLine == null) {
String line = fReader.readLine();
if (line == null)
return null;

int lineLen = fGC.textExtent(line).x;
if (lineLen < fMaxWidth) {
return line;
}
fLine = line;
fLineBreakIterator.setText(line);
fOffset = 0;
}
int breakOffset = findNextBreakOffset(fOffset);
String res;
if (breakOffset != BreakIterator.DONE) {
res = fLine.substring(fOffset, breakOffset);
fOffset = findWordBegin(breakOffset);
if (fOffset == fLine.length()) {
fLine = null;
}
} else {
res = fLine.substring(fOffset);
fLine = null;
}
return res;
return fReader.readLine();
}

private int findNextBreakOffset(int currOffset) {
int currWidth = 0;
int nextOffset = fLineBreakIterator.following(currOffset);
while (nextOffset != BreakIterator.DONE) {
String word = fLine.substring(currOffset, nextOffset);
int wordWidth = fGC.textExtent(word).x;
int nextWidth = wordWidth + currWidth;
if (nextWidth > fMaxWidth) {
if (currWidth > 0)
return currOffset;

if (!fBreakWords)
return nextOffset;

// need to fit into fMaxWidth
int length = word.length();
while (length >= 0) {
length--;
word = word.substring(0, length);
wordWidth = fGC.textExtent(word).x;
if (wordWidth + currWidth < fMaxWidth)
return currOffset + length;
}
return nextOffset;
}
currWidth = nextWidth;
currOffset = nextOffset;
nextOffset = fLineBreakIterator.next();
}
return nextOffset;
}

private int findWordBegin(int idx) {
while (idx < fLine.length() && Character.isWhitespace(fLine.charAt(idx))) {
idx++;
}
return idx;
}
}

0 comments on commit ba5f4f0

Please sign in to comment.