1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-25 01:45:33 +02:00

Bug 510998 - Use the semantic highlighter to color the preview code on the Syntax Coloring preference page

Previously, the highlighted ranges in the preview widget were hard-coded.

Change-Id: Ib1221ae69fcbe7138549d1483da10bad2251430e
This commit is contained in:
Nathan Ridge 2017-01-25 02:01:09 -05:00
parent b10699be77
commit 4ed57293e6
6 changed files with 214 additions and 140 deletions

View file

@ -12,8 +12,11 @@
package org.eclipse.cdt.internal.ui.editor;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.StringConverter;
@ -27,10 +30,27 @@ import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.parser.FileContent;
import org.eclipse.cdt.core.parser.IParserLogService;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IncludeFileContentProvider;
import org.eclipse.cdt.core.parser.ParserUtil;
import org.eclipse.cdt.core.parser.ScannerInfo;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.CSourceViewerConfiguration;
import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.ui.text.IColorManager;
import org.eclipse.cdt.ui.text.ISemanticToken;
import org.eclipse.cdt.internal.core.parser.scanner.CharArray;
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingReconciler.AbstractPositionCollector;
import org.eclipse.cdt.internal.ui.text.CPresentationReconciler;
import org.eclipse.cdt.internal.ui.text.CSourceViewerScalableConfiguration;
@ -267,8 +287,8 @@ public class SemanticHighlightingManager implements IPropertyChangeListener {
/** The presentation reconciler */
protected CPresentationReconciler fPresentationReconciler;
/** The hard-coded ranges */
protected HighlightedRange[][] fHardcodedRanges;
/** Library declarations used by the previewer widget in the preferences */
private String fPreviewerLibraryDecls;
/**
* Install the semantic highlighting on the given editor infrastructure
@ -300,15 +320,17 @@ public class SemanticHighlightingManager implements IPropertyChangeListener {
/**
* Installs the semantic highlighting on the given source viewer infrastructure.
* No reconciliation will be performed.
*
* This is used for highlighting the code in the previewer window in the preferences.
*
* @param sourceViewer the source viewer
* @param colorManager the color manager
* @param preferenceStore the preference store
* @param hardcodedRanges the hard-coded ranges to be highlighted
* @param previewerLibraryDecls library declarations required for the previewer code to be valid
*/
public void install(CSourceViewer sourceViewer, IColorManager colorManager,
IPreferenceStore preferenceStore, HighlightedRange[][] hardcodedRanges) {
fHardcodedRanges= hardcodedRanges;
IPreferenceStore preferenceStore, String previewerLibraryDecls) {
fPreviewerLibraryDecls = previewerLibraryDecls;
install(null, sourceViewer, colorManager, preferenceStore);
}
@ -325,47 +347,125 @@ public class SemanticHighlightingManager implements IPropertyChangeListener {
fReconciler= new SemanticHighlightingReconciler();
fReconciler.install(fEditor, fSourceViewer, fPresenter, fSemanticHighlightings, fHighlightings);
} else {
fPresenter.updatePresentation(null, createHardcodedPositions(), new HighlightedPosition[0]);
fPresenter.updatePresentation(null, computePreviewerPositions(), new HighlightedPosition[0]);
}
}
/**
* Computes the hard-coded positions from the hard-coded ranges
*
* @return the hard-coded positions
* Computes the positions for the preview code.
*/
protected HighlightedPosition[] createHardcodedPositions() {
List<HighlightedPosition> positions= new ArrayList<HighlightedPosition>();
for (int i= 0; i < fHardcodedRanges.length; i++) {
HighlightedRange range= null;
HighlightingStyle hl= null;
for (int j= 0; j < fHardcodedRanges[i].length; j++ ) {
hl= getHighlighting(fHardcodedRanges[i][j].getKey());
if (hl.isEnabled()) {
range= fHardcodedRanges[i][j];
break;
}
protected HighlightedPosition[] computePreviewerPositions() {
// Before parsing and coloring the preview code, prepend library declarations
// required to make it valid.
CharArray previewCode = new CharArray(fPreviewerLibraryDecls + fSourceViewer.getDocument().get());
// Parse the preview code.
ILanguage language = GPPLanguage.getDefault();
FileContent content = new InternalFileContent("<previewer>", previewCode); //$NON-NLS-1$
IScannerInfo scanInfo = new ScannerInfo();
IncludeFileContentProvider fileCreator = IncludeFileContentProvider.getEmptyFilesProvider();
IParserLogService log = ParserUtil.getParserLogService();
IASTTranslationUnit tu = null;
try {
tu = language.getASTTranslationUnit(content, scanInfo, fileCreator, null, 0, log);
} catch (CoreException e) {
CUIPlugin.log(e);
return new HighlightedPosition[] {};
}
// Correct highlighting of external SDK references requires an index-based AST.
// Since we don't have an index-based AST here, we swap out the external SDK
// highlighting with a custom one that recognizes certain hardcoded functions
// that are present in the preview code.
List<SemanticHighlighting> highlightings = new ArrayList<>();
for (SemanticHighlighting highlighting : fSemanticHighlightings) {
if (SemanticHighlightings.isExternalSDKHighlighting(highlighting)) {
highlightings.add(new PreviewerExternalSDKHighlighting());
} else {
highlightings.add(highlighting);
}
if (range != null)
positions.add(fPresenter.createHighlightedPosition(range.getOffset(), range.getLength(), hl));
}
return positions.toArray(new HighlightedPosition[positions.size()]);
// Compute the highlighted positions for the preview code.
PreviewerPositionCollector collector = new PreviewerPositionCollector(
highlightings.toArray(new SemanticHighlighting[highlightings.size()]), fHighlightings);
tu.accept(collector);
List<HighlightedPosition> positions = collector.getPositions();
// Since the code that was parsed and colored included library declarations as
// a prefix, the offsets in the highlighted positions reflect offsets in the
// library declarations + preview code. Since what we're actually showing is
// the preview code without the library declarations, adjust the offsets
// accordingly.
int libraryDeclsLen = fPreviewerLibraryDecls.length();
List<HighlightedPosition> adjustedPositions = new ArrayList<>();
for (HighlightedPosition position : positions) {
if (position.offset >= libraryDeclsLen) {
position.offset -= libraryDeclsLen;
adjustedPositions.add(position);
}
}
return adjustedPositions.toArray(new HighlightedPosition[adjustedPositions.size()]);
}
/**
* Returns the highlighting corresponding to the given key.
*
* @param key the highlighting key as returned by {@link SemanticHighlighting#getPreferenceKey()}
* @return the corresponding highlighting
*/
private HighlightingStyle getHighlighting(String key) {
for (int i= 0; i < fSemanticHighlightings.length; i++) {
SemanticHighlighting semanticHighlighting= fSemanticHighlightings[i];
if (key.equals(semanticHighlighting.getPreferenceKey()))
return fHighlightings[i];
// A custom version of the highlighting for external SDK functions, for use
// by the previewer. Just highlights names that match a hardcoded list of
// SDK functions that appear in the previewer code.
private static class PreviewerExternalSDKHighlighting extends SemanticHighlightingWithOwnPreference {
static private final Set<String> fHarcodedSDKFunctions;
static {
fHarcodedSDKFunctions = new HashSet<String>();
fHarcodedSDKFunctions.add("fprintf"); //$NON-NLS-1$
// add others as necessary
}
@Override
public boolean consumes(ISemanticToken token) {
IASTNode node = token.getNode();
if (!(node instanceof IASTName)) {
return false;
}
String name = new String(((IASTName) node).getSimpleID());
return fHarcodedSDKFunctions.contains(name);
}
// These methods aren't used by PositionCollector.
@Override
public RGB getDefaultDefaultTextColor() {
return null;
}
@Override
public String getDisplayName() {
return null;
}
@Override
public String getPreferenceKey() {
return null;
}
@Override
public boolean isEnabledByDefault() {
return false;
}
}
// Simple implementation of AbstractPositionCollector for the previewer.
private class PreviewerPositionCollector extends AbstractPositionCollector {
private List<HighlightedPosition> fPositions = new ArrayList<>();
public PreviewerPositionCollector(SemanticHighlighting[] highlightings,
HighlightingStyle[] highlightingStyles) {
super(highlightings, highlightingStyles);
}
@Override
protected void addPosition(int offset, int length, HighlightingStyle highlightingStyle) {
fPositions.add(fPresenter.createHighlightedPosition(offset, length, highlightingStyle));
}
public List<HighlightedPosition> getPositions() {
return fPositions;
}
return null;
}
/**
@ -384,7 +484,6 @@ public class SemanticHighlightingManager implements IPropertyChangeListener {
fColorManager= null;
fConfiguration= null;
fPresentationReconciler= null;
fHardcodedRanges= null;
}
/**

View file

@ -64,19 +64,30 @@ import org.eclipse.cdt.internal.ui.text.ICReconcilingListener;
*/
public class SemanticHighlightingReconciler implements ICReconcilingListener {
private class PositionCollectorRequirements {
public boolean visitImplicitNames = false;
public boolean visitExpressions = false;
}
/**
* Collects positions from the AST.
* This abstract version exists so it can be reused by the previewer widget.
* The concrete version used by the reconciler follows.
*/
private class PositionCollector extends ASTVisitor {
public static abstract class AbstractPositionCollector extends ASTVisitor {
private SemanticHighlighting[] fHighlightings;
private HighlightingStyle[] fHighlightingStyles;
/** The semantic token */
private SemanticToken fToken= new SemanticToken();
public PositionCollector(PositionCollectorRequirements requirements) {
private class PositionCollectorRequirements {
public boolean visitImplicitNames = false;
public boolean visitExpressions = false;
}
public AbstractPositionCollector(SemanticHighlighting[] highlightings,
HighlightingStyle[] highlightingStyles) {
fHighlightings = highlightings;
fHighlightingStyles = highlightingStyles;
PositionCollectorRequirements requirements = getRequirements();
shouldVisitTranslationUnit= true;
shouldVisitNames= true;
shouldVisitDeclarations= true;
@ -88,6 +99,22 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener {
shouldVisitImplicitNames = requirements.visitImplicitNames;
shouldVisitImplicitNameAlternates = requirements.visitImplicitNames;
}
private PositionCollectorRequirements getRequirements() {
PositionCollectorRequirements result = new PositionCollectorRequirements();
for (int i = 0; i < fHighlightings.length; i++) {
SemanticHighlighting sh = fHighlightings[i];
if (fHighlightingStyles[i].isEnabled()) {
if (sh.requiresImplicitNames()) {
result.visitImplicitNames = true;
}
if (sh.requiresExpressions()) {
result.visitExpressions = true;
}
}
}
return result;
}
@Override
public int visit(IASTTranslationUnit tu) {
@ -193,17 +220,17 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener {
private boolean visitNode(IASTNode node) {
boolean consumed= false;
fToken.update(node);
for (int i= 0, n= fJobSemanticHighlightings.length; i < n; ++i) {
SemanticHighlighting semanticHighlighting= fJobSemanticHighlightings[i];
for (int i= 0, n= fHighlightings.length; i < n; ++i) {
SemanticHighlighting semanticHighlighting= fHighlightings[i];
// If the semantic highlighting doesn't color expressions, don't bother
// passing it one to begin with.
if (node instanceof IASTExpression && !semanticHighlighting.requiresExpressions()) {
continue;
}
if (fJobHighlightings[i].isEnabled() && semanticHighlighting.consumes(fToken)) {
if (fHighlightingStyles[i].isEnabled() && semanticHighlighting.consumes(fToken)) {
IASTNodeLocation location = getLocationToHighlight(node);
if (location != null) {
highlightLocation(location, fJobHighlightings[i]);
highlightLocation(location, fHighlightingStyles[i]);
}
consumed= true;
break;
@ -269,9 +296,18 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener {
*
* @param offset The range offset
* @param length The range length
* @param highlighting The highlighting
* @param highlightingStyle The highlighting style
*/
private void addPosition(int offset, int length, HighlightingStyle highlightingStyle) {
protected abstract void addPosition(int offset, int length, HighlightingStyle highlightingStyle);
}
private class PositionCollector extends AbstractPositionCollector {
public PositionCollector() {
super(fJobSemanticHighlightings, fJobHighlightings);
}
@Override
protected void addPosition(int offset, int length, HighlightingStyle highlightingStyle) {
boolean isExisting= false;
// TODO: use binary search
for (int i= 0, n= fRemovedPositions.size(); i < n; i++) {
@ -291,7 +327,6 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener {
fAddedPositions.add(position);
}
}
}
/** The C editor this semantic highlighting reconciler is installed on */
@ -364,7 +399,7 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener {
if (ast == null || fJobPresenter.isCanceled())
return;
PositionCollector collector= new PositionCollector(getRequirements());
PositionCollector collector= new PositionCollector();
startReconcilingPositions();
@ -389,22 +424,6 @@ public class SemanticHighlightingReconciler implements ICReconcilingListener {
}
}
private PositionCollectorRequirements getRequirements() {
PositionCollectorRequirements result = new PositionCollectorRequirements();
for (int i = 0; i < fSemanticHighlightings.length; i++) {
SemanticHighlighting sh = fSemanticHighlightings[i];
if (fHighlightings[i].isEnabled()) {
if (sh.requiresImplicitNames()) {
result.visitImplicitNames = true;
}
if (sh.requiresExpressions()) {
result.visitExpressions = true;
}
}
}
return result;
}
/**
* Starts reconciling positions.
*/

View file

@ -2068,6 +2068,14 @@ public class SemanticHighlightings {
return false;
}
/**
* Returns whether the given highlighting is the highlighting for external SDK functions.
* For the previewer widget in the preferences, this is swapped out with a different implementation.
*/
public static boolean isExternalSDKHighlighting(SemanticHighlighting highlighting) {
return highlighting instanceof ExternalSDKHighlighting;
}
/**
* Do not instantiate
*/

View file

@ -24,7 +24,6 @@ import org.eclipse.jface.preference.ColorSelector;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.viewers.IColorProvider;
@ -71,7 +70,6 @@ import org.eclipse.cdt.ui.text.IColorManager;
import org.eclipse.cdt.internal.ui.editor.CSourceViewer;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlighting;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightedRange;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingWithOwnPreference;
import org.eclipse.cdt.internal.ui.editor.SemanticHighlightings;
import org.eclipse.cdt.internal.ui.text.SimpleCSourceViewerConfiguration;
@ -362,6 +360,11 @@ class CEditorColoringConfigurationBlock extends AbstractConfigurationBlock {
* The font metrics.
*/
private FontMetrics fFontMetrics;
/**
* Library declarations needed to make the preview code valid without actually
* resolving #includes in it.
*/
private String fPreviewerLibraryDecls;
public CEditorColoringConfigurationBlock(OverlayPreferenceStore store) {
super(store);
@ -822,6 +825,8 @@ class CEditorColoringConfigurationBlock extends AbstractConfigurationBlock {
CSourcePreviewerUpdater.registerPreviewer(fPreviewViewer, configuration, store);
fPreviewViewer.setEditable(false);
String libraryDecls= loadPreviewContentFromFile("ColorSettingPreviewLibraryDecls.txt"); //$NON-NLS-1$
fPreviewerLibraryDecls= libraryDecls;
String content= loadPreviewContentFromFile("ColorSettingPreviewCode.txt"); //$NON-NLS-1$
IDocument document= new Document(content);
CUIPlugin.getDefault().getTextTools().setupCDocumentPartitioner(document, ICPartitions.C_PARTITIONING, null);
@ -859,7 +864,8 @@ class CEditorColoringConfigurationBlock extends AbstractConfigurationBlock {
private void installSemanticHighlighting() {
if (fSemanticHighlightingManager == null) {
fSemanticHighlightingManager= new SemanticHighlightingManager();
fSemanticHighlightingManager.install(fPreviewViewer, fColorManager, getPreferenceStore(), createPreviewerRanges());
fSemanticHighlightingManager.install(fPreviewViewer, fColorManager, getPreferenceStore(),
fPreviewerLibraryDecls);
}
}
@ -873,73 +879,6 @@ class CEditorColoringConfigurationBlock extends AbstractConfigurationBlock {
}
}
/**
* Create the hard coded previewer ranges. Must be sorted by ascending offset.
*
* @return the hard coded previewer ranges
*/
private SemanticHighlightingManager.HighlightedRange[][] createPreviewerRanges() {
// TODO(nathanridge): It would be nicer to actually parse the previewed code.
return new SemanticHighlightingManager.HighlightedRange[][] {
{ createHighlightedRange( 2, 8, 5, SemanticHighlightings.MACRO_DEFINITION) },
{ createHighlightedRange( 3, 16, 3, SemanticHighlightings.NAMESPACE) },
{ createHighlightedRange( 5, 21, 4, SemanticHighlightings.TYPEDEF) },
{ createHighlightedRange( 6, 11, 6, SemanticHighlightings.FUNCTION_DECLARATION), createHighlightedRange( 6, 11, 6, SemanticHighlightings.FUNCTION) },
{ createHighlightedRange( 6, 18, 4, SemanticHighlightings.TYPEDEF) },
{ createHighlightedRange( 6, 23, 9, SemanticHighlightings.PARAMETER_VARIABLE) },
{ createHighlightedRange( 7, 6, 9, SemanticHighlightings.PARAMETER_VARIABLE) },
{ createHighlightedRange( 7, 22, 7, SemanticHighlightings.EXTERNAL_SDK), createHighlightedRange( 7, 22, 7, SemanticHighlightings.FUNCTION) },
{ createHighlightedRange( 7, 30, 6, SemanticHighlightings.GLOBAL_VARIABLE) },
{ createHighlightedRange( 8, 2, 4, SemanticHighlightings.GLOBAL_VARIABLE) },
{ createHighlightedRange( 8, 7, 2, SemanticHighlightings.OVERLOADED_OPERATOR) },
{ createHighlightedRange( 9, 9, 9, SemanticHighlightings.PARAMETER_VARIABLE) },
{ createHighlightedRange(11, 5, 7, SemanticHighlightings.FUNCTION_DECLARATION), createHighlightedRange(11, 5, 7, SemanticHighlightings.FUNCTION) },
{ createHighlightedRange(12, 6, 7, SemanticHighlightings.CLASS) },
{ createHighlightedRange(14, 7, 6, SemanticHighlightings.ENUM) },
{ createHighlightedRange(14, 16, 4, SemanticHighlightings.ENUMERATOR) },
{ createHighlightedRange(14, 22, 3, SemanticHighlightings.ENUMERATOR) },
{ createHighlightedRange(14, 27, 3, SemanticHighlightings.ENUMERATOR) },
{ createHighlightedRange(15, 14, 11, SemanticHighlightings.STATIC_FIELD), createHighlightedRange(14, 14, 11, SemanticHighlightings.FIELD) },
{ createHighlightedRange(16, 6, 5, SemanticHighlightings.FIELD) },
{ createHighlightedRange(17, 10, 6, SemanticHighlightings.ENUM) },
{ createHighlightedRange(17, 17, 7, SemanticHighlightings.METHOD_DECLARATION), createHighlightedRange(16, 17, 7, SemanticHighlightings.METHOD) },
{ createHighlightedRange(18, 7, 6, SemanticHighlightings.METHOD_DECLARATION), createHighlightedRange(17, 7, 6, SemanticHighlightings.METHOD) },
{ createHighlightedRange(18, 14, 6, SemanticHighlightings.ENUM) },
{ createHighlightedRange(18, 21, 1, SemanticHighlightings.PARAMETER_VARIABLE) },
{ createHighlightedRange(19, 8, 5, SemanticHighlightings.LOCAL_VARIABLE_DECLARATION) },
{ createHighlightedRange(19, 20, 5, SemanticHighlightings.MACRO_REFERENCE) },
{ createHighlightedRange(20, 0, 5, SemanticHighlightings.LABEL) },
{ createHighlightedRange(20, 7, 6, SemanticHighlightings.FUNCTION) },
{ createHighlightedRange(20, 14, 5, SemanticHighlightings.LOCAL_VARIABLE) },
{ createHighlightedRange(21, 4, 7, SemanticHighlightings.METHOD) },
{ createHighlightedRange(22, 4, 12, SemanticHighlightings.STATIC_METHOD_INVOCATION), createHighlightedRange(21, 4, 12, SemanticHighlightings.METHOD) },
{ createHighlightedRange(23, 4, 7, SemanticHighlightings.PROBLEM) },
{ createHighlightedRange(24, 4, 7, SemanticHighlightings.FUNCTION) },
{ createHighlightedRange(24, 12, 5, SemanticHighlightings.VARIABLE_PASSED_BY_NONCONST_REF), createHighlightedRange(24, 12, 5, SemanticHighlightings.LOCAL_VARIABLE) },
{ createHighlightedRange(26, 14, 12, SemanticHighlightings.METHOD_DECLARATION), createHighlightedRange(24, 14, 12, SemanticHighlightings.METHOD) },
};
}
/**
* Create a highlighted range on the previewers document with the given line, column, length and key.
*
* @param line the line
* @param column the column
* @param length the length
* @param key the key
* @return the highlighted range
*/
private HighlightedRange createHighlightedRange(int line, int column, int length, String key) {
try {
IDocument document= fPreviewViewer.getDocument();
int offset= document.getLineOffset(line) + column;
return new HighlightedRange(offset, length, key);
} catch (BadLocationException x) {
CUIPlugin.log(x);
}
return null;
}
/**
* Returns the current highlighting color list item.
*

View file

@ -15,7 +15,7 @@ public:
enum Number { ZERO, ONE, TWO };
static char staticField;
int field;
virtual Number vmethod();
virtual Number vmethod() const;
void method(Number n) const {
int local= (int)MACRO('\0');
label: myfunc(local);

View file

@ -0,0 +1,9 @@
namespace std {
class ostream {
ostream& operator<<(const char*);
};
ostream cout;
}
struct FILE {};
FILE* stdout;
int fprintf(FILE*, const char*, ...);