Skip to content

Commit

Permalink
[StickyScrolling] Use source viewer to calculate the sticky lines
Browse files Browse the repository at this point in the history
Use the source viewer instead of the text widget to calculate the sticky lines. The source viewer is the standard instance for source code operations in JDT and other editors.

Preparation for #2398
  • Loading branch information
Christopher-Hermann committed Dec 5, 2024
1 parent ce9874f commit 97881bf
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

import org.eclipse.swt.custom.StyledText;

import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.source.ISourceViewer;

/**
* This class provides sticky lines for the given source code in the source viewer. The
* implementation is completely based on indentation and therefore works by default for several
Expand All @@ -33,14 +36,16 @@ public class DefaultStickyLinesProvider implements IStickyLinesProvider {
private StickyLinesProperties fProperties;

@Override
public List<IStickyLine> getStickyLines(StyledText textWidget, int lineNumber, StickyLinesProperties properties) {
public List<IStickyLine> getStickyLines(ISourceViewer sourceViewer, int lineNumber, StickyLinesProperties properties) {
this.fProperties= properties;
LinkedList<IStickyLine> stickyLines= new LinkedList<>();

StyledText textWidget= sourceViewer.getTextWidget();
int textWidgetLineNumber= mapLineNumberToWidget(sourceViewer, lineNumber);
try {
int startIndetation= getStartIndentation(lineNumber, textWidget);
int startIndetation= getStartIndentation(textWidgetLineNumber, textWidget);

for (int i= lineNumber, previousIndetation= startIndetation; i >= 0; i--) {
for (int i= textWidgetLineNumber, previousIndetation= startIndetation; i >= 0; i--) {
String line= textWidget.getLine(i);
int indentation= getIndentation(line);

Expand All @@ -50,7 +55,7 @@ public List<IStickyLine> getStickyLines(StyledText textWidget, int lineNumber, S

if (indentation < previousIndetation) {
previousIndetation= indentation;
stickyLines.addFirst(new StickyLine(i, textWidget));
stickyLines.addFirst(new StickyLine(mapLineNumberToViewer(sourceViewer, i), textWidget));
}
}
} catch (IllegalArgumentException e) {
Expand Down Expand Up @@ -101,4 +106,18 @@ private int getIndentation(String line) {
return line.length() - line.stripLeading().length();
}

private int mapLineNumberToWidget(ISourceViewer sourceViewer, int line) {
if (sourceViewer instanceof ITextViewerExtension5 extension) {
return extension.modelLine2WidgetLine(line);
}
return line;
}

private int mapLineNumberToViewer(ISourceViewer sourceViewer, int line) {
if (sourceViewer instanceof ITextViewerExtension5 extension) {
return extension.widgetLine2ModelLine(line);
}
return line;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,35 @@

import java.util.List;

import org.eclipse.swt.custom.StyledText;

import org.eclipse.jface.text.source.ISourceViewer;

/**
* A sticky lines provider calculates the sticky lines for a given text widget. The sticky lines
* A sticky lines provider calculates the sticky lines for a given source viewer. The sticky lines
* will be displayed in the top area of the editor.
*
* TODO move to public package and add since 3.19
*/
public interface IStickyLinesProvider {

/**
* Calculate the sticky lines for the source code of the given textWidget. Specific properties,
* such as the <code>tabWidht</code> and the source viewer, can be retrieved from the
* Calculate the sticky lines for the source code of the given sourceViewer. Specific
* properties, such as the <code>tabWidht</code>, can be retrieved from the
* <code>properties</code>.
*
* @param textWidget The text widget containing the source code
* @param sourceViewer The source viewer containing the source code and gives access to the text
* widget
* @param lineNumber The line number to calculate the sticky lines for
* @param properties Properties for additional information
* @return The list of sticky lines to show
*/
public List<IStickyLine> getStickyLines(StyledText textWidget, int lineNumber, StickyLinesProperties properties);
public List<IStickyLine> getStickyLines(ISourceViewer sourceViewer, int lineNumber, StickyLinesProperties properties);

/**
* Additional properties and access in order to calculate the sticky lines.
*
* @param tabWith The with of a tab
* @param sourceViewer The sourceViewer to access additional information
*/
record StickyLinesProperties(int tabWith, ISourceViewer sourceViewer) {
record StickyLinesProperties(int tabWith) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ private void updateStickyScrollingControls() {
for (int i= 0; i < getNumberStickyLines(); i++) {
IStickyLine stickyLine= stickyLines.get(i);
stickyLineTextJoiner.add(stickyLine.getText());
int lineNumber= getSourceViewerLineNumber(stickyLine.getLineNumber());
int lineNumber= stickyLine.getLineNumber();
stickyLineNumberJoiner.add(fillLineNumberWithLeadingSpaces(lineNumber + 1));
}

Expand All @@ -222,13 +222,6 @@ private void updateStickyScrollingControls() {
}
}

private int getSourceViewerLineNumber(int i) {
if (sourceViewer instanceof ITextViewerExtension5 extension) {
return extension.widgetLine2ModelLine(i);
}
return i;
}

private String fillLineNumberWithLeadingSpaces(int lineNumber) {
int lineCount= sourceViewer.getDocument().getNumberOfLines();
int lineNumberLength= String.valueOf(lineCount).length();
Expand Down Expand Up @@ -399,6 +392,8 @@ private int getNumberStickyLines() {
* resized/moved.<br>
*/
private void addSourceViewerListeners() {
StyledText textWidget= sourceViewer.getTextWidget();

if (sourceViewer instanceof ITextViewerExtension4 extension) {
textPresentationListener= e -> {
Job.create("Update sticky lines styling", (ICoreRunnable) monitor -> { //$NON-NLS-1$
Expand All @@ -411,13 +406,12 @@ private void addSourceViewerListeners() {
}

caretListener= new StickyScollingCaretListener();
sourceViewer.getTextWidget().addCaretListener(caretListener);
sourceViewer.getTextWidget().addKeyListener(caretListener);
textWidget.addCaretListener(caretListener);
textWidget.addKeyListener(caretListener);

controlListener= new ControlListener() {
@Override
public void controlResized(ControlEvent e) {
StyledText textWidget= sourceViewer.getTextWidget();
limitVisibleStickyLinesToTextWidgetHeight(textWidget);
layoutStickyLines();
if (stickyScrollingHandler != null) {
Expand All @@ -430,7 +424,7 @@ public void controlMoved(ControlEvent e) {
layoutStickyLines();
}
};
sourceViewer.getTextWidget().addControlListener(controlListener);
textWidget.addControlListener(controlListener);
}

private void limitVisibleStickyLinesToTextWidgetHeight(StyledText textWidget) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import java.util.LinkedList;
import java.util.List;

import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;

Expand Down Expand Up @@ -140,7 +139,7 @@ private StickyScrollingControlSettings loadControlSettings(IPreferenceStore stor

private StickyLinesProperties loadStickyLinesProperties(IPreferenceStore store) {
int tabWidth= store.getInt(EDITOR_TAB_WIDTH);
return new StickyLinesProperties(tabWidth, sourceViewer);
return new StickyLinesProperties(tabWidth);
}

@Override
Expand All @@ -155,11 +154,10 @@ public void viewportChanged(int newVerticalOffset) {
private void calculateAndShowStickyLines() {
List<IStickyLine> stickyLines= Collections.emptyList();

StyledText textWidget= sourceViewer.getTextWidget();
int startLine= textWidget.getTopIndex();
int startLine= sourceViewer.getTopIndex();

if (startLine > 0) {
stickyLines= stickyLinesProvider.getStickyLines(textWidget, startLine, stickyLinesProperties);
stickyLines= stickyLinesProvider.getStickyLines(sourceViewer, sourceViewer.getTopIndex(), stickyLinesProperties);
}

if (stickyLines == null) {
Expand All @@ -179,11 +177,10 @@ private List<IStickyLine> adaptStickyLinesToVisibleArea(List<IStickyLine> sticky
LinkedList<IStickyLine> adaptedStickyLines= new LinkedList<>(stickyLines);

int firstVisibleLine= startLine + adaptedStickyLines.size();
StyledText textWidget= sourceViewer.getTextWidget();
int maximumLines= textWidget.getLineCount();
int numberOfLines= sourceViewer.getDocument().getNumberOfLines();

for (int i= startLine + 1; i <= firstVisibleLine && i < maximumLines; i++) {
List<IStickyLine> stickyLinesInLineI= stickyLinesProvider.getStickyLines(textWidget, i, stickyLinesProperties);
for (int i= startLine + 1; i <= firstVisibleLine && i < numberOfLines; i++) {
List<IStickyLine> stickyLinesInLineI= stickyLinesProvider.getStickyLines(sourceViewer, i, stickyLinesProperties);

if (stickyLinesInLineI.size() > adaptedStickyLines.size()) {
adaptedStickyLines= new LinkedList<>(stickyLinesInLineI);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.SourceViewer;

import org.eclipse.ui.internal.texteditor.stickyscroll.IStickyLinesProvider.StickyLinesProperties;
Expand All @@ -46,12 +50,12 @@ public void setup() {
sourceViewer = new SourceViewer(shell, null, SWT.None);
stickyLinesProvider = new DefaultStickyLinesProvider();
textWidget = sourceViewer.getTextWidget();
stickyLinesProperties = new StickyLinesProperties(4, sourceViewer);
stickyLinesProperties = new StickyLinesProperties(4);
}

@Test
public void testEmptySourceCode() {
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(textWidget, 0, stickyLinesProperties);
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(sourceViewer, 0, stickyLinesProperties);

assertThat(stickyLines, is(empty()));
}
Expand All @@ -63,7 +67,7 @@ public void testSingleStickyLine() {
line 2<""";
setText(text);

List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(textWidget, 1, stickyLinesProperties);
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(sourceViewer, 1, stickyLinesProperties);

assertEquals(1, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
Expand All @@ -78,7 +82,7 @@ public void testLineUnderStickyLine() {
line 4""";
setText(text);

List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(textWidget, 1, stickyLinesProperties);
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(sourceViewer, 1, stickyLinesProperties);

assertEquals(1, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
Expand All @@ -93,7 +97,7 @@ public void testNewStickyRoot() {
line 4<""";
setText(text);

List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(textWidget, 3, stickyLinesProperties);
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(sourceViewer, 3, stickyLinesProperties);

assertEquals(1, stickyLines.size());
assertEquals(2, stickyLines.get(0).getLineNumber());
Expand All @@ -109,7 +113,7 @@ public void testIgnoreEmptyLines() {
line 3<""";
setText(text);

List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(textWidget, 4, stickyLinesProperties);
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(sourceViewer, 4, stickyLinesProperties);

assertEquals(2, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
Expand All @@ -118,14 +122,14 @@ public void testIgnoreEmptyLines() {

@Test
public void testLinesWithTabs() {
stickyLinesProperties = new StickyLinesProperties(2, sourceViewer);
stickyLinesProperties = new StickyLinesProperties(2);
String text = """
line 1
\tline 2
\t\tline 3<""";
setText(text);

List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(textWidget, 2, stickyLinesProperties);
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(sourceViewer, 2, stickyLinesProperties);

assertEquals(2, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
Expand All @@ -143,7 +147,7 @@ public void testStartAtEmptyLineWithNext() {
textWidget.setText(text);
textWidget.setTopIndex(3);

List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(textWidget, 3, stickyLinesProperties);
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(sourceViewer, 3, stickyLinesProperties);

assertEquals(2, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
Expand All @@ -160,13 +164,31 @@ public void testStartAtEmptyLineWithPrevious() {
line 4""";
setText(text);

List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(textWidget, 3, stickyLinesProperties);
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(sourceViewer, 3, stickyLinesProperties);

assertEquals(2, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
assertEquals(1, stickyLines.get(1).getLineNumber());
}

@Test
public void testStickyLineWithSourceViewerLineMapping() {
sourceViewer = new SourceViewerWithLineMapping(shell, null, SWT.None);
textWidget = sourceViewer.getTextWidget();

String text = """
line 1
line 2<""";
setText(text);

// Source viewer line 43 that is mapped to line 1 in the text widget
List<IStickyLine> stickyLines = stickyLinesProvider.getStickyLines(sourceViewer, 1 + 42, stickyLinesProperties);

assertEquals(1, stickyLines.size());
// Source viewer line 42 that is mapped to line 0 in the text widget
assertEquals(0 + 42, stickyLines.get(0).getLineNumber());
}

/**
* Set the text into the text widget and set the top index to the line
* containing the <.
Expand All @@ -175,4 +197,32 @@ private void setText(String text) {
textWidget.setText(text);
}

private class SourceViewerWithLineMapping extends SourceViewer implements ITextViewerExtension5 {

public SourceViewerWithLineMapping(Composite parent, IVerticalRuler ruler, int styles) {
super(parent, ruler, styles);
}

@Override
public IRegion[] getCoveredModelRanges(IRegion modelRange) {
return null;
}

@Override
public boolean exposeModelRange(IRegion modelRange) {
return false;
}

@Override
public int widgetLine2ModelLine(int widgetLine) {
return widgetLine + 42;
}

@Override
public int modelLine2WidgetLine(int widgetLine) {
return widgetLine - 42;
}

}

}
Loading

0 comments on commit 97881bf

Please sign in to comment.