blob: 679a7f9ce8fa8a42b67ae26ef75455183c0273d7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* QNX Software System
* Anton Leherbauer (Wind River Systems)
* Sergey Prigogin (Google)
* Marc-Andre Laperle (Ericsson) - Mostly copied from CSourceViewerConfiguration
* Nathan Ridge
* Manish Khurana
*******************************************************************************/
package org.eclipse.cdt.lsp.core;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
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.editor.SemanticHighlightingManager.HighlightedPosition;
import org.eclipse.cdt.internal.ui.text.CCodeScanner;
import org.eclipse.cdt.internal.ui.text.CCommentScanner;
import org.eclipse.cdt.internal.ui.text.CPreprocessorScanner;
import org.eclipse.cdt.internal.ui.text.CPresentationReconciler;
import org.eclipse.cdt.internal.ui.text.PartitionDamager;
import org.eclipse.cdt.internal.ui.text.SingleTokenCScanner;
import org.eclipse.cdt.internal.ui.text.TokenStore;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.ILanguageUI;
import org.eclipse.cdt.ui.text.AbstractCScanner;
import org.eclipse.cdt.ui.text.ICColorConstants;
import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.ui.text.ICTokenScanner;
import org.eclipse.cdt.ui.text.ITokenStore;
import org.eclipse.cdt.ui.text.ITokenStoreFactory;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DefaultPositionUpdater;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Position;
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.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
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++
* Server.
*/
public class PresentationReconcilerCPP extends CPresentationReconciler {
private CCommentScanner fSinglelineCommentScanner;
private CCommentScanner fMultilineCommentScanner;
private SingleTokenCScanner fStringScanner;
private AbstractCScanner fCodeScanner;
private LineBackgroundListenerCPP fLineBackgroundListener = new LineBackgroundListenerCPP();
private ITextViewer textViewer;
private TextInputListenerCPP textInputListener;
private BracketInserter fBracketInserter;
private DefaultPositionUpdater semanticHighlightingPositionUpdater;
public static final String SEMANTIC_HIGHLIGHTING_POSITION_CATEGORY = "org.eclipse.cdt.lsp.core.semanticHighlight"; //$NON-NLS-1$
public static final String INACTIVE_CODE_HIGHLIGHTING_POSITION_CATEGORY = "org.eclipse.cdt.lsp.core.inactiveCodeHighlight"; //$NON-NLS-1$
// A set containing all active objects of PresentationReconcilerCPP.
public static Set<PresentationReconcilerCPP> presentationReconcilers = ConcurrentHashMap.newKeySet();
protected ITokenStoreFactory getTokenStoreFactory() {
return new ITokenStoreFactory() {
@Override
public ITokenStore createTokenStore(String[] propertyColorNames) {
return new TokenStore(CUIPlugin.getDefault().getTextTools().getColorManager(),
CUIPlugin.getDefault().getCombinedPreferenceStore(), propertyColorNames);
}
};
}
protected ILanguage getLanguage() {
// fallback
return GPPLanguage.getDefault();
}
protected RuleBasedScanner getCodeScanner(ILanguage language) {
if (fCodeScanner != null) {
return fCodeScanner;
}
RuleBasedScanner scanner = null;
if (language != null) {
ICLanguageKeywords keywords = language.getAdapter(ICLanguageKeywords.class);
if (keywords != null) {
scanner = new CCodeScanner(getTokenStoreFactory(), keywords);
} else {
ILanguageUI languageUI = language.getAdapter(ILanguageUI.class);
if (languageUI != null) {
scanner = languageUI.getCodeScanner();
}
}
}
if (scanner == null) {
scanner = new CCodeScanner(getTokenStoreFactory(), GPPLanguage.getDefault());
}
if (scanner instanceof AbstractCScanner) {
fCodeScanner = (AbstractCScanner) scanner;
}
return scanner;
}
public PresentationReconcilerCPP() {
fStringScanner = new SingleTokenCScanner(getTokenStoreFactory(), ICColorConstants.C_STRING);
fMultilineCommentScanner = new CCommentScanner(getTokenStoreFactory(), ICColorConstants.C_MULTI_LINE_COMMENT);
fSinglelineCommentScanner = new CCommentScanner(getTokenStoreFactory(), ICColorConstants.C_SINGLE_LINE_COMMENT);
setDocumentPartitioning(CUIPlugin.getDefault().getTextTools().getDocumentPartitioning());
ILanguage language = getLanguage();
RuleBasedScanner scanner = getCodeScanner(language);
DefaultDamagerRepairer dr = new DefaultDamagerRepairer(scanner);
setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
dr = new DefaultDamagerRepairer(fSinglelineCommentScanner);
setDamager(dr, ICPartitions.C_SINGLE_LINE_COMMENT);
setRepairer(dr, ICPartitions.C_SINGLE_LINE_COMMENT);
dr = new DefaultDamagerRepairer(fMultilineCommentScanner);
setDamager(dr, ICPartitions.C_MULTI_LINE_COMMENT);
setRepairer(dr, ICPartitions.C_MULTI_LINE_COMMENT);
ICTokenScanner docCommentSingleScanner = getSinglelineDocCommentScanner(null);
if (docCommentSingleScanner != null) {
dr = new DefaultDamagerRepairer(docCommentSingleScanner);
setDamager(dr, ICPartitions.C_SINGLE_LINE_DOC_COMMENT);
setRepairer(dr, ICPartitions.C_SINGLE_LINE_DOC_COMMENT);
}
ICTokenScanner docCommentMultiScanner = getMultilineDocCommentScanner(null);
if (docCommentMultiScanner != null) {
dr = new DefaultDamagerRepairer(docCommentMultiScanner);
setDamager(dr, ICPartitions.C_MULTI_LINE_DOC_COMMENT);
setRepairer(dr, ICPartitions.C_MULTI_LINE_DOC_COMMENT);
}
dr = new DefaultDamagerRepairer(fStringScanner);
setDamager(dr, ICPartitions.C_STRING);
setRepairer(dr, ICPartitions.C_STRING);
dr = new DefaultDamagerRepairer(fStringScanner);
setDamager(dr, ICPartitions.C_CHARACTER);
setRepairer(dr, ICPartitions.C_CHARACTER);
dr = new DefaultDamagerRepairer(getPreprocessorScanner(language));
setDamager(new PartitionDamager(), ICPartitions.C_PREPROCESSOR);
setRepairer(dr, ICPartitions.C_PREPROCESSOR);
}
@Override
protected TextPresentation createPresentation(IRegion damage, IDocument document) {
TextPresentation presentation = super.createPresentation(damage, document);
IDocument doc = textViewer.getDocument();
URI uri = Server2ClientProtocolExtension.getUri(doc);
if (uri == null) {
return presentation;
}
Position[] returnedPositions = null;
List<StyleRange> styleRanges = new ArrayList<>();
HighlightedPosition[] highlightedPositions;
/*
* Adding Semantic Highlighting Position Category to so that we don't get a
* BadPositionCategoryException if this method is called before setupDocument()
* could the add new position category.
*/
addSemanticHighlightPositionCategory(doc);
try {
returnedPositions = doc.getPositions(SEMANTIC_HIGHLIGHTING_POSITION_CATEGORY);
} catch (BadPositionCategoryException e) {
Activator.log(e);
}
if (returnedPositions == null) {
return presentation;
}
highlightedPositions = Arrays.copyOf(returnedPositions, returnedPositions.length, HighlightedPosition[].class);
int damageStartOffset = damage.getOffset();
int damageEndOffset = damageStartOffset + damage.getLength();
for (HighlightedPosition eachPosition : highlightedPositions) {
// Find each position that resides in or overlaps the damage region and create StyleRange for it.
if ((eachPosition.getOffset() + eachPosition.getLength()) >= damageStartOffset
&& eachPosition.getOffset() < damageEndOffset) {
StyleRange range = eachPosition.createStyleRange();
styleRanges.add(range);
}
}
StyleRange[] styleRangesArray = new StyleRange[styleRanges.size()];
styleRangesArray = styleRanges.toArray(styleRangesArray);
presentation.replaceStyleRanges(styleRangesArray);
return presentation;
}
/**
* Returns the C multi-line doc comment scanner for this configuration.
*
* @return the C multi-line doc comment scanner
*/
protected ICTokenScanner getMultilineDocCommentScanner(IResource resource) {
if (fMultilineDocCommentScanner == null) {
if (resource == null) {
resource = ResourcesPlugin.getWorkspace().getRoot();
}
// IDocCommentViewerConfiguration owner= DocCommentOwnerManager.getInstance().getCommentOwner(resource).getMultilineConfiguration();
// fMultilineDocCommentScanner= owner.createCommentScanner(getTokenStoreFactory());
if (fMultilineDocCommentScanner == null) {
// fallback: normal comment highlighting
fMultilineDocCommentScanner = fMultilineCommentScanner;
}
}
return fMultilineDocCommentScanner;
}
protected ICTokenScanner fMultilineDocCommentScanner;
/**
* The C single-line doc comment scanner.
*/
protected ICTokenScanner fSinglelineDocCommentScanner;
protected ICTokenScanner getSinglelineDocCommentScanner(IResource resource) {
if (fSinglelineDocCommentScanner == null) {
if (resource == null) {
resource = ResourcesPlugin.getWorkspace().getRoot();
}
// IDocCommentViewerConfiguration owner= DocCommentOwnerManager.getInstance().getCommentOwner(resource).getSinglelineConfiguration();
// fSinglelineDocCommentScanner= owner.createCommentScanner(getTokenStoreFactory());
if (fSinglelineDocCommentScanner == null) {
// fallback: normal comment highlighting
fSinglelineDocCommentScanner = fSinglelineCommentScanner;
}
}
return fSinglelineDocCommentScanner;
}
protected AbstractCScanner fPreprocessorScanner;
protected RuleBasedScanner getPreprocessorScanner(ILanguage language) {
if (fPreprocessorScanner != null) {
return fPreprocessorScanner;
}
AbstractCScanner scanner = null;
ICLanguageKeywords keywords = language == null ? null
: (ICLanguageKeywords) language.getAdapter(ICLanguageKeywords.class);
if (keywords != null) {
scanner = new CPreprocessorScanner(getTokenStoreFactory(), keywords);
}
if (scanner == null) {
keywords = GPPLanguage.getDefault().getAdapter(ICLanguageKeywords.class);
scanner = new CPreprocessorScanner(getTokenStoreFactory(), keywords);
}
fPreprocessorScanner = scanner;
return fPreprocessorScanner;
}
public class TextInputListenerCPP implements ITextInputListener {
@Override
public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
setupDocument(newInput);
}
@Override
public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
}
}
private void addSemanticHighlightPositionCategory(IDocument document) {
if (!document.containsPositionCategory(SEMANTIC_HIGHLIGHTING_POSITION_CATEGORY)) {
document.addPositionCategory(SEMANTIC_HIGHLIGHTING_POSITION_CATEGORY);
semanticHighlightingPositionUpdater = new DefaultPositionUpdater(SEMANTIC_HIGHLIGHTING_POSITION_CATEGORY);
document.addPositionUpdater(semanticHighlightingPositionUpdater);
}
}
private void addInactiveCodeHighlightingCategory(IDocument document) {
if (!document.containsPositionCategory(INACTIVE_CODE_HIGHLIGHTING_POSITION_CATEGORY)) {
document.addPositionCategory(INACTIVE_CODE_HIGHLIGHTING_POSITION_CATEGORY);
DefaultPositionUpdater inactiveCodeHighlightingPositionUpdater = new DefaultPositionUpdater(
INACTIVE_CODE_HIGHLIGHTING_POSITION_CATEGORY);
document.addPositionUpdater(inactiveCodeHighlightingPositionUpdater);
}
}
public void setupDocument(IDocument newDocument) {
if (newDocument != null) {
fLineBackgroundListener.setCurrentDocument(newDocument);
// Adding Semantic Highlighting Position Category and a DefaultPositionUpdater to the document.
addSemanticHighlightPositionCategory(newDocument);
// Adding Inactive Code Highlighting Position Category and a DefaultPositionUpdater to the document.
addInactiveCodeHighlightingCategory(newDocument);
}
}
public ITextViewer getTextViewer() {
return textViewer;
}
public DefaultPositionUpdater getSemanticHighlightingPositionUpdater() {
return semanticHighlightingPositionUpdater;
}
@Override
public void install(ITextViewer viewer) {
super.install(viewer);
this.textViewer = viewer;
textInputListener = new TextInputListenerCPP();
viewer.addTextInputListener(textInputListener);
IDocument document = viewer.getDocument();
if (document != null) {
textInputListener.inputDocumentChanged(null, document);
}
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
public void uninstall() {
super.uninstall();
textViewer.getTextWidget().removeLineBackgroundListener(fLineBackgroundListener);
textViewer.removeTextInputListener(textInputListener);
((TextViewer) textViewer).removeVerifyKeyListener(fBracketInserter);
presentationReconcilers.remove(this);
}
}