--- a/java.hints/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProvider.java Fri Jul 10 14:46:26 2009 +0200 +++ a/java.hints/src/org/netbeans/modules/java/hints/infrastructure/ErrorHintsProvider.java Mon Jul 20 12:02:24 2009 +0200 @@ -85,6 +85,7 @@ import org.netbeans.api.lexer.Token; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.modules.editor.NbEditorDocument; import org.netbeans.modules.editor.java.Utilities; import org.netbeans.modules.java.hints.spi.ErrorRule; import org.netbeans.modules.java.hints.spi.ErrorRule.Data; @@ -93,6 +94,7 @@ import org.netbeans.spi.editor.hints.HintsController; import org.openide.cookies.LineCookie; import org.openide.text.NbDocument; +import org.openide.util.Exceptions; @@ -119,8 +121,20 @@ errorKind2Severity.put(Diagnostic.Kind.NOTE, Severity.WARNING); errorKind2Severity.put(Diagnostic.Kind.OTHER, Severity.WARNING); } + + /** + * @return errors for whole file + */ + List computeErrors(CompilationInfo info, Document doc) throws IOException { + return computeErrors(info, doc, null); + } - List computeErrors(CompilationInfo info, Document doc) throws IOException { + /** + * @param forPosition position for ehich errors would be computed + * @return errors for line specified by forPosition + * @throws IOException + */ + List computeErrors(CompilationInfo info, Document doc, Integer forPosition) throws IOException { List errors = info.getDiagnostics(); List descs = new ArrayList(); @@ -128,7 +142,7 @@ ERR.log(ErrorManager.INFORMATIONAL, "errors = " + errors ); Map data = new HashMap(); - + for (Diagnostic d : errors) { if (isCanceled()) return null; @@ -162,13 +176,25 @@ final String desc = d.getMessage(null); final Position[] range = getLine(info, d, doc, (int)d.getStartPosition(), (int)d.getEndPosition()); - + if (isCanceled()) return null; if (range[0] == null || range[1] == null) continue; + if (forPosition != null) { + try { + int posRowStart = org.netbeans.editor.Utilities.getRowStart((NbEditorDocument) doc, forPosition); + int errRowStart = org.netbeans.editor.Utilities.getRowStart((NbEditorDocument) doc, range[0].getOffset()); + if (posRowStart != errRowStart) { + continue; + } + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + } + descs.add(ErrorDescriptionFactory.createErrorDescription(errorKind2Severity.get(d.getKind()), desc, ehm, doc, range[0], range[1])); } @@ -477,7 +503,7 @@ if (errors == null) //meaning: cancelled return ; - HintsController.setErrors(doc, "java-hints", errors); + HintsController.setErrors(doc, ErrorHintsProvider.class.getName(), errors); long end = System.currentTimeMillis(); --- a/java.hints/src/org/netbeans/modules/java/hints/infrastructure/HintsTask.java Fri Jul 10 14:46:26 2009 +0200 +++ a/java.hints/src/org/netbeans/modules/java/hints/infrastructure/HintsTask.java Mon Jul 20 12:02:24 2009 +0200 @@ -92,7 +92,7 @@ private Map> presetHints; public HintsTask() {} - + public HintsTask(List hints) { presetHints = new EnumMap>(Kind.class); @@ -113,7 +113,7 @@ return computeHints(info, new TreePath(info.getCompilationUnit())); } - private List computeHints(CompilationInfo info, TreePath startAt) { + public List computeHints(CompilationInfo info, TreePath startAt) { Map> hints = presetHints != null ? presetHints : RulesManager.getInstance().getHints(false); if (hints.isEmpty()) { --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 1436de250f92 Mon Jul 20 12:02:24 2009 +0200 @@ -0,0 +1,146 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.java.hints.infrastructure; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.swing.text.Document; +import org.netbeans.api.java.source.CompilationController; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.Task; +import org.netbeans.editor.BaseDocument; +import org.netbeans.editor.Utilities; +import org.netbeans.spi.editor.hints.Context; +import org.netbeans.spi.editor.hints.ErrorDescription; +import org.netbeans.spi.editor.hints.LazyFixList; +import org.netbeans.spi.editor.hints.PositionRefresher; +import org.openide.util.Exceptions; + +/** + * Refreshes all Java Hints on current line upon Alt-Enter or mouseclick + * @author Max Sauer + */ +public class JavaHintsPositionRefresher implements PositionRefresher { + + public Map> getErrorDescriptionsAt(Context context, Document doc) { + JavaSource js = JavaSource.forDocument(doc); + final Map> eds = new HashMap>(); + + try { + js.runUserActionTask(new RefreshTask(eds, context, doc), true); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + + return eds; + } + + + private class RefreshTask implements Task { + + private final Map> eds; + private final Context ctx; + private final Document doc; + + public RefreshTask( Map> eds, Context ctx, Document doc) { + this.eds = eds; + this.ctx = ctx; + this.doc = doc; + } + + public void run(CompilationController controller) throws Exception { + controller.toPhase(JavaSource.Phase.RESOLVED); + int position = ctx.getPosition(); + + //TODO: better cancel handling (propagate into tasks?) + if (ctx.isCanceled()) { + return; + } + + //SuggestionsTask + SuggestionsTask suggestionsTask = new SuggestionsTask(); + suggestionsTask.run(controller); + eds.put(SuggestionsTask.class.getName(), suggestionsTask.getSuggestions()); + + if (ctx.isCanceled()) { + return; + } + + //HintsTask + int rowStart = Utilities.getRowStart((BaseDocument) doc, position); + int rowEnd = Utilities.getRowEnd((BaseDocument) doc, position); + Set errs = new HashSet(); + Set encounteredLeafs = new HashSet(); + HintsTask task = new HintsTask(); + for (int i = rowStart; i < rowEnd; i++) { + TreePath path = controller.getTreeUtilities().pathFor(i); + Tree leaf = path.getLeaf(); + if (!encounteredLeafs.contains(leaf)) { + errs.addAll(task.computeHints(controller, path)); + encounteredLeafs.add(leaf); + } + } + + eds.put(HintsTask.class.getName(), new ArrayList(errs)); + + if (ctx.isCanceled()) { + return; + } + + //ErrorHints + final List errors = new ErrorHintsProvider().computeErrors(controller, doc, position); + for (ErrorDescription ed : errors) { + LazyFixList fixes = ed.getFixes(); + if (fixes instanceof CreatorBasedLazyFixList) { //compute fixes, since they're lazy computed + ((CreatorBasedLazyFixList) ed.getFixes()).compute(controller, ctx.getCancel()); + } + } + eds.put(ErrorHintsProvider.class.getName(), errors); + } + + } +} --- a/java.hints/src/org/netbeans/modules/java/hints/infrastructure/SuggestionsTask.java Fri Jul 10 14:46:26 2009 +0200 +++ a/java.hints/src/org/netbeans/modules/java/hints/infrastructure/SuggestionsTask.java Mon Jul 20 12:02:24 2009 +0200 @@ -63,8 +63,11 @@ * @author Jan Lahoda */ public class SuggestionsTask extends ScanningCancellableTask { + + private final List result; public SuggestionsTask() { + result = new ArrayList(); } public void run(CompilationInfo info) throws Exception { @@ -95,7 +98,6 @@ } int position = CaretAwareJavaSourceTaskFactory.getLastPosition(info.getFileObject()); - List result = new ArrayList(); if (position != (-1)) { TreePath tp = info.getTreeUtilities().pathFor(position); @@ -137,5 +139,9 @@ HintsController.setErrors(info.getFileObject(), SuggestionsTask.class.getName(), result); } - + + public List getSuggestions() { + return result; + } + } --- a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml Fri Jul 10 14:46:26 2009 +0200 +++ a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml Mon Jul 20 12:02:24 2009 +0200 @@ -94,6 +94,7 @@ + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 1436de250f92 Mon Jul 20 12:02:24 2009 +0200 @@ -0,0 +1,197 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.java.hints.infrastructure; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.swing.text.Document; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.java.lexer.JavaTokenId; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.api.java.source.SourceUtilsTestUtil; +import org.netbeans.api.java.source.TestUtilities; +import org.netbeans.api.lexer.InputAttributes; +import org.netbeans.api.lexer.Language; +import org.netbeans.api.lexer.LanguagePath; +import org.netbeans.api.lexer.Token; +import static org.junit.Assert.*; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.editor.java.JavaKit; +import org.netbeans.modules.java.JavaDataLoader; +import org.netbeans.modules.parsing.api.indexing.IndexingManager; +import org.netbeans.spi.editor.hints.Context; +import org.netbeans.spi.editor.hints.ErrorDescription; +import org.netbeans.spi.editor.hints.PositionRefresher; +import org.netbeans.spi.editor.mimelookup.MimeDataProvider; +import org.netbeans.spi.lexer.LanguageEmbedding; +import org.netbeans.spi.lexer.LanguageProvider; +import org.openide.cookies.EditorCookie; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.loaders.DataObject; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * Test class for JavaHints implementation of Editor Hints {@link PositionRefresher} + * @author Max Sauer + */ +public class JavaHintsPositionRefresherTest extends NbTestCase { + + private FileObject sourceRoot; + private CompilationInfo info; + private Document doc; + + public JavaHintsPositionRefresherTest(String name) { + super(name); + } + + + @Override + public void setUp() throws Exception { + super.setUp(); + SourceUtilsTestUtil.prepareTest(new String[]{"org/netbeans/modules/java/editor/resources/layer.xml"}, + new Object[]{JavaDataLoader.class, + new MimeDataProvider() { + public Lookup getLookup(MimePath mimePath) { + return Lookups.fixed(new Object[] { + new JavaKit(), + }); + } + }, + new LanguageProvider() { + public Language findLanguage(String mimePath) { + return JavaTokenId.language(); + } + + public LanguageEmbedding findLanguageEmbedding(Token token, + LanguagePath languagePath, + InputAttributes inputAttributes) { + return null; + } + } + }); + } + + private void prepareTest(String fileName, String code) throws Exception { + clearWorkDir(); + + FileObject workFO = FileUtil.toFileObject(getWorkDir()); + + assertNotNull(workFO); + + sourceRoot = workFO.createFolder("src"); + + FileObject buildRoot = workFO.createFolder("build"); + FileObject cache = workFO.createFolder("cache"); + + FileObject data = FileUtil.createData(sourceRoot, fileName); + File dataFile = FileUtil.toFile(data); + + assertNotNull(dataFile); + + TestUtilities.copyStringToFile(dataFile, code); + +// SourceUtilsTestUtil.prepareTest(new String[0], new Object[]{ +// new JavaCustomIndexer.Factory()}); + SourceUtilsTestUtil.prepareTest(sourceRoot, buildRoot, cache); + + DataObject od = DataObject.find(data); + EditorCookie ec = od.getCookie(EditorCookie.class); + + assertNotNull(ec); + + doc = ec.openDocument(); + doc.putProperty(Language.class, JavaTokenId.language()); + doc.putProperty("mimeType", "text/x-java"); + + //XXX: takes a long time + //re-index, in order to find classes-living-elsewhere + IndexingManager.getDefault().refreshIndexAndWait(sourceRoot.getURL(), null); + + JavaSource js = JavaSource.forFileObject(data); + + assertNotNull(js); + + info = SourceUtilsTestUtil.getCompilationInfo(js, Phase.RESOLVED); + + assertNotNull(info); + } + + private Context prepareContext(int position) { + Context ctx = null; + try { + Constructor constructor = Context.class.getDeclaredConstructor(int.class, AtomicBoolean.class); + constructor.setAccessible(true); + ctx = (Context) constructor.newInstance(new Integer(position), new AtomicBoolean()); + } catch (Exception ex) { + ex.printStackTrace(); + } + return ctx; + } + + public void testErrorHint0() throws Exception { + performTest("test/Test.java", "package test; public class Test {public void foo() {\n| new Foo();}}", "1:0-1:14:error:illegal start of expression1:6-1:9:error:cannot find symbol\n symbol : class Foo\n location: class test.Test"); + } + + private void performTest(String fileName , String code, String expected) throws Exception { + prepareTest(fileName, code); + int[] caretPosition = new int[1]; + code = org.netbeans.modules.java.hints.TestUtilities.detectOffsets(code, caretPosition); + Context ctx = prepareContext(caretPosition[0]); + Map> errorDescriptionsAt = new JavaHintsPositionRefresher().getErrorDescriptionsAt(ctx, doc); + StringBuffer buf = new StringBuffer(); + for (Entry> e : errorDescriptionsAt.entrySet()) { + for (ErrorDescription ed : e.getValue()) { + buf.append(ed.toString()); + } + + } + assertEquals("Provided error messages differ. ", expected, buf.toString()); + } + + +} --- a/spi.editor.hints/apichanges.xml Fri Jul 10 14:46:26 2009 +0200 +++ a/spi.editor.hints/apichanges.xml Mon Jul 20 12:02:24 2009 +0200 @@ -105,7 +105,19 @@ - + + + Added PositionRefresher interface and Context + + + + + + Added new interface and context class in order to be able to provide up-to-date hints on current line. + + + + --- a/spi.editor.hints/nbproject/project.properties Fri Jul 10 14:46:26 2009 +0200 +++ a/spi.editor.hints/nbproject/project.properties Mon Jul 20 12:02:24 2009 +0200 @@ -39,4 +39,4 @@ javac.compilerargs=-Xlint:unchecked javac.source=1.5 javadoc.arch=${basedir}/arch.xml -spec.version.base=1.8.0 +spec.version.base=1.8.1 --- a/spi.editor.hints/src/org/netbeans/modules/editor/hints/AnnotationHolder.java Fri Jul 10 14:46:26 2009 +0200 +++ a/spi.editor.hints/src/org/netbeans/modules/editor/hints/AnnotationHolder.java Mon Jul 20 12:02:24 2009 +0200 @@ -61,6 +61,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -660,7 +661,7 @@ detachAnnotation(previous); } - attachAnnotation(line, pea); + attachAnnotation(line, pea); } void updateHighlightsOnLine(Position line) throws IOException { @@ -998,6 +999,35 @@ return new ArrayList(line2Annotations.values()); } + public void setErrorsForLine(final int offset, final Map> errs) { + + doc.render(new Runnable() { + + public void run() { + try { + Position pos = getPosition(Utilities.getLineOffset(doc, offset), true); + + List errsForCurrentLine = getErrorsForLine(pos, true); + + //for each layer + for (Entry> e : errs.entrySet()) { + //get errors for this layer, all lines + Set errorsForLayer = new HashSet(getErrorsForLayer(e.getKey())); + errorsForLayer.removeAll(errsForCurrentLine); //remove all for current line + e.getValue().addAll(errorsForLayer); //add the rest to those provided by refresher + } + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + } + }); + + for (Entry> e : errs.entrySet()) { + final List eds = e.getValue(); + setErrorDescriptions(e.getKey(), eds); //set updated + } + } + public synchronized List getErrorsGE(int offset) { try { Position current = null; --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 1436de250f92 Mon Jul 20 12:02:24 2009 +0200 @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ +package org.netbeans.modules.editor.hints; + +import java.util.concurrent.atomic.AtomicBoolean; +import org.netbeans.spi.editor.hints.Context; + +/** + * Accessor for {@link Context}'s package private constructor + * @author Max Sauer + */ +public abstract class ContextAccessor { + + public static ContextAccessor DEFAULT; + + public static ContextAccessor getDefault() { + if (DEFAULT != null) { + return DEFAULT; + } + + Class c = Context.class; + try { + Class.forName(c.getName(), true, c.getClassLoader()); + } catch (ClassNotFoundException ex) { + assert false : ex; + } + + assert DEFAULT != null; + return DEFAULT; + } + + public abstract Context newContext(int position, AtomicBoolean cancel); +} --- a/spi.editor.hints/src/org/netbeans/modules/editor/hints/HintsUI.java Fri Jul 10 14:46:26 2009 +0200 +++ a/spi.editor.hints/src/org/netbeans/modules/editor/hints/HintsUI.java Mon Jul 20 12:02:24 2009 +0200 @@ -41,7 +41,16 @@ package org.netbeans.modules.editor.hints; -import java.awt.*; +import java.awt.AWTEvent; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.HeadlessException; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; import java.awt.event.AWTEventListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; @@ -53,6 +62,10 @@ import java.io.IOException; import java.lang.ref.Reference; import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; @@ -62,6 +75,7 @@ import javax.swing.text.JTextComponent; import javax.swing.text.Position; import org.netbeans.api.editor.EditorRegistry; +import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.editor.AnnotationDesc; import org.netbeans.editor.Annotations; import org.netbeans.editor.BaseDocument; @@ -71,7 +85,10 @@ import org.netbeans.modules.editor.hints.borrowed.ListCompletionView; import org.netbeans.modules.editor.hints.borrowed.ScrollCompletionPane; import org.netbeans.spi.editor.hints.ChangeInfo; +import org.netbeans.spi.editor.hints.Context; +import org.netbeans.spi.editor.hints.ErrorDescription; import org.netbeans.spi.editor.hints.Fix; +import org.netbeans.spi.editor.hints.PositionRefresher; import org.openide.ErrorManager; import org.openide.cookies.EditCookie; import org.openide.cookies.EditorCookie; @@ -81,6 +98,7 @@ import org.openide.loaders.DataObjectNotFoundException; import org.openide.text.Annotation; import org.openide.util.Exceptions; +import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; import org.openide.util.Task; @@ -114,11 +132,13 @@ private JLabel hintIcon; private ScrollCompletionPane hintListComponent; private JLabel errorTooltip; + private AtomicBoolean cancel; /** Creates a new instance of HintsUI */ private HintsUI() { EditorRegistry.addPropertyChangeListener(this); propertyChange(null); + cancel = new AtomicBoolean(false); } public JTextComponent getComponent() { @@ -138,6 +158,14 @@ this.compRef = new WeakReference(comp); register(); } + } + + private AnnotationHolder getAnnotationHolder(Document doc) { + DataObject od = (DataObject) doc.getProperty(Document.StreamDescriptionProperty); + if (od == null) { + return null; + } + return AnnotationHolder.getInstance(od.getPrimaryFile()); } private void register() { @@ -422,13 +450,8 @@ } private ParseErrorAnnotation findAnnotation(Document doc, AnnotationDesc desc, int lineNum) { - DataObject od = (DataObject) doc.getProperty(Document.StreamDescriptionProperty); - - if (od == null) - return null; - - AnnotationHolder annotations = AnnotationHolder.getInstance(od.getPrimaryFile()); - + AnnotationHolder annotations = getAnnotationHolder(doc); + if (annotations != null) { for (Annotation a : annotations.getAnnotations()) { if (a instanceof ParseErrorAnnotation) { @@ -444,15 +467,18 @@ return null; } - + boolean invokeDefaultAction(boolean onlyActive) { JTextComponent comp = getComponent(); if (comp == null) { Logger.getLogger(HintsUI.class.getName()).log(Level.WARNING, "HintsUI.invokeDefaultAction called, but comp == null"); return false; } - + Document doc = comp.getDocument(); + + cancel.set(false); + refresh(doc, comp.getCaretPosition()); if (doc instanceof BaseDocument) { Annotations annotations = ((BaseDocument) doc).getAnnotations(); @@ -507,7 +533,7 @@ if (comp == null) { return; } - boolean bulbShowing = hintIcon != null && hintIcon.isShowing(); +// boolean bulbShowing = hintIcon != null && hintIcon.isShowing(); boolean errorTooltipShowing = errorTooltip != null && errorTooltip.isShowing(); boolean popupShowing = hintListComponent != null && hintListComponent.isShowing(); @@ -541,6 +567,9 @@ } else if ( e.getKeyCode() == KeyEvent.VK_ESCAPE ) { if ( popupShowing ) { removePopup(); + } else { + //user is tired of waiting for refresh before popup is shown + cancel.set(true); } } else if ( popupShowing ) { InputMap input = hintListComponent.getInputMap(); @@ -712,4 +741,16 @@ return input; } + private void refresh(Document doc, int pos) { + Context context = ContextAccessor.getDefault().newContext(pos, cancel); + String mimeType = org.netbeans.lib.editor.util.swing.DocumentUtilities.getMimeType(doc); + Lookup lookup = MimeLookup.getLookup(mimeType); + Collection refreshers = lookup.lookupAll(PositionRefresher.class); + //set errors from all available refreshers + for (PositionRefresher ref : refreshers) { + Map> layer2Errs = ref.getErrorDescriptionsAt(context, doc); + getAnnotationHolder(doc).setErrorsForLine(pos, layer2Errs); + } + } + } --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 1436de250f92 Mon Jul 20 12:02:24 2009 +0200 @@ -0,0 +1,95 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.spi.editor.hints; + +import java.util.concurrent.atomic.AtomicBoolean; +import org.netbeans.modules.editor.hints.ContextAccessor; + +/** + * Context for {@link PositionRefresher} + * Provides position of current alt-enter invocation and its cancel status + * + * @author Max Sauer + * @since 1.8.1 + */ +public final class Context { + + final AtomicBoolean cancel; + final int position; + + Context(int position, AtomicBoolean cancel) { + this.position = position; + this.cancel = cancel; + } + + /** + * @return true if invocation has been canceled + */ + public boolean isCanceled() { + return cancel.get(); + } + + /** + * @return Caret offset inside current document + */ + public int getPosition() { + return position; + } + + /** + * @return cancel status + */ + public AtomicBoolean getCancel() { + return cancel; + } + + static { + ContextAccessor.DEFAULT = new ComtextImpl(); + } +} + +final class ComtextImpl extends ContextAccessor { + + @Override + public Context newContext(int position, AtomicBoolean cancel) { + return new Context(position, cancel); + } + +} --- a/spi.editor.hints/src/org/netbeans/spi/editor/hints/ErrorDescription.java Fri Jul 10 14:46:26 2009 +0200 +++ a/spi.editor.hints/src/org/netbeans/spi/editor/hints/ErrorDescription.java Mon Jul 20 12:02:24 2009 +0200 @@ -53,11 +53,11 @@ */ public final class ErrorDescription { - private String description; - private Severity severity; - private LazyFixList fixes; - private PositionBounds span; - private FileObject file; + private final String description; + private final Severity severity; + private final LazyFixList fixes; + private final PositionBounds span; + private final FileObject file; /** * The constructor is intentionally not public. Use --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 1436de250f92 Mon Jul 20 12:02:24 2009 +0200 @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.spi.editor.hints; + +import java.util.List; +import java.util.Map; +import javax.swing.text.Document; + +/** + * Refresher invoked upon mouse click or Alt-Enter. Clients should register + * its implementations inside layer for appropriate mimetype and provide all + * {@link ErrorDescription}s for given line (specified by position). + * + * Contract is that fixes will be already computed in the time of the retrieval. + * + * @author Max Sauer + * @since 1.8.1 + */ +public interface PositionRefresher { + + /** + * @param position current caret position inside document + * @param doc current document + * @return map of layer name to {@link ErrorDescription}s for current line + */ + public Map> getErrorDescriptionsAt(Context context, Document doc); + +}