1 19 package org.netbeans.modules.retouche.editor.completion; 20 21 import java.io.IOException ; 22 import java.util.ArrayList ; 23 import java.util.Collection ; 24 import java.util.Iterator ; 25 import java.util.List ; 26 import javax.swing.JToolTip ; 27 import javax.swing.text.BadLocationException ; 28 import javax.swing.text.Document ; 29 import javax.swing.text.JTextComponent ; 30 import org.netbeans.api.editor.completion.Completion; 31 import org.netbeans.api.gsf.CompletionProposal; 32 import org.netbeans.api.gsf.Completable; 33 import org.netbeans.api.gsf.Parser; 34 import org.netbeans.api.gsf.CancellableTask; 35 import org.netbeans.api.gsf.Element; 36 import org.netbeans.api.gsf.ElementHandle; 37 import org.netbeans.api.gsf.ElementKind; 38 import org.netbeans.api.lexer.TokenHierarchy; 39 import org.netbeans.api.lexer.TokenId; 40 import org.netbeans.api.lexer.TokenSequence; 41 import org.netbeans.api.retouche.source.CompilationController; 42 import org.netbeans.api.retouche.source.CompilationInfo; 43 import org.netbeans.api.retouche.source.Phase; 44 import org.netbeans.api.retouche.source.Source; 45 import org.netbeans.editor.BaseDocument; 46 import org.netbeans.editor.Registry; 47 import org.netbeans.modules.gsf.GsfHtmlFormatter; 48 import org.netbeans.modules.gsf.Language; 49 import org.netbeans.spi.editor.completion.*; 50 import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery; 51 import org.netbeans.spi.editor.completion.support.AsyncCompletionTask; 52 import org.openide.ErrorManager; 53 import org.openide.util.Exceptions; 54 55 56 62 public class GsfCompletionProvider implements CompletionProvider { 63 private static boolean caseSensitive = true; 65 66 public int getAutoQueryTypes(JTextComponent component, String typedText) { 67 75 return 0; 76 } 77 78 public static boolean isJavaContext(final JTextComponent component, final int offset) { 80 org.netbeans.api.lexer.Language language = (org.netbeans.api.lexer.Language)component.getDocument().getProperty(org.netbeans.api.lexer.Language.class); 81 if (language == null) { 82 return true; 83 } 84 TokenSequence ts = TokenHierarchy.get(component.getDocument()).tokenSequence(); 85 86 if (ts == null) 87 return false; 88 if (!ts.moveNext() || ts.move(offset) == 0) 89 return true; 90 ts.moveNext(); 92 TokenId tokenId = ts.token().id(); 93 return !language.tokenCategoryMembers("comment").contains(tokenId); } 95 96 public static boolean startsWith(String theString, String prefix) { 97 if ((prefix == null) || (prefix.length() == 0)) { 98 return true; 99 } 100 101 return caseSensitive ? theString.startsWith(prefix) 102 : theString.toLowerCase().startsWith(prefix.toLowerCase()); 103 } 104 105 public CompletionTask createTask(int type, JTextComponent component) { 106 if (((type & COMPLETION_QUERY_TYPE) != 0) || (type == TOOLTIP_QUERY_TYPE) || 107 (type == DOCUMENTATION_QUERY_TYPE)) { 108 return new AsyncCompletionTask(new JavaCompletionQuery(type, 109 component.getSelectionStart()), component); 110 } 111 112 return null; 113 } 114 115 static CompletionTask createDocTask(Element element, CompilationInfo info) { JavaCompletionQuery query = new JavaCompletionQuery(DOCUMENTATION_QUERY_TYPE, -1); 117 118 Language language = info.getLanguage(); 119 Parser parser = language.getParser(); 120 if (parser != null) { 121 query.element = parser.createHandle(info, element); 122 } 123 124 return new AsyncCompletionTask(query, Registry.getMostActiveComponent()); 125 } 126 127 static final class JavaCompletionQuery extends AsyncCompletionQuery implements CancellableTask<CompilationController> { 128 private Collection <CompletionItem> results; 129 private JToolTip toolTip; 130 private CompletionDocumentation documentation; 131 private int anchorOffset; 132 private JTextComponent component; 133 private int queryType; 134 private int caretOffset; 135 private String filterPrefix; 136 private ElementHandle element; 137 138 139 private JavaCompletionQuery(int queryType, int caretOffset) { 140 this.queryType = queryType; 141 this.caretOffset = caretOffset; 142 } 143 144 @Override 145 protected void preQueryUpdate(JTextComponent component) { 146 int newCaretOffset = component.getSelectionStart(); 147 148 if (newCaretOffset >= caretOffset) { 149 try { 150 if (isJavaIdentifierPart(component.getDocument() 151 .getText(caretOffset, 152 newCaretOffset - caretOffset))) { 153 return; 154 } 155 } catch (BadLocationException e) { 156 } 157 } 158 159 Completion.get().hideCompletion(); 160 } 161 162 @Override 163 protected void prepareQuery(JTextComponent component) { 164 this.component = component; 165 } 166 167 @Override 168 protected void query(CompletionResultSet resultSet, Document doc, int caretOffset) { 169 try { 170 this.caretOffset = caretOffset; 171 172 if (isJavaContext(component, caretOffset)) { 173 results = null; 174 documentation = null; 175 toolTip = null; 176 anchorOffset = -1; 177 178 Source js = Source.forDocument(doc); 179 js.runUserActionTask(this, (queryType & COMPLETION_QUERY_TYPE) == 0); 180 181 if ((queryType & COMPLETION_QUERY_TYPE) != 0) { 182 if (results != null) { 183 resultSet.addAllItems(results); 184 } 185 } else if (queryType == TOOLTIP_QUERY_TYPE) { 186 if (toolTip != null) { 187 resultSet.setToolTip(toolTip); 188 } 189 } else if (queryType == DOCUMENTATION_QUERY_TYPE) { 190 if (doc != null) { 191 resultSet.setDocumentation(documentation); 192 } 193 } 194 195 if (anchorOffset > -1) { 196 resultSet.setAnchorOffset(anchorOffset); 197 } 198 } 199 } catch (IOException ioe) { 200 Exceptions.printStackTrace(ioe); 201 } finally { 202 resultSet.finish(); 203 } 204 } 205 206 @Override 207 protected boolean canFilter(JTextComponent component) { 208 filterPrefix = null; 209 210 int newOffset = component.getSelectionStart(); 211 212 if ((queryType & COMPLETION_QUERY_TYPE) != 0) { 213 if (newOffset >= caretOffset) { 214 if (anchorOffset > -1) { 215 try { 216 String prefix = 217 component.getDocument() 218 .getText(anchorOffset, newOffset - anchorOffset); 219 220 if (isJavaIdentifierPart(prefix)) { 221 filterPrefix = prefix; 222 } 223 } catch (BadLocationException e) { 224 } 225 } 226 } 227 228 return filterPrefix != null; 229 } else if (queryType == TOOLTIP_QUERY_TYPE) { 230 try { 231 if ((newOffset - caretOffset) > 0) { 232 filterPrefix = component.getDocument() 233 .getText(caretOffset, newOffset - caretOffset); 234 } else if ((newOffset - caretOffset) < 0) { 235 filterPrefix = component.getDocument() 236 .getText(newOffset, caretOffset - newOffset); 237 } 238 } catch (BadLocationException ex) { 239 } 240 241 return ((filterPrefix != null) && (filterPrefix.indexOf(',') == -1) && 242 (filterPrefix.indexOf('(') == -1) && (filterPrefix.indexOf(')') == -1)); } 244 245 return false; 246 } 247 248 @Override 249 protected void filter(CompletionResultSet resultSet) { 250 try { 251 if ((queryType & COMPLETION_QUERY_TYPE) != 0) { 252 if (results != null) { 253 resultSet.addAllItems(getFilteredData(results, filterPrefix)); 254 } 255 } else if (queryType == TOOLTIP_QUERY_TYPE) { 256 resultSet.setToolTip(toolTip); 257 } 258 259 resultSet.setAnchorOffset(anchorOffset); 260 } catch (Exception ex) { 261 Exceptions.printStackTrace(ex); 262 } 263 264 resultSet.finish(); 265 } 266 267 public void run(CompilationController controller) 268 throws Exception { 269 if ((queryType & COMPLETION_QUERY_TYPE) != 0) { 270 resolveCompletion(controller); 271 } else if (queryType == TOOLTIP_QUERY_TYPE) { 272 resolveToolTip(controller); 273 } else if (queryType == DOCUMENTATION_QUERY_TYPE) { 274 resolveDocumentation(controller); 275 } 276 } 277 278 public void cancel() { 279 } 280 281 private void resolveToolTip(final CompilationController controller) 282 throws IOException { 283 } 285 286 private void resolveDocumentation(CompilationController controller) 287 throws IOException { 288 controller.toPhase(Phase.RESOLVED); 289 if (element != null) { 295 documentation = GsfCompletionDoc.create(controller, element); 296 } 297 } 298 299 private void resolveCompletion(CompilationController controller) 301 throws IOException { 302 Env env = getCompletionEnvironment(controller, true); 303 int offset = env.getOffset(); 304 String prefix = env.getPrefix(); 305 results = new ArrayList <CompletionItem>(); 306 anchorOffset = env.getOffset() - ((prefix != null) ? prefix.length() : 0); 307 308 Language language = controller.getLanguage(); 309 Completable completer = language.getCompletionProvider(); 310 311 if (completer != null) { 312 List <CompletionProposal> proposals = 313 completer.complete(controller, offset, prefix, caseSensitive, new CompletionFormatter()); 314 315 if (proposals != null) { 316 for (CompletionProposal proposal : proposals) { 317 GsfCompletionItem item = GsfCompletionItem.createItem(proposal, controller); 318 319 if (item != null) { 320 results.add(item); 321 } 322 } 323 } 324 } 325 } 326 327 private boolean isJavaIdentifierPart(String text) { 329 for (int i = 0; i < text.length(); i++) { 330 if (!(Character.isJavaIdentifierPart(text.charAt(i)))) { 331 return false; 332 } 333 } 334 335 return true; 336 } 337 338 private Collection getFilteredData(Collection <CompletionItem> data, String prefix) { 339 if (prefix.length() == 0) { 340 return data; 341 } 342 343 List ret = new ArrayList (); 344 345 for (Iterator <CompletionItem> it = data.iterator(); it.hasNext();) { 346 CompletionItem itm = it.next(); 347 348 if (startsWith(itm.getInsertPrefix().toString(), prefix)) { 349 ret.add(itm); 350 } 351 352 } 355 356 return ret; 357 } 358 359 private Env getCompletionEnvironment(CompilationController controller, boolean upToOffset) 360 throws IOException { 361 int length = controller.getDocument().getLength(); 366 if (caretOffset > length) { 367 caretOffset = length; 368 } 369 370 int offset = caretOffset; 371 String prefix = null; 372 373 375 try { 376 Language language = controller.getLanguage(); 378 Completable completer = language.getCompletionProvider(); 379 if (completer != null) { 380 prefix = completer.getPrefix(controller, offset); 381 } 382 if (prefix == null) { 383 int[] blk = 384 org.netbeans.editor.Utilities.getIdentifierBlock((BaseDocument)controller.getDocument(), 385 offset); 386 387 if (blk != null) { 388 int start = blk[0]; 389 390 if (start < offset) { 391 prefix = controller.getDocument().getText(start, offset - start); 392 } 393 } 394 } 395 } catch (BadLocationException ex) { 396 ErrorManager.getDefault().notify(ex); 397 } catch (IOException ex) { 398 ErrorManager.getDefault().notify(ex); 399 } 400 401 controller.toPhase(Phase.PARSED); 402 403 return new Env(offset, prefix, controller); 404 } 405 406 private class Env { 407 private int offset; 408 private String prefix; 409 private CompilationController controller; 410 411 private Env(int offset, String prefix, CompilationController controller) { 412 this.offset = offset; 413 this.prefix = prefix; 414 this.controller = controller; 415 } 416 417 public int getOffset() { 418 return offset; 419 } 420 421 public String getPrefix() { 422 return prefix; 423 } 424 425 public CompilationController getController() { 426 return controller; 427 } 428 } 429 } 430 431 432 private static class CompletionFormatter extends GsfHtmlFormatter { 433 private static final String METHOD_COLOR = "<font color=#000000>"; private static final String PARAMETER_NAME_COLOR = "<font color=#a06001>"; private static final String END_COLOR = "</font>"; private static final String CLASS_COLOR = "<font color=#560000>"; private static final String PKG_COLOR = "<font color=#808080>"; private static final String KEYWORD_COLOR = "<font color=#000099>"; private static final String FIELD_COLOR = "<font color=#008618>"; private static final String VARIABLE_COLOR = "<font color=#00007c>"; private static final String CONSTRUCTOR_COLOR = "<font color=#b28b00>"; private static final String INTERFACE_COLOR = "<font color=#404040>"; 444 @Override 445 public void parameters(boolean start) { 446 assert start != isParameter; 447 isParameter = start; 448 449 if (isParameter) { 450 sb.append(PARAMETER_NAME_COLOR); 451 } else { 452 sb.append(END_COLOR); 453 } 454 } 455 456 @Override 457 public void name(ElementKind kind, boolean start) { 458 assert start != isName; 459 isName = start; 460 461 if (isName) { 462 switch (kind) { 463 case CONSTRUCTOR: 464 sb.append(CONSTRUCTOR_COLOR); 465 sb.append("<b>"); 466 break; 467 case METHOD: 468 sb.append(METHOD_COLOR); 469 sb.append("<b>"); 470 break; 471 case CLASS: 472 sb.append(CLASS_COLOR); 473 break; 474 case ATTRIBUTE: 475 case FIELD: 476 sb.append(FIELD_COLOR); 477 sb.append("<b>"); 478 break; 479 case MODULE: 480 sb.append(PKG_COLOR); 481 break; 482 case KEYWORD: 483 sb.append(KEYWORD_COLOR); 484 sb.append("<b>"); 485 break; 486 case VARIABLE: 487 sb.append(VARIABLE_COLOR); 488 sb.append("<b>"); 489 break; 490 default: 491 sb.append("<font>"); 492 } 493 } else { 494 switch (kind) { 495 case CONSTRUCTOR: 496 case METHOD: 497 case ATTRIBUTE: 498 case FIELD: 499 case KEYWORD: 500 case VARIABLE: 501 sb.append("</b>"); 502 break; 503 } 504 sb.append(END_COLOR); 505 } 506 } 507 508 } 509 } 510 | Popular Tags |