Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
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
7 changes: 6 additions & 1 deletion .github/workflows/tests-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -555,19 +555,24 @@ jobs:
# We modify the JBang scripts directly to avoid issues with relative paths
for f in ${{ steps.changed-jablib-files.outputs.all_changed_files }}; do
case "$f" in
.jbang/*)
# skip scripts
continue
;;
jablib-examples/*)
# skip scripts
continue
;;
jabkit/*)
# only JabKit needs its modified sources
# only JabKit needs its modified sources if jabkit was modified
if [ "${{ matrix.script }}" != ".jbang/JabKitLauncher.java" ]; then
continue
fi
;;
esac
echo "//SOURCES ../$f" >> "${{ matrix.script }}"
done
- run: cat ${{ matrix.script }}
- run: jbang build "${{ matrix.script }}"
shell: bash
- run: jbang "${{ matrix.script }}" --help
Expand Down
1 change: 1 addition & 0 deletions .jbang/JabLsLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,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/LatexDefinitonProvider.java
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,13 @@
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 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 +24,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 +49,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 new ArrayList<>(fileFieldParser.parse().stream().map(LinkedFilePosition::linkedFile).toList());
Copy link
Member

Choose a reason for hiding this comment

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

To create a modifiable list, the usual way is with .collect(Colltectors.toList()) (or similar).

Also add to JavaDoc that the list is modifiable.

However - why do you need the result be modifiable? I think, the caller should convert it to a modifiable list (I might be wrong here, did not check all the code, but only thought of parsing and that the parsed representation should be read-only, because the underlying thing does not change)

}

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 +67,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 +80,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 +123,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 +137,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 +203,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 @@ -12,6 +12,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.SequencedCollection;
import java.util.UUID;

import org.jabref.architecture.AllowedToUseLogic;
Expand Down Expand Up @@ -167,9 +168,9 @@ 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
LinkedHashSet<Path> fileDirs = new LinkedHashSet<>(3);
SequencedCollection<Path> fileDirs = new LinkedHashSet<>(3);

Optional<Path> userFileDirectory = metaData.getUserFileDirectory(preferences.getUserAndHost()).map(this::getFileDirectoryPath);
userFileDirectory.ifPresent(fileDirs::add);
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
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import org.jabref.languageserver.LspClientHandler;
import org.jabref.languageserver.util.definition.DefinitionProvider;
import org.jabref.languageserver.util.definition.DefinitionProviderFactory;
import org.jabref.logic.FilePreferences;

import org.eclipse.lsp4j.DocumentLink;
import org.eclipse.lsp4j.Location;
Expand All @@ -19,27 +21,38 @@ public class LspLinkHandler {

private static final Logger LOGGER = LoggerFactory.getLogger(LspLinkHandler.class);

private final LspClientHandler clientHandler;
private final LspParserHandler parserHandler;
private final FilePreferences preferences;

public LspLinkHandler(LspParserHandler parserHandler) {
public LspLinkHandler(LspClientHandler clientHandler, LspParserHandler parserHandler, FilePreferences preferences) {
this.clientHandler = clientHandler;
this.parserHandler = parserHandler;
this.preferences = preferences;
}

public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> provideDefinition(String languageId, String uri, String content, Position position) {
if (!clientHandler.isStandalone() && !"bibtex".equals(languageId)) {
return CompletableFuture.completedFuture(Either.forLeft(List.of()));
}

List<Location> locations = List.of();
Optional<DefinitionProvider> provider = DefinitionProviderFactory.getDefinitionProvider(parserHandler, languageId);
Optional<DefinitionProvider> provider = DefinitionProviderFactory.getDefinitionProvider(preferences, parserHandler, languageId);
if (provider.isPresent()) {
locations = provider.get().provideDefinition(content, position);
locations = provider.get().provideDefinition(uri, content, position);
}
Either<List<? extends Location>, List<? extends LocationLink>> toReturn = Either.forLeft(locations);
return CompletableFuture.completedFuture(toReturn);
}

public CompletableFuture<List<DocumentLink>> provideDocumentLinks(String languageId, String content) {
public CompletableFuture<List<DocumentLink>> provideDocumentLinks(String fileUri, String languageId, String content) {
if (clientHandler.isStandalone()) {
return CompletableFuture.completedFuture(List.of());
}
List<DocumentLink> documentLinks = List.of();
Optional<DefinitionProvider> provider = DefinitionProviderFactory.getDefinitionProvider(parserHandler, languageId);
Optional<DefinitionProvider> provider = DefinitionProviderFactory.getDefinitionProvider(preferences, parserHandler, languageId);
if (provider.isPresent()) {
documentLinks = provider.get().provideDocumentLinks(content);
documentLinks = provider.get().provideDocumentLinks(fileUri, content);
}
return CompletableFuture.completedFuture(documentLinks);
}
Expand Down
Loading