| /******************************************************************************* |
| * 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); |
| } |
| } |