1 19 20 package org.netbeans.lib.editor.codetemplates; 21 22 import java.awt.event.ActionEvent ; 23 import java.awt.event.ActionListener ; 24 import java.awt.event.KeyEvent ; 25 import java.awt.event.KeyListener ; 26 import java.beans.PropertyChangeEvent ; 27 import java.beans.PropertyChangeListener ; 28 import java.util.Collections ; 29 import java.util.List ; 30 import java.util.logging.Level ; 31 import java.util.logging.Logger ; 32 import javax.swing.KeyStroke ; 33 import javax.swing.Timer ; 34 import javax.swing.event.CaretEvent ; 35 import javax.swing.event.CaretListener ; 36 import javax.swing.event.DocumentEvent ; 37 import javax.swing.event.DocumentListener ; 38 import javax.swing.text.BadLocationException ; 39 import javax.swing.text.Document ; 40 import javax.swing.text.JTextComponent ; 41 import javax.swing.text.Position ; 42 import org.netbeans.editor.Acceptor; 43 import org.netbeans.editor.AcceptorFactory; 44 import org.netbeans.editor.BaseDocument; 45 import org.netbeans.editor.Settings; 46 import org.netbeans.editor.SettingsChangeEvent; 47 import org.netbeans.editor.SettingsChangeListener; 48 import org.netbeans.editor.SettingsNames; 49 import org.netbeans.editor.SettingsUtil; 50 import org.netbeans.lib.editor.util.swing.DocumentUtilities; 51 import org.netbeans.spi.editor.hints.ErrorDescription; 52 import org.netbeans.spi.editor.hints.ErrorDescriptionFactory; 53 import org.netbeans.spi.editor.hints.HintsController; 54 import org.netbeans.spi.editor.hints.Severity; 55 import org.openide.ErrorManager; 56 import org.openide.util.NbBundle; 57 58 59 66 67 final class AbbrevDetection implements SettingsChangeListener, DocumentListener , 68 PropertyChangeListener , KeyListener , CaretListener { 69 70 76 private static final String ABBREV_IGNORE_MODIFICATION_DOC_PROPERTY 77 = "abbrev-ignore-modification"; 79 private static final String SURROUND_WITH = NbBundle.getMessage(SurroundWithFix.class, "TXT_SurroundWithHint_Label"); private static final int SURROUND_WITH_DELAY = 250; 81 82 private static final AbbrevExpander[] abbrevExpanders = { new CodeTemplateAbbrevExpander() }; 83 84 public static AbbrevDetection get(JTextComponent component) { 85 AbbrevDetection ad = (AbbrevDetection)component.getClientProperty(AbbrevDetection.class); 86 if (ad == null) { 87 ad = new AbbrevDetection(component); 88 component.putClientProperty(AbbrevDetection.class, ad); 89 } 90 return ad; 91 } 92 93 private JTextComponent component; 94 95 96 private Document doc; 97 98 101 private Position abbrevEndPosition; 102 103 106 private StringBuffer abbrevChars = new StringBuffer (); 107 108 109 private Acceptor expandAcceptor; 110 111 112 private Acceptor resetAcceptor; 113 114 private ErrorDescription errorDescription = null; 115 private List surrounsWithFixes = null; 116 private Timer surroundsWithTimer; 117 118 private AbbrevDetection(JTextComponent component) { 119 this.component = component; 120 component.addCaretListener(this); 121 doc = component.getDocument(); 122 if (doc != null) { 123 doc.addDocumentListener(this); 124 } 125 126 Settings.addSettingsChangeListener(this); 127 128 settingsChange(null); 130 131 component.addKeyListener(this); 132 component.addPropertyChangeListener(this); 133 134 surroundsWithTimer = new Timer (0, new ActionListener () { 135 public void actionPerformed(ActionEvent e) { 136 showSurroundWithHint(); 137 } 138 }); 139 surroundsWithTimer.setRepeats(false); 140 } 141 142 public void settingsChange(SettingsChangeEvent evt) { 143 Document d = doc; 144 Class kitClass = (d instanceof BaseDocument) 145 ? ((BaseDocument)d).getKitClass() 146 : org.netbeans.editor.BaseKit.class; 147 148 expandAcceptor = SettingsUtil.getAcceptor(kitClass, SettingsNames.ABBREV_EXPAND_ACCEPTOR, AcceptorFactory.FALSE); 149 resetAcceptor = SettingsUtil.getAcceptor(kitClass, SettingsNames.ABBREV_RESET_ACCEPTOR, AcceptorFactory.TRUE); 150 } 151 152 public void insertUpdate(DocumentEvent evt) { 153 if (!isIgnoreModification()) { 154 if (DocumentUtilities.isTypingModification(evt) && !isAbbrevDisabled()) { 155 int offset = evt.getOffset(); 156 int length = evt.getLength(); 157 appendTypedText(offset, length); 158 } else { resetAbbrevChars(); 160 } 161 } 162 } 163 164 public void removeUpdate(DocumentEvent evt) { 165 if (!isIgnoreModification()) { 166 if (DocumentUtilities.isTypingModification(evt) && !isAbbrevDisabled()) { 167 int offset = evt.getOffset(); 168 int length = evt.getLength(); 169 removeAbbrevText(offset, length); 170 } else { resetAbbrevChars(); 172 } 173 } 174 } 175 176 public void changedUpdate(DocumentEvent evt) { 177 } 178 179 public void propertyChange(PropertyChangeEvent evt) { 180 if ("document".equals(evt.getPropertyName())) { 181 if (doc != null) { 182 doc.removeDocumentListener(this); 183 } 184 185 doc = component.getDocument(); 186 if (doc != null) { 187 doc.addDocumentListener(this); 188 } 189 190 settingsChange(null); 191 } 192 } 193 194 public void keyPressed(KeyEvent evt) { 195 checkExpansionKeystroke(evt); 196 } 197 198 public void keyReleased(KeyEvent evt) { 199 checkExpansionKeystroke(evt); 200 } 201 202 public void keyTyped(KeyEvent evt) { 203 checkExpansionKeystroke(evt); 204 } 205 206 public void caretUpdate(CaretEvent evt) { 207 if (evt.getDot() != evt.getMark()) { 208 surroundsWithTimer.setInitialDelay(SURROUND_WITH_DELAY); 209 surroundsWithTimer.restart(); 210 } else { 211 surroundsWithTimer.stop(); 212 hideSurroundWithHint(); 213 } 214 } 215 216 private boolean isIgnoreModification() { 217 return Boolean.TRUE.equals(doc.getProperty(ABBREV_IGNORE_MODIFICATION_DOC_PROPERTY)); 218 } 219 220 private boolean isAbbrevDisabled() { 221 return org.netbeans.editor.Abbrev.isAbbrevDisabled(component); 222 } 223 224 private void checkExpansionKeystroke(KeyEvent evt) { 225 KeyStroke expandKeyStroke = AbbrevSettings.getDefaultExpansionKeyStroke(); 226 if (abbrevEndPosition != null && component != null 227 && component.getCaretPosition() == abbrevEndPosition.getOffset() 228 ) { 229 Document doc = component.getDocument(); 230 String mimeType = (String )doc.getProperty("mimeType"); if (mimeType != null) { 232 expandKeyStroke = AbbrevSettings.get(mimeType).getExpandKeyStroke(); 233 } 234 235 if (expandKeyStroke.equals(KeyStroke.getKeyStrokeForEvent(evt))) { 236 if (expand()) { 237 evt.consume(); 238 } 239 } 240 } 241 } 242 243 246 private CharSequence getAbbrevText() { 247 return abbrevChars; 248 } 249 250 253 private void resetAbbrevChars() { 254 synchronized(abbrevChars) { 255 abbrevChars.setLength(0); 256 abbrevEndPosition = null; 257 } 258 } 259 260 private void appendTypedText(int offset, int insertLength) { 261 if (abbrevEndPosition == null 262 || offset + insertLength != abbrevEndPosition.getOffset() 263 ) { 264 resetAbbrevChars(); 266 } 267 268 if (abbrevEndPosition == null) { try { 270 if (offset == 0 274 || resetAcceptor.accept(DocumentUtilities.getText(doc, offset - 1, 1).charAt(0)) 275 ) { 276 abbrevEndPosition = doc.createPosition(offset + insertLength); 277 } 278 } catch (BadLocationException e) { 279 ErrorManager.getDefault().notify(e); 280 } 281 } 282 283 if (abbrevEndPosition != null) { 284 try { 285 String typedText = doc.getText(offset, insertLength); boolean textAccepted = true; 287 for (int i = typedText.length() - 1; i >= 0; i--) { 288 if (resetAcceptor.accept(typedText.charAt(i))) { 289 textAccepted = false; 294 break; 295 } 296 } 297 298 if (textAccepted) { 299 abbrevChars.append(typedText); 300 } else { 302 resetAbbrevChars(); 303 } 304 305 } catch (BadLocationException e) { 306 ErrorManager.getDefault().notify(e); 307 resetAbbrevChars(); 308 } 309 } 310 } 311 312 private void removeAbbrevText(int offset, int removeLength) { 313 synchronized(abbrevChars) { 314 if (abbrevEndPosition != null) { 315 if (offset == abbrevEndPosition.getOffset() 317 && abbrevChars.length() >= removeLength 318 ) { abbrevChars.setLength(abbrevChars.length() - removeLength); 320 321 } else { 322 resetAbbrevChars(); 323 } 324 } 325 } 326 } 327 328 public boolean expand() { 329 CharSequence abbrevText = getAbbrevText(); 330 int abbrevEndOffset = abbrevEndPosition.getOffset(); 331 for (int i = 0; i < abbrevExpanders.length; i++) { 332 if (abbrevExpanders[i].expand(component, 333 abbrevEndOffset - abbrevText.length(), abbrevText) 334 ) { 335 resetAbbrevChars(); 336 return true; 337 } 338 } 339 return false; 340 } 341 342 private void showSurroundWithHint() { 343 surrounsWithFixes = SurroundWithFix.getFixes(component); 344 if (!surrounsWithFixes.isEmpty()) { 345 try { 346 Position pos = doc.createPosition(component.getCaretPosition()); 347 errorDescription = ErrorDescriptionFactory.createErrorDescription(Severity.HINT, SURROUND_WITH, surrounsWithFixes, doc, pos, pos); 348 HintsController.setErrors(doc, SURROUND_WITH, Collections.singleton(errorDescription)); 349 } catch (BadLocationException ble) { 350 Logger.getLogger("global").log(Level.WARNING, ble.getMessage(), ble); 351 } 352 } else { 353 hideSurroundWithHint(); 354 } 355 } 356 357 private void hideSurroundWithHint() { 358 if (surrounsWithFixes != null) 359 surrounsWithFixes = null; 360 if (errorDescription != null) { 361 errorDescription = null; 362 HintsController.setErrors(doc, SURROUND_WITH, Collections.emptySet()); 363 } 364 } 365 } 366 | Popular Tags |