bobbylight / AutoComplete

A code completion library for Swing text components, with special support for RSyntaxTextArea.
BSD 3-Clause "New" or "Revised" License
164 stars 55 forks source link

Customize order of completions? #64

Open BalduinLandolt opened 4 years ago

BalduinLandolt commented 4 years ago

Thanks for the great packages! I'm using RSyntaxTextArea for a tool to speed up transcribing medieval manuscripts.

Now I'm trying to implement code completion too. But BasicCompletion doesn't really help me all that much, as it sorts alphabetically.

Instead, I want all the words in the transcription to show up as completions, sorted by how often they appear in the transcription. (Assuming that words that have been common so far, would be more likely to show up again.)

My spontaneous approach was to write a Class WeightedCompletion extending BasicCompletion, with an integer weight (how often the word appears). That class, appart from adding the constructors with weight, overrides compareTo:

        @Override
        public int compareTo(Completion other){
            if (other instanceof WeightedCompletion){
                WeightedCompletion wc = (WeightedCompletion)other;
                int res = -1*Integer.valueOf(getWeight()).compareTo(Integer.valueOf(wc.getWeight()));
                if (res == 0)
                        return super.compareTo(other);
                return res;
            } else {
                return super.compareTo(other);
            }
        }

This works quite neatly when the caret is at the beginning of a new line of after a blank space, whren I hit Ctrl+Space: The completions all appear in the right order.
But, when I start typing a letter, and then hit Ctrl+Space, no code completion popup shows up anymore.

I have not been able to track the problem down, really... but clearly it doesn't like my compareTo.

Long story short: Is there a good way of getting completions in an order other than alphabetical, that I'm missing?

Any ideas/help would be greatly appreciated!

paul-griffith commented 4 years ago

In your CompletionProvider, if you're extending AbstractCompletionProvider, you can override getCompletionByInputText to do something like:

return completions.stream()
    .filter(comp -> comp instanceof WeightedCompletion)
    .filter(comp -> comp.getInputText().startsWith(inputText))
    .sorted(Comparator.comparingInt(WeightedCompletion::getWeight))
    .collect(Collectors.toList());

No need to write the custom comparator yourself.

antimatter84 commented 3 years ago

I too have a problem with the sorting. I just want to retain the order in which the completions are added to the provider. I adapted @paul-griffith 's solution to this:

return completions.stream()
    .filter(comp -> comp.getinputText().startsWith(inputText)
    .collect(Collectors.toList());

I also replaced the addCompletion() and addCompletions() methods with an implementation that does NOT sort after adding a completion. Debugging of my CompletionProvider (extends DefaultCompletionProvider) shows, that the completion objects are then in the order I add them.

But somewhere still a sorting occurs (maybe CompletionProviderBase/AbstractCompletionProvider) and they are not shown in the desired order. How can this be achieved?

SOLVED: I have extended BasicCompletion and there I override compareTo() to always return 0. This leaves the order of the completions unchanged.

tttwang23 commented 3 years ago

In these situations it is easier to sort using an external comparator. It is less buggy if all completions are ordered by source text only (say case insensitive primary ordering, then case sensitive secondary ordering). If you hack compareTo() then inserting completions into a sorted collection will have unpredictable behaviors.

ptorngren commented 1 year ago

I have the same issue, and have solved it by using an external comparator that I apply by overriding org.fife.ui.autocomplete.CompletionProviderBase.getCompletions(JTextComponent comp):

public List<Completion> getCompletions(JTextComponent comp) { List<Completion> completions = super.getCompletions(comp); completions.sort(CompletionComparator.INSTANCE); return completions; }

However, this adds a third sort to the operation. Probably not a big issue, but it would have been nice to be able to replace the default org.fife.ui.autocomplete.CompletionProviderBase.SORT_BY_RELEVANCE_COMPARATOR,

By converting this to a regular field with an option to set it externally (or in the constructor), I could easily set my own sort algorithm without the need to override the method and add an extra sort.

Another option that at least takes away the extra sort is to offer the ability to disable the relevance by setting a "sortByRelevance" flag that seems to have been prepared:

if (/*sortByRelevance*/true) { completions.sort(SORT_BY_RELEVANCE_COMPARATOR); }