Skip to content
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

LaTeX integration latest changes #5167

Merged
merged 7 commits into from
Aug 2, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/entryeditor/EntryEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ private List<EntryEditorTab> createTabs() {
tabs.add(sourceTab);

// LaTeX citations tab
tabs.add(new LatexCitationsTab(databaseContext, preferencesService, taskExecutor));
tabs.add(new LatexCitationsTab(databaseContext, preferencesService, taskExecutor, dialogService));

return tabs;
}
Expand Down
32 changes: 20 additions & 12 deletions src/main/java/org/jabref/gui/entryeditor/LatexCitationsTab.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package org.jabref.gui.entryeditor;

import java.nio.file.Path;
import java.util.Optional;
import java.util.stream.Collectors;

import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;

import org.jabref.gui.DialogService;
import org.jabref.gui.icon.IconTheme;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.l10n.Localization;
Expand All @@ -27,8 +28,8 @@ public class LatexCitationsTab extends EntryEditorTab {
private final ProgressIndicator progressIndicator;

public LatexCitationsTab(BibDatabaseContext databaseContext, PreferencesService preferencesService,
TaskExecutor taskExecutor) {
this.viewModel = new LatexCitationsTabViewModel(databaseContext, preferencesService, taskExecutor);
TaskExecutor taskExecutor, DialogService dialogService) {
this.viewModel = new LatexCitationsTabViewModel(databaseContext, preferencesService, taskExecutor, dialogService);
this.searchPane = new StackPane();
this.progressIndicator = new ProgressIndicator();

Expand Down Expand Up @@ -62,14 +63,25 @@ private void setSearchPane() {
});
}

private VBox getLatexDirectoryBox() {
Text latexDirectoryText = new Text(String.format("%n%n%s: %s", Localization.lang("Current search directory"),
viewModel.directoryProperty().get()));
latexDirectoryText.setStyle("-fx-font-weight: bold;");
Button latexDirectoryButton = new Button(Localization.lang("Set LaTeX file directory"));
latexDirectoryButton.setOnAction(event -> viewModel.setLatexDirectory());

return new VBox(15, latexDirectoryText, latexDirectoryButton);
}

private ScrollPane getCitationsPane() {
Text titleText = new Text(Localization.lang("Citations found"));
titleText.getStyleClass().add("recommendation-heading");

VBox citationsBox = new VBox(20, titleText);
Path basePath = viewModel.directoryProperty().get();
citationsBox.getChildren().addAll(viewModel.getCitationList().stream().map(
citation -> citation.getDisplayGraphic(basePath, Optional.empty())).collect(Collectors.toList()));
citation -> citation.getDisplayGraphic(viewModel.directoryProperty().get(), Optional.empty())).collect(Collectors.toList()));

citationsBox.getChildren().add(getLatexDirectoryBox());

ScrollPane citationsPane = new ScrollPane();
citationsPane.setContent(citationsBox);
Expand All @@ -82,13 +94,9 @@ private ScrollPane getNotFoundPane() {
notFoundTitleText.getStyleClass().add("recommendation-heading");

Text notFoundText = new Text(Localization.lang("No LaTeX files containing this entry were found."));
notFoundText.setStyle("-fx-font-size: 110%");

Text notFoundAdviceText = new Text(Localization.lang(
"You can set the LaTeX file directory in the 'Library properties' dialog."));
notFoundAdviceText.setStyle("-fx-font-weight: bold;");
notFoundText.setStyle("-fx-font-size: 110%;");

VBox notFoundBox = new VBox(20, notFoundTitleText, notFoundText, notFoundAdviceText);
VBox notFoundBox = new VBox(20, notFoundTitleText, notFoundText, getLatexDirectoryBox());
ScrollPane notFoundPane = new ScrollPane();
notFoundPane.setContent(notFoundBox);

Expand All @@ -103,7 +111,7 @@ private ScrollPane getErrorPane() {
Text errorMessageText = new Text(viewModel.searchErrorProperty().get());
errorMessageText.setStyle("-fx-font-family: monospace;-fx-font-size: 120%;");

VBox errorBox = new VBox(20, errorTitleText, errorMessageText);
VBox errorBox = new VBox(20, errorTitleText, errorMessageText, getLatexDirectoryBox());
ScrollPane errorPane = new ScrollPane();
errorPane.setContent(errorBox);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
Expand All @@ -18,8 +20,10 @@
import javafx.collections.ObservableList;

import org.jabref.gui.AbstractViewModel;
import org.jabref.gui.DialogService;
import org.jabref.gui.texparser.CitationViewModel;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.DirectoryDialogConfiguration;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.texparser.DefaultTexParser;
Expand All @@ -45,17 +49,20 @@ enum Status {
private final BibDatabaseContext databaseContext;
private final PreferencesService preferencesService;
private final TaskExecutor taskExecutor;
private final DialogService dialogService;
private final ObjectProperty<Path> directory;
private final ObservableList<CitationViewModel> citationList;
private final ObjectProperty<Status> status;
private final StringProperty searchError;
private Future<?> searchTask;
private TexParserResult texParserResult;

public LatexCitationsTabViewModel(BibDatabaseContext databaseContext, PreferencesService preferencesService,
TaskExecutor taskExecutor) {
TaskExecutor taskExecutor, DialogService dialogService) {
this.databaseContext = databaseContext;
this.preferencesService = preferencesService;
this.taskExecutor = taskExecutor;
this.dialogService = dialogService;
this.directory = new SimpleObjectProperty<>(null);
this.citationList = FXCollections.observableArrayList();
this.status = new SimpleObjectProperty<>(Status.IN_PROGRESS);
Expand Down Expand Up @@ -95,7 +102,7 @@ private void startSearch(String citeKey) {
.onRunning(() -> status.set(Status.IN_PROGRESS))
.onSuccess(status::set)
.onFailure(error -> {
searchError.set(String.format("%s%n%n%s", error.getMessage(), error.getCause()));
searchError.set(error.getMessage());
status.set(Status.ERROR);
})
.executeWith(taskExecutor);
Expand All @@ -111,23 +118,53 @@ private void cancelSearch() {
}

private Status searchAndParse(String citeKey) throws IOException {
directory.set(databaseContext.getMetaData().getLaTexFileDirectory(preferencesService.getUser())
.orElseGet(preferencesService::getWorkingDir));
List<Path> texFiles;
try (Stream<Path> filesStream = Files.walk(directory.get())) {
texFiles = filesStream.filter(path -> path.toFile().isFile() && path.toString().endsWith(TEX_EXT))
.collect(Collectors.toList());
} catch (IOException e) {
LOGGER.error("Error searching files", e);
throw new IOException("Error searching files", e);
Path newDirectory = databaseContext.getMetaData().getLaTexFileDirectory(preferencesService.getUser())
.orElseGet(preferencesService::getWorkingDir);

if (texParserResult == null || !newDirectory.equals(directory.get())) {
directory.set(newDirectory);

if (Files.notExists(newDirectory)) {
throw new IOException(String.format("Current search directory does not exist: %s", newDirectory));
}

List<Path> texFiles = searchDirectory(newDirectory, new ArrayList<>());
texParserResult = new DefaultTexParser().parse(texFiles);
}

TexParserResult texParserResult = new DefaultTexParser().parse(citeKey, texFiles);
citationList.setAll(texParserResult.getCitations().values().stream().map(CitationViewModel::new).collect(Collectors.toList()));
citationList.setAll(texParserResult.getCitationsByKey(citeKey).stream().map(CitationViewModel::new).collect(Collectors.toList()));

return citationList.isEmpty() ? Status.NO_RESULTS : Status.CITATIONS_FOUND;
}

private List<Path> searchDirectory(Path directory, List<Path> texFiles) {
Map<Boolean, List<Path>> fileListPartition;
try (Stream<Path> filesStream = Files.list(directory)) {
fileListPartition = filesStream.collect(Collectors.partitioningBy(path -> path.toFile().isDirectory()));
} catch (IOException e) {
LOGGER.error(String.format("Error searching files: %s", e.getMessage()));
return texFiles;
}

List<Path> subDirectories = fileListPartition.get(true);
List<Path> files = fileListPartition.get(false)
.stream()
.filter(path -> path.toString().endsWith(TEX_EXT))
.collect(Collectors.toList());
texFiles.addAll(files);
subDirectories.forEach(subDirectory -> searchDirectory(subDirectory, texFiles));

return texFiles;
}

public void setLatexDirectory() {
DirectoryDialogConfiguration directoryDialogConfiguration = new DirectoryDialogConfiguration.Builder()
.withInitialDirectory(directory.get()).build();

dialogService.showDirectorySelectionDialog(directoryDialogConfiguration).ifPresent(selectedDirectory ->
databaseContext.getMetaData().setLaTexFileDirectory(preferencesService.getUser(), selectedDirectory.toAbsolutePath()));
}

public boolean shouldShow() {
return preferencesService.getEntryEditorPreferences().shouldShowLatexCitationsTab();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@
import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator;
import de.saxsys.mvvmfx.utils.validation.ValidationMessage;
import de.saxsys.mvvmfx.utils.validation.ValidationStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ParseTexDialogViewModel extends AbstractViewModel {

private static final Logger LOGGER = LoggerFactory.getLogger(ParseTexDialogViewModel.class);
private static final String TEX_EXT = ".tex";
private final DialogService dialogService;
private final TaskExecutor taskExecutor;
Expand Down Expand Up @@ -131,7 +134,7 @@ public void searchButtonClicked() {

private FileNodeViewModel searchDirectory(Path directory) throws IOException {
if (directory == null || !directory.toFile().exists() || !directory.toFile().isDirectory()) {
throw new IOException("Error searching an invalid directory");
throw new IOException(String.format("Error searching files: %s", directory));
}

FileNodeViewModel parent = new FileNodeViewModel(directory);
Expand All @@ -140,7 +143,8 @@ private FileNodeViewModel searchDirectory(Path directory) throws IOException {
try (Stream<Path> filesStream = Files.list(directory)) {
fileListPartition = filesStream.collect(Collectors.partitioningBy(path -> path.toFile().isDirectory()));
} catch (IOException e) {
throw new IOException("Error searching files", e);
LOGGER.error(String.format("Error searching files: %s", e.getMessage()));
return parent;
}

List<Path> subDirectories = fileListPartition.get(true);
Expand Down
53 changes: 20 additions & 33 deletions src/main/java/org/jabref/logic/texparser/DefaultTexParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;
import java.io.LineNumberReader;
import java.io.UncheckedIOException;
import java.nio.channels.ClosedChannelException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -58,56 +59,47 @@ public TexParserResult getTexParserResult() {

@Override
public TexParserResult parse(String citeString) {
matchCitation(null, Paths.get(""), 1, citeString);
matchCitation(Paths.get(""), 1, citeString);
return texParserResult;
}

@Override
public TexParserResult parse(Path texFile) {
return parse(null, Collections.singletonList(texFile));
return parse(Collections.singletonList(texFile));
}

@Override
public TexParserResult parse(List<Path> texFiles) {
return parse(null, texFiles);
}

/**
* Parse a list of TEX files for searching a given entry.
*
* @param entryKey String that contains the entry key we are searching (null for all entries)
* @param texFiles List of Path objects linked to a TEX file
* @return a TexParserResult, which contains all data related to the bibliographic entries
*/
public TexParserResult parse(String entryKey, List<Path> texFiles) {
texParserResult.addFiles(texFiles);

List<Path> referencedFiles = new ArrayList<>();

for (Path file : texFiles) {
if (Files.notExists(file)) {
LOGGER.error(String.format("File does not exist: %s", file));
continue;
}

try (LineNumberReader lineNumberReader = new LineNumberReader(Files.newBufferedReader(file))) {
for (String line = lineNumberReader.readLine(); line != null; line = lineNumberReader.readLine()) {
// Skip comments and blank lines.
if (line.isEmpty() || line.charAt(0) == '%') {
continue;
}
// Skip the citation matching if the line does not contain the given entry to speed up the parsing.
if (entryKey == null || line.contains(entryKey)) {
matchCitation(entryKey, file, lineNumberReader.getLineNumber(), line);
}
matchCitation(file, lineNumberReader.getLineNumber(), line);
matchNestedFile(file, texFiles, referencedFiles, line);
}
} catch (ClosedChannelException e) {
LOGGER.info("Parsing has been interrupted");
return texParserResult;
} catch (IOException e) {
LOGGER.error("Error opening a TEX file", e);
LOGGER.error("Parsing has been interrupted");
return null;
} catch (IOException | UncheckedIOException e) {
LOGGER.error(String.format("Error searching files: %s", e.getMessage()));
}
}

// Parse all files referenced by TEX files, recursively.
if (!referencedFiles.isEmpty()) {
parse(entryKey, referencedFiles);
parse(referencedFiles);
}

return texParserResult;
Expand All @@ -116,12 +108,11 @@ public TexParserResult parse(String entryKey, List<Path> texFiles) {
/**
* Find cites along a specific line and store them.
*/
private void matchCitation(String entryKey, Path file, int lineNumber, String line) {
private void matchCitation(Path file, int lineNumber, String line) {
Matcher citeMatch = CITE_PATTERN.matcher(line);

while (citeMatch.find()) {
Arrays.stream(citeMatch.group(CITE_GROUP).split(","))
.filter(key -> entryKey == null || key.equals(entryKey))
.forEach(key -> texParserResult.addKey(key, file, lineNumber, citeMatch.start(), citeMatch.end(), line));
}
}
Expand All @@ -133,16 +124,12 @@ private void matchNestedFile(Path file, List<Path> texFiles, List<Path> referenc
Matcher includeMatch = INCLUDE_PATTERN.matcher(line);

while (includeMatch.find()) {
StringBuilder include = new StringBuilder(includeMatch.group(INCLUDE_GROUP));

if (!include.toString().endsWith(TEX_EXT)) {
include.append(TEX_EXT);
}
String include = includeMatch.group(INCLUDE_GROUP);

Path folder = file.getParent();
Path inputFile = (folder == null)
? Paths.get(include.toString())
: folder.resolve(include.toString());
Path inputFile = file.toAbsolutePath().getParent().resolve(
include.endsWith(TEX_EXT)
? include
: String.format("%s%s", include, TEX_EXT));

if (!texFiles.contains(inputFile)) {
referencedFiles.add(inputFile);
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/l10n/JabRef_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2120,5 +2120,6 @@ Search\ citations\ for\ this\ entry\ in\ LaTeX\ files=Search citations for this
Citations\ found=Citations found
No\ citations\ found=No citations found
No\ LaTeX\ files\ containing\ this\ entry\ were\ found.=No LaTeX files containing this entry were found.
You\ can\ set\ the\ LaTeX\ file\ directory\ in\ the\ 'Library\ properties'\ dialog.=You can set the LaTeX file directory in the 'Library properties' dialog.
Selected\ entry\ does\ not\ have\ an\ associated\ BibTeX\ key.=Selected entry does not have an associated BibTeX key.
Current\ search\ directory=Current search directory
Set\ LaTeX\ file\ directory=Set LaTeX file directory