Bug 343023 - Clear the initial stack trace console message on the first
edit

The JavaStackTraceConsole shows a simple help message when opened for
the first time. If this message is still shown if user types something
for the first time the message is cleared.

Change-Id: Ia298428293493b95a0e61833857b2662d6a9b338
Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
diff --git a/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceConsoleTest.java b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceConsoleTest.java
index 466ed03..d11b319 100644
--- a/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceConsoleTest.java
+++ b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceConsoleTest.java
@@ -11,6 +11,7 @@
  * Contributors:
  *     SAP SE - initial API and implementation
  *     Paul Pazderski - Bug 546900: Tests to check initial console content and content persistence
+ *     Paul Pazderski - Bug 343023: Tests for 'clear initial content on first edit'
  *******************************************************************************/
 package org.eclipse.jdt.debug.tests.console;
 
@@ -26,17 +27,30 @@
 
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jdt.debug.tests.AbstractDebugTest;
+import org.eclipse.jdt.debug.tests.TestUtil;
 import org.eclipse.jdt.debug.ui.console.JavaStackTraceConsoleFactory;
 import org.eclipse.jdt.internal.debug.ui.console.ConsoleMessages;
 import org.eclipse.jdt.internal.debug.ui.console.JavaStackTraceConsole;
+import org.eclipse.jdt.internal.debug.ui.console.JavaStackTraceConsolePage;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.BadPositionCategoryException;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.Position;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ST;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.console.ConsolePlugin;
 import org.eclipse.ui.console.IConsole;
 import org.eclipse.ui.console.IConsoleManager;
+import org.eclipse.ui.console.TextConsoleViewer;
 import org.eclipse.ui.internal.console.ConsoleHyperlinkPosition;
+import org.eclipse.ui.internal.console.ConsoleView;
 
 /**
  * Tests {@link JavaStackTraceConsole}
@@ -171,6 +185,90 @@
 		assertEquals("Failed to restore previous content.", storedContent, fConsole.getDocument().get());
 	}
 
+	/**
+	 * Test for Bug 343023. Test if initial content is cleared on first edit and not cleared if it was changed programmatically before.
+	 */
+	public void testClearInitialContent() {
+		PlatformUI.getWorkbench().getDisplay().syncExec(() -> {
+			// type at start of initial content
+			TextConsoleViewer viewer = getConsolesViewer();
+			viewer.getTextWidget().invokeAction(ST.TEXT_START);
+			doKeyStroke(viewer.getTextWidget(), 'a');
+			assertEquals("Initial content was not cleared.", "a", fConsole.getDocument().get());
+			doKeyStroke(viewer.getTextWidget(), 'b');
+			assertEquals("Content was cleared again.", "ab", fConsole.getDocument().get());
+			removeConsole(false);
+
+			// type inside initial content
+			initConsole(true);
+			viewer = getConsolesViewer();
+			viewer.getTextWidget().setCaretOffset(5);
+			doKeyStroke(viewer.getTextWidget(), 'a');
+			assertEquals("Initial content was not cleared.", "a", fConsole.getDocument().get());
+			doKeyStroke(viewer.getTextWidget(), 'b');
+			assertEquals("Content was cleared again.", "ab", fConsole.getDocument().get());
+			removeConsole(false);
+
+			// type at end of initial content
+			initConsole(true);
+			viewer = getConsolesViewer();
+			viewer.getTextWidget().invokeAction(ST.TEXT_END);
+			doKeyStroke(viewer.getTextWidget(), 'a');
+			assertEquals("Initial content was not cleared.", "a", fConsole.getDocument().get());
+			doKeyStroke(viewer.getTextWidget(), 'b');
+			assertEquals("Content was cleared again.", "ab", fConsole.getDocument().get());
+			removeConsole(false);
+
+			// select all initial content
+			initConsole(true);
+			viewer = getConsolesViewer();
+			viewer.getTextWidget().selectAll();
+			doKeyStroke(viewer.getTextWidget(), 'a');
+			assertEquals("Initial content was not cleared.", "a", fConsole.getDocument().get());
+			doKeyStroke(viewer.getTextWidget(), 'b');
+			assertEquals("Content was cleared again.", "ab", fConsole.getDocument().get());
+			removeConsole(false);
+
+			// select part of initial content
+			initConsole(true);
+			viewer = getConsolesViewer();
+			viewer.getTextWidget().setSelection(5, 10);
+			doKeyStroke(viewer.getTextWidget(), 'a');
+			assertEquals("Initial content was not cleared.", "a", fConsole.getDocument().get());
+			doKeyStroke(viewer.getTextWidget(), 'b');
+			assertEquals("Content was cleared again.", "ab", fConsole.getDocument().get());
+			removeConsole(false);
+
+			// paste inside initial content
+			initConsole(true);
+			viewer = getConsolesViewer();
+			viewer.getTextWidget().setCaretOffset(5);
+			viewer.getTextWidget().insert("at foo.bar.Type.method1(IILjava/lang/String;)V(Type.java:1)");
+			assertEquals("Initial content was not cleared.", "at foo.bar.Type.method1(IILjava/lang/String;)V(Type.java:1)", fConsole.getDocument().get());
+			doKeyStroke(viewer.getTextWidget(), 'b');
+			assertTrue("Content was cleared again.", fConsole.getDocument().getLength() > 5);
+			removeConsole(false);
+
+			// user edit after something already modified the content (expect no magic clear)
+			initConsole(true);
+			viewer = getConsolesViewer();
+			String text = "Text set programmatically";
+			viewer.getTextWidget().setText(text);
+			viewer.getTextWidget().invokeAction(ST.TEXT_END);
+			doKeyStroke(viewer.getTextWidget(), '!');
+			assertEquals(text + "!", fConsole.getDocument().get());
+			removeConsole(true); // preserve content this time!
+
+			// check first edit does not clear if console is initialized with custom content
+			initConsole(false);
+			viewer = getConsolesViewer();
+			int lengthBefore = fConsole.getDocument().getLength();
+			doKeyStroke(viewer.getTextWidget(), 'a');
+			assertEquals("Custom content was cleared.", lengthBefore + 1, fConsole.getDocument().getLength());
+			// do not remove last console so tearDown can do something
+		});
+	}
+
 	private IDocument consoleDocumentWithText(String text) throws InterruptedException {
 		IDocument document = fConsole.getDocument();
 		document.set(text);
@@ -223,4 +321,45 @@
 	public void assertInitialContent() {
 		assertEquals("Console not loaded with initial content.", ConsoleMessages.JavaStackTraceConsole_0, fConsole.getDocument().get());
 	}
+
+	/**
+	 * Tries to get the viewer of the currently tested console.
+	 *
+	 * @return the consoles viewer
+	 */
+	private TextConsoleViewer getConsolesViewer() {
+		TestUtil.waitForJobs(getName(), 100, DEFAULT_TIMEOUT);
+		IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+		assertNotNull(workbenchWindow);
+		IWorkbenchPage activePage = workbenchWindow.getActivePage();
+		assertNotNull(activePage);
+		JavaStackTraceConsolePage page = null;
+		for (IViewReference vref : activePage.getViewReferences()) {
+			IViewPart view = vref.getView(false);
+			if (view instanceof ConsoleView) {
+				ConsoleView consoleView = (ConsoleView) view;
+				if (consoleView.getConsole() == fConsole && consoleView.getCurrentPage() instanceof JavaStackTraceConsolePage) {
+					page = (JavaStackTraceConsolePage) consoleView.getCurrentPage();
+					break;
+				}
+			}
+		}
+		assertNotNull(page);
+		return page.getViewer();
+	}
+
+	/**
+	 * Simulate user pressing a key.
+	 *
+	 * @param widget
+	 *            widget to type in
+	 * @param c
+	 *            character to type
+	 */
+	private void doKeyStroke(StyledText widget, char c) {
+		final Event e = new Event();
+		e.character = c;
+		widget.notifyListeners(SWT.KeyDown, e);
+		widget.notifyListeners(SWT.KeyUp, e);
+	}
 }
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceConsole.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceConsole.java
index aeb5a88..6ab52b4 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceConsole.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceConsole.java
@@ -11,6 +11,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Paul Pazderski - Bug 546900: Fix IO handling in JavaStacktraceConsole
+ *     Paul Pazderski - Bug 343023: Clear the initial stack trace console message on first edit
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.ui.console;
 
@@ -33,7 +34,9 @@
 import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
 import org.eclipse.jdt.internal.debug.ui.JavaDebugImages;
 import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.DocumentEvent;
 import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
 import org.eclipse.jface.text.rules.FastPartitioner;
 import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
 import org.eclipse.jface.util.IPropertyChangeListener;
@@ -87,6 +90,19 @@
             }
         }
     };
+	/** Memorize if stack trace console is showing the initial "How to use" text at the moment. */
+	boolean showsUsageHint = false;
+	/** Document listener to recognize if initial "How to use" text is changed programmatically. Removes itself after first document change. */
+	private final IDocumentListener documentsFirstChangeListener = new IDocumentListener() {
+		@Override
+		public void documentAboutToBeChanged(DocumentEvent event) {
+		}
+		@Override
+		public void documentChanged(DocumentEvent event) {
+			event.getDocument().removeDocumentListener(documentsFirstChangeListener);
+			showsUsageHint = false;
+		}
+	};
 
 	/**
 	 * Constructor
@@ -122,6 +138,8 @@
             }
         } else {
 			getDocument().set(ConsoleMessages.JavaStackTraceConsole_0);
+			getDocument().addDocumentListener(documentsFirstChangeListener);
+			showsUsageHint = true;
 		}
     }
 
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceConsoleViewer.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceConsoleViewer.java
index 47a954b..bc24d96 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceConsoleViewer.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceConsoleViewer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2012 IBM Corporation and others.
+ * Copyright (c) 2005, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,20 +10,18 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Paul Pazderski - Bug 343023: Clear the initial stack trace console message on first edit
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.ui.console;
 
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-
-import org.eclipse.jface.preference.IPreferenceStore;
-
-import org.eclipse.jface.text.ITextOperationTarget;
-
-import org.eclipse.ui.console.TextConsoleViewer;
-
 import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants;
 import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.DocumentCommand;
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.console.TextConsoleViewer;
 
 /**
  * provides the viewer for Java stack trace consoles
@@ -44,7 +42,24 @@
 		getTextWidget().setOrientation(SWT.LEFT_TO_RIGHT);
 
 		IPreferenceStore fPreferenceStore = JDIDebugUIPlugin.getDefault().getPreferenceStore();
-        fAutoFormat = fPreferenceStore.getBoolean(IJDIPreferencesConstants.PREF_AUTO_FORMAT_JSTCONSOLE);
+		fAutoFormat = fPreferenceStore.getBoolean(IJDIPreferencesConstants.PREF_AUTO_FORMAT_JSTCONSOLE);
+	}
+
+	/**
+	 * Additional to the parents customization this override implements the clearing of the initial stack trace console content on first edit. It
+	 * modifies the first event as if the user selected the whole initial content before typing.
+	 *
+	 * @param command
+	 *            the document command representing the verify event
+	 */
+	@Override
+	protected void customizeDocumentCommand(DocumentCommand command) {
+		if (fConsole.showsUsageHint) {
+			command.offset = 0;
+			command.length = getDocument().getLength();
+			command.caretOffset = command.length;
+		}
+		super.customizeDocumentCommand(command);
 	}
 
 	/**
@@ -54,8 +69,9 @@
 	public void doOperation(int operation) {
 		super.doOperation(operation);
 
-		if (fAutoFormat && operation == ITextOperationTarget.PASTE)
+		if (fAutoFormat && operation == ITextOperationTarget.PASTE) {
 			fConsole.format();
+		}
 	}
 
 	/**