Skip to content

feat: support open reference at semantic scholar #13278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

brandon-lau0
Copy link
Contributor

This PR adds new "Search Semantic Scholar" option to the list of available search engines to allow users to directly link to within Jabref.
Screenshot 2025-05-30 at 5 01 32 PM

Screenshot 2025-05-30 at 5 01 08 PM

The URL is also configurable within the "Search Engine URL Templates" section in the "Web search" tab in Preferences. For example, we add the {author} parameter to search via title and author.

Screenshot 2025-05-30 at 5 02 02 PM

Screenshot 2025-05-30 at 5 04 14 PM

Mandatory checks

  • I own the copyright of the code submitted and I license it under the MIT license
  • Change in CHANGELOG.md described in a way that is understandable for the average user (if change is visible to the user)
  • [/] Tests created for changes (if applicable)
  • Manually tested changed features in running JabRef (always required)
  • Screenshots added in PR description (if change is visible to the user)
  • Checked developer's documentation: Is the information available and up to date? If not, I outlined it in this pull request.
  • Checked documentation: Is the information available and up to date? If not, I created an issue at https://github.com/JabRef/user-documentation/issues or, even better, I submitted a pull request to the documentation repository.

@brandon-lau0 brandon-lau0 marked this pull request as ready for review June 8, 2025 02:32
@brandon-lau0 brandon-lau0 changed the title Semantic scholar search option 2 feat: support open reference at semantic scholar Jun 8, 2025
@jabref-machine
Copy link
Collaborator

Your pull request needs to link an issue correctly.

To ease organizational workflows, please link this pull-request to the issue with syntax as described in https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue:

Linking a pull request to an issue using a keyword

You can link a pull request to an issue by using a supported keyword in the pull request's description or in a commit message.

Examples

  • Fixes #xyz links pull-request to issue. Merging the PR will close the issue.
  • Fixes https://github.com/JabRef/jabref/issues/xyz links pull-request to issue. Merging the PR will close the issue.
  • Fixes https://github.com/Koppor/jabref/issues/xyz links pull-request to issue. Merging the PR will close the issue.
  • Fixes [#xyz](https://github.com/JabRef/jabref/issues/xyz) links pull-request to issue. Merging the PR will NOT close the issue.

@@ -37,6 +37,9 @@ public enum StandardActions implements Action {
EXTRACT_FILE_REFERENCES_OFFLINE(Localization.lang("Extract references from file (offline)"), IconTheme.JabRefIcons.FILE_STAR),
OPEN_URL(Localization.lang("Open URL or DOI"), IconTheme.JabRefIcons.WWW, KeyBinding.OPEN_URL_OR_DOI),
SEARCH_SHORTSCIENCE(Localization.lang("Search ShortScience")),
SEARCH_GOOGLE_SCHOLAR(Localization.lang("Search Google Scholar")),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new string 'Search Google Scholar' should be consistent with existing strings and grouped semantically with similar search actions.

@@ -37,6 +37,9 @@ public enum StandardActions implements Action {
EXTRACT_FILE_REFERENCES_OFFLINE(Localization.lang("Extract references from file (offline)"), IconTheme.JabRefIcons.FILE_STAR),
OPEN_URL(Localization.lang("Open URL or DOI"), IconTheme.JabRefIcons.WWW, KeyBinding.OPEN_URL_OR_DOI),
SEARCH_SHORTSCIENCE(Localization.lang("Search ShortScience")),
SEARCH_GOOGLE_SCHOLAR(Localization.lang("Search Google Scholar")),
SEARCH_SEMANTIC_SCHOLAR(Localization.lang("Search Semantic Scholar")),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new string 'Search Semantic Scholar' should be consistent with existing strings and grouped semantically with similar search actions.

@@ -37,6 +37,9 @@ public enum StandardActions implements Action {
EXTRACT_FILE_REFERENCES_OFFLINE(Localization.lang("Extract references from file (offline)"), IconTheme.JabRefIcons.FILE_STAR),
OPEN_URL(Localization.lang("Open URL or DOI"), IconTheme.JabRefIcons.WWW, KeyBinding.OPEN_URL_OR_DOI),
SEARCH_SHORTSCIENCE(Localization.lang("Search ShortScience")),
SEARCH_GOOGLE_SCHOLAR(Localization.lang("Search Google Scholar")),
SEARCH_SEMANTIC_SCHOLAR(Localization.lang("Search Semantic Scholar")),
SEARCH(Localization.lang("Search...")),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new string 'Search...' should be consistent with existing strings and grouped semantically with similar search actions.

@@ -36,6 +36,7 @@
import org.jabref.gui.specialfields.SpecialFieldMenuItemFactory;
import org.jabref.logic.citationstyle.CitationStyleOutputFormat;
import org.jabref.logic.citationstyle.CitationStylePreviewLayout;
import org.jabref.logic.importer.ImporterPreferences;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ImporterPreferences import is added but not used in the patch. Unused imports should be removed to maintain clean code.

final List<BibEntry> bibEntries = stateManager.getSelectedEntries();

if (bibEntries.size() != 1) {
dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notification message should be in sentence case, not title case, to maintain consistency with other user interface texts.

Comment on lines +48 to +50
try {
NativeDesktop.openExternalViewer(databaseContext, preferences, url, StandardField.URL, dialogService, bibEntries.getFirst());
} catch (IOException ex) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try block covers too many statements. It should be minimized to cover only the statement that might throw the IOException.

final List<BibEntry> bibEntries = stateManager.getSelectedEntries();

if (bibEntries.size() != 1) {
dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notification message should be in sentence case, not title case, to maintain consistency with other user interface texts.

dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
return;
}
externalLinkCreator.getGoogleScholarSearchURL(bibEntries.getFirst()).ifPresent(url -> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method getGoogleScholarSearchURL should not return null. It should return an Optional to avoid null checks and improve code safety.

Comment on lines +48 to +50
try {
NativeDesktop.openExternalViewer(databaseContext, preferences, url, StandardField.URL, dialogService, bibEntries.getFirst());
} catch (IOException ex) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try block covers too many statements. It should be minimized to only cover the statement that might throw the IOException.

@jabref-machine
Copy link
Collaborator

JUnit tests of jablib are failing. You can see which checks are failing by locating the box "Some checks were not successful" on the pull request page. To see the test output, locate "Tests / Unit tests (pull_request)" and click on it.

You can then run these tests in IntelliJ to reproduce the failing tests locally. We offer a quick test running howto in the section Final build system checks in our setup guide.

@@ -41,7 +44,7 @@ public void execute() {
dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
return;
}
ExternalLinkCreator.getShortScienceSearchURL(bibEntries.getFirst()).ifPresent(url -> {
externalLinkCreator.getShortScienceSearchURL(bibEntries.getFirst()).ifPresent(url -> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try block covers too many statements. It should only cover the statement that might throw an IOException to improve readability and maintainability.

final List<BibEntry> bibEntries = stateManager.getSelectedEntries();

if (bibEntries.size() != 1) {
dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notification message should be in sentence case, not title case, to maintain consistency with other user interface texts.

@@ -41,7 +44,7 @@ public void execute() {
dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
return;
}
ExternalLinkCreator.getShortScienceSearchURL(bibEntries.getFirst()).ifPresent(url -> {
externalLinkCreator.getShortScienceSearchURL(bibEntries.getFirst()).ifPresent(url -> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try block covers too many statements, which can make it harder to identify the exact source of an exception. It should cover as few statements as possible.

@@ -196,6 +216,14 @@ public void storeSettings() {
if (apikeyPersistAvailableProperty.get()) {
preferences.getImporterPreferences().getApiKeys().addAll(apiKeys);
}

// Save custom URL templates to preferences
Map<String, String> templates = searchEngines.stream()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Collectors.toMap without handling duplicate keys can lead to runtime exceptions. Ensure that duplicate keys are handled appropriately.

author.ifPresent(a -> uriBuilder.addParameter("author", a));
return uriBuilder.toString();
} catch (URISyntaxException ex) {
throw new AssertionError("ShortScience URL is invalid.", ex);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exceptions for control flow is not recommended. Instead, handle the exception in a way that does not rely on throwing an AssertionError for normal operations.

author.ifPresent(a -> uriBuilder.addParameter("author", a));
return uriBuilder.toString();
} catch (URISyntaxException ex) {
throw new AssertionError("Default Google Scholar URL is invalid.", ex);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exceptions for control flow is not recommended. Instead, handle the exception in a way that does not rely on throwing an AssertionError for normal operations.

author.ifPresent(a -> uriBuilder.addParameter("author", a));
return uriBuilder.toString();
} catch (URISyntaxException ex) {
throw new AssertionError("Default Semantic Scholar URL is invalid.", ex);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exceptions for control flow is not recommended. Instead, handle the exception in a way that does not rely on throwing an AssertionError for normal operations.

author.ifPresent(a -> uriBuilder.addParameter("author", a));
return uriBuilder.toString();
} catch (URISyntaxException ex) {
throw new AssertionError("ShortScience URL is invalid.", ex);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exceptions for control flow is not recommended. Exceptions should be reserved for truly exceptional conditions, not for normal control flow.

author.ifPresent(a -> uriBuilder.addParameter("author", a));
return uriBuilder.toString();
} catch (URISyntaxException ex) {
throw new AssertionError("Default Google Scholar URL is invalid.", ex);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exceptions for control flow is not recommended. Exceptions should be reserved for truly exceptional conditions, not for normal control flow.

author.ifPresent(a -> uriBuilder.addParameter("author", a));
return uriBuilder.toString();
} catch (URISyntaxException ex) {
throw new AssertionError("Default Semantic Scholar URL is invalid.", ex);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exceptions for control flow is not recommended. Exceptions should be reserved for truly exceptional conditions, not for normal control flow.

@@ -1,9 +1,12 @@
package org.jabref.logic.util;

import java.net.MalformedURLException;
import java.util.Collections;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement for Collections is unnecessary as the code should use List.of() for creating empty lists instead of Collections.emptyList().

@@ -1,9 +1,12 @@
package org.jabref.logic.util;

import java.net.MalformedURLException;
import java.util.Collections;
import java.util.List;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement for List is unnecessary as the code should use List.of() for creating empty lists instead of Collections.emptyList().

@koppor
Copy link
Member

koppor commented Jun 8, 2025

Follow up at #13153

@brandon-lau0 please, just push to your branch and do not create a new PR. New PRs cost time on our side and reduce time to give feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants