Skip to content

Commit

Permalink
[StickyScrolling] Move text and style calculation to StickyLine
Browse files Browse the repository at this point in the history
Move the text and style calculation to the StickyLine itself in order to
enable the sticky line provider to overwrite the default behavior. This
is needed to apply custom texts or custom styles for the sticky lines.

Preparation for #2398
  • Loading branch information
Christopher-Hermann committed Nov 4, 2024
1 parent 56115fc commit 9474c95
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public class DefaultStickyLinesProvider implements IStickyLinesProvider {
private StickyLinesProperties fProperties;

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

try {
int startIndetation= getStartIndentation(lineNumber, textWidget);
Expand All @@ -50,7 +50,7 @@ public List<StickyLine> getStickyLines(StyledText textWidget, int lineNumber, St

if (indentation < previousIndetation) {
previousIndetation= indentation;
stickyLines.addFirst(new StickyLine(line, i));
stickyLines.addFirst(new StickyLine(i, textWidget));
}
}
} catch (IllegalArgumentException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright (c) 2024 SAP SE.
*
* 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
*
* Contributors:
* SAP SE - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.texteditor.stickyscroll;

import org.eclipse.swt.custom.StyleRange;

/**
* Representation of a sticky line.
*/
public interface IStickyLine {

/**
* Returns the line number of the sticky line.
*
* @return the line number of the sticky line
*/
int getLineNumber();

/**
* Returns the text of the sticky line.
*
* @return the text of the sticky line
*/
String getText();

/**
* Returns the style ranges of the sticky line.
*
* @return the style ranges of the sticky line
*/
StyleRange[] getStyleRanges();

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public interface IStickyLinesProvider {
* @param properties Properties for additional information
* @return The list of sticky lines to show
*/
public List<StickyLine> getStickyLines(StyledText textWidget, int lineNumber, StickyLinesProperties properties);
public List<IStickyLine> getStickyLines(StyledText textWidget, int lineNumber, StickyLinesProperties properties);

/**
* Additional properties and access in order to calculate the sticky lines.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,47 @@
*******************************************************************************/
package org.eclipse.ui.internal.texteditor.stickyscroll;

import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;

/**
*
* A record representing a sticky line containing the text to display, and line number. It serves as
* an abstraction to represent sticky line for sticky scrolling.
*
* @param text the text of the corresponding sticky line
* @param lineNumber the specific line number of the sticky line
* Default implementation of {@link IStickyLine}. Information about the text and style ranges are
* calculated from the given text widget.
*/
public record StickyLine(String text, int lineNumber) {
public class StickyLine implements IStickyLine {

private int lineNumber;

private String text;

private StyledText textWidget;

public StickyLine(int lineNumber, StyledText textWidget) {
this.lineNumber= lineNumber;
this.textWidget= textWidget;
}

@Override
public int getLineNumber() {
return this.lineNumber;
}

@Override
public String getText() {
if (text == null) {
text= textWidget.getLine(lineNumber);
}
return text;
}

@Override
public StyleRange[] getStyleRanges() {
int offsetAtLine= textWidget.getOffsetAtLine(lineNumber);
StyleRange[] styleRanges= textWidget.getStyleRanges(offsetAtLine, getText().length());
for (StyleRange styleRange : styleRanges) {
styleRange.start= styleRange.start - offsetAtLine;
}
return styleRanges;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
package org.eclipse.ui.internal.texteditor.stickyscroll;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.StringJoiner;
Expand Down Expand Up @@ -65,7 +63,7 @@
/**
* This class builds a control that is rendered on top of the given source viewer. The controls
* shows the sticky lines that are set via {@link #setStickyLines(List)} on top of the source
* viewer. The {@link StickyLine#lineNumber()} is linked to to corresponding line number in the
* viewer. The {@link StickyLine#getLineNumber()} is linked to to corresponding line number in the
* given source viewer, with index starting at 0.
*
* As part of its responsibilities, the class handles layout arrangement and styling of the sticky
Expand All @@ -87,7 +85,7 @@ public class StickyScrollingControl {

private static final String DISABLE_CSS= "org.eclipse.e4.ui.css.disabled"; //$NON-NLS-1$

private List<StickyLine> stickyLines;
private List<IStickyLine> stickyLines;

private ISourceViewer sourceViewer;

Expand Down Expand Up @@ -135,7 +133,7 @@ public StickyScrollingControl(ISourceViewer sourceViewer, IVerticalRuler vertica
*
* @param stickyLines The sticky lines to show
*/
public void setStickyLines(List<StickyLine> stickyLines) {
public void setStickyLines(List<IStickyLine> stickyLines) {
if (!stickyLines.equals(this.stickyLines)) {
this.stickyLines= stickyLines;
updateStickyScrollingControls();
Expand Down Expand Up @@ -206,9 +204,9 @@ private void updateStickyScrollingControls() {
StringJoiner stickyLineTextJoiner= new StringJoiner(System.lineSeparator());
StringJoiner stickyLineNumberJoiner= new StringJoiner(System.lineSeparator());
for (int i= 0; i < getNumberStickyLines(); i++) {
StickyLine stickyLine= stickyLines.get(i);
stickyLineTextJoiner.add(stickyLine.text());
int lineNumber= getSourceViewerLineNumber(stickyLine.lineNumber());
IStickyLine stickyLine= stickyLines.get(i);
stickyLineTextJoiner.add(stickyLine.getText());
int lineNumber= getSourceViewerLineNumber(stickyLine.getLineNumber());
stickyLineNumberJoiner.add(fillLineNumberWithLeadingSpaces(lineNumber + 1));
}

Expand Down Expand Up @@ -244,14 +242,20 @@ private void styleStickyLines() {
return;
}

List<StyleRange> stickyLinesStyleRanges= new ArrayList<>();
int stickyLineTextOffset= 0;
for (int i= 0; i < getNumberStickyLines(); i++) {
StickyLine stickyLine= stickyLines.get(i);
stickyLinesStyleRanges.addAll(getStickyLineStyleRanges(stickyLine, stickyLineTextOffset));
stickyLineTextOffset+= stickyLine.text().length() + System.lineSeparator().length();
int stickyLineOffset= 0;
List<StyleRange> styleRanges= new ArrayList<>();
for (IStickyLine stickyLine : stickyLines) {
StyleRange[] ranges= stickyLine.getStyleRanges();
if (ranges != null) {
for (StyleRange styleRange : ranges) {
styleRange.start+= stickyLineOffset;
styleRanges.add(styleRange);
}
}

stickyLineOffset+= stickyLine.getText().length() + System.lineSeparator().length();
}
stickyLineText.setStyleRanges(stickyLinesStyleRanges.toArray(StyleRange[]::new));
stickyLineText.setStyleRanges(styleRanges.toArray(StyleRange[]::new));

stickyLineNumber.setFont(textWidget.getFont());
stickyLineNumber.setStyleRange(new StyleRange(0, stickyLineNumber.getText().length(), settings.lineNumberColor(), null));
Expand All @@ -263,22 +267,6 @@ private void styleStickyLines() {
stickyLineText.setLeftMargin(textWidget.getLeftMargin());
}

private List<StyleRange> getStickyLineStyleRanges(StickyLine stickyLine, int stickyLineTextOffset) {
int lineNumber= stickyLine.lineNumber();
try {
StyledText textWidget= sourceViewer.getTextWidget();
int offsetAtLine= textWidget.getOffsetAtLine(lineNumber);
StyleRange[] styleRanges= textWidget.getStyleRanges(offsetAtLine, stickyLine.text().length());
for (StyleRange styleRange : styleRanges) {
styleRange.start= styleRange.start - offsetAtLine + stickyLineTextOffset;
}
return Arrays.asList(styleRanges);
} catch (IllegalArgumentException e) {
//Styling could not be copied, skip!
return Collections.emptyList();
}
}

private void layoutStickyLines() {
if (getNumberStickyLines() == 0) {
stickyLinesCanvas.setVisible(false);
Expand Down Expand Up @@ -365,12 +353,12 @@ private void calculateAndSetStickyLinesCanvasBounds() {

private void navigateToClickedLine(MouseEvent event) {
int clickedStickyLineIndex= stickyLineText.getLineIndex(event.y);
StickyLine clickedStickyLine= stickyLines.get(clickedStickyLineIndex);
IStickyLine clickedStickyLine= stickyLines.get(clickedStickyLineIndex);

try {
int offset= sourceViewer.getDocument().getLineOffset(clickedStickyLine.lineNumber());
int offset= sourceViewer.getDocument().getLineOffset(clickedStickyLine.getLineNumber());
sourceViewer.setSelectedRange(offset, 0);
ensureSourceViewerLineVisible(clickedStickyLine.lineNumber());
ensureSourceViewerLineVisible(clickedStickyLine.getLineNumber());
} catch (BadLocationException e) {
//Do not navigate
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public void viewportChanged(int newVerticalOffset) {
}

private void calculateAndShowStickyLines() {
List<StickyLine> stickyLines= Collections.emptyList();
List<IStickyLine> stickyLines= Collections.emptyList();

StyledText textWidget= sourceViewer.getTextWidget();
int startLine= textWidget.getTopIndex();
Expand All @@ -171,19 +171,19 @@ private void calculateAndShowStickyLines() {
stickyScrollingControl.setStickyLines(stickyLines);
}

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

LinkedList<StickyLine> adaptedStickyLines= new LinkedList<>(stickyLines);
LinkedList<IStickyLine> 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);
List<IStickyLine> stickyLinesInLineI= stickyLinesProvider.getStickyLines(textWidget, 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 @@ -16,7 +16,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.junit.Assert.assertEquals;

import java.util.List;

Expand Down Expand Up @@ -51,7 +51,7 @@ public void setup() {

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

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

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

assertThat(stickyLines, contains(new StickyLine("line 1", 0)));
assertEquals(1, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
}

@Test
Expand All @@ -77,9 +78,10 @@ public void testLineUnderStickyLine() {
line 4""";
setText(text);

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

assertThat(stickyLines, contains(new StickyLine("line 1", 0)));
assertEquals(1, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
}

@Test
Expand All @@ -91,9 +93,10 @@ public void testNewStickyRoot() {
line 4<""";
setText(text);

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

assertThat(stickyLines, contains(new StickyLine("line 3", 2)));
assertEquals(1, stickyLines.size());
assertEquals(2, stickyLines.get(0).getLineNumber());
}

@Test
Expand All @@ -106,9 +109,11 @@ public void testIgnoreEmptyLines() {
line 3<""";
setText(text);

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

assertThat(stickyLines, contains(new StickyLine("line 1", 0), new StickyLine(" line 2", 2)));
assertEquals(2, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
assertEquals(2, stickyLines.get(1).getLineNumber());
}

@Test
Expand All @@ -120,9 +125,11 @@ public void testLinesWithTabs() {
\t\tline 3<""";
setText(text);

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

assertThat(stickyLines, contains(new StickyLine("line 1", 0), new StickyLine("\tline 2", 1)));
assertEquals(2, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
assertEquals(1, stickyLines.get(1).getLineNumber());
}

@Test
Expand All @@ -136,9 +143,11 @@ public void testStartAtEmptyLineWithNext() {
textWidget.setText(text);
textWidget.setTopIndex(3);

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

assertThat(stickyLines, contains(new StickyLine("line 1", 0), new StickyLine(" line 2", 2)));
assertEquals(2, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
assertEquals(2, stickyLines.get(1).getLineNumber());
}

@Test
Expand All @@ -151,9 +160,11 @@ public void testStartAtEmptyLineWithPrevious() {
line 4""";
setText(text);

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

assertThat(stickyLines, contains(new StickyLine("line 1", 0), new StickyLine(" line 2", 1)));
assertEquals(2, stickyLines.size());
assertEquals(0, stickyLines.get(0).getLineNumber());
assertEquals(1, stickyLines.get(1).getLineNumber());
}

/**
Expand Down
Loading

0 comments on commit 9474c95

Please sign in to comment.