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-2006 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.gsfret.editor.completion;
042:
043: import java.io.IOException;
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Set;
049: import javax.swing.JToolTip;
050: import javax.swing.text.AbstractDocument;
051: import javax.swing.text.BadLocationException;
052: import javax.swing.text.Document;
053: import javax.swing.text.JTextComponent;
054: import org.netbeans.api.editor.completion.Completion;
055: import org.netbeans.modules.gsf.api.CompletionProposal;
056: import org.netbeans.modules.gsf.api.Completable;
057: import org.netbeans.modules.gsf.api.CancellableTask;
058: import org.netbeans.modules.gsf.api.Completable.QueryType;
059: import org.netbeans.modules.gsf.api.ElementHandle;
060: import org.netbeans.modules.gsf.api.ElementKind;
061: import org.netbeans.modules.gsf.api.NameKind;
062: import org.netbeans.modules.gsf.api.ParameterInfo;
063: import org.netbeans.api.lexer.TokenHierarchy;
064: import org.netbeans.api.lexer.TokenId;
065: import org.netbeans.api.lexer.TokenSequence;
066: import org.netbeans.napi.gsfret.source.CompilationController;
067: import org.netbeans.napi.gsfret.source.CompilationInfo;
068: import org.netbeans.napi.gsfret.source.Phase;
069: import org.netbeans.napi.gsfret.source.Source;
070: import org.netbeans.napi.gsfret.source.SourceUtils;
071: import org.netbeans.editor.BaseDocument;
072: import org.netbeans.editor.Registry;
073: import org.netbeans.editor.Settings;
074: import org.netbeans.editor.SettingsChangeEvent;
075: import org.netbeans.editor.SettingsChangeListener;
076: import org.netbeans.editor.SettingsUtil;
077: import org.netbeans.editor.ext.ExtSettingsDefaults;
078: import org.netbeans.editor.ext.ExtSettingsNames;
079: import org.netbeans.modules.gsf.GsfEditorKitFactory;
080: import org.netbeans.modules.gsf.GsfHtmlFormatter;
081: import org.netbeans.modules.gsf.Language;
082: import org.netbeans.modules.gsf.LanguageRegistry;
083: import org.netbeans.spi.editor.completion.*;
084: import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
085: import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
086: import org.openide.ErrorManager;
087: import org.openide.filesystems.FileObject;
088: import org.openide.util.Exceptions;
089: import org.openide.util.NbBundle;
090:
091: /**
092: * Code completion provider - delegates to language plugin for actual population of result set.
093: * Based on JavaCompletionProvider by Dusan Balek.
094: *
095: * @todo I may be able to rip out the code I had in here to work around the
096: * automatic completion vs "No Suggestions" issue; see
097: * http://hg.netbeans.org/main?cmd=changeset;node=6740db8e6988
098: *
099: * @author Tor Norbye
100: */
101: public class GsfCompletionProvider implements CompletionProvider {
102:
103: private static final String COMMENT_CATEGORY_NAME = "comment";
104:
105: /**
106: * Flag which is set when we're in a query that was initiated
107: * automatically rather than through an explicit gesture
108: */
109: private static boolean isAutoQuery;
110: private static boolean expectingCreateTask;
111:
112: public static Completable getCompletable(CompilationInfo info,
113: int offset) {
114: try {
115: return getCompletable(info.getDocument(), offset);
116: } catch (IOException ioe) {
117: Exceptions.printStackTrace(ioe);
118: return null;
119: }
120: }
121:
122: static Completable getCompletable(Document doc, int offset) {
123: BaseDocument baseDoc = (BaseDocument) doc;
124: List<Language> list = LanguageRegistry.getInstance()
125: .getEmbeddedLanguages(baseDoc, offset);
126: for (Language l : list) {
127: if (l.getCompletionProvider() != null) {
128: return l.getCompletionProvider();
129: }
130: }
131:
132: return null;
133: }
134:
135: private static boolean isInCompletion(JTextComponent component) {
136: Object o = component.getClientProperty("completion-active"); // NOI18N
137: return o == Boolean.TRUE;
138: }
139:
140: public static int autoQueryTypes(JTextComponent component,
141: String typedText) {
142: if (typedText.length() > 0) {
143: Completable provider = getCompletable(component
144: .getDocument(), component.getCaretPosition());
145: if (provider != null) {
146: QueryType autoQuery = provider.getAutoQuery(component,
147: typedText);
148: switch (autoQuery) {
149: case NONE:
150: return 0;
151: case STOP: {
152: isAutoQuery = false;
153: Completion.get().hideAll();
154: return 0;
155: }
156: case COMPLETION:
157: return COMPLETION_QUERY_TYPE;
158: case DOCUMENTATION:
159: return DOCUMENTATION_QUERY_TYPE;
160: case TOOLTIP:
161: return TOOLTIP_QUERY_TYPE;
162: case ALL_COMPLETION:
163: return COMPLETION_ALL_QUERY_TYPE;
164: }
165: }
166: }
167:
168: return 0;
169: }
170:
171: public int getAutoQueryTypes(JTextComponent component,
172: String typedText) {
173: boolean isCompleting = isInCompletion(component);
174: int type = autoQueryTypes(component, typedText);
175: if (!isCompleting) {
176: isAutoQuery = (type != 0);
177: expectingCreateTask = (type != 0); // I get createTask even during editing (or just when matches==0?)
178: }
179:
180: return type;
181: }
182:
183: // From Utilities
184: public static boolean isJavaContext(final JTextComponent component,
185: final int offset) {
186: Document doc = component.getDocument();
187: org.netbeans.api.lexer.Language language = (org.netbeans.api.lexer.Language) doc
188: .getProperty(org.netbeans.api.lexer.Language.class);
189: if (language == null) {
190: return true;
191: }
192: if (doc instanceof AbstractDocument) {
193: ((AbstractDocument) doc).readLock();
194: }
195: try {
196: TokenSequence ts = TokenHierarchy.get(
197: component.getDocument()).tokenSequence();
198:
199: if (ts == null) {
200: return false;
201: }
202: if (!ts.moveNext() || ts.move(offset) == 0) {
203: return true;
204: }
205: if (!ts.moveNext()) { // Move to the next token after move(offset)
206: return false;
207: }
208:
209: TokenId tokenId = ts.token().id();
210:
211: Set s = language.tokenCategories().contains(
212: COMMENT_CATEGORY_NAME) ? language
213: .tokenCategoryMembers(COMMENT_CATEGORY_NAME) : null;
214:
215: return s == null || !s.contains(tokenId); //NOI18N
216: } finally {
217: if (doc instanceof AbstractDocument) {
218: ((AbstractDocument) doc).readUnlock();
219: }
220: }
221: }
222:
223: public static boolean startsWith(String theString, String prefix) {
224: if (theString == null || theString.length() == 0)
225: return false;
226: if (prefix == null || prefix.length() == 0)
227: return true;
228: return isCaseSensitive() ? theString.startsWith(prefix)
229: : theString.toLowerCase().startsWith(
230: prefix.toLowerCase());
231: }
232:
233: public CompletionTask createTask(int type, JTextComponent component) {
234: if (!expectingCreateTask) {
235: isAutoQuery = false;
236: }
237:
238: if (((type & COMPLETION_QUERY_TYPE) != 0)
239: || (type == TOOLTIP_QUERY_TYPE)
240: || (type == DOCUMENTATION_QUERY_TYPE)) {
241: return new AsyncCompletionTask(new JavaCompletionQuery(
242: type, component.getSelectionStart()), component);
243: }
244:
245: return null;
246: }
247:
248: static CompletionTask createDocTask(ElementHandle element,
249: CompilationInfo info) { // TODO - use ComObjectHandle ??
250: JavaCompletionQuery query = new JavaCompletionQuery(
251: DOCUMENTATION_QUERY_TYPE, -1);
252: query.element = element;
253:
254: return new AsyncCompletionTask(query, Registry
255: .getMostActiveComponent());
256: }
257:
258: static final class JavaCompletionQuery extends AsyncCompletionQuery
259: implements CancellableTask<CompilationController> {
260: private Collection<CompletionItem> results;
261: private JToolTip toolTip;
262: private CompletionDocumentation documentation;
263: private int anchorOffset;
264: //private int toolTipOffset;
265: private JTextComponent component;
266: private int queryType;
267: private int caretOffset;
268: private String filterPrefix;
269: private ElementHandle element;
270: private Source source;
271:
272: /** The compilation info that the Element was generated for */
273:
274: private JavaCompletionQuery(int queryType, int caretOffset) {
275: this .queryType = queryType;
276: this .caretOffset = caretOffset;
277: }
278:
279: @Override
280: protected void preQueryUpdate(JTextComponent component) {
281: int newCaretOffset = component.getSelectionStart();
282:
283: if (newCaretOffset >= caretOffset) {
284: try {
285: if (isJavaIdentifierPart(component.getDocument()
286: .getText(caretOffset,
287: newCaretOffset - caretOffset))) {
288: return;
289: }
290: } catch (BadLocationException e) {
291: }
292: }
293:
294: Completion.get().hideCompletion();
295: }
296:
297: @Override
298: protected void prepareQuery(JTextComponent component) {
299: this .component = component;
300: }
301:
302: @Override
303: protected void query(CompletionResultSet resultSet,
304: Document doc, int caretOffset) {
305: try {
306: this .caretOffset = caretOffset;
307: if (queryType == TOOLTIP_QUERY_TYPE
308: || queryType == DOCUMENTATION_QUERY_TYPE
309: || isJavaContext(component, caretOffset)) {
310: results = null;
311: documentation = null;
312: toolTip = null;
313: anchorOffset = -1;
314: Source js = Source.forDocument(doc);
315: if (js == null) {
316: FileObject fo = null;
317: if (element != null) {
318: fo = element.getFileObject();
319: if (fo != null) {
320: js = Source.forFileObject(fo);
321: }
322: }
323: }
324: //if (queryType == DOCUMENTATION_QUERY_TYPE && element != null) {
325: // FileObject fo = SourceUtils.getFile(element, js.getClasspathInfo());
326: // if (fo != null)
327: // js = Source.forFileObject(fo);
328: //}
329: if (js != null) {
330: if (SourceUtils.isScanInProgress())
331: resultSet.setWaitText(NbBundle.getMessage(
332: GsfCompletionProvider.class,
333: "scanning-in-progress")); //NOI18N
334: js.runUserActionTask(this , true);
335: if ((queryType & COMPLETION_QUERY_TYPE) != 0) {
336: if (results != null)
337: resultSet.addAllItems(results);
338: } else if (queryType == TOOLTIP_QUERY_TYPE) {
339: if (toolTip != null)
340: resultSet.setToolTip(toolTip);
341: } else if (queryType == DOCUMENTATION_QUERY_TYPE) {
342: if (documentation != null)
343: resultSet
344: .setDocumentation(documentation);
345: }
346: if (anchorOffset > -1)
347: resultSet.setAnchorOffset(anchorOffset);
348: }
349: }
350: } catch (IOException ioe) {
351: Exceptions.printStackTrace(ioe);
352: } finally {
353: resultSet.finish();
354: }
355: }
356:
357: @Override
358: protected boolean canFilter(JTextComponent component) {
359: filterPrefix = null;
360:
361: int newOffset = component.getSelectionStart();
362:
363: if ((queryType & COMPLETION_QUERY_TYPE) != 0) {
364: if (newOffset >= caretOffset) {
365: if (anchorOffset > -1) {
366: try {
367: String prefix = component.getDocument()
368: .getText(anchorOffset,
369: newOffset - anchorOffset);
370:
371: if (isJavaIdentifierPart(prefix)) {
372: filterPrefix = prefix;
373: }
374: } catch (BadLocationException e) {
375: }
376: }
377: }
378:
379: return filterPrefix != null;
380: } else if (queryType == TOOLTIP_QUERY_TYPE) {
381: try {
382: if (newOffset == caretOffset)
383: filterPrefix = "";
384: else if (newOffset - caretOffset > 0)
385: filterPrefix = component.getDocument().getText(
386: caretOffset, newOffset - caretOffset);
387: else if (newOffset - caretOffset < 0)
388: filterPrefix = component.getDocument().getText(
389: newOffset, caretOffset - newOffset);
390: } catch (BadLocationException ex) {
391: }
392: return (filterPrefix != null
393: && filterPrefix.indexOf(',') == -1
394: && filterPrefix.indexOf('(') == -1 && filterPrefix
395: .indexOf(')') == -1); // NOI18N
396: }
397:
398: return false;
399: }
400:
401: @Override
402: protected void filter(CompletionResultSet resultSet) {
403: try {
404: if ((queryType & COMPLETION_QUERY_TYPE) != 0) {
405: if (results != null) {
406: if (filterPrefix != null) {
407: resultSet.addAllItems(getFilteredData(
408: results, filterPrefix));
409: } else {
410: Completion.get().hideDocumentation();
411: Completion.get().hideCompletion();
412: }
413: }
414: } else if (queryType == TOOLTIP_QUERY_TYPE) {
415: resultSet.setToolTip(toolTip);
416: }
417:
418: resultSet.setAnchorOffset(anchorOffset);
419: } catch (Exception ex) {
420: Exceptions.printStackTrace(ex);
421: }
422:
423: resultSet.finish();
424: }
425:
426: public void run(CompilationController controller)
427: throws Exception {
428: if (controller.getDocument() == null) {
429: return;
430: }
431:
432: if ((queryType & COMPLETION_QUERY_TYPE) != 0) {
433: resolveCompletion(controller);
434: } else if (queryType == TOOLTIP_QUERY_TYPE) {
435: resolveToolTip(controller);
436: } else if (queryType == DOCUMENTATION_QUERY_TYPE) {
437: resolveDocumentation(controller);
438: }
439: GsfCompletionItem.tipProposal = null;
440: }
441:
442: public void cancel() {
443: }
444:
445: private void resolveToolTip(
446: final CompilationController controller)
447: throws IOException {
448: CompletionProposal proposal = GsfCompletionItem.tipProposal;
449: Env env = getCompletionEnvironment(controller, false);
450: Completable completer = env.getCompletable();
451:
452: if (completer != null) {
453: int offset = env.getOffset();
454: ParameterInfo info = completer.parameters(controller,
455: offset, proposal);
456: if (info != ParameterInfo.NONE) {
457:
458: List<String> params = info.getNames();
459:
460: // Take the parameter list, and balance them out into
461: // a "2d" set of lists used by the method params tip component:
462: // a list of lists - one for each row, and each row is a list
463: // for the clumn
464: int MAX_WIDTH = 50; // Max width before wrapping to the next line
465: int column = 0;
466: List<List<String>> parameterList = new ArrayList<List<String>>();
467: List<String> p = new ArrayList<String>();
468: for (int length = params.size(), i = 0; i < length; i++) {
469: String parameter = params.get(i);
470: if (i < length - 1) {
471: parameter = parameter + ", ";
472: }
473: p.add(parameter);
474:
475: column += parameter.length();
476: if (column > MAX_WIDTH) {
477: column = 0;
478: parameterList.add(p);
479: p = new ArrayList<String>();
480:
481: }
482: }
483: if (p.size() > 0) {
484: parameterList.add(p);
485: }
486:
487: int index = info.getCurrentIndex();
488: anchorOffset = info.getAnchorOffset();
489: toolTip = new MethodParamsTipPaintComponent(
490: parameterList, index, component);
491: //startPos = (int)sourcePositions.getEndPosition(env.getRoot(), mi.getMethodSelect());
492: //String text = controller.getText().substring(startPos, offset);
493: //anchorOffset = startPos + controller.getPositionConverter().getOriginalPosition(text.indexOf('(')); //NOI18N
494: //toolTipOffset = startPos + controller.getPositionConverter().getOriginalPosition(text.lastIndexOf(',')); //NOI18N
495: //if (toolTipOffset < anchorOffset)
496: // toolTipOffset = anchorOffset;
497: return;
498:
499: }
500: }
501: }
502:
503: private void resolveDocumentation(
504: CompilationController controller) throws IOException {
505: controller.toPhase(Phase.RESOLVED);
506:
507: if (element != null) {
508: documentation = GsfCompletionDoc.create(controller,
509: element);
510: } else {
511: Env env = getCompletionEnvironment(controller, false);
512: int offset = env.getOffset();
513: String prefix = env.getPrefix();
514: results = new ArrayList<CompletionItem>();
515: anchorOffset = env.getOffset()
516: - ((prefix != null) ? prefix.length() : 0);
517:
518: Completable completer = env.getCompletable();
519:
520: if (completer != null) {
521: List<CompletionProposal> proposals = completer
522: .complete(controller, offset, prefix,
523: NameKind.EXACT_NAME,
524: QueryType.DOCUMENTATION,
525: isCaseSensitive(),
526: new CompletionFormatter());
527:
528: if (proposals != null) {
529: for (CompletionProposal proposal : proposals) {
530: ElementHandle element = proposal
531: .getElement();
532: if (element != null) {
533: documentation = GsfCompletionDoc
534: .create(controller, element);
535: // TODO - find some way to show the multiple overloaded methods?
536: if (documentation.getText() != null
537: && documentation.getText()
538: .length() > 0) {
539: // Make sure we at least pick an alternative that has documentation
540: break;
541: }
542: }
543: }
544: }
545: }
546: }
547: }
548:
549: private void resolveCompletion(CompilationController controller)
550: throws IOException {
551: Env env = getCompletionEnvironment(controller, true);
552: int offset = env.getOffset();
553: String prefix = env.getPrefix();
554: results = new ArrayList<CompletionItem>();
555: anchorOffset = env.getOffset()
556: - ((prefix != null) ? prefix.length() : 0);
557:
558: Completable completer = env.getCompletable();
559:
560: if (completer != null) {
561: List<CompletionProposal> proposals = completer
562: .complete(
563: controller,
564: offset,
565: prefix,
566: isCaseSensitive() ? NameKind.PREFIX
567: : NameKind.CASE_INSENSITIVE_PREFIX,
568: QueryType.COMPLETION,
569: isCaseSensitive(),
570: new CompletionFormatter());
571:
572: if (proposals != null) {
573: for (CompletionProposal proposal : proposals) {
574: GsfCompletionItem item = GsfCompletionItem
575: .createItem(proposal, controller);
576:
577: if (item != null) {
578: results.add(item);
579: }
580: }
581: }
582:
583: // If we automatically queried, and there were no hits, take it down
584: if (isAutoQuery
585: && (proposals == null || proposals.size() == 0)) {
586: Completion.get().hideCompletion();
587: expectingCreateTask = false;
588: }
589: }
590: }
591:
592: // TODO - delegate to language support!
593: private boolean isJavaIdentifierPart(String text) {
594: for (int i = 0; i < text.length(); i++) {
595: if (!(Character.isJavaIdentifierPart(text.charAt(i)))) {
596: return false;
597: }
598: }
599:
600: return true;
601: }
602:
603: private Collection getFilteredData(
604: Collection<CompletionItem> data, String prefix) {
605: if (prefix.length() == 0) {
606: return data;
607: }
608:
609: List ret = new ArrayList();
610:
611: for (Iterator<CompletionItem> it = data.iterator(); it
612: .hasNext();) {
613: CompletionItem itm = it.next();
614:
615: if (startsWith(itm.getInsertPrefix().toString(), prefix)) {
616: ret.add(itm);
617: }
618:
619: // else if (itm instanceof LazyTypeCompletionItem && Utilities.startsWith(((LazyTypeCompletionItem)itm).getItemText(), prefix))
620: // ret.add(itm);
621: }
622:
623: return ret;
624: }
625:
626: /**
627: *
628: * @param upToOffset If set, complete only up to the given caret offset, otherwise complete
629: * the full symbol at the offset
630: */
631: private Env getCompletionEnvironment(
632: CompilationController controller, boolean upToOffset)
633: throws IOException {
634: // If you invoke code completion while indexing is in progress, the
635: // completion job (which stores the caret offset) will be delayed until
636: // indexing is complete - potentially minutes later. When the job
637: // is finally run we need to make sure the caret position is still valid. (93017)
638: Document doc = controller.getDocument();
639: int length = doc != null ? doc.getLength()
640: : (int) controller.getFileObject().getSize();
641: if (caretOffset > length) {
642: caretOffset = length;
643: }
644:
645: int offset = caretOffset;
646: String prefix = null;
647:
648: //
649: // TODO - handle the upToOffset parameter
650: // Look at the parse tree, and find the corresponding end node
651: // offset...
652:
653: Completable completer = getCompletable(controller, offset);
654: try {
655: // TODO: use the completion helper to get the contxt
656: if (completer != null) {
657: prefix = completer.getPrefix(controller, offset,
658: upToOffset);
659: }
660: if (prefix == null) {
661: int[] blk = org.netbeans.editor.Utilities
662: .getIdentifierBlock(
663: (BaseDocument) controller
664: .getDocument(), offset);
665:
666: if (blk != null) {
667: int start = blk[0];
668:
669: if (start < offset) {
670: if (upToOffset) {
671: prefix = controller.getDocument()
672: .getText(start, offset - start);
673: } else {
674: prefix = controller.getDocument()
675: .getText(start, blk[1] - start);
676: }
677: }
678: }
679: }
680: } catch (BadLocationException ex) {
681: ErrorManager.getDefault().notify(ex);
682: } catch (IOException ex) {
683: ErrorManager.getDefault().notify(ex);
684: }
685:
686: controller.toPhase(Phase.PARSED);
687:
688: return new Env(offset, prefix, controller, completer);
689: }
690:
691: private class Env {
692: private int offset;
693: private String prefix;
694: private CompilationController controller;
695: private Completable completable;
696: private boolean autoCompleting;
697:
698: private Env(int offset, String prefix,
699: CompilationController controller,
700: Completable completable) {
701: this .offset = offset;
702: this .prefix = prefix;
703: this .controller = controller;
704: this .completable = completable;
705: }
706:
707: public int getOffset() {
708: return offset;
709: }
710:
711: public String getPrefix() {
712: return prefix;
713: }
714:
715: public boolean isAutoCompleting() {
716: return autoCompleting;
717: }
718:
719: public void setAutoCompleting(boolean autoCompleting) {
720: this .autoCompleting = autoCompleting;
721: }
722:
723: public CompilationController getController() {
724: return controller;
725: }
726:
727: public Completable getCompletable() {
728: return completable;
729: }
730: }
731: }
732:
733: /** Format parameters in orange etc. */
734: private static class CompletionFormatter extends GsfHtmlFormatter {
735: private static final String METHOD_COLOR = "<font color=#000000>"; //NOI18N
736: private static final String PARAMETER_NAME_COLOR = "<font color=#a06001>"; //NOI18N
737: private static final String END_COLOR = "</font>"; // NOI18N
738: private static final String CLASS_COLOR = "<font color=#560000>"; //NOI18N
739: private static final String PKG_COLOR = "<font color=#808080>"; //NOI18N
740: private static final String KEYWORD_COLOR = "<font color=#000099>"; //NOI18N
741: private static final String FIELD_COLOR = "<font color=#008618>"; //NOI18N
742: private static final String VARIABLE_COLOR = "<font color=#00007c>"; //NOI18N
743: private static final String CONSTRUCTOR_COLOR = "<font color=#b28b00>"; //NOI18N
744: private static final String INTERFACE_COLOR = "<font color=#404040>"; //NOI18N
745: private static final String PARAMETERS_COLOR = "<font color=#808080>"; //NOI18N
746: private static final String ACTIVE_PARAMETER_COLOR = "<font color=#000000>"; //NOI18N
747:
748: @Override
749: public void parameters(boolean start) {
750: assert start != isParameter;
751: isParameter = start;
752:
753: if (isParameter) {
754: sb.append(PARAMETER_NAME_COLOR);
755: } else {
756: sb.append(END_COLOR);
757: }
758: }
759:
760: @Override
761: public void active(boolean start) {
762: if (start) {
763: sb.append(ACTIVE_PARAMETER_COLOR);
764: sb.append("<b>");
765: } else {
766: sb.append("</b>");
767: sb.append(END_COLOR);
768: }
769: }
770:
771: @Override
772: public void name(ElementKind kind, boolean start) {
773: assert start != isName;
774: isName = start;
775:
776: if (isName) {
777: switch (kind) {
778: case CONSTRUCTOR:
779: sb.append(CONSTRUCTOR_COLOR);
780: break;
781: case CALL:
782: sb.append(PARAMETERS_COLOR);
783: break;
784: case DB:
785: case METHOD:
786: sb.append(METHOD_COLOR);
787: break;
788: case CLASS:
789: sb.append(CLASS_COLOR);
790: break;
791: case FIELD:
792: sb.append(FIELD_COLOR);
793: break;
794: case MODULE:
795: sb.append(PKG_COLOR);
796: break;
797: case KEYWORD:
798: sb.append(KEYWORD_COLOR);
799: sb.append("<b>");
800: break;
801: case VARIABLE:
802: sb.append(VARIABLE_COLOR);
803: sb.append("<b>");
804: break;
805: default:
806: sb.append("<font>");
807: }
808: } else {
809: switch (kind) {
810: case KEYWORD:
811: case VARIABLE:
812: sb.append("</b>");
813: break;
814: }
815: sb.append(END_COLOR);
816: }
817: }
818:
819: }
820:
821: // From Utilities
822: private static boolean caseSensitive = true;
823: private static boolean inited;
824:
825: public static boolean isCaseSensitive() {
826: lazyInit();
827: return caseSensitive;
828: }
829:
830: private static class SettingsListener implements
831: SettingsChangeListener {
832:
833: public void settingsChange(SettingsChangeEvent evt) {
834: setCaseSensitive(SettingsUtil.getBoolean(
835: GsfEditorKitFactory.GsfEditorKit.class,
836: ExtSettingsNames.COMPLETION_CASE_SENSITIVE,
837: ExtSettingsDefaults.defaultCompletionCaseSensitive));
838: }
839: }
840:
841: private static SettingsChangeListener settingsListener = new SettingsListener();
842:
843: public static void setCaseSensitive(boolean b) {
844: lazyInit();
845: caseSensitive = b;
846: }
847:
848: private static void lazyInit() {
849: if (!inited) {
850: inited = true;
851: Settings.addSettingsChangeListener(settingsListener);
852: setCaseSensitive(SettingsUtil.getBoolean(
853: GsfEditorKitFactory.GsfEditorKit.class,
854: ExtSettingsNames.COMPLETION_CASE_SENSITIVE,
855: ExtSettingsDefaults.defaultCompletionCaseSensitive));
856: }
857: }
858: }
|