blob: 03a756ce3dbbfbf759f89bb89f03e572ea4ce861 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 IBM Corporation 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
*
* 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.lsp4e.cpp.language;
import java.net.URI;
import java.util.ArrayList;
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.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.PreferenceConstants;
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.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.text.BadLocationException;
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.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;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
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++
* Server.
*/
@SuppressWarnings("restriction")
public class PresentationReconcilerCPP extends CPresentationReconciler {
private CCommentScanner fSinglelineCommentScanner;
private CCommentScanner fMultilineCommentScanner;
private SingleTokenCScanner fStringScanner;
private AbstractCScanner fCodeScanner;
private CqueryLineBackgroundListener fLineBackgroundListener = new CqueryLineBackgroundListener();
private ITextViewer textViewer;
private TextInputListenerCPP textInputListener;
private BracketInserter fBracketInserter;
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;
}
List<HighlightSymbol> semanticHighlights = CquerySemanticHighlights.uriToSemanticHighlightsMapping.get(uri);
if(semanticHighlights == null) {
return presentation;
}
IPreferenceStore store = CUIPlugin.getDefault().getPreferenceStore();
List<StyleRange> styleRanges = new ArrayList<>();
for (HighlightSymbol highlight : semanticHighlights) {
String highlightingName = HighlightSymbol.getHighlightingName(highlight.getKind(), highlight.getParentKind(), highlight.getStorage());
String colorKey = PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX
+ highlightingName + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_COLOR_SUFFIX;
boolean isBold = store.getBoolean(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX
+ highlightingName + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_BOLD_SUFFIX);
boolean isItalic = store.getBoolean(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX
+ highlightingName + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ITALIC_SUFFIX);
boolean isUnderline = store.getBoolean(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX
+ highlightingName + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_UNDERLINE_SUFFIX);
boolean isStrikethrough = store.getBoolean(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX
+ highlightingName + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_STRIKETHROUGH_SUFFIX);
Color color = new Color(Display.getCurrent(),
PreferenceConverter.getColor(CUIPlugin.getDefault().getPreferenceStore(), colorKey));
int damageStartOffset = damage.getOffset();
int damageEndOffset = damageStartOffset + damage.getLength();
List<Range> ranges = highlight.getRanges();
for (Range range : ranges) {
int offset = 0, length = 0;
try {
offset = doc.getLineOffset(range.getStart().getLine()) + range.getStart().getCharacter();
length = doc.getLineOffset(range.getEnd().getLine()) + range.getEnd().getCharacter() - offset;
} catch (BadLocationException e) {
Activator.log(e);
}
if ((offset + length) >= damageStartOffset && offset < damageEndOffset) {
StyleRange styleRange = new StyleRange(offset, length, color, null);
if (isBold) {
styleRange.fontStyle = SWT.BOLD;
}
if (isItalic) {
styleRange.fontStyle |= SWT.ITALIC;
}
if (isUnderline) {
styleRange.underline = true;
}
if (isStrikethrough) {
styleRange.strikeout = true;
}
styleRanges.add(styleRange);
}
}
}
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) {
}
}
public void setupDocument(IDocument newDocument) {
if (newDocument != null) {
fLineBackgroundListener.setCurrentDocument(newDocument);
}
}
public ITextViewer getTextViewer() {
return textViewer;
}
@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);
}
}