Skip to content

Commit

Permalink
Refactoring sticky lines provider and handler
Browse files Browse the repository at this point in the history
Refactors the sticky lines provider and extract the method to an interface. This is a preparation step for providing a extension point for sticky lines provider.
  • Loading branch information
Christopher-Hermann committed Sep 26, 2024
1 parent c603575 commit 0fa0c4b
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,26 @@

/**
* This class provides sticky lines for the given source code in the source viewer. The
* implementation is completely based on indentation and therefore should work by default for
* several languages.
* implementation is completely based on indentation and therefore works by default for several
* languages.
*/
public class StickyLinesProvider {
public class DefaultStickyLinesProvider implements IStickyLinesProvider {

private final static int IGNORE_INDENTATION= -1;
private final static int IGNORE_LINE_INDENTATION= -1;

private final static String TAB= "\t"; //$NON-NLS-1$

private int tabWidth= 4;

/**
* Calculate the sticky lines for the given source code in the source viewer for the given
* vertical offset.
*
* @param verticalOffset The vertical offset line index of the first visible line
* @param sourceViewer The source viewer containing the source code
* @return A list of sticky lines
*/
public List<StickyLine> get(int verticalOffset, ISourceViewer sourceViewer) {
LinkedList<StickyLine> stickyLines= new LinkedList<>();
private StickyLinesProperties fProperties;

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

this.fProperties= properties;
LinkedList<StickyLine> stickyLines= new LinkedList<>();

try {
StyledText textWidget= sourceViewer.getTextWidget();
int startLine= textWidget.getTopIndex();
Expand All @@ -71,7 +65,7 @@ private void calculateStickyLinesForLineNumber(LinkedList<StickyLine> stickyLine
String line= textWidget.getLine(i);
int indentation= getIndentation(line);

if (indentation == IGNORE_INDENTATION) {
if (indentation == IGNORE_LINE_INDENTATION) {
continue;
}

Expand All @@ -91,7 +85,7 @@ private void calculateStickyLinesUnderStickyLineControl(LinkedList<StickyLine> s

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

Expand Down Expand Up @@ -123,7 +117,7 @@ private int mapLineNumberToSourceViewerLine(int lineNumber, ISourceViewer source

private int getStartIndentation(int startFromLine, StyledText styledText) {
int indentation= getIndentation(styledText.getLine(startFromLine));
if (indentation != IGNORE_INDENTATION) {
if (indentation != IGNORE_LINE_INDENTATION) {
return indentation;
} else {
int nextContentLine= getIndentation(getNextContentLine(startFromLine, styledText));
Expand Down Expand Up @@ -154,21 +148,12 @@ private String getPreviousContentLine(int startFromLine, StyledText styledText)

private int getIndentation(String line) {
if (line == null || line.isBlank()) {
return IGNORE_INDENTATION;
return IGNORE_LINE_INDENTATION;
}
String tabAsSpaces= String.join("", Collections.nCopies(tabWidth, " ")); //$NON-NLS-1$ //$NON-NLS-2$
String tabAsSpaces= String.join("", Collections.nCopies(fProperties.tabWith(), " ")); //$NON-NLS-1$ //$NON-NLS-2$

line= line.replace(TAB, tabAsSpaces);
return line.length() - line.stripLeading().length();
}

/**
* Sets the with in spaces of a tab in the editor.
*
* @param tabWidth The amount of spaces a tab is using.
*/
public void setTabWidth(int tabWidth) {
this.tabWidth= tabWidth;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*******************************************************************************
* 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 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 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 sourceViewer. Specific
* properties, such as the <code>tabWidht</code> can be retrieved from the
* <code>properties</code>.
*
* @param sourceViewer The source viewer containing the source code and information about the
* first visible line
* @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);

/**
* Properties required to calculate the sticky lines.
*
* @param tabWith The with of a tab
*/
record StickyLinesProperties(int tabWith) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH;

import java.time.Duration;
import java.util.Collections;
import java.util.List;

import org.eclipse.swt.graphics.Color;
Expand All @@ -37,9 +38,10 @@
import org.eclipse.jface.text.source.IVerticalRuler;

import org.eclipse.ui.internal.editors.text.EditorsPlugin;
import org.eclipse.ui.internal.texteditor.stickyscroll.IStickyLinesProvider.StickyLinesProperties;

/**
* A sticky scrolling handler that retrieves stick lines from the {@link StickyLinesProvider} and
* A sticky scrolling handler that retrieves stick lines from a {@link IStickyLinesProvider} and
* shows them in a {@link StickyScrollingControl} on top of the given source viewer.
*/
public class StickyScrollingHandler implements IViewportListener {
Expand All @@ -50,94 +52,95 @@ public class StickyScrollingHandler implements IViewportListener {

private StickyScrollingControl stickyScrollingControl;

private int tabWidth;

private IPropertyChangeListener propertyChangeListener;

private IPreferenceStore preferenceStore;

private StickyLinesProvider stickyLinesProvider;
private IStickyLinesProvider stickyLinesProvider;

private StickyLinesProperties stickyLinesProperties;

private Throttler throttler;

private int verticalOffset;

/**
* Creates a StickyScrollingHandlerIndentation that will be linked to the given source viewer.
* The sticky scrolling will be computed by the default {@link StickyLinesProvider}.
* Creates a StickyScrollingHandler that will be linked to the given source viewer. The sticky
* lines will be provided by the {@link DefaultStickyLinesProvider}.
*
* @param sourceViewer The source viewer to link the handler
* @param sourceViewer The source viewer to link the handler with
* @param verticalRuler The vertical ruler of the source viewer
* @param preferenceStore The preference store
*/
public StickyScrollingHandler(ISourceViewer sourceViewer, IVerticalRuler verticalRuler, IPreferenceStore preferenceStore) {
this(sourceViewer, verticalRuler, preferenceStore, new StickyLinesProvider());
this(sourceViewer, verticalRuler, preferenceStore, new DefaultStickyLinesProvider());
}

/**
* Creates a StickyScrollingHandlerIndentation that will be linked to the given source viewer.
* Creates a StickyScrollingHandler that will be linked to the given source viewer. The sticky
* lines will be provided by the given <code>stickyLinesProvider</code>.
*
* @param sourceViewer The source viewer to link the handler
* @param sourceViewer The source viewer to link the handler with
* @param verticalRuler The vertical ruler of the source viewer
* @param preferenceStore The preference store
* @param stickyLinesProvider The sticky scrolling computer
* @param stickyLinesProvider The sticky scrolling provider
*/
public StickyScrollingHandler(ISourceViewer sourceViewer, IVerticalRuler verticalRuler, IPreferenceStore preferenceStore,
StickyLinesProvider stickyLinesProvider) {
IStickyLinesProvider stickyLinesProvider) {
this.sourceViewer= sourceViewer;

throttler= new Throttler(sourceViewer.getTextWidget().getDisplay(), Duration.ofMillis(THROTTLER_DELAY), this::calculateAndShowStickyLines);
this.stickyLinesProvider= stickyLinesProvider;

StickyScrollingControlSettings settings= loadAndListenForProperties(preferenceStore);
listenForPropertiesChanges(preferenceStore);
stickyLinesProperties= loadStickyLinesProperties(preferenceStore);
StickyScrollingControlSettings settings= loadControlSettings(preferenceStore);

stickyScrollingControl= new StickyScrollingControl(sourceViewer, verticalRuler, settings, this);

sourceViewer.addViewportListener(this);
}

private StickyScrollingControlSettings loadAndListenForProperties(IPreferenceStore store) {
private void listenForPropertiesChanges(IPreferenceStore store) {
preferenceStore= store;
propertyChangeListener= e -> {
if (e.getProperty().equals(EDITOR_TAB_WIDTH) || e.getProperty().equals(EDITOR_STICKY_SCROLLING_MAXIMUM_COUNT)
|| e.getProperty().equals(EDITOR_CURRENT_LINE_COLOR) || e.getProperty().equals(EDITOR_LINE_NUMBER_RULER)
|| e.getProperty().equals(STICKY_LINES_SEPARATOR_COLOR)) {
if (stickyScrollingControl != null && !sourceViewer.getTextWidget().isDisposed()) {
StickyScrollingControlSettings settings= loadSettings(preferenceStore);
StickyScrollingControlSettings settings= loadControlSettings(preferenceStore);
stickyScrollingControl.applySettings(settings);
stickyLinesProvider.setTabWidth(tabWidth);
stickyLinesProperties= loadStickyLinesProperties(preferenceStore);
}
}
};
store.addPropertyChangeListener(propertyChangeListener);
return loadSettings(store);
}

private StickyScrollingControlSettings loadSettings(IPreferenceStore store) {
tabWidth= store.getInt(EDITOR_TAB_WIDTH);

private StickyScrollingControlSettings loadControlSettings(IPreferenceStore store) {
int stickyScrollingMaxCount= store.getInt(EDITOR_STICKY_SCROLLING_MAXIMUM_COUNT);

Color lineNumberColor= new Color(PreferenceConverter.getColor(store, EDITOR_LINE_NUMBER_RULER_COLOR));
sourceViewer.getTextWidget().addDisposeListener(e -> lineNumberColor.dispose());

Color stickyLineHoverColor= new Color(PreferenceConverter.getColor(store, EDITOR_CURRENT_LINE_COLOR));
sourceViewer.getTextWidget().addDisposeListener(e -> stickyLineHoverColor.dispose());

Color stickyLineBackgroundColor= sourceViewer.getTextWidget().getBackground();

boolean showLineNumbers= store.getBoolean(EDITOR_LINE_NUMBER_RULER);

Color stickyLineSeparatorColor= null;
if (EditorsPlugin.getDefault() != null) {
RGB rgb= PreferenceConverter.getColor(store, STICKY_LINES_SEPARATOR_COLOR);
ISharedTextColors sharedTextColors= EditorsPlugin.getDefault().getSharedTextColors();
stickyLineSeparatorColor= sharedTextColors.getColor(rgb);
}

return new StickyScrollingControlSettings(stickyScrollingMaxCount,
lineNumberColor, stickyLineHoverColor, stickyLineBackgroundColor, stickyLineSeparatorColor, showLineNumbers);
}

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

@Override
public void viewportChanged(int newVerticalOffset) {
if (this.verticalOffset == newVerticalOffset) {
Expand All @@ -148,7 +151,10 @@ public void viewportChanged(int newVerticalOffset) {
}

private void calculateAndShowStickyLines() {
List<StickyLine> stickyLines= stickyLinesProvider.get(verticalOffset, sourceViewer);
List<StickyLine> stickyLines= stickyLinesProvider.getStickyLines(sourceViewer, stickyLinesProperties);
if (stickyLines == null) {
stickyLines= Collections.emptyList();
}
stickyScrollingControl.setStickyLines(stickyLines);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import org.eclipse.jface.text.tests.codemining.CodeMiningTest;

import org.eclipse.ui.internal.texteditor.stickyscroll.StickyLinesProviderTest;
import org.eclipse.ui.internal.texteditor.stickyscroll.DefaultStickyLinesProviderTest;
import org.eclipse.ui.internal.texteditor.stickyscroll.StickyScrollingControlTest;
import org.eclipse.ui.internal.texteditor.stickyscroll.StickyScrollingHandlerTest;

Expand Down Expand Up @@ -49,7 +49,7 @@

StickyScrollingControlTest.class,
StickyScrollingHandlerTest.class,
StickyLinesProviderTest.class,
DefaultStickyLinesProviderTest.class,

CodeMiningTest.class,
})
Expand Down
Loading

0 comments on commit 0fa0c4b

Please sign in to comment.