Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMickael Istria2019-04-01 18:43:35 -0400
committerMickael Istria2019-04-01 18:57:59 -0400
commited76a4ee96d3a0ea40b6bf4d1447f2af49cb9f7f (patch)
treeccf16a832d3b255db597d44103c2c19bfea07be6
parent4b9a8a739f42da547975427154744c981d0811ba (diff)
downloadeclipse.platform.text-ed76a4ee96d3a0ea40b6bf4d1447f2af49cb9f7f.tar.gz
eclipse.platform.text-ed76a4ee96d3a0ea40b6bf4d1447f2af49cb9f7f.tar.xz
eclipse.platform.text-ed76a4ee96d3a0ea40b6bf4d1447f2af49cb9f7f.zip
Bug 546020 - [code mining] "Index out of Bounds" when inline w/ foldingI20190407-1800I20190406-1800I20190405-1800I20190404-1800I20190403-1800I20190402-1800
Change-Id: I7b10e2d8122372bec5452225313142cf6ec58f3c Signed-off-by: Mickael Istria <mistria@redhat.com>
-rw-r--r--org.eclipse.jface.text.tests/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.jface.text.tests/pom.xml2
-rw-r--r--org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java5
-rw-r--r--org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ScreenshotOnFailureRule.java2
-rw-r--r--org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningProjectionViewerTest.java162
-rw-r--r--org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java (renamed from org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/CodeMiningTest.java)4
-rw-r--r--org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/DelayedEchoCodeMiningProvider.java (renamed from org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/DelayedEchoCodeMiningProvider.java)2
-rw-r--r--org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/StaticContentLineCodeMining.java32
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java16
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java22
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java10
11 files changed, 236 insertions, 24 deletions
diff --git a/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF b/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF
index 7194ac38c..4d334bb30 100644
--- a/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF
@@ -2,11 +2,12 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Plugin.name
Bundle-SymbolicName: org.eclipse.jface.text.tests
-Bundle-Version: 3.11.600.qualifier
+Bundle-Version: 3.11.700.qualifier
Bundle-Vendor: %Plugin.providerName
Bundle-Localization: plugin
Export-Package:
org.eclipse.jface.text.tests,
+ org.eclipse.jface.text.tests.codemining,
org.eclipse.jface.text.tests.contentassist,
org.eclipse.jface.text.tests.reconciler,
org.eclipse.jface.text.tests.rules,
diff --git a/org.eclipse.jface.text.tests/pom.xml b/org.eclipse.jface.text.tests/pom.xml
index 25643b9d0..18ea858c5 100644
--- a/org.eclipse.jface.text.tests/pom.xml
+++ b/org.eclipse.jface.text.tests/pom.xml
@@ -19,7 +19,7 @@
</parent>
<groupId>org.eclipse.jface</groupId>
<artifactId>org.eclipse.jface.text.tests</artifactId>
- <version>3.11.600-SNAPSHOT</version>
+ <version>3.11.700-SNAPSHOT</version>
<packaging>eclipse-test-plugin</packaging>
<properties>
<testSuite>${project.artifactId}</testSuite>
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java
index 7e822ffd8..d35ff7e74 100644
--- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java
+++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/JFaceTextTestSuite.java
@@ -17,6 +17,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
+import org.eclipse.jface.text.tests.codemining.CodeMiningTest;
+import org.eclipse.jface.text.tests.codemining.CodeMiningProjectionViewerTest;
import org.eclipse.jface.text.tests.contentassist.AsyncContentAssistTest;
import org.eclipse.jface.text.tests.reconciler.AbstractReconcilerTest;
import org.eclipse.jface.text.tests.rules.DefaultPartitionerTest;
@@ -55,7 +57,8 @@ import org.eclipse.jface.text.tests.templates.persistence.TemplatePersistenceDat
WordRuleTest.class,
TemplatePersistenceDataTest.class,
- CodeMiningTest.class
+ CodeMiningTest.class,
+ CodeMiningProjectionViewerTest.class
})
public class JFaceTextTestSuite {
// see @SuiteClasses
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ScreenshotOnFailureRule.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ScreenshotOnFailureRule.java
index 3deb57992..da2da9594 100644
--- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ScreenshotOnFailureRule.java
+++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/ScreenshotOnFailureRule.java
@@ -17,7 +17,7 @@ import org.junit.rules.TestWatcher;
import org.eclipse.test.Screenshots;
-final class ScreenshotOnFailureRule extends TestWatcher {
+public final class ScreenshotOnFailureRule extends TestWatcher {
@Override
protected void failed(Throwable e, org.junit.runner.Description description) {
Screenshots.takeScreenshot(description.getTestClass(), description.getMethodName());
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningProjectionViewerTest.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningProjectionViewerTest.java
new file mode 100644
index 000000000..9e06ba19b
--- /dev/null
+++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningProjectionViewerTest.java
@@ -0,0 +1,162 @@
+/**
+ * Copyright (c) 2019 Red Hat Inc., and others
+ *
+ * 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:
+ * - Mickael Istria (Red Hat Inc.)
+ */
+package org.eclipse.jface.text.tests.codemining;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.ILogListener;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.codemining.ICodeMining;
+import org.eclipse.jface.text.codemining.ICodeMiningProvider;
+import org.eclipse.jface.text.codemining.LineContentCodeMining;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.AnnotationPainter;
+import org.eclipse.jface.text.source.IAnnotationAccess;
+import org.eclipse.jface.text.source.ISharedTextColors;
+import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
+import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
+import org.eclipse.jface.text.source.projection.ProjectionSupport;
+import org.eclipse.jface.text.source.projection.ProjectionViewer;
+import org.eclipse.jface.text.tests.util.DisplayHelper;
+
+public class CodeMiningProjectionViewerTest {
+
+ private final class RepeatLettersCodeMiningProvider implements ICodeMiningProvider {
+ @Override
+ public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) {
+ List<LineContentCodeMining> codeMinings = new ArrayList<>();
+ for (int i = 0; i < viewer.getDocument().getLength(); i++) {
+ try {
+ char c= viewer.getDocument().getChar(i);
+ if (Character.isLetter(c)) {
+ codeMinings.add(new StaticContentLineCodeMining(i, c, this));
+ }
+ } catch (BadLocationException e) {
+ e.printStackTrace();
+ }
+ }
+ return CompletableFuture.completedFuture(codeMinings);
+ }
+
+ @Override
+ public void dispose() {
+ }
+ }
+
+ private Shell fParent;
+ private ProjectionViewer fViewer;
+
+ @Before
+ public void setUp() {
+ fParent= new Shell();
+ fParent.setSize(500, 200);
+ fParent.setLayout(new FillLayout());
+ fViewer= new ProjectionViewer(fParent, null, null, false, SWT.NONE);
+ IAnnotationAccess annotationAccess = new IAnnotationAccess() {
+ @Override
+ public Object getType(Annotation annotation) {
+ return annotation.getType();
+ }
+
+ @Override
+ public boolean isMultiLine(Annotation annotation) {
+ return true;
+ }
+
+ @Override
+ public boolean isTemporary(Annotation annotation) {
+ return true;
+ }
+ };
+ // code minings
+ AnnotationPainter painter = new AnnotationPainter(fViewer, annotationAccess);
+ fViewer.addPainter(painter);
+ fViewer.setCodeMiningAnnotationPainter(painter);
+ // projection/folding
+ fViewer.setDocument(new Document(), new ProjectionAnnotationModel());
+ ProjectionSupport projectionSupport = new ProjectionSupport(fViewer, annotationAccess, new ISharedTextColors() {
+ @Override
+ public Color getColor(RGB rgb) {
+ return null;
+ }
+
+ @Override
+ public void dispose() {
+ }
+ });
+ projectionSupport.install();
+ fViewer.doOperation(ProjectionViewer.TOGGLE);
+ }
+
+ @After
+ public void tearDown() {
+ fParent.dispose();
+ }
+
+ @Test
+ public void testCollapse() throws Exception {
+ fViewer.setCodeMiningProviders(new ICodeMiningProvider[] {
+ new RepeatLettersCodeMiningProvider()
+ });
+ fViewer.getDocument().set("1a\n2a\n3a\n4a\n5a\n6a\n");
+ ProjectionAnnotation annotation= new ProjectionAnnotation(true);
+ fViewer.getProjectionAnnotationModel().addAnnotation(annotation, new Position(0, fViewer.getDocument().getLineOffset(4)));
+ fViewer.doOperation(ProjectionViewer.COLLAPSE_ALL);
+ fViewer.updateCodeMinings();
+ fParent.open();
+
+ Bundle bundle = Platform.getBundle("org.eclipse.ui.workbench");
+ ILog log = null;
+ AtomicReference<IStatus> logError = new AtomicReference<>();
+ ILogListener logListener= (status, plugin) -> {
+ logError.set(status);
+ };
+ if (bundle != null && bundle.getState() == Bundle.ACTIVE) {
+ log = Platform.getLog(bundle);
+ log.addLogListener(logListener);
+ }
+ try {
+ // without workbench, next line throws Exception directly
+ DisplayHelper.sleep(fParent.getDisplay(), 1000);
+ Assert.assertNull(logError.get());
+ } finally {
+ if (log != null) {
+ log.removeLogListener(logListener);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/CodeMiningTest.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java
index a02f64b5b..cac5fde14 100644
--- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/CodeMiningTest.java
+++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java
@@ -11,7 +11,7 @@
* Contributors:
* - Mickael Istria (Red Hat Inc.) - initial implementation
*******************************************************************************/
-package org.eclipse.jface.text.tests;
+package org.eclipse.jface.text.tests.codemining;
import java.util.concurrent.atomic.AtomicInteger;
@@ -41,6 +41,8 @@ import org.eclipse.jface.text.reconciler.MonoReconciler;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.AnnotationPainter;
import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.text.tests.ScreenshotOnFailureRule;
+import org.eclipse.jface.text.tests.TextViewerTest;
import org.eclipse.jface.text.tests.util.DisplayHelper;
public class CodeMiningTest {
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/DelayedEchoCodeMiningProvider.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/DelayedEchoCodeMiningProvider.java
index 8dd93c96f..ac976cda6 100644
--- a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/DelayedEchoCodeMiningProvider.java
+++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/DelayedEchoCodeMiningProvider.java
@@ -11,7 +11,7 @@
* Contributors:
* - Mickael Istria (Red Hat Inc.) - initial implementation
*******************************************************************************/
-package org.eclipse.jface.text.tests;
+package org.eclipse.jface.text.tests.codemining;
import java.util.ArrayList;
import java.util.List;
diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/StaticContentLineCodeMining.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/StaticContentLineCodeMining.java
new file mode 100644
index 000000000..59ef8f6e6
--- /dev/null
+++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/StaticContentLineCodeMining.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2019 Red Hat Inc., and others
+ *
+ * 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:
+ * - Mickael Istria (Red Hat Inc.)
+ */
+package org.eclipse.jface.text.tests.codemining;
+
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.codemining.ICodeMiningProvider;
+import org.eclipse.jface.text.codemining.LineContentCodeMining;
+
+public class StaticContentLineCodeMining extends LineContentCodeMining {
+
+ public StaticContentLineCodeMining(int i, char c, ICodeMiningProvider repeatLettersCodeMiningProvider) {
+ super(new Position(i, 1), repeatLettersCodeMiningProvider);
+ setLabel(Character.toString(c));
+ }
+
+ @Override
+ public boolean isResolved() {
+ return true;
+ }
+
+}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java
index 2eeea983c..464186604 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/AbstractInlinedAnnotation.java
@@ -22,8 +22,10 @@ import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
+import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.ISourceViewer;
@@ -72,14 +74,24 @@ public abstract class AbstractInlinedAnnotation extends Annotation {
}
/**
- * Returns the position where the annotation must be drawn.
+ * Returns the position where the annotation must be drawn. For {@link ITextViewerExtension5}
+ * (enabling folding with widget/model projection), this position is the <strong>model</strong>
+ * position.
*
- * @return the position where the annotation must be drawn.
+ * @return the model position where the annotation must be drawn.
*/
public Position getPosition() {
return position;
}
+ final Position computeWidgetPosition() {
+ if (fViewer instanceof ITextViewerExtension5) {
+ IRegion region= ((ITextViewerExtension5) fViewer).modelRange2WidgetRange(new Region(position.getOffset(), position.getLength()));
+ return new Position(region.getOffset(), region.getLength());
+ }
+ return position;
+ }
+
/**
* Returns the {@link StyledText} widget where the annotation must be drawn.
*
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java
index 1bf56916b..c29c6332f 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java
@@ -32,7 +32,7 @@ import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy;
class InlinedAnnotationDrawingStrategy implements IDrawingStrategy {
@Override
- public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
+ public void draw(Annotation annotation, GC gc, StyledText textWidget, int widgetoffset, int length, Color color) {
if (!(annotation instanceof AbstractInlinedAnnotation)) {
return;
}
@@ -40,7 +40,7 @@ class InlinedAnnotationDrawingStrategy implements IDrawingStrategy {
// The annotation is not in visible lines, don't draw it.
return;
}
- draw((AbstractInlinedAnnotation) annotation, gc, textWidget, offset, length,
+ draw((AbstractInlinedAnnotation) annotation, gc, textWidget, widgetoffset, length,
color);
}
@@ -50,16 +50,16 @@ class InlinedAnnotationDrawingStrategy implements IDrawingStrategy {
* @param annotation the annotation to be drawn
* @param gc the graphics context, <code>null</code> when in clearing mode
* @param textWidget the text widget to draw on
- * @param offset the offset of the line
+ * @param widgetOffset the offset of the line
* @param length the length of the line
* @param color the color of the line
*/
- public static void draw(AbstractInlinedAnnotation annotation, GC gc, StyledText textWidget, int offset, int length,
+ public static void draw(AbstractInlinedAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length,
Color color) {
if (annotation instanceof LineHeaderAnnotation) {
- draw((LineHeaderAnnotation) annotation, gc, textWidget, offset, length, color);
+ draw((LineHeaderAnnotation) annotation, gc, textWidget, widgetOffset, length, color);
} else {
- draw((LineContentAnnotation) annotation, gc, textWidget, offset, length, color);
+ draw((LineContentAnnotation) annotation, gc, textWidget, widgetOffset, length, color);
}
}
@@ -125,16 +125,16 @@ class InlinedAnnotationDrawingStrategy implements IDrawingStrategy {
* @param annotation the annotation to be drawn
* @param gc the graphics context, <code>null</code> when in clearing mode
* @param textWidget the text widget to draw on
- * @param offset the offset of the line
+ * @param widgetOffset the offset of the line in the widget (not model)
* @param length the length of the line
* @param color the color of the line
*/
- private static void draw(LineContentAnnotation annotation, GC gc, StyledText textWidget, int offset, int length,
+ private static void draw(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length,
Color color) {
- if (annotation.drawRightToPreviousChar(offset)) {
- drawAsRightOfPreviousCharacter(annotation, gc, textWidget, offset, length, color);
+ if (annotation.drawRightToPreviousChar(widgetOffset)) {
+ drawAsRightOfPreviousCharacter(annotation, gc, textWidget, widgetOffset, length, color);
} else {
- drawAsLeftOf1stCharacter(annotation, gc, textWidget, offset, length, color);
+ drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
}
}
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java
index 76c97148b..332a8a583 100644
--- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java
@@ -108,15 +108,15 @@ public class LineContentAnnotation extends AbstractInlinedAnnotation {
* @return the style to apply with GlyphMetrics width only if needed.
*/
StyleRange updateStyle(StyleRange style) {
- boolean usePreviousChar= drawRightToPreviousChar(getPosition().getOffset());
+ Position widgetPosition= computeWidgetPosition();
+ boolean usePreviousChar= drawRightToPreviousChar(widgetPosition.getOffset());
if (width == 0 || getRedrawnCharacterWidth() == 0) {
return null;
}
int fullWidth= width + getRedrawnCharacterWidth();
if (style == null) {
style= new StyleRange();
- Position position= getPosition();
- style.start= position.getOffset();
+ style.start= widgetPosition.getOffset();
if (usePreviousChar) {
style.start--;
}
@@ -144,8 +144,8 @@ public class LineContentAnnotation extends AbstractInlinedAnnotation {
return style;
}
- boolean drawRightToPreviousChar(int offset) {
- return getTextWidget().getLineAtOffset(offset) == getTextWidget().getLineAtOffset(offset - 1);
+ boolean drawRightToPreviousChar(int widgetOffset) {
+ return getTextWidget().getLineAtOffset(widgetOffset) == getTextWidget().getLineAtOffset(widgetOffset - 1);
}
}

Back to the top