Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[StickyScrolling] Refactoring sticky lines provider and handler #2266

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
BeckerWdf marked this conversation as resolved.
Show resolved Hide resolved
*/
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
Loading