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

Push to external application config #10303

Merged
merged 37 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6d32f30
Introduce SelfContainedSaveConfiguration
koppor Sep 3, 2023
de5b38d
prepare adding cite key delimiter and start end chars
Siedlerchr Sep 3, 2023
e1e244f
Fix checkstyle
koppor Sep 3, 2023
801b77e
Fix NPE
koppor Sep 3, 2023
37d078d
Merge remote-tracking branch 'upstream/fix-exception-at-preferences' …
Siedlerchr Sep 3, 2023
0911ab5
add preferences and text fields
Siedlerchr Sep 3, 2023
8fd02fc
fix sublime escaping
Siedlerchr Sep 3, 2023
7767f09
fix emacs
Siedlerchr Sep 3, 2023
6914d6b
add
Siedlerchr Sep 3, 2023
6b87071
fix checkstyle
Siedlerchr Sep 3, 2023
3de2baf
fix imports
Siedlerchr Sep 3, 2023
11bbbd0
Merge remote-tracking branch 'upstream/main' into pushToExternalConfig
Siedlerchr Sep 3, 2023
ebbb5ab
checkstyle
Siedlerchr Sep 3, 2023
d3d7cd0
checkstyle the xt
Siedlerchr Sep 3, 2023
9ed8713
Update JabRef_en.properties
Siedlerchr Sep 3, 2023
687dc83
Introduced dissectCiteCommand
calixtus Sep 3, 2023
69500e1
Fixed compile
calixtus Sep 3, 2023
8c95de6
fix cite key and add error logger
Siedlerchr Sep 3, 2023
e448314
fix npe
Siedlerchr Sep 3, 2023
cc42d49
fix
Siedlerchr Sep 3, 2023
8318e86
fix index of
Siedlerchr Sep 3, 2023
f6c3276
Update JabRef_en.properties
Siedlerchr Sep 3, 2023
57debd2
fix texstudio insert
Siedlerchr Sep 4, 2023
5e5b09f
Shorten localization strings for emacs (and group the pushing localiz…
koppor Sep 4, 2023
690b961
Rename "couldNotConnect" to "couldNotPush"
koppor Sep 4, 2023
26e4b3c
Some debug
koppor Sep 4, 2023
03cd51c
Merge branch 'pushToExternalConfig' of github.com:JabRef/jabref into …
koppor Sep 4, 2023
04a266d
New variable pushToApplicationPreferences
koppor Sep 4, 2023
628ee5d
Works on Windows
koppor Sep 4, 2023
48555e4
Use "replace" instead of "replaceAll"
koppor Sep 4, 2023
ce7b0a7
Shorten messages for Vim
koppor Sep 4, 2023
147b744
Add logger to Vim execution, too
koppor Sep 4, 2023
bee8eac
Style: LOGGER first
koppor Sep 4, 2023
d3eca8b
Modified test for linux
calixtus Sep 4, 2023
9d358e4
Merge remote-tracking branch 'upstream/pushToExternalConfig' into pus…
calixtus Sep 4, 2023
fc0843e
Make emacs test "portable"
koppor Sep 4, 2023
6bea642
Fix localization language
koppor Sep 4, 2023
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
4 changes: 2 additions & 2 deletions src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ public JabRefFrame(Stage mainStage) {
this.fileUpdateMonitor = Globals.getFileUpdateMonitor();
this.entryTypesManager = Globals.entryTypesManager;
this.globalSearchBar = new GlobalSearchBar(this, stateManager, prefs, undoManager, dialogService);
this.pushToApplicationCommand = new PushToApplicationCommand(stateManager, dialogService, prefs);
this.fileHistory = new FileHistoryMenu(prefs.getGuiPreferences().getFileHistory(), dialogService, getOpenDatabaseAction());
this.taskExecutor = Globals.TASK_EXECUTOR;
this.pushToApplicationCommand = new PushToApplicationCommand(stateManager, dialogService, prefs, taskExecutor);
this.fileHistory = new FileHistoryMenu(prefs.getGuiPreferences().getFileHistory(), dialogService, getOpenDatabaseAction());
this.setOnKeyTyped(key -> {
if (this.fileHistory.isShowing()) {
if (this.fileHistory.openFileByKey(key)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@

<Label text="%Cite command" GridPane.rowIndex="2"/>
<TextField fx:id="citeCommand"
prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
prefWidth="300.0" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
</GridPane>
</HBox>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class ExternalTab extends AbstractPreferenceTabView<ExternalTabViewModel>
@FXML private CheckBox autoOpenAttachedFolders;
@FXML private ComboBox<PushToApplication> pushToApplicationCombo;
@FXML private TextField citeCommand;

@FXML private CheckBox useCustomTerminal;
@FXML private TextField customTerminalCommand;
@FXML private Button customTerminalBrowse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,6 @@ public StringProperty citeCommandProperty() {
return this.citeCommandProperty;
}

// Open console

public BooleanProperty useCustomTerminalProperty() {
return this.useCustomTerminalProperty;
}
Expand Down
48 changes: 43 additions & 5 deletions src/main/java/org/jabref/gui/push/AbstractPushToApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,48 @@
public abstract class AbstractPushToApplication implements PushToApplication {

private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPushToApplication.class);
private static final String CITE_KEY1 = "key1";
private static final String CITE_KEY2 = "key2";

protected boolean couldNotCall; // Set to true in case the command could not be executed, e.g., if the file is not found
protected boolean couldNotConnect; // Set to true in case the tunnel to the program (if one is used) does not operate
protected boolean couldNotPush; // Set to true in case the tunnel to the program (if one is used) does not operate
protected boolean notDefined; // Set to true if the corresponding path is not defined in the preferences

protected String commandPath;

protected final DialogService dialogService;
protected final PreferencesService preferencesService;

private String cachedCiteCommand;
private String cachedCitePrefix;
private String cachedCiteSuffix;
private String cachedCiteDelimiter;

public AbstractPushToApplication(DialogService dialogService, PreferencesService preferencesService) {
this.dialogService = dialogService;
this.preferencesService = preferencesService;
}

private void dissectCiteCommand() {
String preferencesCiteCommand = preferencesService.getExternalApplicationsPreferences().getCiteCommand();

if (preferencesCiteCommand != null && preferencesCiteCommand.equals(cachedCiteCommand)) {
return;
}

cachedCiteCommand = preferencesCiteCommand;

int indexKey1 = cachedCiteCommand.indexOf(CITE_KEY1);
int indexKey2 = cachedCiteCommand.indexOf(CITE_KEY2);
if (indexKey1 < 0 || indexKey2 < 0 || indexKey2 < (indexKey1 + CITE_KEY1.length())) {
return;
}

cachedCitePrefix = preferencesCiteCommand.substring(0, indexKey1);
cachedCiteDelimiter = preferencesCiteCommand.substring(preferencesCiteCommand.lastIndexOf(CITE_KEY1) + CITE_KEY1.length(), indexKey2);
cachedCiteSuffix = preferencesCiteCommand.substring(preferencesCiteCommand.lastIndexOf(CITE_KEY2) + CITE_KEY2.length());
}

@Override
public JabRefIcon getApplicationIcon() {
return IconTheme.JabRefIcons.APPLICATION_GENERIC;
Expand All @@ -58,7 +85,7 @@ public Action getAction() {

@Override
public void pushEntries(BibDatabaseContext database, List<BibEntry> entries, String keyString) {
couldNotConnect = false;
couldNotPush = false;
couldNotCall = false;
notDefined = false;

Expand Down Expand Up @@ -108,7 +135,7 @@ public void onOperationCompleted() {
dialogService.showErrorDialogAndWait(
Localization.lang("Error pushing entries"),
Localization.lang("Could not call executable") + " '" + commandPath + "'.");
} else if (couldNotConnect) {
} else if (couldNotPush) {
dialogService.showErrorDialogAndWait(
Localization.lang("Error pushing entries"),
Localization.lang("Could not connect to %0", getDisplayName()) + ".");
Expand Down Expand Up @@ -142,8 +169,19 @@ protected String getCommandName() {
return null;
}

protected String getCiteCommand() {
return preferencesService.getExternalApplicationsPreferences().getCiteCommand();
protected String getCitePrefix() {
dissectCiteCommand();
return cachedCitePrefix;
}

public String getDelimiter() {
dissectCiteCommand();
return cachedCiteDelimiter;
}

protected String getCiteSuffix() {
dissectCiteCommand();
return cachedCiteSuffix;
}

@Override
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/jabref/gui/push/PushToApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ public interface PushToApplication {
Action getAction();

PushToApplicationSettings getSettings(PushToApplication application, PushToApplicationPreferences pushToApplicationPreferences);

String getDelimiter();
}
15 changes: 9 additions & 6 deletions src/main/java/org/jabref/gui/push/PushToApplicationCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
import javafx.scene.control.MenuItem;

import org.jabref.gui.DialogService;
import org.jabref.gui.Globals;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.Action;
import org.jabref.gui.actions.ActionFactory;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.BindingsHelper;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
Expand All @@ -41,13 +41,15 @@ public class PushToApplicationCommand extends SimpleCommand {
private final PreferencesService preferencesService;

private final List<Object> reconfigurableControls = new ArrayList<>();
private final TaskExecutor taskExecutor;

private PushToApplication application;

public PushToApplicationCommand(StateManager stateManager, DialogService dialogService, PreferencesService preferencesService) {
public PushToApplicationCommand(StateManager stateManager, DialogService dialogService, PreferencesService preferencesService, TaskExecutor taskExecutor) {
this.stateManager = stateManager;
this.dialogService = dialogService;
this.preferencesService = preferencesService;
this.taskExecutor = taskExecutor;

setApplication(preferencesService.getPushToApplicationPreferences()
.getActiveApplicationName());
Expand Down Expand Up @@ -95,7 +97,7 @@ public Action getAction() {
return application.getAction();
}

private static String getKeyString(List<BibEntry> entries) {
private static String getKeyString(List<BibEntry> entries, String delimiter) {
StringBuilder result = new StringBuilder();
Optional<String> citeKey;
boolean first = true;
Expand All @@ -109,7 +111,7 @@ private static String getKeyString(List<BibEntry> entries) {
result.append(citeKey.get());
first = false;
} else {
result.append(',').append(citeKey.get());
result.append(delimiter).append(citeKey.get());
}
}
return result.toString();
Expand All @@ -132,11 +134,12 @@ public void execute() {
// All set, call the operation in a new thread:
BackgroundTask.wrap(this::pushEntries)
.onSuccess(s -> application.onOperationCompleted())
.executeWith(Globals.TASK_EXECUTOR);
.onFailure(ex -> LOGGER.error("Error pushing citation", ex))
.executeWith(taskExecutor);
}

private void pushEntries() {
BibDatabaseContext database = stateManager.getActiveDatabase().orElseThrow(() -> new NullPointerException("Database null"));
application.pushEntries(database, stateManager.getSelectedEntries(), getKeyString(stateManager.getSelectedEntries()));
application.pushEntries(database, stateManager.getSelectedEntries(), getKeyString(stateManager.getSelectedEntries(), application.getDelimiter()));
}
}
58 changes: 38 additions & 20 deletions src/main/java/org/jabref/gui/push/PushToEmacs.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

import org.jabref.gui.DialogService;
Expand All @@ -24,6 +25,9 @@ public class PushToEmacs extends AbstractPushToApplication {

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

/**
* @param preferencesService getPushToApplicationPreferences(), getExternalApplicationsPreferences(), and getFilePreferences() are used
*/
public PushToEmacs(DialogService dialogService, PreferencesService preferencesService) {
super(dialogService, preferencesService);
}
Expand All @@ -40,43 +44,60 @@ public JabRefIcon getApplicationIcon() {

@Override
public void pushEntries(BibDatabaseContext database, List<BibEntry> entries, String keys) {
couldNotConnect = false;
couldNotPush = false;
couldNotCall = false;
notDefined = false;

commandPath = preferencesService.getPushToApplicationPreferences().getCommandPaths().get(this.getDisplayName());
PushToApplicationPreferences pushToApplicationPreferences = preferencesService.getPushToApplicationPreferences();

commandPath = pushToApplicationPreferences.getCommandPaths().get(this.getDisplayName());

if ((commandPath == null) || commandPath.trim().isEmpty()) {
notDefined = true;
return;
}

commandPath = preferencesService.getPushToApplicationPreferences().getCommandPaths().get(this.getDisplayName());
commandPath = pushToApplicationPreferences.getCommandPaths().get(this.getDisplayName());

String[] addParams = preferencesService.getPushToApplicationPreferences().getEmacsArguments().split(" ");
String[] addParams = pushToApplicationPreferences.getEmacsArguments().split(" ");
try {
String[] com = new String[addParams.length + 2];
com[0] = commandPath;
System.arraycopy(addParams, 0, com, 1, addParams.length);
String prefix;
String suffix;
prefix = "(with-current-buffer (window-buffer) (insert ";
suffix = "))";

// Surrounding with is handled below
String prefix = "(with-current-buffer (window-buffer (selected-window)) (insert ";
String suffix = "))";

if (OS.WINDOWS) {
// Windows gnuclient/emacsclient escaping:
// java string: "(insert \\\"\\\\cite{Blah2001}\\\")";
// so cmd receives: (insert \"\\cite{Blah2001}\")
// so emacs receives: (insert "\cite{Blah2001}")
com[com.length - 1] = prefix.concat("\\\"\\" + getCiteCommand().replaceAll("\\\\", "\\\\\\\\") + "{" + keys + "}\\\"").concat(suffix);

com[com.length - 1] = prefix.concat("\""
+ getCitePrefix().replace("\\", "\\\\")
+ keys
+ getCiteSuffix().replace("\\", "\\\\")
+ "\"").concat(suffix)
.replace("\"", "\\\"");
} else {
// Linux gnuclient/emacslient escaping:
// java string: "(insert \"\\\\cite{Blah2001}\")"
// so sh receives: (insert "\\cite{Blah2001}")
// so emacs receives: (insert "\cite{Blah2001}")
com[com.length - 1] = prefix.concat("\"" + getCiteCommand().replaceAll("\\\\", "\\\\\\\\") + "{" + keys + "}\"").concat(suffix);
com[com.length - 1] = prefix.concat("\""
+ getCitePrefix().replace("\\", "\\\\")
+ keys
+ getCiteSuffix().replace("\\", "\\\\")
+ "\"").concat(suffix);
}

LOGGER.atDebug()
.setMessage("Executing command {}")
.addArgument(() -> Arrays.toString(com))
.log();

final Process p = Runtime.getRuntime().exec(com);

JabRefExecutorService.INSTANCE.executeAndWait(() -> {
Expand All @@ -92,30 +113,27 @@ public void pushEntries(BibDatabaseContext database, List<BibEntry> entries, Str
}
// Error stream has been closed. See if there were any errors:
if (!sb.toString().trim().isEmpty()) {
LOGGER.warn("Push to Emacs error: " + sb);
couldNotConnect = true;
LOGGER.warn("Push to Emacs error: {}", sb);
couldNotPush = true;
}
} catch (IOException e) {
LOGGER.warn("File problem.", e);
LOGGER.warn("Error handling std streams", e);
}
});
} catch (IOException excep) {
couldNotCall = true;
LOGGER.warn("Problem pushing to Emacs.", excep);
couldNotCall = true;
}
}

@Override
public void onOperationCompleted() {
if (couldNotConnect) {
if (couldNotPush) {
dialogService.showErrorDialogAndWait(Localization.lang("Error pushing entries"),
Localization.lang("Could not connect to a running gnuserv process. Make sure that "
+ "Emacs or XEmacs is running, and that the server has been started "
+ "(by running the command 'server-start'/'gnuserv-start')."));
Localization.lang("Could not push to a running emacs daemon."));
} else if (couldNotCall) {
dialogService.showErrorDialogAndWait(Localization.lang("Error pushing entries"),
Localization.lang("Could not run the gnuclient/emacsclient program. Make sure you have "
+ "the emacsclient/gnuclient program installed and available in the PATH."));
Localization.lang("Could not run the emacs client."));
} else {
super.onOperationCompleted();
}
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/jabref/gui/push/PushToLyx.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public JabRefIcon getApplicationIcon() {

@Override
public void onOperationCompleted() {
if (couldNotConnect) {
if (couldNotPush) {
dialogService.showErrorDialogAndWait(Localization.lang("Error pushing entries"),
Localization.lang("Verify that LyX is running and that the lyxpipe is valid.")
+ "[" + commandPath + "]");
Expand All @@ -60,7 +60,7 @@ public PushToApplicationSettings getSettings(PushToApplication application, Push

@Override
public void pushEntries(BibDatabaseContext database, final List<BibEntry> entries, final String keyString) {
couldNotConnect = false;
couldNotPush = false;
couldNotCall = false;
notDefined = false;

Expand All @@ -79,7 +79,7 @@ public void pushEntries(BibDatabaseContext database, final List<BibEntry> entrie
// See if it helps to append ".in":
lp = new File(commandPath + ".in");
if (!lp.exists() || !lp.canWrite()) {
couldNotConnect = true;
couldNotPush = true;
return;
}
}
Expand Down
12 changes: 9 additions & 3 deletions src/main/java/org/jabref/gui/push/PushToSublimeText.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public JabRefIcon getApplicationIcon() {

@Override
public void pushEntries(BibDatabaseContext database, List<BibEntry> entries, String keyString) {
couldNotConnect = false;
couldNotPush = false;
couldNotCall = false;
notDefined = false;

Expand Down Expand Up @@ -74,11 +74,17 @@ public void pushEntries(BibDatabaseContext database, List<BibEntry> entries, Str

@Override
protected String[] getCommandLine(String keyString) {
String citeCommand = getCitePrefix();
// we need to escape the extra slashses
if (getCitePrefix().contains("\\")) {
citeCommand = "\"\\" + getCitePrefix();
}

if (OS.WINDOWS) {
// TODO we might need to escape the inner double quotes with """ """
return new String[] {"cmd.exe", "/c", "\"" + commandPath + "\"" + "--command \"insert {\\\"characters\\\": \"\\" + getCiteCommand() + "{" + keyString + "}\"}\""};
return new String[] {"cmd.exe", "/c", "\"" + commandPath + "\"" + "--command \"insert {\\\"characters\\\": \"\\" + getCitePrefix() + keyString + getCiteSuffix() + "\"}\""};
} else {
return new String[] {"sh", "-c", "\"" + commandPath + "\"" + " --command 'insert {\"characters\": \"\\" + getCiteCommand() + "{" + keyString + "}\"}'"};
return new String[] {"sh", "-c", "\"" + commandPath + "\"" + " --command 'insert {\"characters\": \"" + citeCommand + keyString + getCiteSuffix() + "\"}'"};
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/push/PushToTeXstudio.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ public JabRefIcon getApplicationIcon() {

@Override
protected String[] getCommandLine(String keyString) {
return new String[] {commandPath, "--insert-cite", String.format("%s{%s}", getCiteCommand(), keyString)};
return new String[] {commandPath, "--insert-cite", String.format("%s%s%s", getCitePrefix(), keyString, getCiteSuffix())};
}
}
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/push/PushToTexmaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ public JabRefIcon getApplicationIcon() {

@Override
protected String[] getCommandLine(String keyString) {
return new String[] {commandPath, "-insert", getCiteCommand() + "{" + keyString + "}"};
return new String[] {commandPath, "-insert", getCitePrefix() + keyString + getCiteSuffix()};
}
}
Loading
Loading