Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ab9b3f5
add pdf opening ability
palukku Nov 10, 2025
3d7c638
fix jbang
palukku Nov 10, 2025
714e3c7
fix format
palukku Nov 10, 2025
9a31138
fix checkstyle
palukku Nov 10, 2025
93a8bad
fix tests
palukku Nov 10, 2025
b3cf132
remove unused import
palukku Nov 10, 2025
13e1d1e
Add debug
koppor Nov 9, 2025
4956a86
.jbang scripts should not be included inside themselves
koppor Nov 10, 2025
8d07104
Add ability to open pdfs in standalone nad gui mode
palukku Nov 10, 2025
99b46cd
update execution rights
palukku Nov 10, 2025
d618738
More `@NonNull`
koppor Nov 10, 2025
5b0aa66
More `@NonNull`
koppor Nov 10, 2025
8cfe32f
Fix data type
koppor Nov 10, 2025
cc75b7a
Fix catch of NPE in context of Optionals
koppor Nov 10, 2025
08f90e5
Add `@NullMarked` and refactor Optional use
koppor Nov 10, 2025
6a26f18
More close constructors
koppor Nov 10, 2025
2bb3f84
Add link to other tests
koppor Nov 10, 2025
a1242d7
Output field at parsing
koppor Nov 10, 2025
47f02c1
Initial test for BibDefinitionProvider
koppor Nov 11, 2025
1105762
Merge remote-tracking branch 'origin/main' into add-pdf-open
koppor Nov 11, 2025
e982620
address comments
palukku Nov 20, 2025
07ae63b
Fix file path on databasecontext
palukku Nov 25, 2025
e2e3446
Fix test
koppor Nov 27, 2025
98d4108
Merge remote-tracking branch 'origin/main' into add-pdf-open
koppor Nov 27, 2025
8a7e0b7
Fix formatting
koppor Nov 27, 2025
ec07091
Fix Path.of - and remove obsolete method
koppor Nov 27, 2025
e67e877
Group ".remote" together in module-info
koppor Nov 27, 2025
ccf5e00
Fix URI.from() architecture rule
koppor Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .jbang/JabLsLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
//SOURCES ../jabls/src/main/java/org/jabref/languageserver/util/LspLinkHandler.java
//SOURCES ../jabls/src/main/java/org/jabref/languageserver/util/LspParserHandler.java
//SOURCES ../jabls/src/main/java/org/jabref/languageserver/util/LspRangeUtil.java
//SOURCES ../jabls/src/main/java/org/jabref/languageserver/util/definition/BibDefinitionProvider.java
//SOURCES ../jabls/src/main/java/org/jabref/languageserver/util/definition/DefinitionProvider.java
//SOURCES ../jabls/src/main/java/org/jabref/languageserver/util/definition/DefinitionProviderFactory.java
//SOURCES ../jabls/src/main/java/org/jabref/languageserver/util/definition/LatexDefinitionProvider.java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,7 @@ public DonationPreferences getDonationPreferences() {
}

donationPreferences = getDonationPreferencesFromBackingStore(DonationPreferences.getDefault());

EasyBind.listen(donationPreferences.neverShowAgainProperty(), (_, _, newValue) -> putBoolean(DONATION_NEVER_SHOW, newValue));
EasyBind.listen(donationPreferences.lastShownEpochDayProperty(), (_, _, newValue) -> putInt(DONATION_LAST_SHOWN_EPOCH_DAY, newValue.intValue()));
return donationPreferences;
Expand Down
2 changes: 1 addition & 1 deletion jablib/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
exports org.jabref.logic.protectedterms;
exports org.jabref.logic.remote;
exports org.jabref.logic.remote.client;
exports org.jabref.logic.remote.server;
exports org.jabref.logic.net.ssl;
exports org.jabref.logic.citationstyle;
exports org.jabref.architecture;
Expand Down Expand Up @@ -85,7 +86,6 @@
exports org.jabref.logic.biblog;
exports org.jabref.model.biblog;
exports org.jabref.model.http;
exports org.jabref.logic.remote.server;
exports org.jabref.logic.util.strings;
exports org.jabref.model.openoffice;
exports org.jabref.logic.openoffice;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
///
/// **Opposite class:**
/// [`BibDatabaseWriter`](org.jabref.logic.exporter.BibDatabaseWriter)
///
/// FIXME: This class relies on `char`, but should use [java.lang.Character] to be fully Unicode compliant.
public class BibtexParser implements Parser {
private static final Logger LOGGER = LoggerFactory.getLogger(BibtexParser.class);
private static final int LOOKAHEAD = 1024;
Expand Down Expand Up @@ -759,7 +761,7 @@ private void parseField(BibEntry entry) throws IOException {
Field field = FieldFactory.parseField(parseTextToken());

skipWhitespace();
consume('=');
consume(field, '=');
String content = parseFieldContent(field);
if (!content.isEmpty()) {
if (entry.hasField(field)) {
Expand Down Expand Up @@ -1172,6 +1174,19 @@ private void consume(char expected) throws IOException {
}
}

private void consume(Field field, char expected) throws IOException {
int character = read();

if (character != expected) {
throw new IOException(
"Error at line " + line
+ " after column " + column
+ " (" + field.getName() + "): Expected "
+ expected + " but received "
+ (char) character + " (" + character + ")");
}
}

private boolean consumeUncritically(char expected) throws IOException {
int character;
// @formatter:off
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.jabref.logic.util.URLUtil;
import org.jabref.model.entry.LinkedFile;
import org.jabref.model.util.Range;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -21,7 +25,7 @@ public class FileFieldParser {

private boolean windowsPath;

public FileFieldParser(String value) {
private FileFieldParser(String value) {
if (value == null) {
this.value = null;
} else {
Expand All @@ -46,11 +50,16 @@ public FileFieldParser(String value) {
public static List<LinkedFile> parse(String value) {
// We need state to have a more clean code. Thus, we instantiate the class and then return the result
FileFieldParser fileFieldParser = new FileFieldParser(value);
return fileFieldParser.parse();
return fileFieldParser.parse().stream().map(LinkedFilePosition::linkedFile).collect(Collectors.toList());
}

public List<LinkedFile> parse() {
List<LinkedFile> files = new ArrayList<>();
public static Map<LinkedFile, Range> parseToPosition(String value) {
FileFieldParser fileFieldParser = new FileFieldParser(value);
return fileFieldParser.parse().stream().collect(HashMap::new, (map, position) -> map.put(position.linkedFile(), position.range()), HashMap::putAll);
}

private List<LinkedFilePosition> parse() {
List<LinkedFilePosition> files = new ArrayList<>();

if ((value == null) || value.trim().isEmpty()) {
return files;
Expand All @@ -59,7 +68,7 @@ public List<LinkedFile> parse() {
if (LinkedFile.isOnlineLink(value.trim())) {
// needs to be modifiable
try {
return List.of(new LinkedFile(URLUtil.create(value), ""));
return List.of(new LinkedFilePosition(new LinkedFile(URLUtil.create(value), ""), new Range(0, value.length() - 1)));
} catch (MalformedURLException e) {
LOGGER.error("invalid url", e);
return files;
Expand All @@ -72,6 +81,7 @@ public List<LinkedFile> parse() {
resetDataStructuresForNextElement();
boolean inXmlChar = false;
boolean escaped = false;
int startColumn = 0;

for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
Expand Down Expand Up @@ -114,7 +124,8 @@ public List<LinkedFile> parse() {
}
} else if (!escaped && (c == ';') && !inXmlChar) {
linkedFileData.add(charactersOfCurrentElement.toString());
files.add(convert(linkedFileData));
files.add(new LinkedFilePosition(convert(linkedFileData), new Range(startColumn, i)));
startColumn = i + 1;

// next iteration
resetDataStructuresForNextElement();
Expand All @@ -127,7 +138,7 @@ public List<LinkedFile> parse() {
linkedFileData.add(charactersOfCurrentElement.toString());
}
if (!linkedFileData.isEmpty()) {
files.add(convert(linkedFileData));
files.add(new LinkedFilePosition(convert(linkedFileData), new Range(startColumn, value.length() - 1)));
}
return files;
}
Expand Down Expand Up @@ -193,4 +204,7 @@ static LinkedFile convert(List<String> entry) {
entry.clear();
return field;
}

private record LinkedFilePosition(LinkedFile linkedFile, Range range) {
}
}
6 changes: 3 additions & 3 deletions jablib/src/main/java/org/jabref/logic/util/io/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -403,9 +403,9 @@ public static Optional<Path> findSingleFileRecursively(String filename, Path roo
return Optional.empty();
}

public static Optional<Path> find(final BibDatabaseContext databaseContext,
public static Optional<Path> find(@NonNull BibDatabaseContext databaseContext,
@NonNull String fileName,
FilePreferences filePreferences) {
@NonNull FilePreferences filePreferences) {
return find(fileName, databaseContext.getFileDirectories(filePreferences));
}

Expand All @@ -416,7 +416,7 @@ public static Optional<Path> find(final BibDatabaseContext databaseContext,
* Will look in each of the given directories starting from the beginning and
* returning the first found file to match if any.
*/
public static Optional<Path> find(String fileName, List<Path> directories) {
public static Optional<Path> find(@NonNull String fileName, @NonNull List<@NonNull Path> directories) {
if (directories.isEmpty()) {
// Fallback, if no directories to resolve are passed
Path path = Path.of(fileName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ public class BibDatabaseContext {

@Nullable
private DatabaseSynchronizer dbmsSynchronizer;

@Nullable
private CoarseChangeFilter dbmsListener;

private DatabaseLocation location;

public BibDatabaseContext() {
Expand Down Expand Up @@ -172,7 +174,7 @@ public boolean isStudy() {
* @param preferences The fileDirectory preferences
* @return List of existing absolute paths
*/
public List<Path> getFileDirectories(FilePreferences preferences) {
public @NonNull List<@NonNull Path> getFileDirectories(@NonNull FilePreferences preferences) {
// Paths are a) ordered and b) should be contained only once in the result
SequencedSet<Path> fileDirs = new LinkedHashSet<>(3);

Expand Down
7 changes: 3 additions & 4 deletions jablib/src/main/java/org/jabref/model/entry/LinkedFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Represents the link to an external file (e.g. associated PDF file).
* This class is {@link Serializable} which is needed for drag and drop in gui
*/
/// Represents the link to an external file (e.g. associated PDF file).
/// This class is {@link Serializable} which is needed for drag and drop in gui
/// The conversion from String ([org.jabref.model.entry.field.StandardField.FILE]) is done at [org.jabref.logic.importer.util.FileFieldParser#parse(String)]
@AllowedToUseLogic("Uses FileUtil from logic")
@NullMarked
public class LinkedFile implements Serializable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;

// Other tests for reading can be found at [org.jabref.logic.importer.fileformat.BibtexImporterTest]
class ParserResultTest {
@Test
void isEmptyForNewParseResult() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ private static Stream<Arguments> stringsToParseTest() throws MalformedURLExcepti
Arguments.of(
List.of(new LinkedFile("", "A:\\Zotero\\storage\\test.pdf", "")),
"A:\\Zotero\\storage\\test.pdf"
),
// Mixed path
Arguments.of(
List.of(new LinkedFile("", "C:/Users/Philip/Downloads/corti-et-al-2009-cocoa-and-cardiovascular-health.pdf", "")),
":C\\\\:/Users/Philip/Downloads/corti-et-al-2009-cocoa-and-cardiovascular-health.pdf:PDF"
)
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jabref.model.entry.identifier;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;

import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -30,8 +31,8 @@ void invalidRfc() {
}

@Test
void getExternalUri() {
void getExternalUri() throws URISyntaxException {
RFC rfc = new RFC("rfc7276");
assertEquals(Optional.of(URI.create("https://www.rfc-editor.org/rfc/rfc7276")), rfc.getExternalURI());
assertEquals(Optional.of(new URI("https://www.rfc-editor.org/rfc/rfc7276")), rfc.getExternalURI());
}
}
3 changes: 3 additions & 0 deletions jabls/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ dependencies {

// route all requests to java.util.logging to SLF4J (which in turn routes to tinylog)
testImplementation("org.slf4j:jul-to-slf4j")

testImplementation("org.mockito:mockito-core")
}

javaModuleTesting.whitebox(testing.suites["test"]) {
requires.add("org.junit.jupiter.api")
requires.add("org.mockito")
}

tasks.test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,8 @@ public void didOpen(DidOpenTextDocumentParams params) {

if ("bibtex".equals(textDocument.getLanguageId())) {
diagnosticHandler.computeAndPublishDiagnostics(client, textDocument.getUri(), textDocument.getText(), textDocument.getVersion());
} else {
contentCache.put(textDocument.getUri(), textDocument.getText());
}
contentCache.put(textDocument.getUri(), textDocument.getText());
}

@Override
Expand All @@ -79,9 +78,8 @@ public void didChange(DidChangeTextDocumentParams params) {

if ("bibtex".equalsIgnoreCase(languageId)) {
diagnosticHandler.computeAndPublishDiagnostics(client, textDocument.getUri(), contentChange.getText(), textDocument.getVersion());
} else {
contentCache.put(textDocument.getUri(), contentChange.getText());
}
contentCache.put(textDocument.getUri(), contentChange.getText());
}

@Override
Expand All @@ -96,9 +94,6 @@ public void didSave(DidSaveTextDocumentParams params) {

@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> definition(DefinitionParams params) {
if (!clientHandler.isStandalone()) {
return CompletableFuture.completedFuture(Either.forLeft(List.of()));
}
if (fileUriToLanguageId.containsKey(params.getTextDocument().getUri())) {
String fileUri = params.getTextDocument().getUri();
return linkHandler.provideDefinition(fileUriToLanguageId.get(fileUri), fileUri, contentCache.get(fileUri), params.getPosition());
Expand All @@ -108,11 +103,8 @@ public CompletableFuture<Either<List<? extends Location>, List<? extends Locatio

@Override
public CompletableFuture<List<DocumentLink>> documentLink(DocumentLinkParams params) {
if (clientHandler.isStandalone()) {
return CompletableFuture.completedFuture(List.of());
}
String fileUri = params.getTextDocument().getUri();
return linkHandler.provideDocumentLinks(fileUriToLanguageId.get(fileUri), contentCache.get(fileUri));
return linkHandler.provideDocumentLinks(fileUri, fileUriToLanguageId.get(fileUri), contentCache.get(fileUri));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public LspClientHandler(RemoteMessageHandler messageHandler, CliPreferences cliP
this.settings = ExtensionSettings.getDefaultSettings();
this.parserHandler = new LspParserHandler();
this.diagnosticHandler = new LspDiagnosticHandler(this, parserHandler, cliPreferences, abbreviationRepository);
this.linkHandler = new LspLinkHandler(parserHandler);
this.linkHandler = new LspLinkHandler(this, parserHandler, cliPreferences.getFilePreferences());
this.workspaceService = new BibtexWorkspaceService(this, diagnosticHandler);
this.textDocumentService = new BibtexTextDocumentService(messageHandler, this, diagnosticHandler, linkHandler);
this.messageHandler = messageHandler;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package org.jabref.languageserver.util;

import java.util.List;
import java.util.stream.Stream;

import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.integrity.IntegrityCheck;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.preferences.CliPreferences;

import org.eclipse.lsp4j.Diagnostic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LspIntegrityCheck {

private static final Logger LOGGER = LoggerFactory.getLogger(LspIntegrityCheck.class);
private static final boolean ALLOW_INTEGER_EDITION = true;

private final CliPreferences cliPreferences;
Expand All @@ -30,12 +34,19 @@ public List<Diagnostic> check(ParserResult parserResult) {
ALLOW_INTEGER_EDITION
);

return parserResult.getDatabaseContext().getEntries().stream().flatMap(entry -> integrityCheck.checkEntry(entry).stream().map(message -> {
if (entry.getFieldOrAlias(message.field()).isPresent()) {
return LspDiagnosticBuilder.create(parserResult, message.message()).setField(message.field()).setEntry(entry).build();
} else {
return LspDiagnosticBuilder.create(parserResult, message.message()).setEntry(entry).build();
return parserResult.getDatabaseContext().getEntries().stream().flatMap(entry -> {
try {
return integrityCheck.checkEntry(entry).stream().map(message -> {
if (entry.getFieldOrAlias(message.field()).isPresent()) {
return LspDiagnosticBuilder.create(parserResult, message.message()).setField(message.field()).setEntry(entry).build();
} else {
return LspDiagnosticBuilder.create(parserResult, message.message()).setEntry(entry).build();
}
});
} catch (NullPointerException nullPointerException) {
LOGGER.debug("Error while performing integrity check.", nullPointerException);
}
})).toList();
return Stream.of();
}).toList();
}
}
Loading
Loading