Skip to content

Commit

Permalink
[StickyScrolling] Move complex line adaption to central handler
Browse files Browse the repository at this point in the history
Move sticky lines adaptation to the growing sticky lines control into the central sticky scrolling handler. This change simplifies the sticky lines provider, making implementations for specific languages more straightforward and maintainable.

Preparation for eclipse-platform#2398
  • Loading branch information
Christopher-Hermann committed Oct 15, 2024
1 parent 1eead54 commit e2c4efe
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@

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 @@ -36,83 +33,31 @@ public class DefaultStickyLinesProvider implements IStickyLinesProvider {
private StickyLinesProperties fProperties;

@Override
public List<StickyLine> getStickyLines(ISourceViewer sourceViewer, StickyLinesProperties properties) {
if (sourceViewer.getTopIndex() == 0) {
return Collections.emptyList();
}

public List<StickyLine> getStickyLines(StyledText textWidget, int lineNumber, StickyLinesProperties properties) {
this.fProperties= properties;
LinkedList<StickyLine> stickyLines= new LinkedList<>();

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

calculateStickyLinesForLineNumber(stickyLines, sourceViewer, startLine);
calculateStickyLinesUnderStickyLineControl(stickyLines, sourceViewer, startLine);
} catch (IllegalArgumentException e) {
stickyLines.clear();
}
for (int i= lineNumber, previousIndetation= startIndetation; i >= 0; i--) {
String line= textWidget.getLine(i);
int indentation= getIndentation(line);

return stickyLines;
}
if (indentation == IGNORE_LINE_INDENTATION) {
continue;
}

private void calculateStickyLinesForLineNumber(LinkedList<StickyLine> stickyLines, ISourceViewer sourceViewer, int lineNumber) {
StyledText textWidget= sourceViewer.getTextWidget();
int startIndetation= getStartIndentation(lineNumber, textWidget);

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

if (indentation == IGNORE_LINE_INDENTATION) {
continue;
}

if (indentation < previousIndetation) {
previousIndetation= indentation;
stickyLines.addFirst(new StickyLine(line, mapLineNumberToSourceViewerLine(i, sourceViewer)));
}
}
}

private void calculateStickyLinesUnderStickyLineControl(LinkedList<StickyLine> stickyLines, ISourceViewer sourceViewer, int startLine) {
int firstBelowControl= startLine + stickyLines.size();
StyledText textWidget= sourceViewer.getTextWidget();
int lineCount= textWidget.getLineCount();

for (int i= startLine; i < firstBelowControl && i < lineCount; i++) {

String line= textWidget.getLine(i);
int indentation= getIndentation(line);
if (indentation == IGNORE_LINE_INDENTATION) {
continue;
}

while (!stickyLines.isEmpty() && indentation <= getLastStickyLineIndentation(stickyLines) && i < firstBelowControl) {
stickyLines.removeLast();
firstBelowControl--;
}

String nextContentLine= getNextContentLine(i, textWidget);
if (getIndentation(nextContentLine) > indentation && i < firstBelowControl) {
stickyLines.addLast(new StickyLine(line, mapLineNumberToSourceViewerLine(i, sourceViewer)));
firstBelowControl++;
continue;
if (indentation < previousIndetation) {
previousIndetation= indentation;
stickyLines.addFirst(new StickyLine(line, i));
}
}
} catch (IllegalArgumentException e) {
stickyLines.clear();
}
}

private int getLastStickyLineIndentation(LinkedList<StickyLine> stickyLines) {
String text= stickyLines.getLast().text();
return getIndentation(text);
}

private int mapLineNumberToSourceViewerLine(int lineNumber, ISourceViewer sourceViewer) {
if (sourceViewer instanceof ITextViewerExtension5 extension) {
return extension.widgetLine2ModelLine(lineNumber);
}
return lineNumber;
return stickyLines;
}

private int getStartIndentation(int startFromLine, StyledText styledText) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,32 @@
import org.eclipse.jface.text.source.ISourceViewer;

/**
* A sticky lines provider calculates the sticky lines for a given source viewer. The sticky lines
* A sticky lines provider calculates the sticky lines for a given text widget. 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 sourceViewer. Specific
* properties, such as the <code>tabWidht</code> can be retrieved from the
* 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
* <code>properties</code>.
*
* @param sourceViewer The source viewer containing the source code and information about the
* first visible line
* @param textWidget The text widget containing the source code
* @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
*
* @see ISourceViewer#getTopIndex()
* @see ISourceViewer#getTextWidget()
* @see StyledText#getTopIndex()
*/
public List<StickyLine> getStickyLines(ISourceViewer sourceViewer, StickyLinesProperties properties);
public List<StickyLine> getStickyLines(StyledText textWidget, int lineNumber, StickyLinesProperties properties);

/**
* Properties required to calculate the sticky lines.
* 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) {
record StickyLinesProperties(int tabWith, ISourceViewer sourceViewer) {
}

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

String newStickyLineText= stickyLineTextJoiner.toString();
Expand All @@ -223,6 +224,13 @@ 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 @@ -257,9 +265,6 @@ private void styleStickyLines() {

private List<StyleRange> getStickyLineStyleRanges(StickyLine stickyLine, int stickyLineTextOffset) {
int lineNumber= stickyLine.lineNumber();
if (sourceViewer instanceof ITextViewerExtension5 extension) {
lineNumber= extension.modelLine2WidgetLine(lineNumber);
}
try {
StyledText textWidget= sourceViewer.getTextWidget();
int offsetAtLine= textWidget.getOffsetAtLine(lineNumber);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@

import java.time.Duration;
import java.util.Collections;
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 @@ -138,7 +140,7 @@ private StickyScrollingControlSettings loadControlSettings(IPreferenceStore stor

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

@Override
Expand All @@ -151,13 +153,52 @@ public void viewportChanged(int newVerticalOffset) {
}

private void calculateAndShowStickyLines() {
List<StickyLine> stickyLines= stickyLinesProvider.getStickyLines(sourceViewer, stickyLinesProperties);
List<StickyLine> stickyLines= Collections.emptyList();

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

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

if (stickyLines == null) {
stickyLines= Collections.emptyList();
}

stickyLines= adaptStickyLinesToVisibleArea(stickyLines, startLine);

stickyScrollingControl.setStickyLines(stickyLines);
}

private List<StickyLine> adaptStickyLinesToVisibleArea(List<StickyLine> stickyLines, int startLine) {
if (stickyLines.isEmpty()) {
return stickyLines;
}

LinkedList<StickyLine> adaptedStickyLines= new LinkedList<>(stickyLines);

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

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

if (stickyLinesInLineI.size() > adaptedStickyLines.size()) {
adaptedStickyLines= new LinkedList<>(stickyLinesInLineI);
firstVisibleLine= startLine + adaptedStickyLines.size();
}

while (stickyLinesInLineI.size() < adaptedStickyLines.size() && i < firstVisibleLine) {
adaptedStickyLines.removeLast();
firstVisibleLine--;
}
}

return adaptedStickyLines;
}

/**
* Uninstalls the sticky scrolling handler from the source viewer. This completely disposes the
* {@link StickyScrollingControl} and removes all corresponding listeners.
Expand Down
Loading

0 comments on commit e2c4efe

Please sign in to comment.