Skip to content

Commit b9bd27c

Browse files
authored
[WIP] Complete rework of the auto completion (#2965)
* Works in principle * Code cleanup * Fix build * Fix build [cont] * Properly implement person name suggestions * Move auto completion support completely to gui package * Replace auto completion support in search field by JavaFX * Improve searchbar * Code cleanup and changelog entry added * Fix tests
1 parent 49d56cf commit b9bd27c

File tree

108 files changed

+1739
-3119
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+1739
-3119
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#
2121
- The buttons were changed to icons.
2222
- Completely new interface to add or modify linked files.
2323
- Removed the hidden feature that a double click in the editor inserted the current date.
24+
- Complete new implementation of the the auto complete feature.
2425
- All authors and editors are separated using semicolons when exporting to csv. [#2762](https://github.com/JabRef/jabref/issues/2762)
2526
- Improved wording of "Show recommendations: into "Show 'Related Articles' tab" in the preferences
2627
- We added integration of the Library of Congress catalog as a fetcher based on the [LCCN identifier](https://en.wikipedia.org/wiki/Library_of_Congress_Control_Number). [Feature request 636 in the forum](http://discourse.jabref.org/t/loc-marc-mods-connection/636)

src/main/java/org/jabref/gui/BasePanel.java

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@
5050
import org.jabref.gui.actions.BaseAction;
5151
import org.jabref.gui.actions.CleanupAction;
5252
import org.jabref.gui.actions.CopyBibTeXKeyAndLinkAction;
53+
import org.jabref.gui.autocompleter.AutoCompletePreferences;
54+
import org.jabref.gui.autocompleter.AutoCompleteUpdater;
55+
import org.jabref.gui.autocompleter.PersonNameSuggestionProvider;
56+
import org.jabref.gui.autocompleter.SuggestionProviders;
5357
import org.jabref.gui.bibtexkeypattern.SearchFixDuplicateLabels;
5458
import org.jabref.gui.contentselector.ContentSelectorDialog;
5559
import org.jabref.gui.desktop.JabRefDesktop;
@@ -96,10 +100,6 @@
96100
import org.jabref.gui.worker.CitationStyleToClipboardWorker;
97101
import org.jabref.gui.worker.MarkEntriesAction;
98102
import org.jabref.gui.worker.SendAsEMailAction;
99-
import org.jabref.logic.autocompleter.AutoCompletePreferences;
100-
import org.jabref.logic.autocompleter.AutoCompleter;
101-
import org.jabref.logic.autocompleter.AutoCompleterFactory;
102-
import org.jabref.logic.autocompleter.ContentAutoCompleters;
103103
import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternUtil;
104104
import org.jabref.logic.citationstyle.CitationStyleCache;
105105
import org.jabref.logic.citationstyle.CitationStyleOutputFormat;
@@ -131,6 +131,7 @@
131131
import org.jabref.model.entry.BibEntry;
132132
import org.jabref.model.entry.EntryType;
133133
import org.jabref.model.entry.FieldName;
134+
import org.jabref.model.entry.InternalBibtexFields;
134135
import org.jabref.model.entry.event.EntryChangedEvent;
135136
import org.jabref.model.entry.event.EntryEventSource;
136137
import org.jabref.model.entry.specialfields.SpecialField;
@@ -179,7 +180,7 @@ public class BasePanel extends JPanel implements ClipboardOwner, FileUpdateListe
179180
private boolean saving;
180181
private boolean updatedExternally;
181182
// AutoCompleter used in the search bar
182-
private AutoCompleter<String> searchAutoCompleter;
183+
private PersonNameSuggestionProvider searchAutoCompleter;
183184
private boolean baseChanged;
184185
private boolean nonUndoableChange;
185186
// Used to track whether the base has changed since last save.
@@ -193,7 +194,7 @@ public class BasePanel extends JPanel implements ClipboardOwner, FileUpdateListe
193194
private PreambleEditor preambleEditor;
194195
// Keeps track of the preamble dialog if it is open.
195196
private StringDialog stringDialog;
196-
private ContentAutoCompleters autoCompleters;
197+
private SuggestionProviders suggestionProviders;
197198

198199
/** the query the user searches when this basepanel is active */
199200
private Optional<SearchQuery> currentSearchQuery = Optional.empty();
@@ -258,9 +259,11 @@ public static void runWorker(AbstractWorker worker) throws Exception {
258259
clb.update(); // Runs the update() method on the EDT.
259260
}
260261

261-
// Returns a collection of AutoCompleters, which are populated from the current library
262-
public ContentAutoCompleters getAutoCompleters() {
263-
return autoCompleters;
262+
/**
263+
* Returns a collection of suggestion providers, which are populated from the current library.
264+
*/
265+
public SuggestionProviders getSuggestionProviders() {
266+
return suggestionProviders;
264267
}
265268

266269
public String getTabTitle() {
@@ -1364,17 +1367,7 @@ public void setupMainPanel() {
13641367
instantiateSearchAutoCompleter();
13651368
this.getDatabase().registerListener(new SearchAutoCompleteListener());
13661369

1367-
AutoCompletePreferences autoCompletePreferences = new AutoCompletePreferences(Globals.prefs);
1368-
// Set up AutoCompleters for this panel:
1369-
if (Globals.prefs.getBoolean(JabRefPreferences.AUTO_COMPLETE)) {
1370-
autoCompleters = new ContentAutoCompleters(getDatabase(), bibDatabaseContext.getMetaData(),
1371-
autoCompletePreferences, Globals.journalAbbreviationLoader);
1372-
// ensure that the autocompleters are in sync with entries
1373-
this.getDatabase().registerListener(new AutoCompleteListener());
1374-
} else {
1375-
// create empty ContentAutoCompleters() if autoCompletion is deactivated
1376-
autoCompleters = new ContentAutoCompleters();
1377-
}
1370+
setupAutoCompletion();
13781371

13791372
// restore floating search result
13801373
// (needed if preferences have been changed which causes a recreation of the main table)
@@ -1390,17 +1383,30 @@ public void setupMainPanel() {
13901383
splitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, event -> saveDividerLocation());
13911384
}
13921385

1386+
/**
1387+
* Set up auto completion for this database
1388+
*/
1389+
private void setupAutoCompletion() {
1390+
AutoCompletePreferences autoCompletePreferences = Globals.prefs.getAutoCompletePreferences();
1391+
if (autoCompletePreferences.shouldAutoComplete()) {
1392+
suggestionProviders = new SuggestionProviders(autoCompletePreferences, Globals.journalAbbreviationLoader);
1393+
suggestionProviders.indexDatabase(getDatabase());
1394+
// Ensure that the suggestion providers are in sync with entries
1395+
this.getDatabase().registerListener(new AutoCompleteUpdater(suggestionProviders));
1396+
} else {
1397+
// Create empty suggestion providers if auto completion is deactivated
1398+
suggestionProviders = new SuggestionProviders();
1399+
}
1400+
}
1401+
13931402
public void updateSearchManager() {
13941403
frame.getGlobalSearchBar().setAutoCompleter(searchAutoCompleter);
13951404
}
13961405

13971406
private void instantiateSearchAutoCompleter() {
1398-
AutoCompletePreferences autoCompletePreferences = new AutoCompletePreferences(Globals.prefs);
1399-
AutoCompleterFactory autoCompleterFactory = new AutoCompleterFactory(autoCompletePreferences,
1400-
Globals.journalAbbreviationLoader);
1401-
searchAutoCompleter = autoCompleterFactory.getPersonAutoCompleter();
1407+
searchAutoCompleter = new PersonNameSuggestionProvider(InternalBibtexFields.getPersonNameFields());
14021408
for (BibEntry entry : bibDatabaseContext.getDatabase().getEntries()) {
1403-
searchAutoCompleter.addBibtexEntry(entry);
1409+
searchAutoCompleter.indexEntry(entry);
14041410
}
14051411
}
14061412

@@ -2107,29 +2113,12 @@ private class SearchAutoCompleteListener {
21072113

21082114
@Subscribe
21092115
public void listen(EntryAddedEvent addedEntryEvent) {
2110-
searchAutoCompleter.addBibtexEntry(addedEntryEvent.getBibEntry());
2111-
}
2112-
2113-
@Subscribe
2114-
public void listen(EntryChangedEvent entryChangedEvent) {
2115-
searchAutoCompleter.addBibtexEntry(entryChangedEvent.getBibEntry());
2116-
}
2117-
}
2118-
2119-
/**
2120-
* Ensures that auto completers are up to date when entries are changed AKA Let the auto completer, if any, harvest
2121-
* words from the entry
2122-
*/
2123-
private class AutoCompleteListener {
2124-
2125-
@Subscribe
2126-
public void listen(EntryAddedEvent addedEntryEvent) {
2127-
BasePanel.this.autoCompleters.addEntry(addedEntryEvent.getBibEntry());
2116+
searchAutoCompleter.indexEntry(addedEntryEvent.getBibEntry());
21282117
}
21292118

21302119
@Subscribe
21312120
public void listen(EntryChangedEvent entryChangedEvent) {
2132-
BasePanel.this.autoCompleters.addEntry(entryChangedEvent.getBibEntry());
2121+
searchAutoCompleter.indexEntry(entryChangedEvent.getBibEntry());
21332122
}
21342123
}
21352124

@@ -2145,7 +2134,7 @@ public void listen(EntryAddedEvent addedEntryEvent) {
21452134

21462135
@Subscribe
21472136
public void listen(EntryChangedEvent entryChangedEvent) {
2148-
frame.getGlobalSearchBar().setDontSelectSearchBar(true);
2137+
frame.getGlobalSearchBar().setDontSelectSearchBar();
21492138
frame.getGlobalSearchBar().performSearch();
21502139
}
21512140

src/main/java/org/jabref/gui/JabRefFrame.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ public void windowClosing(WindowEvent e) {
624624
if (currentSearchQuery.isPresent()) {
625625
content = currentSearchQuery.get().getQuery();
626626
}
627-
globalSearchBar.setSearchTerm(content, true);
627+
globalSearchBar.setSearchTerm(content);
628628
}
629629

630630
currentBasePanel.getPreviewPanel().updateLayout();

src/main/java/org/jabref/gui/actions/ManageKeywordsAction.java

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,15 @@
2525
import javax.swing.JTextField;
2626

2727
import org.jabref.Globals;
28-
import org.jabref.JabRefGUI;
2928
import org.jabref.gui.BasePanel;
3029
import org.jabref.gui.JabRefFrame;
31-
import org.jabref.gui.autocompleter.AutoCompleteListener;
3230
import org.jabref.gui.keyboard.KeyBinding;
3331
import org.jabref.gui.undo.NamedCompound;
3432
import org.jabref.gui.undo.UndoableFieldChange;
35-
import org.jabref.logic.autocompleter.AutoCompleter;
3633
import org.jabref.logic.l10n.Localization;
3734
import org.jabref.logic.specialfields.SpecialFieldsUtils;
3835
import org.jabref.model.FieldChange;
3936
import org.jabref.model.entry.BibEntry;
40-
import org.jabref.model.entry.FieldName;
4137
import org.jabref.model.entry.Keyword;
4238
import org.jabref.model.entry.KeywordList;
4339
import org.jabref.model.strings.StringUtil;
@@ -53,19 +49,13 @@
5349
public class ManageKeywordsAction extends MnemonicAwareAction {
5450

5551
private final JabRefFrame frame;
56-
52+
private final KeywordList sortedKeywordsOfAllEntriesBeforeUpdateByUser = new KeywordList();
5753
private JDialog diag;
58-
59-
6054
private DefaultListModel<Keyword> keywordListModel;
61-
6255
private JRadioButton intersectKeywords;
6356
private JRadioButton mergeKeywords;
64-
6557
private boolean canceled;
6658

67-
private final KeywordList sortedKeywordsOfAllEntriesBeforeUpdateByUser = new KeywordList();
68-
6959

7060
public ManageKeywordsAction(JabRefFrame frame) {
7161
putValue(Action.NAME, Localization.menuTitle("Manage keywords"));
@@ -171,31 +161,6 @@ public void keyPressed(KeyEvent arg0) {
171161
}
172162
});
173163

174-
AutoCompleter<String> autoComp = JabRefGUI.getMainFrame().getCurrentBasePanel().getAutoCompleters()
175-
.get(FieldName.KEYWORDS);
176-
AutoCompleteListener acl = new AutoCompleteListener(autoComp);
177-
keyword.addKeyListener(acl);
178-
keyword.addFocusListener(acl);
179-
keyword.addKeyListener(new KeyListener() {
180-
181-
@Override
182-
public void keyTyped(KeyEvent e) {
183-
// Do nothing
184-
}
185-
186-
@Override
187-
public void keyReleased(KeyEvent e) {
188-
// Do nothing
189-
}
190-
191-
@Override
192-
public void keyPressed(KeyEvent e) {
193-
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
194-
addActionListener.actionPerformed(null);
195-
}
196-
}
197-
});
198-
199164
// Key bindings:
200165
ActionMap am = builder.getPanel().getActionMap();
201166
InputMap im = builder.getPanel().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.jabref.gui.autocompleter;
2+
3+
import java.util.Locale;
4+
5+
public class AppendPersonNamesStrategy implements AutoCompletionStrategy {
6+
7+
/**
8+
* true if the input should be split at a single white space instead of the usual delimiter " and " for names.
9+
* Useful if the input consists of a list of last names.
10+
*/
11+
private final boolean separationBySpace;
12+
13+
public AppendPersonNamesStrategy() {
14+
this(false);
15+
}
16+
17+
public AppendPersonNamesStrategy(boolean separationBySpace) {
18+
this.separationBySpace = separationBySpace;
19+
}
20+
21+
@Override
22+
public AutoCompletionInput analyze(String input) {
23+
if (this.separationBySpace) {
24+
return determinePrefixAndReturnRemainder(input, " ");
25+
} else {
26+
return determinePrefixAndReturnRemainder(input, " and ");
27+
}
28+
}
29+
30+
private AutoCompletionInput determinePrefixAndReturnRemainder(String input, String delimiter) {
31+
int index = input.toLowerCase(Locale.ROOT).lastIndexOf(delimiter);
32+
if (index >= 0) {
33+
String prefix = input.substring(0, index + delimiter.length());
34+
String rest = input.substring(index + delimiter.length());
35+
return new AutoCompletionInput(prefix, rest);
36+
} else {
37+
return new AutoCompletionInput("", input);
38+
}
39+
}
40+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.jabref.gui.autocompleter;
2+
3+
/**
4+
* For "ONLY_FULL", the auto completer returns the full name, e.g. "Smith, Bob"
5+
* For "ONLY_ABBREVIATED", the auto completer returns the first name abbreviated, e.g. "Smith, B."
6+
* For "BOTH", the auto completer returns both versions.
7+
*/
8+
public enum AutoCompleteFirstNameMode {
9+
ONLY_FULL,
10+
ONLY_ABBREVIATED,
11+
BOTH;
12+
13+
public static AutoCompleteFirstNameMode parse(String input) {
14+
try {
15+
return AutoCompleteFirstNameMode.valueOf(input);
16+
} catch (IllegalArgumentException ex) {
17+
// Should only occur when preferences are set directly via preferences.put and not via setFirstnameMode
18+
return AutoCompleteFirstNameMode.BOTH;
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)