Bug 535428. Add indentation and bracket completion support in LSP4E-CPP

Change-Id: Ib9187a3ad28796305c47fe8e9543e1aed6a7bf58
Signed-off-by: Manish Khurana <mkmanishkhurana98@gmail.com>
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java
index 90cab56..feb5059 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java
@@ -469,17 +469,19 @@
 		}
 	}
 
-	private class ExitPolicy implements IExitPolicy {
+	private static class ExitPolicy implements IExitPolicy {
 		final char fExitCharacter;
 		final char fEscapeCharacter;
 		final Deque<BracketLevel> fStack;
 		final int fSize;
+		ISourceViewer sourceViewer;
 
-		public ExitPolicy(char exitCharacter, char escapeCharacter, Deque<BracketLevel> stack) {
+		public ExitPolicy(char exitCharacter, char escapeCharacter, Deque<BracketLevel> stack, ISourceViewer sViewer) {
 			fExitCharacter = exitCharacter;
 			fEscapeCharacter = escapeCharacter;
 			fStack = stack;
 			fSize = fStack.size();
+			sourceViewer = sViewer;
 		}
 
 		@Override
@@ -496,7 +498,7 @@
 				// When entering an anonymous class between the parenthesis', we don't want
 				// to jump after the closing parenthesis when return is pressed.
 				if (event.character == SWT.CR && offset > 0) {
-					IDocument document = getSourceViewer().getDocument();
+					IDocument document = sourceViewer.getDocument();
 					try {
 						if (document.getChar(offset - 1) == '{')
 							return new ExitFlags(ILinkedModeListener.EXIT_ALL, true);
@@ -508,7 +510,7 @@
 		}
 
 		private boolean isMasked(int offset) {
-			IDocument document = getSourceViewer().getDocument();
+			IDocument document = sourceViewer.getDocument();
 			try {
 				return fEscapeCharacter == document.getChar(offset - 1);
 			} catch (BadLocationException e) {
@@ -603,7 +605,7 @@
 //		}
 	}
 
-	private class BracketInserter implements VerifyKeyListener, ILinkedModeListener {
+	public static class BracketInserter implements VerifyKeyListener, ILinkedModeListener {
 		private boolean fCloseBrackets = true;
 		private boolean fCloseStrings = true;
 		private boolean fCloseAngularBrackets = true;
@@ -611,6 +613,18 @@
 		private final String CATEGORY = toString();
 		private final IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY);
 		private final Deque<BracketLevel> fBracketLevelStack = new ArrayDeque<>();
+		private ISourceViewer sourceViewer;
+		private boolean isGenericEditor;
+		private TextEditor fEditor;
+
+		public BracketInserter(TextEditor editor, boolean isGenericEditor) {
+			fEditor = editor;
+			this.isGenericEditor = isGenericEditor;
+		}
+
+		public void setSourceViewer(ISourceViewer sViewer) {
+			sourceViewer = sViewer;
+		}
 
 		public void setCloseBracketsEnabled(boolean enabled) {
 			fCloseBrackets = enabled;
@@ -638,8 +652,14 @@
 		@Override
 		public void verifyKey(VerifyEvent event) {
 			// Early pruning to minimize overhead for normal typing.
-			if (!event.doit || getInsertMode() != SMART_INSERT)
+			if (!event.doit)
 				return;
+
+//			Need to check that it is Generic Editor or CEditor before checking "Smart Insert" mode
+//			because Generic Editor doesn't have a "Smart Insert" mode.
+			if(!isGenericEditor)
+				if (fEditor.getInsertMode() != SMART_INSERT)
+						return;
 			switch (event.character) {
 				case '(':
 				case '<':
@@ -652,7 +672,6 @@
 					return;
 			}
 
-			final ISourceViewer sourceViewer = getSourceViewer();
 			IDocument document = sourceViewer.getDocument();
 
 			final Point selection = sourceViewer.getSelectedRange();
@@ -661,7 +680,7 @@
 			try {
 				IRegion startLine = document.getLineInformationOfOffset(offset);
 				IRegion endLine = document.getLineInformationOfOffset(offset + length);
-				if (startLine != endLine && isBlockSelectionModeEnabled()) {
+				if (startLine != endLine && fEditor.isBlockSelectionModeEnabled()) {
 					return;
 				}
 
@@ -730,7 +749,7 @@
 						return;
 				}
 
-				if (!validateEditorInputState())
+				if (!fEditor.validateEditorInputState())
 					return;
 
 				final char character = event.character;
@@ -764,7 +783,7 @@
 
 				level.fUI = new EditorLinkedModeUI(model, sourceViewer);
 				level.fUI.setSimpleMode(true);
-				level.fUI.setExitPolicy(new ExitPolicy(closingCharacter, getEscapeCharacter(closingCharacter), fBracketLevelStack));
+				level.fUI.setExitPolicy(new ExitPolicy(closingCharacter, getEscapeCharacter(closingCharacter), fBracketLevelStack, sourceViewer));
 				level.fUI.setExitPosition(sourceViewer, offset + 2, 0, Integer.MAX_VALUE);
 				level.fUI.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
 				level.fUI.enter();
@@ -821,7 +840,6 @@
 				return;
 
 			// remove brackets
-			final ISourceViewer sourceViewer = getSourceViewer();
 			final IDocument document = sourceViewer.getDocument();
 			if (document instanceof IDocumentExtension) {
 				IDocumentExtension extension = (IDocumentExtension) document;
@@ -1284,7 +1302,7 @@
     protected CPairMatcher fBracketMatcher = new CPairMatcher(BRACKETS);
 
 	/** The bracket inserter. */
-	private final BracketInserter fBracketInserter = new BracketInserter();
+	private final BracketInserter fBracketInserter = new BracketInserter(this, false);
 
 	/** Listener to annotation model changes that updates the error tick in the tab image */
 	private CEditorErrorTickUpdater fCEditorErrorTickUpdater;
@@ -2535,12 +2553,14 @@
 		fBracketInserter.setCloseStringsEnabled(closeStrings);
 
 		ISourceViewer sourceViewer = getSourceViewer();
-		if (sourceViewer instanceof ITextViewerExtension)
+		if (sourceViewer instanceof ITextViewerExtension) {
+			fBracketInserter.setSourceViewer(sourceViewer);
 			((ITextViewerExtension) sourceViewer).prependVerifyKeyListener(fBracketInserter);
+		}
 
 		if (isMarkingOccurrences())
 			installOccurrencesFinder(false);
-		
+
 		if(isShowingOverrideIndicators())
 			installOverrideIndicator(false);
 	}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAutoIndentStrategy.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAutoIndentStrategy.java
index 77053a6..7a95340 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAutoIndentStrategy.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CAutoIndentStrategy.java
@@ -67,6 +67,11 @@
 	private String fPartitioning;
 	private final ICProject fProject;
 
+	/*
+	 *	boolean to skip "Smart Insert" mode check because Generic Editor doesn't have a "Smart Insert" mode.
+	 */
+	private boolean alwaysUseSmartMode = false;
+
 	/**
 	 * Creates a new C auto indent strategy for the given document partitioning.
 	 *
@@ -78,6 +83,11 @@
 		fProject = project;
  	}
 
+	public CAutoIndentStrategy(String partitioning, ICProject project, boolean alwaysUseSmartMode) {
+		this(partitioning, project);
+		this.alwaysUseSmartMode = alwaysUseSmartMode;
+	}
+
 	private int getBracketCount(IDocument d, int start, int end, boolean ignoreCloseBrackets) throws BadLocationException {
 		int bracketcount = 0;
 		while (start < end) {
@@ -1151,6 +1161,9 @@
 	private boolean computeSmartMode() {
 		IWorkbenchPage page = CUIPlugin.getActivePage();
 		if (page != null)  {
+			if (alwaysUseSmartMode) {
+				return true;
+			}
 			IEditorPart part = page.getActiveEditor();
 			if (part instanceof MultiPageEditorPart) {
 				part= (IEditorPart)part.getAdapter(ITextEditorExtension3.class);
diff --git a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/META-INF/MANIFEST.MF b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/META-INF/MANIFEST.MF
index b816272..68b58e7 100644
--- a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/META-INF/MANIFEST.MF
+++ b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/META-INF/MANIFEST.MF
@@ -24,4 +24,5 @@
  org.eclipse.lsp4e.cpp.language.cquery
 Bundle-Activator: org.eclipse.lsp4e.cpp.language.Activator
 Bundle-ActivationPolicy: lazy
-Import-Package: org.eclipse.ui.texteditor
+Import-Package: org.eclipse.ui.editors.text,
+ org.eclipse.ui.texteditor
diff --git a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/plugin.xml b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/plugin.xml
index 58929f9..f81c0b3 100644
--- a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/plugin.xml
+++ b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/plugin.xml
@@ -62,4 +62,11 @@
             class="org.eclipse.lsp4e.cpp.language.PreferenceInitializer">
       </initializer>
    </extension>
+   <extension
+         point="org.eclipse.ui.genericeditor.autoEditStrategies">
+      <autoEditStrategy
+            class="org.eclipse.lsp4e.cpp.language.AutoIndentStrategyCPP"
+            contentType="org.eclipse.lsp4e.languages.cpp">
+      </autoEditStrategy>
+   </extension>
 </plugin>
diff --git a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/AutoIndentStrategyCPP.java b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/AutoIndentStrategyCPP.java
new file mode 100644
index 0000000..8289ef4
--- /dev/null
+++ b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/AutoIndentStrategyCPP.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Manish Khurana , Nathan Ridge and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.lsp4e.cpp.language;
+
+import org.eclipse.cdt.internal.ui.text.CAutoIndentStrategy;
+import org.eclipse.cdt.ui.CUIPlugin;
+
+/*
+ * 	Class to re-use existing auto-indentation support of CEditor in Generic Editor of LSP4E-CPP.
+ */
+
+@SuppressWarnings("restriction")
+public class AutoIndentStrategyCPP extends CAutoIndentStrategy {
+
+	public AutoIndentStrategyCPP() {
+		// TODO: Pass in the project so the auto edit strategy respects the project's preferences.
+		super(CUIPlugin.getDefault().getTextTools().getDocumentPartitioning(), null, true);
+	}
+}
diff --git a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/PresentationReconcilerCPP.java b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/PresentationReconcilerCPP.java
index 1b16aeb..03a756c 100644
--- a/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/PresentationReconcilerCPP.java
+++ b/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/PresentationReconcilerCPP.java
@@ -26,6 +26,7 @@
 import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage;
 import org.eclipse.cdt.core.model.ICLanguageKeywords;
 import org.eclipse.cdt.core.model.ILanguage;
+import org.eclipse.cdt.internal.ui.editor.CEditor.BracketInserter;
 import org.eclipse.cdt.internal.ui.text.CCodeScanner;
 import org.eclipse.cdt.internal.ui.text.CCommentScanner;
 import org.eclipse.cdt.internal.ui.text.CPreprocessorScanner;
@@ -52,8 +53,10 @@
 import org.eclipse.jface.text.ITextInputListener;
 import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.TextViewer;
 import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
 import org.eclipse.jface.text.rules.RuleBasedScanner;
+import org.eclipse.jface.text.source.SourceViewer;
 import org.eclipse.lsp4e.cpp.language.cquery.CquerySemanticHighlights;
 import org.eclipse.lsp4e.cpp.language.cquery.HighlightSymbol;
 import org.eclipse.lsp4j.Range;
@@ -62,6 +65,8 @@
 import org.eclipse.swt.custom.StyledText;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.editors.text.TextEditor;
 
 /**
  * Hack-ish reconciler to get some colors in the generic editor using the C/C++
@@ -77,6 +82,7 @@
 	private CqueryLineBackgroundListener fLineBackgroundListener = new CqueryLineBackgroundListener();
 	private ITextViewer textViewer;
 	private TextInputListenerCPP textInputListener;
+	private BracketInserter fBracketInserter;
 
 	public static Set<PresentationReconcilerCPP> presentationReconcilers = ConcurrentHashMap.newKeySet();
 
@@ -345,6 +351,19 @@
 		StyledText textWidget = textViewer.getTextWidget();
 		textWidget.addLineBackgroundListener(fLineBackgroundListener);
 		presentationReconcilers.add(this);
+
+//		Using asyncExec() to make sure that by the time Runnable runs,
+//		the Editor is active and we don't get a NPE.
+		Display.getDefault().asyncExec(new Runnable() {
+			@Override
+			public void run() {
+//				To provide bracket auto-completion support of CEditor in Generic Editor of LSP4E-CPP
+
+				TextEditor editor = (TextEditor) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
+				fBracketInserter = new BracketInserter(editor, true);
+				fBracketInserter.setSourceViewer((SourceViewer) textViewer);
+				((TextViewer) textViewer).prependVerifyKeyListener(fBracketInserter);
+			}});
 	}
 
 	@Override
@@ -352,6 +371,7 @@
 		super.uninstall();
 		textViewer.getTextWidget().removeLineBackgroundListener(fLineBackgroundListener);
 		textViewer.removeTextInputListener(textInputListener);
+		((TextViewer) textViewer).removeVerifyKeyListener(fBracketInserter);
 		presentationReconcilers.remove(this);
 	}
 }