Bug 119603 - [api][content assist] allow contributions to filter and sort proposals
Summary: [api][content assist] allow contributions to filter and sort proposals
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Text (show other bugs)
Version: 3.2   Edit
Hardware: PC All
: P3 normal (vote)
Target Milestone: 3.2 M5   Edit
Assignee: Tom Hofmann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-12-07 05:54 EST by Tom Hofmann CLA
Modified: 2006-02-07 03:54 EST (History)
8 users (show)

See Also:


Attachments
119603.diff.txt (12.53 KB, patch)
2006-01-19 11:25 EST, Tom Hofmann CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tom Hofmann CLA 2005-12-07 05:54:39 EST
Split off bug 110913:

There are currently no provisions that allow contributions to filter or sort the content assist proposals contributed by other completion proposal computers.

Currently we don't have any provisions for either problem. Please add your thoughts and requirements to this PR.

---

A) Filtering

Problem: Eclipse Rule "Add, don't remove". 
We generally don't allow an extension to remove (filter) contributions made by another extension. In code assist, this may be needed however. The use case is APT, wich may be able to offer much preciser proposals inside annotations than JDT.

B) Sorting

1) Overall Sorting
How can multiple extensions settle for a common ranking mechanism without knowing each other?

One idea is to create coarse-grained slots on a number range which all contributions could use, while still allowing an extension to more precisely sort its contributions. 

Example: 

Probable       [75.0, 100.0)
Over Average   [50.0,  75.0)
Bulk           [25.0,  50.0)
Possible       [ 0.0,  25.0)

JDT could still create a very precise ranking of its proposals inside one slot.

2) Relative ranking
There are a number of places where an extension B knows about the proposals of extension A and wants to rank its proposals depending on the A proposals.

Example:
Templates that match a Java keyword should be ranked just before the proposal for that keyword.
Comment 1 Tom Hofmann CLA 2005-12-07 05:57:19 EST
added CC list from bug 110913
Comment 2 Tom Hofmann CLA 2006-01-17 10:47:51 EST
(In reply to bug 110913 comment #5)
> The Mylar Technology subproject filters proposals based on whether or not they
> are in the current degree-of-interest by briniging these to the top of the list. 
>  To do this, it must get a unique handle identifier for the structural element
> corresponding to the proposal.  For Java this is the handle identifier for the
> IMember corresponding to the MemberProposalInfo.  Currently it's quite painful
> to go from a proposal to the corresponding element, and I think that virutal
> proxy style identifiers for proposals are contextual information that's useful
> for any plug-in that wants to filter them. 

The problem is that there aren't just proposals for Java elements. There may be simple textual proposals (word completions in javadoc), template completions, and even the proposals from jdt-core are not all for java elements: consider keyword completions, for example.

You want to be able to use the information about the proposed Java element *if* it is there, but need to be able to deal with other proposals as well.
Comment 3 Mik Kersten CLA 2006-01-17 12:14:52 EST
Right, there are those cases where proposals don't correspond to structural elements in the workspace (and Mylar doesn't affect the sorting of those).  But for the ones that are structural elements:

1) Java elements:
All I need at this point is for the visibility of MemberProposalInfo.resolveMember() to be public instead of protected, since I can get the identifier from the IMember.

2) Others (e.g. XML, HTML)
At some point, perhaps post 3.2, it would be useful to be able to get at the identitity of the element via the proposal (e.g. via something like getCorrespondingElement(), or an adapter on ProposalInfo, or an ElementProposalInfo subclass).  It would be great if you could keep this in mind, in case it turns out to be useful to others as well. 

Would it be possible to get (1) for M5?  It seems that this stuff should remain internal and subject to change, but it would make things considerably easier for Mylar.  Note that if resolveMember() currently only gets called once in he lifecycel of the proposal it could be a good idea to stick the last resolved member in a field.  But for Mylar's usage of this performance is a non-issue even if I call this method reflectively, so just the visibility change is sufficient.
Comment 4 Tom Hofmann CLA 2006-01-18 13:35:57 EST
Performance is an issue though... we try to avoid creating and resolving all IJavaElements. This is especially costly when displaying many types, e.g. when invoking content assist here (| == caret):

class A {
   S|
}

Resolving all ITypes starting with 'S' (282 in my test workbench) takes at least something in the range of seconds.

Anyway, I could probably make resolveMember public (as always with no guarantees as this is internal code).

The idea that any proposal should be able to answer the domain element it completes ("getCorrespondingElement") - I don't see how this is doable; the type would have to be 'Object' or 'String' as there are no generic model objects in eclipse - filters and sorters would have to cast around to do something useable.

Hmm, not too optimistic here...
Comment 5 Mik Kersten CLA 2006-01-18 14:12:37 EST
Right--I think that JDT needs to avoid resolving all IJavaElements, but give a filtering client the option to resolve that if the element is needed to determine sorting or filtering.  Making that method public will achieve that, but it defintely makes sense that this should stay internal.  Fyi, when using Mylar and having the IType's resolved on my 1.8 Centrino ThinkPad, with 1421 matches for S* in my rather large workspace, the proposals take under a second to come up without being cached so the performance is acceptable.  But thanks for that pointer--I'll use it for manual performance checks.

Regarding mapping to domain elements--yup, I realize that's a problem.  Sometimes I think that it would be useful to have something like an IVirtualProxy interface for all elements that can be identified via a handle identifier in the workspace.  I already have something similar in place so that the Mylar core and UI stuff can handle java, resources, and xml stuff without being coupled to it.

But that's another discussion, and in the meantime it would be very helpful and address the key Java case if you could make resolveMember public.  
Comment 6 Tom Hofmann CLA 2006-01-19 10:03:48 EST
After discussing the 'sorting' requirement with Martin and Benno:

Jdt-ui has already introduced a MRU history for Java types (see org.eclipse.jdt.internal.corext.util.TypeInfoHistory) that remembers the types recently used in content assist, organize imports and possibly during other user actions.

The plan is to extend the MRU history to cover other Java elements as well, mainly methods.

@Mik
Wouldn't it be better if you got to participate when the relevance boost for a Java element is computed? Instead of a proposalSorting extension point, that is.

Of course you'd still need a way to get the Java element from a proposal, but we can do that.
Comment 7 Tom Hofmann CLA 2006-01-19 11:25:21 EST
Created attachment 33271 [details]
119603.diff.txt

This is what an API for filtering and sorting proposals could look like. I think that the sorting problem was better addressed by something along the lines of comment 6.
Comment 8 Mik Kersten CLA 2006-01-19 12:24:15 EST
Here's the thing: Mylar can't simply bump up the order of the MRU list, because it replaces the MRU list (please see my 3 comments on bug 106000).  MRU lists end up feeling like MS Office adaptive menus because they tend to have too much of the wrong stuff in them and not enough of the right stuff in them.  What the Mylar usage data is indicating is that an MRU approach doesn't scale.  It works for people who have a workspace where they frequently work on a subset of a large system with unde a dozen projects, but doesn't scale to large workspaces and many different tasks.

Mylar knows which Java elements that are part of the current task context, and so these need to be sepearted on top of the list.  That's why if (and only if) you have a task active Mylar puts a separator into content assist--usage data shows that the few elements above the bar are *very* likely to be selected as part of the completion.  It's also why it replaces the type history MRU list if you have a context active (and puts it back if you don't)--the types it puts there are guaranteed to be relevant to the current task.

The key here is that Mylar doesn't contribute to an MRU ranking, it is a new technology that's an alternative to MRU rankings.  The nice thing is that we get to share some UI (e.g. the separator in Open Type).  If JDT chose to use a separator for the MRU list to make the distinction between structural/domain and usage-based rankings explicit in content assist, Mylar should replace that too as an alternative ranking.  It would be bad if there were both an MRU and an task context interest ranking both showing up. 

Regarding the patch, I need to be able to continue doing the following 3 things:
- stick in a fake proposal that's a separator (----------)
- move proposals that are in the current task's degree of interest above that separator (for Java elements that involves getting the handle identifier)
- sort the proposal above the separator according to degree of interest 

Extending the JavaCompletionProcessor is working fine, but it would be better if resolveMember() were public.  With the patch, would I still be able to stick in the separator?  Also, I don't actually want any proposals to disappear, so it looks like I wouldn't use the filtering? 





Comment 9 Tom Hofmann CLA 2006-01-19 12:33:38 EST
I guess I wasn't too clear - I do understand that Mylar is more than just an MRU list...

The (unfinished) idea is, that there could be a degree-of-interest service in eclipse which could be contributed to. The default implementation provided by us would be MRU based, but you would be able to extend (replace?) that.

The advantage of that would be that elements are not just boosted for content assist, but also in other circumstances (organize import...).
Comment 10 Mik Kersten CLA 2006-01-19 12:46:06 EST
Right, if that were extensible and the strategy for determining it were replacable, that would be great.  Using JDT and Mylar to figuring out what that generic facility should be, and what the corresponding UI mechanisms should (e.g. the seaprator) sounds like a great idea to me.  And yes, this needs to apply to stuff like orgainize imports to spare us all from accidentally importing the wrong List by hitting Enter too quickly.

Is this something that you're considering for 3.2?  What's the time frame for the MRU content assist sorting?  If you guys decided that the separator was a good idea, and the strategy for determining 'hits' could be replaced, perhaps Mylar could guinea pig that appraoch for content assist and type history...
Comment 11 Tom Hofmann CLA 2006-01-19 12:48:44 EST
I surface the java element from AbstractJavaCompletionProposal now - note that the public accessor for the ProposalInfo is gone, but you now get the java element (getJavaElement()).

Not sure about the history/relevance-boost thing - we just discussed this today and we have to decide yet in what direction to go. 

The UI (separator etc.) is yet another thing...
Comment 12 Mik Kersten CLA 2006-01-19 15:48:42 EST
Great!!  I'll look for getJavaElement() in when I move to the next integration build.

Regarding the relevance boost, my suggestion is to consider the UI if adding it.  Right now it's really nice that the completion order predictably sorts based on Java-specific heuristics, and I think that users get accustomed to that predictability.  That's why I had to add the separator for 'history' elements--even with Mylar's very high accuracy it just felt too unpredictable to munge these two very different kinds of rankings. 
Comment 13 Tom Hofmann CLA 2006-01-24 14:22:31 EST
@Mik: after discussing this with Martin we decided not to come up with a general relevance extension point / API and stick with the one proposed for code assist.

@All: I released a first implementation of the javaCompletionProposalSorters extension point > 20060124. Please use and comment! The Java default sorting (by relevance / alphabetically) uses the new extension point and more can be plugged in - see the preference page.
Comment 14 Mik Kersten CLA 2006-02-01 20:49:35 EST
Having the IJavaElement visible is a big help AbstractJavaCompletionProposal.  I'd like to use the javaCompletionProposalSorters extension point, but it doesn't allow me to add in the separator that indicates which proposals are in the task context.  Since this seems like a special case for Mylar, and Mylar benefits from the additiona control over the processor, my question is:

* Is it best to continue to create my own processor via overriding JavaSourceViewerConfiguration.getContentAssistant(..)?  Down side is that it requries a custom CompilationUnitEditor to be associated with .java.

* Or does it make sense to request an extension point for overriding the processor, similar to javadocCompletionProcessor?
Comment 15 Tom Hofmann CLA 2006-02-02 03:21:44 EST
(In reply to comment #14)
> * Is it best to continue to create my own processor via overriding
> JavaSourceViewerConfiguration.getContentAssistant(..)?  Down side is that it
> requries a custom CompilationUnitEditor to be associated with .java.
> 
> * Or does it make sense to request an extension point for overriding the
> processor, similar to javadocCompletionProcessor?

Already there: you can add your IJavaCompletionProposalComputer to contribute proposals to the Java editor's content assist process. See the javaCompletionProposalComputer.exsd extension point description.
Comment 16 Mik Kersten CLA 2006-02-02 14:37:17 EST
Duh, I should have found that.  I've ported things over that extension point and it's working great!  In the spirit of moving towards comment#6, rather than reording proposals Mylar's ProposalComputer projects the interest model onto the relevance level, and inserts a custom separator with the right relevance level threshold.  It's not ideal mixing the usage infomration with relevance heuristics via the int field, since these are orthogonal, but it's working well in terms JDT integration and doesn't require a custom sorter.

One comment: from the user's point of view, is there any reason to surface two seperate Java proposal computers in the preference page?  I can't imagine having one enabled and not the other.
Comment 17 Tom Hofmann CLA 2006-02-03 03:14:31 EST
(In reply to comment #16)
> One comment: from the user's point of view, is there any reason to surface two
> seperate Java proposal computers in the preference page?  I can't imagine
> having one enabled and not the other.

It's not only about enablement - the really great and intuitive UI of the work in progress preference page lets you also define the order of proposal categories that will be cycled through as you repeatedly presss Ctrl+Space. Alternatively, you can also assign separate keybindings to the different categories, e.g:

Ctrl+Space: Java proposals except types
Shift+Space: Type proposals
Alt+/: word (hippie) completions

The Mylar-Man will probably prefer a single list with the relevant proposals sorted magically on top, but other users prefer to have more control over what is shown...

"if I type 'hash' and want to complete to 'HashMap', why does 'hashCode()' show up at all?"

"I want to expand a template / macro with one single keypress!"
Comment 18 Mik Kersten CLA 2006-02-06 22:54:18 EST
In general I agree that the additional control is useful and really like the way it has been exposed both in UI and extension points.  But regarding the Java type/non-type distinction, while I don't have any real objections, I'm still having a hard time imaging more than a small proportion of very advanced users making use of that distinction.  Can't distinguishing whether the user is trying to complete a Java type name (HashMap) or a local/var (hashCode) can come from whether the first letter they typed is a capital or not?  That seems easier than remembering another completion trigger.

While the discussion is still interesting, I'm marking this report verified.  Quick question though: is there a place that reliably captures the range of the relevance ranking?  I'd like to be able to refer to or compute what the maximum relevance value of a Java element can be.
Comment 19 Tom Hofmann CLA 2006-02-07 03:54:10 EST
(In reply to comment #18)
> In general I agree that the additional control is useful and really like the
> way it has been exposed both in UI and extension points. 

Thanks - my comment about the great UI wasn't meant too serious really... We have bug 110147 to update the preference page for 3.2.

> But regarding the
> Java type/non-type distinction, while I don't have any real objections, I'm
> still having a hard time imaging more than a small proportion of very advanced
> users making use of that distinction.  Can't distinguishing whether the user is
> trying to complete a Java type name (HashMap) or a local/var (hashCode) can
> come from whether the first letter they typed is a capital or not?  That seems
> easier than remembering another completion trigger.

Different users have different usage patterns...

> Quick question though: is there a place that reliably captures the range of the
> relevance ranking?  I'd like to be able to refer to or compute what the maximum
> relevance value of a Java element can be.

I remember reading that the relevance of org.eclipse.jdt.core.CompletionProposal is defined to be in [1, 100], but cannot find that definition now. However, this is not adhered to in jdt-ui's IJavaCompletionProposal anyway. We currently don't define a relevance range as we constantly tune the used values - you are allowed to use the relevance, but there is no contract attached to it.

Aside, I think a relevance scheme covering a broad set of different proposal kinds is hard to come up. It would probably use a floating point range rather than an integer range, to make it easier to sneak in a proposal between two others.