1 19 20 package org.netbeans.modules.lexer.editorbridge; 21 22 import java.util.List ; 23 import java.util.Stack ; 24 import javax.swing.text.AttributeSet ; 25 import javax.swing.text.Document ; 26 import org.netbeans.api.lexer.TokenHierarchyEvent; 27 import org.netbeans.api.lexer.TokenHierarchyListener; 28 import org.netbeans.editor.DrawLayer; 29 import org.netbeans.editor.DrawContext; 30 import org.netbeans.editor.MarkFactory; 31 import org.netbeans.editor.Coloring; 32 import org.netbeans.api.lexer.TokenId; 33 import org.netbeans.api.lexer.Token; 34 import javax.swing.text.JTextComponent ; 35 import java.beans.PropertyChangeListener ; 36 import java.beans.PropertyChangeEvent ; 37 import java.util.Enumeration ; 38 import javax.swing.text.EditorKit ; 39 import org.netbeans.api.editor.mimelookup.MimeLookup; 40 import org.netbeans.api.editor.mimelookup.MimePath; 41 import org.netbeans.api.editor.settings.FontColorSettings; 42 import org.netbeans.api.lexer.Language; 43 import org.netbeans.api.lexer.LanguagePath; 44 import org.netbeans.api.lexer.TokenHierarchy; 45 import org.netbeans.api.lexer.TokenSequence; 46 import org.openide.util.Lookup; 47 48 final class LexerLayer extends DrawLayer.AbstractLayer { 49 50 public static final String NAME = "lexer-layer"; 52 public static final int VISIBILITY = 1050; 53 54 private static final Coloring NULL_COLORING = new Coloring(); 55 56 private final JTextComponent component; 57 58 private Listener listener; 59 60 private TokenHierarchy listenerHierarchy; 61 62 private Stack <TokenSequence> pastSequences; 63 private TokenSequence tokenSequence; 64 private boolean moveNext = true; 65 private boolean goToEmbed = true; 66 67 private int tokenEndOffset; 68 69 private Coloring coloring; 70 71 private boolean active; 72 73 74 public LexerLayer(JTextComponent component) { 75 super(NAME); 76 77 assert (component != null); 78 this.component = component; 79 80 Document doc = component.getDocument(); 81 component.addPropertyChangeListener( 82 new PropertyChangeListener () { 83 public void propertyChange(PropertyChangeEvent evt) { 84 if ("document".equals(evt.getPropertyName())) { if (listenerHierarchy != null) { 87 listenerHierarchy.removeTokenHierarchyListener(listener); 88 listenerHierarchy = null; 89 } 90 } 91 } 92 } 93 ); 94 } 95 96 public boolean extendsEOL() { 97 return true; 98 } 99 100 public void init(final DrawContext ctx) { 101 coloring = null; 102 tokenEndOffset = 0; 103 int startOffset = ctx.getStartOffset(); 104 105 TokenHierarchy hi = tokenHierarchy(); 106 active = (hi != null); 107 108 if (active) { 109 pastSequences = new Stack <TokenSequence>(); 110 tokenSequence = hi.tokenSequence(); 111 tokenSequence.move(startOffset); 112 if (tokenSequence.moveNext()) { 113 updateTokenEndOffsetAndColoring(startOffset); 114 } else { active = false; 116 } 117 } 118 } 119 120 public boolean isActive(DrawContext ctx, MarkFactory.DrawMark mark) { 121 return active; 122 } 123 124 public void updateContext(DrawContext ctx) { 125 if (coloring != null) { 126 coloring.apply(ctx); 127 } 128 129 int fragEndOffset = ctx.getFragmentOffset() + ctx.getFragmentLength(); 130 while (active && fragEndOffset >= tokenEndOffset) { 131 if (!moveNext || tokenSequence.moveNext()) { 132 updateTokenEndOffsetAndColoring(-1); 133 } else { 134 if (pastSequences.isEmpty()) { 135 active = false; 136 } else { 137 tokenSequence = pastSequences.pop(); 138 if ((tokenSequence.offset() + tokenSequence.token().length()) > tokenEndOffset) { 139 goToEmbed = false; 141 moveNext = false; 142 } 143 } 144 } 145 } 146 } 147 148 private TokenHierarchy tokenHierarchy() { 149 TokenHierarchy hi = TokenHierarchy.get(component.getDocument()); 150 if (hi != null && listenerHierarchy == null) { 152 if (listener == null) { 153 listener = new Listener(); 154 } 155 listenerHierarchy = hi; 156 listenerHierarchy.addTokenHierarchyListener(listener); 157 } 158 return hi; 159 } 160 161 private void updateTokenEndOffsetAndColoring(int offset) { 162 int origOffset = tokenSequence.offset(); 163 boolean isInside = tokenSequence.offset() < offset; 164 Token origToken = tokenSequence.token(); 165 LanguagePath origPath = tokenSequence.languagePath(); 166 boolean wasEmbedd = false; 167 168 while (origOffset == tokenSequence.offset() && goToEmbed) { 169 TokenSequence embed = tokenSequence.embedded(); 170 171 if (embed != null) { 172 wasEmbedd = true; 173 if (offset == (-1)) { 174 if (embed.moveNext()) { 175 pastSequences.push(tokenSequence); 176 tokenSequence = embed; 177 } else { 178 break; 179 } 180 } else { 181 embed.move(offset); 182 if (embed.moveNext()) { 183 pastSequences.push(tokenSequence); 184 tokenSequence = embed; 185 } else { 186 break; 187 } 188 } 189 } else { 190 break; 191 } 192 } 193 194 goToEmbed = true; 195 196 Token token; 197 LanguagePath path; 198 199 if (origOffset == tokenSequence.offset() || isInside) { 200 token = tokenSequence.token(); 201 tokenEndOffset = tokenSequence.offset() + token.length(); 202 path = tokenSequence.languagePath(); 203 moveNext = true; 204 } else { 205 token = origToken; 206 tokenEndOffset = tokenSequence.offset(); 207 path = origPath; 208 moveNext = !wasEmbedd; 209 } 210 211 setNextActivityChangeOffset(tokenEndOffset); 212 coloring = findColoring(token.id(), path); 213 } 214 215 private Coloring findColoring(TokenId tokenId, LanguagePath languagePath) { 216 MimePath mimePath = languagePathToMimePathHack(languagePath); 217 Lookup lookup = MimeLookup.getLookup(mimePath); 218 FontColorSettings fcs = lookup.lookup(FontColorSettings.class); 219 AttributeSet attribs = findFontAndColors(fcs, tokenId, languagePath.innerLanguage()); 220 221 223 return attribs == null ? NULL_COLORING : Coloring.fromAttributeSet(attribs); 224 } 225 226 private void dumpAttribs(AttributeSet attribs, String token, String lang) { 227 231 StringBuilder sb = new StringBuilder (); 232 sb.append("Attribs for token '"); sb.append(token); 234 sb.append("', language '"); sb.append(lang); 236 sb.append("' = {"); 238 if (attribs != null) { 239 Enumeration <?> keys = attribs.getAttributeNames(); 240 while (keys.hasMoreElements()) { 241 Object key = keys.nextElement(); 242 Object value = attribs.getAttribute(key); 243 244 sb.append("'" + key + "' = '" + value + "'"); if (keys.hasMoreElements()) { 246 sb.append(", "); } 248 } 249 } 250 251 sb.append("} LexerLayer.this = "); sb.append(this.toString()); 253 254 System.out.println(sb.toString()); 255 } 256 257 private MimePath languagePathToMimePathHack(LanguagePath languagePath) { 281 String mimeType = getMimeType(component); 282 if (languagePath.size() == 1) { 283 return MimePath.parse(mimeType); 284 } else if (languagePath.size() > 1) { 285 return MimePath.parse(mimeType + "/" + languagePath.subPath(1).mimePath()); } else { 287 throw new IllegalStateException ("LanguagePath should not be empty."); } 289 } 290 291 private String getMimeType(JTextComponent c) { 292 Object mimeTypeProp = c.getDocument().getProperty("mimeType"); if (mimeTypeProp instanceof String ) { 294 return (String ) mimeTypeProp; 295 } else { 296 return c.getUI().getEditorKit(c).getContentType(); 297 } 298 } 299 300 private String updateColoringName(String coloringName) { 301 EditorKit kit = component.getUI().getEditorKit(component); 302 if (kit instanceof LexerEditorKit) { 303 String updatedName = ((LexerEditorKit)kit).updateColoringName(coloringName); 304 if (updatedName != null) { 305 coloringName = updatedName; 306 } 307 } 308 return coloringName; 309 } 310 311 private AttributeSet findFontAndColors(FontColorSettings fcs, TokenId tokenId, Language lang) { 312 String name = tokenId.name(); 314 AttributeSet attribs = fcs.getTokenFontColors(updateColoringName(name)); 315 316 if (attribs == null) { 318 String primary = tokenId.primaryCategory(); 319 if (primary != null) { 320 attribs = fcs.getTokenFontColors(updateColoringName(primary)); 321 } 322 } 323 324 if (attribs == null) { 326 @SuppressWarnings ("unchecked") List <String > categories = ((Language<TokenId>)lang).nonPrimaryTokenCategories(tokenId); 328 for(String c : categories) { 329 attribs = fcs.getTokenFontColors(updateColoringName(c)); 330 if (attribs != null) { 331 break; 332 } 333 } 334 } 335 336 return attribs; 337 } 338 339 private final class Listener implements TokenHierarchyListener { 340 341 public void tokenHierarchyChanged(TokenHierarchyEvent evt) { 342 javax.swing.plaf.TextUI ui = (javax.swing.plaf.TextUI )component.getUI(); 343 int startRepaintOffset = evt.affectedStartOffset(); 344 int endRepaintOffset = Math.max(evt.affectedEndOffset(), startRepaintOffset + 1); 345 ui.damageRange(component, startRepaintOffset, endRepaintOffset); 346 } 347 348 } 350 } 351 352 | Popular Tags |