001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.cnd.highlight.semantic;
042:
043: import java.util.ArrayList;
044: import java.util.List;
045: import javax.swing.text.AttributeSet;
046: import javax.swing.text.Document;
047: import javax.swing.text.StyleConstants;
048: import org.netbeans.api.editor.settings.AttributesUtilities;
049: import org.netbeans.api.editor.settings.EditorStyleConstants;
050: import org.netbeans.api.editor.settings.FontColorSettings;
051: import org.netbeans.editor.BaseDocument;
052: import org.netbeans.modules.cnd.api.model.CsmFile;
053: import org.netbeans.modules.cnd.api.model.CsmFunction;
054: import org.netbeans.modules.cnd.api.model.CsmFunctionDefinition;
055: import org.netbeans.modules.cnd.api.model.CsmMacro;
056: import org.netbeans.modules.cnd.api.model.CsmObject;
057: import org.netbeans.modules.cnd.api.model.CsmOffsetable;
058: import org.netbeans.modules.cnd.api.model.CsmOffsetableDeclaration;
059: import org.netbeans.modules.cnd.api.model.services.CsmFileInfoQuery;
060: import org.netbeans.modules.cnd.api.model.services.CsmFileReferences;
061: import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities;
062: import org.netbeans.modules.cnd.api.model.xref.CsmReference;
063: import org.netbeans.modules.cnd.highlight.semantic.options.SemanticHighlightingOptions;
064: import org.netbeans.modules.cnd.model.tasks.CsmFileTaskFactory.PhaseRunner;
065: import org.netbeans.modules.cnd.modelutil.CsmUtilities;
066: import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
067:
068: /**
069: * Semantic C/C++ code highlighter responsible for "graying out"
070: * inactive code due to preprocessor definitions and highlighting of unobvious
071: * language elements.
072: *
073: * @author Sergey Grinev
074: */
075: public class SemanticHighlighter extends HighlighterBase {
076:
077: private final static String COLORS_INACTIVE = "cc-highlighting-inactive"; // NOI18N
078: private final static String COLORS_MACRO = "cc-highlighting-macros-user"; // NOI18N
079: private final static String COLORS_SYSMACRO = "cc-highlighting-macros-system"; // NOI18N
080: private final static String COLORS_FIELDS = "cc-highlighting-class-fields"; // NOI18N
081: private AttributeSet inactiveColors;
082: private AttributeSet macroColors; //= AttributesUtilities.createImmutable(StyleConstants.Foreground, new Color(0, 105, 0));
083: private AttributeSet sysMacroColors; //= AttributesUtilities.createImmutable(StyleConstants.Foreground, new Color(150, 105, 0));
084: private AttributeSet fieldsColors; //= AttributesUtilities.createImmutable(StyleConstants.Foreground, new Color(175, 175, 0));
085: private AttributeSet functionsColors; // = AttributesUtilities.createImmutable(StyleConstants.Bold, Boolean.TRUE);
086: private final AttributeSet cleanUp = AttributesUtilities
087: .createImmutable(StyleConstants.Underline, null,
088: StyleConstants.StrikeThrough, null,
089: StyleConstants.Background, null,
090: EditorStyleConstants.WaveUnderlineColor, null);
091:
092: public SemanticHighlighter(Document doc) {
093: super (doc);
094: }
095:
096: protected void initFontColors(FontColorSettings fcs) {
097: inactiveColors = AttributesUtilities.createComposite(fcs
098: .getTokenFontColors(COLORS_INACTIVE), cleanUp);
099: macroColors = AttributesUtilities.createComposite(fcs
100: .getTokenFontColors(COLORS_MACRO), cleanUp);
101: sysMacroColors = AttributesUtilities.createComposite(fcs
102: .getTokenFontColors(COLORS_SYSMACRO), cleanUp);
103: if (SemanticHighlightingOptions.SEMANTIC_ADVANCED) {
104: fieldsColors = AttributesUtilities.createComposite(fcs
105: .getTokenFontColors(COLORS_FIELDS), cleanUp);
106: functionsColors = AttributesUtilities.createImmutable(
107: StyleConstants.Bold, Boolean.TRUE);
108: }
109: }
110:
111: public static OffsetsBag getHighlightsBag(Document doc) {
112: if (doc == null) {
113: return null;
114: }
115:
116: OffsetsBag bag = (OffsetsBag) doc
117: .getProperty(SemanticHighlighter.class);
118:
119: if (bag == null) {
120: doc.putProperty(SemanticHighlighter.class,
121: bag = new OffsetsBag(doc));
122: }
123:
124: return bag;
125: }
126:
127: private void update() {
128: BaseDocument doc = getDocument();
129: if (doc != null) {
130: OffsetsBag newBag = new OffsetsBag(doc);
131: newBag.clear();
132: final CsmFile csmFile = CsmUtilities.getCsmFile(doc, false);
133: if (csmFile != null && csmFile.isParsed()) {
134: for (CsmOffsetable block : getInactiveCodeBlocks(csmFile)) {
135: newBag.addHighlight(block.getStartOffset(), block
136: .getEndOffset(), inactiveColors);
137: }
138:
139: // All highlighting would be stationed here till we'll have general csmfileAction infrastructure
140: if (SemanticHighlightingOptions.getEnableMacros()) {
141: boolean diffSystem = SemanticHighlightingOptions
142: .getDifferSystemMacros();
143: for (CsmReference block : getMacroBlocks(csmFile)) {
144: CsmMacro macro = (CsmMacro) block
145: .getReferencedObject();
146: newBag
147: .addHighlight(
148: block.getStartOffset(),
149: block.getEndOffset(),
150: !diffSystem || macro == null
151: || !macro.isSystem() ? macroColors
152: : sysMacroColors);
153: }
154: }
155:
156: if (SemanticHighlightingOptions.getEnableClassFields()) {
157: for (CsmOffsetable block : getFieldsBlocks(csmFile)) {
158: newBag.addHighlight(block.getStartOffset(),
159: block.getEndOffset(), fieldsColors);
160: }
161: }
162:
163: if (SemanticHighlightingOptions
164: .getEnableFunctionNames()) {
165: for (CsmOffsetable block : getFunctionNames(csmFile)) {
166: newBag.addHighlight(block.getStartOffset(),
167: block.getEndOffset(), functionsColors);
168: }
169: }
170: }
171: getHighlightsBag(doc).setHighlights(newBag);
172: }
173: }
174:
175: /*package*/static List<CsmOffsetable> getInactiveCodeBlocks(
176: CsmFile file) {
177: return CsmFileInfoQuery.getDefault().getUnusedCodeBlocks(file);
178: }
179:
180: /*package*/static List<CsmReference> getMacroBlocks(CsmFile file) {
181: return CsmFileInfoQuery.getDefault().getMacroUsages(file);
182: }
183:
184: /*package*/static List<? extends CsmOffsetable> getFieldsBlocks(
185: CsmFile file) {
186: return getBlocksFromReferences(file, new Validator() {
187:
188: public boolean validate(CsmReference ref) {
189: CsmObject obj = ref.getReferencedObject();
190: return obj != null && CsmKindUtilities.isField(obj);
191: }
192: });
193: }
194:
195: /*package*/static List<CsmReference> getFunctionNames(
196: final CsmFile csmFile) {
197: return getBlocksFromReferences(csmFile, new Validator() {
198:
199: public boolean validate(CsmReference ref) {
200: CsmObject csmObject = ref.getReferencedObject();
201: if (CsmKindUtilities.isFunctionDeclaration(csmObject)) {
202: // check if we are in the function declaration
203: CsmOffsetableDeclaration decl = (CsmOffsetableDeclaration) csmObject;
204: if (decl.getContainingFile().equals(csmFile)
205: && decl.getStartOffset() <= ref
206: .getStartOffset()
207: && decl.getEndOffset() >= ref
208: .getEndOffset()) {
209: return true;
210: }
211: // check if we are in function definition name => go to declaration
212: // else it is more useful to jump to definition of function
213: CsmFunctionDefinition definition = ((CsmFunction) csmObject)
214: .getDefinition();
215: if (definition != null) {
216: if (csmFile.equals(definition
217: .getContainingFile())
218: && definition.getStartOffset() <= ref
219: .getStartOffset()
220: && ref.getStartOffset() <= definition
221: .getBody().getStartOffset()) {
222: // it is ok to jump to declaration
223: return true;
224: }
225: }
226: } else if (CsmKindUtilities
227: .isFunctionDefinition(csmObject)) {
228: CsmFunctionDefinition definition = (CsmFunctionDefinition) csmObject;
229: if (csmFile.equals(definition.getContainingFile())
230: && definition.getStartOffset() <= ref
231: .getStartOffset()
232: && ref.getStartOffset() <= definition
233: .getBody().getStartOffset()) {
234: // it is ok to jump to declaration
235: return true;
236: }
237: }
238: return false;
239: }
240: });
241: }
242:
243: private static List<CsmReference> getBlocksFromReferences(
244: CsmFile file, final Validator validator) {
245: final List<CsmReference> out = new ArrayList<CsmReference>();
246: CsmFileReferences.getDefault().accept(file,
247: new CsmFileReferences.Visitor() {
248:
249: public void visit(CsmReference ref) {
250: if (validator.validate(ref)) {
251: out.add(ref);
252: }
253: }
254: });
255: return out;
256: }
257:
258: private interface Validator {
259:
260: boolean validate(CsmReference ref);
261: }
262:
263: // PhaseRunner
264: public void run(Phase phase) {
265: if (phase == Phase.PARSED || phase == Phase.INIT) {
266: try {
267: update();
268: } catch (AssertionError ex) {
269: ex.printStackTrace();
270: } catch (Exception ex) {
271: ex.printStackTrace();
272: }
273: } else if (phase == Phase.CLEANUP) {
274: BaseDocument doc = getDocument();
275: if (doc != null) {
276: //System.err.println("cleanAfterYourself");
277: getHighlightsBag(doc).clear();
278: }
279: }
280: }
281:
282: public boolean isValid() {
283: return true;
284: }
285: }
|