mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
Merge branch better-commands
This commit is contained in:
parent
10ef67a6ec
commit
d3c173dbcd
18 changed files with 185 additions and 237 deletions
|
@ -2,7 +2,6 @@ package io.xpipe.app.browser.action;
|
||||||
|
|
||||||
import io.xpipe.app.browser.BrowserEntry;
|
import io.xpipe.app.browser.BrowserEntry;
|
||||||
import io.xpipe.app.browser.OpenFileSystemModel;
|
import io.xpipe.app.browser.OpenFileSystemModel;
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -13,9 +12,7 @@ public abstract class ExecuteApplicationAction implements LeafAction, Applicatio
|
||||||
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) throws Exception {
|
||||||
ShellControl sc = model.getFileSystem().getShell().orElseThrow();
|
ShellControl sc = model.getFileSystem().getShell().orElseThrow();
|
||||||
for (BrowserEntry entry : entries) {
|
for (BrowserEntry entry : entries) {
|
||||||
var command = detach()
|
var command = createCommand(model, entry);
|
||||||
? ApplicationHelper.createDetachCommand(sc, createCommand(model, entry))
|
|
||||||
: createCommand(model, entry);
|
|
||||||
try (var cc = sc.command(command)
|
try (var cc = sc.command(command)
|
||||||
.withWorkingDirectory(model.getCurrentDirectory().getPath())
|
.withWorkingDirectory(model.getCurrentDirectory().getPath())
|
||||||
.start()) {
|
.start()) {
|
||||||
|
@ -23,19 +20,11 @@ public abstract class ExecuteApplicationAction implements LeafAction, Applicatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detach() && refresh()) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (refresh()) {
|
if (refresh()) {
|
||||||
model.refreshSync();
|
model.refreshSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean detach() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean refresh() {
|
protected boolean refresh() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ package io.xpipe.app.browser.action;
|
||||||
import io.xpipe.app.browser.BrowserEntry;
|
import io.xpipe.app.browser.BrowserEntry;
|
||||||
import io.xpipe.app.browser.OpenFileSystemModel;
|
import io.xpipe.app.browser.OpenFileSystemModel;
|
||||||
import io.xpipe.app.prefs.AppPrefs;
|
import io.xpipe.app.prefs.AppPrefs;
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
|
||||||
import io.xpipe.app.util.TerminalLauncher;
|
import io.xpipe.app.util.TerminalLauncher;
|
||||||
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||||
|
|
||||||
public abstract class MultiExecuteAction implements BranchAction {
|
public abstract class MultiExecuteAction implements BranchAction {
|
||||||
|
|
||||||
protected abstract String createCommand(ShellControl sc, OpenFileSystemModel model, BrowserEntry entry);
|
protected abstract CommandBuilder createCommand(ShellControl sc, OpenFileSystemModel model, BrowserEntry entry);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<LeafAction> getBranchingActions(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
public List<LeafAction> getBranchingActions(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
||||||
|
@ -56,9 +56,7 @@ public abstract class MultiExecuteAction implements BranchAction {
|
||||||
model.withShell(
|
model.withShell(
|
||||||
pc -> {
|
pc -> {
|
||||||
for (BrowserEntry entry : entries) {
|
for (BrowserEntry entry : entries) {
|
||||||
var cmd = ApplicationHelper.createDetachCommand(
|
pc.command(createCommand(pc, model, entry))
|
||||||
pc, createCommand(pc, model, entry));
|
|
||||||
pc.command(cmd)
|
|
||||||
.withWorkingDirectory(model.getCurrentDirectory()
|
.withWorkingDirectory(model.getCurrentDirectory()
|
||||||
.getPath())
|
.getPath())
|
||||||
.execute();
|
.execute();
|
||||||
|
@ -71,27 +69,6 @@ public abstract class MultiExecuteAction implements BranchAction {
|
||||||
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
||||||
return "in background";
|
return "in background";
|
||||||
}
|
}
|
||||||
},
|
|
||||||
new LeafAction() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
|
||||||
model.withShell(
|
|
||||||
pc -> {
|
|
||||||
for (BrowserEntry entry : entries) {
|
|
||||||
pc.command(createCommand(pc, model, entry))
|
|
||||||
.withWorkingDirectory(model.getCurrentDirectory()
|
|
||||||
.getPath())
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName(OpenFileSystemModel model, List<BrowserEntry> entries) {
|
|
||||||
return "wait for completion";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class ErrorEvent {
|
||||||
return EVENT_BASES.remove(t).description(msg);
|
return EVENT_BASES.remove(t).description(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder().throwable(t).description(msg);
|
return builder().throwable(t).description(msg + (t.getMessage() != null ? "\n\n" + t.getMessage() : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ErrorEventBuilder fromMessage(String msg) {
|
public static ErrorEventBuilder fromMessage(String msg) {
|
||||||
|
|
|
@ -11,7 +11,6 @@ import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.storage.DataStorage;
|
import io.xpipe.app.storage.DataStorage;
|
||||||
import io.xpipe.app.terminal.ExternalTerminalType;
|
import io.xpipe.app.terminal.ExternalTerminalType;
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
|
||||||
import io.xpipe.app.util.PasswordLockSecretValue;
|
import io.xpipe.app.util.PasswordLockSecretValue;
|
||||||
import io.xpipe.core.util.InPlaceSecretValue;
|
import io.xpipe.core.util.InPlaceSecretValue;
|
||||||
import io.xpipe.core.util.ModuleHelper;
|
import io.xpipe.core.util.ModuleHelper;
|
||||||
|
@ -503,7 +502,7 @@ public class AppPrefs {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ApplicationHelper.replaceFileArgument(passwordManagerCommand.get(), "KEY", key);
|
return ExternalApplicationHelper.replaceFileArgument(passwordManagerCommand.get(), "KEY", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package io.xpipe.app.prefs;
|
||||||
|
|
||||||
|
import io.xpipe.app.issue.TrackEvent;
|
||||||
|
import io.xpipe.app.util.LocalShell;
|
||||||
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class ExternalApplicationHelper {
|
||||||
|
|
||||||
|
public static String replaceFileArgument(String format, String variable, String file) {
|
||||||
|
// Support for legacy variables that were not upper case
|
||||||
|
variable = variable.toUpperCase(Locale.ROOT);
|
||||||
|
format = format.replace("$" + variable.toLowerCase(Locale.ROOT), "$" + variable.toUpperCase(Locale.ROOT));
|
||||||
|
|
||||||
|
var fileString = file.contains(" ") ? "\"" + file + "\"" : file;
|
||||||
|
// Check if the variable is already quoted
|
||||||
|
return format.replace("\"$" + variable + "\"", fileString).replace("$" + variable, fileString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startAsync(CommandBuilder b) throws Exception {
|
||||||
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
|
var cmd = sc.getShellDialect().launchAsnyc(b);
|
||||||
|
TrackEvent.withDebug("Executing local application")
|
||||||
|
.tag("command", b.buildFull(sc))
|
||||||
|
.tag("adjusted", cmd.buildFull(sc))
|
||||||
|
.handle();
|
||||||
|
try (var c = sc.command(cmd).start()) {
|
||||||
|
c.discardOrThrow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,12 +2,11 @@ package io.xpipe.app.prefs;
|
||||||
|
|
||||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
import io.xpipe.app.util.CommandSupport;
|
||||||
import io.xpipe.app.util.LocalShell;
|
import io.xpipe.app.util.LocalShell;
|
||||||
import io.xpipe.core.process.CommandBuilder;
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
@ -95,10 +94,12 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
|
||||||
public abstract static class PathApplication extends ExternalApplicationType {
|
public abstract static class PathApplication extends ExternalApplicationType {
|
||||||
|
|
||||||
protected final String executable;
|
protected final String executable;
|
||||||
|
protected final boolean explicityAsync;
|
||||||
|
|
||||||
public PathApplication(String id, String executable) {
|
public PathApplication(String id, String executable, boolean explicityAsync) {
|
||||||
super(id);
|
super(id);
|
||||||
this.executable = executable;
|
this.executable = executable;
|
||||||
|
this.explicityAsync = explicityAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
|
@ -110,32 +111,21 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void launch(String title, String args) throws Exception {
|
protected void launch(String title, CommandBuilder args) throws Exception {
|
||||||
try (ShellControl pc = LocalShell.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
if (!ApplicationHelper.isInPath(pc, executable)) {
|
if (!CommandSupport.isInPath(pc, executable)) {
|
||||||
throw ErrorEvent.expected(
|
throw ErrorEvent.expected(
|
||||||
new IOException(
|
new IOException(
|
||||||
"Executable " + executable
|
"Executable " + executable
|
||||||
+ " not found in PATH. Either add it to the PATH and refresh the environment by restarting XPipe, or specify an absolute executable path using the custom terminal setting."));
|
+ " not found in PATH. Either add it to the PATH and refresh the environment by restarting XPipe, or specify an absolute executable path using the custom terminal setting."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShellDialects.isPowershell(pc)) {
|
args.add(0, executable);
|
||||||
var cmd = CommandBuilder.of()
|
if (explicityAsync) {
|
||||||
.add("Start-Process", "-FilePath")
|
ExternalApplicationHelper.startAsync(args);
|
||||||
.addFile(executable)
|
|
||||||
.add("-ArgumentList")
|
|
||||||
.add(pc.getShellDialect().literalArgument(args));
|
|
||||||
pc.executeSimpleCommand(cmd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var toExecute = executable + " " + args;
|
|
||||||
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
|
||||||
toExecute = "start \"" + title + "\" " + toExecute;
|
|
||||||
} else {
|
} else {
|
||||||
toExecute = "nohup " + toExecute + " </dev/null &>/dev/null & disown";
|
pc.executeSimpleCommand(args);
|
||||||
}
|
}
|
||||||
pc.executeSimpleCommand(toExecute);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package io.xpipe.app.prefs;
|
||||||
|
|
||||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
import io.xpipe.app.util.LocalShell;
|
||||||
import io.xpipe.app.util.WindowsRegistry;
|
import io.xpipe.app.util.WindowsRegistry;
|
||||||
import io.xpipe.core.process.CommandBuilder;
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
|
@ -106,9 +106,8 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||||
|
|
||||||
var format =
|
var format =
|
||||||
customCommand.toLowerCase(Locale.ROOT).contains("$file") ? customCommand : customCommand + " $FILE";
|
customCommand.toLowerCase(Locale.ROOT).contains("$file") ? customCommand : customCommand + " $FILE";
|
||||||
ApplicationHelper.executeLocalApplication(
|
ExternalApplicationHelper.startAsync(
|
||||||
CommandBuilder.of().add(ApplicationHelper.replaceFileArgument(format, "FILE", file.toString())),
|
CommandBuilder.of().add(ExternalApplicationHelper.replaceFileArgument(format, "FILE", file.toString())));
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -200,33 +199,28 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||||
throw new IOException("Application " + applicationName + ".app not found");
|
throw new IOException("Application " + applicationName + ".app not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationHelper.executeLocalApplication(
|
ExternalApplicationHelper.startAsync(
|
||||||
CommandBuilder.of()
|
CommandBuilder.of()
|
||||||
.add("open", "-a")
|
.add("open", "-a")
|
||||||
.addFile(execFile.orElseThrow().toString())
|
.addFile(execFile.orElseThrow().toString())
|
||||||
.addFile(file.toString()),
|
.addFile(file.toString()));
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GenericPathType extends ExternalApplicationType.PathApplication implements ExternalEditorType {
|
class GenericPathType extends ExternalApplicationType.PathApplication implements ExternalEditorType {
|
||||||
|
|
||||||
private final boolean detach;
|
public GenericPathType(String id, String command, boolean explicityAsync) {
|
||||||
|
super(id, command, explicityAsync);
|
||||||
public GenericPathType(String id, String command, boolean detach) {
|
|
||||||
super(id, command);
|
|
||||||
this.detach = detach;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(Path file) throws Exception {
|
public void launch(Path file) throws Exception {
|
||||||
ApplicationHelper.executeLocalApplication(
|
var builder = CommandBuilder.of().addFile(executable).addFile(file.toString());
|
||||||
CommandBuilder.of().add(executable).addFile(file.toString()), detach);
|
if (explicityAsync) {
|
||||||
|
ExternalApplicationHelper.startAsync(builder);
|
||||||
|
} else {
|
||||||
|
LocalShell.getShell().executeSimpleCommand(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSelectable() {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +242,7 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||||
|
|
||||||
public WindowsType(String id, String executable, boolean detach) {
|
public WindowsType(String id, String executable, boolean detach) {
|
||||||
super(id, executable);
|
super(id, executable);
|
||||||
this.detach = detach;
|
this.detach = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -262,9 +256,12 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Path> finalLocation = location;
|
var builder = CommandBuilder.of().addFile(location.get().toString()).addFile(file.toString());
|
||||||
ApplicationHelper.executeLocalApplication(
|
if (detach) {
|
||||||
CommandBuilder.of().addFile(finalLocation.get().toString()).addFile(file.toString()), detach);
|
ExternalApplicationHelper.startAsync(builder);
|
||||||
|
} else {
|
||||||
|
LocalShell.getShell().executeSimpleCommand(builder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,10 @@ package io.xpipe.app.terminal;
|
||||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.app.prefs.AppPrefs;
|
import io.xpipe.app.prefs.AppPrefs;
|
||||||
|
import io.xpipe.app.prefs.ExternalApplicationHelper;
|
||||||
import io.xpipe.app.prefs.ExternalApplicationType;
|
import io.xpipe.app.prefs.ExternalApplicationType;
|
||||||
import io.xpipe.app.storage.DataStoreColor;
|
import io.xpipe.app.storage.DataStoreColor;
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
import io.xpipe.app.util.*;
|
||||||
import io.xpipe.app.util.LocalShell;
|
|
||||||
import io.xpipe.app.util.MacOsPermissions;
|
|
||||||
import io.xpipe.app.util.WindowsRegistry;
|
|
||||||
import io.xpipe.core.process.*;
|
import io.xpipe.core.process.*;
|
||||||
import io.xpipe.core.store.FilePath;
|
import io.xpipe.core.store.FilePath;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
@ -23,7 +21,7 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
public interface ExternalTerminalType extends PrefsChoiceValue {
|
public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
ExternalTerminalType CMD = new SimplePathType("app.cmd", "cmd.exe") {
|
ExternalTerminalType CMD = new SimplePathType("app.cmd", "cmd.exe", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -45,7 +43,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ExternalTerminalType POWERSHELL = new SimplePathType("app.powershell", "powershell") {
|
ExternalTerminalType POWERSHELL = new SimplePathType("app.powershell", "powershell", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -73,14 +71,14 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
var base64 = Base64.getEncoder()
|
var base64 = Base64.getEncoder()
|
||||||
.encodeToString(configuration
|
.encodeToString(configuration
|
||||||
.getDialectLaunchCommand()
|
.getDialectLaunchCommand()
|
||||||
.buildCommandBase(sc)
|
.buildBase(sc)
|
||||||
.getBytes(StandardCharsets.UTF_16LE));
|
.getBytes(StandardCharsets.UTF_16LE));
|
||||||
return "\"" + base64 + "\"";
|
return "\"" + base64 + "\"";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ExternalTerminalType PWSH = new SimplePathType("app.pwsh", "pwsh") {
|
ExternalTerminalType PWSH = new SimplePathType("app.pwsh", "pwsh", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -100,14 +98,14 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.add(sc -> {
|
.add(sc -> {
|
||||||
// Fix for https://github.com/PowerShell/PowerShell/issues/18530#issuecomment-1325691850
|
// Fix for https://github.com/PowerShell/PowerShell/issues/18530#issuecomment-1325691850
|
||||||
var c = "$env:PSModulePath=\"\";"
|
var c = "$env:PSModulePath=\"\";"
|
||||||
+ configuration.getDialectLaunchCommand().buildCommandBase(sc);
|
+ configuration.getDialectLaunchCommand().buildBase(sc);
|
||||||
var base64 = Base64.getEncoder().encodeToString(c.getBytes(StandardCharsets.UTF_16LE));
|
var base64 = Base64.getEncoder().encodeToString(c.getBytes(StandardCharsets.UTF_16LE));
|
||||||
return "\"" + base64 + "\"";
|
return "\"" + base64 + "\"";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ExternalTerminalType ALACRITTY_WINDOWS = new SimplePathType("app.alacritty", "alacritty") {
|
ExternalTerminalType ALACRITTY_WINDOWS = new SimplePathType("app.alacritty", "alacritty", false) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -127,12 +125,13 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addQuoted("colors.primary.background='%s'"
|
.addQuoted("colors.primary.background='%s'"
|
||||||
.formatted(configuration.getColor().toHexString()));
|
.formatted(configuration.getColor().toHexString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alacritty is bugged and will not accept arguments with spaces even if they are correctly passed/escaped
|
||||||
|
// So this will not work when the script file has spaces
|
||||||
return b.add("-t")
|
return b.add("-t")
|
||||||
.addQuoted(configuration.getCleanTitle())
|
.addQuoted(configuration.getCleanTitle())
|
||||||
.add("-e")
|
.add("-e")
|
||||||
.add("cmd")
|
.add(configuration.getDialectLaunchCommand());
|
||||||
.add("/c")
|
|
||||||
.addQuoted(configuration.getScriptFile().toString().replaceAll(" ", "^$0"));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType TABBY_WINDOWS = new WindowsType("app.tabby", "Tabby") {
|
ExternalTerminalType TABBY_WINDOWS = new WindowsType("app.tabby", "Tabby") {
|
||||||
|
@ -145,11 +144,20 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
@Override
|
@Override
|
||||||
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
||||||
// Tabby has a very weird handling of output, even detaching with start does not prevent it from printing
|
// Tabby has a very weird handling of output, even detaching with start does not prevent it from printing
|
||||||
|
if (configuration.getScriptDialect().equals(ShellDialects.CMD)) {
|
||||||
|
// It also freezes with any other input than .bat files, why?
|
||||||
|
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of()
|
||||||
|
.addFile(file.toString())
|
||||||
|
.add("run")
|
||||||
|
.addFile(configuration.getScriptFile())
|
||||||
|
.discardOutput());
|
||||||
|
}
|
||||||
|
|
||||||
LocalShell.getShell()
|
LocalShell.getShell()
|
||||||
.executeSimpleCommand(CommandBuilder.of()
|
.executeSimpleCommand(CommandBuilder.of()
|
||||||
.addFile(file.toString())
|
.addFile(file.toString())
|
||||||
.add("run")
|
.add("run")
|
||||||
.addFile(configuration.getScriptFile())
|
.add(configuration.getDialectLaunchCommand())
|
||||||
.discardOutput());
|
.discardOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,9 +191,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
protected void execute(Path file, LaunchConfiguration configuration) throws Exception {
|
||||||
ApplicationHelper.executeLocalApplication(
|
LocalShell.getShell().executeSimpleCommand(CommandBuilder.of().addFile(file.toString()).add("start").add(configuration.getDialectLaunchCommand()));
|
||||||
CommandBuilder.of().addFile(file.toString()).add("start").addFile(configuration.getScriptFile()),
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -199,7 +205,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
return launcherDir.map(Path::of);
|
return launcherDir.map(Path::of);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType WEZ_LINUX = new SimplePathType("app.wezterm", "wezterm-gui") {
|
ExternalTerminalType WEZ_LINUX = new SimplePathType("app.wezterm", "wezterm-gui", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -211,7 +217,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
return CommandBuilder.of().add("start").addFile(configuration.getScriptFile());
|
return CommandBuilder.of().add("start").addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType GNOME_TERMINAL = new PathCheckType("app.gnomeTerminal", "gnome-terminal") {
|
ExternalTerminalType GNOME_TERMINAL = new PathCheckType("app.gnomeTerminal", "gnome-terminal", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -221,7 +227,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
try (ShellControl pc = LocalShell.getShell()) {
|
try (ShellControl pc = LocalShell.getShell()) {
|
||||||
ApplicationHelper.checkIsInPath(
|
CommandSupport.isInPathOrThrow(
|
||||||
pc, executable, toTranslatedString().getValue(), null);
|
pc, executable, toTranslatedString().getValue(), null);
|
||||||
|
|
||||||
var toExecute = CommandBuilder.of()
|
var toExecute = CommandBuilder.of()
|
||||||
|
@ -229,15 +235,13 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addQuoted(configuration.getColoredTitle())
|
.addQuoted(configuration.getColoredTitle())
|
||||||
.add("--")
|
.add("--")
|
||||||
.addFile(configuration.getScriptFile())
|
.addFile(configuration.getScriptFile())
|
||||||
.buildString(pc);
|
|
||||||
// In order to fix this bug which also affects us:
|
// In order to fix this bug which also affects us:
|
||||||
// https://askubuntu.com/questions/1148475/launching-gnome-terminal-from-vscode
|
// https://askubuntu.com/questions/1148475/launching-gnome-terminal-from-vscode
|
||||||
toExecute = "GNOME_TERMINAL_SCREEN=\"\" nohup " + toExecute + " </dev/null &>/dev/null & disown";
|
.envrironment("GNOME_TERMINAL_SCREEN", sc -> "");
|
||||||
pc.executeSimpleCommand(toExecute);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType KONSOLE = new SimplePathType("app.konsole", "konsole") {
|
ExternalTerminalType KONSOLE = new SimplePathType("app.konsole", "konsole", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -257,7 +261,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
return CommandBuilder.of().add("--new-tab", "-e").addFile(configuration.getScriptFile());
|
return CommandBuilder.of().add("--new-tab", "-e").addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType XFCE = new SimplePathType("app.xfce", "xfce4-terminal") {
|
ExternalTerminalType XFCE = new SimplePathType("app.xfce", "xfce4-terminal", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -273,7 +277,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addFile(configuration.getScriptFile());
|
.addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType ELEMENTARY = new SimplePathType("app.elementaryTerminal", "io.elementary.terminal") {
|
ExternalTerminalType ELEMENTARY = new SimplePathType("app.elementaryTerminal", "io.elementary.terminal", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -285,7 +289,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
return CommandBuilder.of().add("--new-tab").add("-e").addFile(configuration.getColoredTitle());
|
return CommandBuilder.of().add("--new-tab").add("-e").addFile(configuration.getColoredTitle());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType TILIX = new SimplePathType("app.tilix", "tilix") {
|
ExternalTerminalType TILIX = new SimplePathType("app.tilix", "tilix", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -301,7 +305,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addFile(configuration.getScriptFile());
|
.addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType TERMINATOR = new SimplePathType("app.terminator", "terminator") {
|
ExternalTerminalType TERMINATOR = new SimplePathType("app.terminator", "terminator", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -318,7 +322,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.add("--new-tab");
|
.add("--new-tab");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType TERMINOLOGY = new SimplePathType("app.terminology", "terminology") {
|
ExternalTerminalType TERMINOLOGY = new SimplePathType("app.terminology", "terminology", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -335,7 +339,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addFile(configuration.getScriptFile());
|
.addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType COOL_RETRO_TERM = new SimplePathType("app.coolRetroTerm", "cool-retro-term") {
|
ExternalTerminalType COOL_RETRO_TERM = new SimplePathType("app.coolRetroTerm", "cool-retro-term", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -351,7 +355,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addFile(configuration.getScriptFile());
|
.addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType GUAKE = new SimplePathType("app.guake", "guake") {
|
ExternalTerminalType GUAKE = new SimplePathType("app.guake", "guake", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -368,7 +372,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addFile(configuration.getScriptFile());
|
.addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType ALACRITTY_LINUX = new SimplePathType("app.alacritty", "alacritty") {
|
ExternalTerminalType ALACRITTY_LINUX = new SimplePathType("app.alacritty", "alacritty", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -389,7 +393,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addFile(configuration.getScriptFile());
|
.addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType TILDA = new SimplePathType("app.tilda", "tilda") {
|
ExternalTerminalType TILDA = new SimplePathType("app.tilda", "tilda", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -401,7 +405,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
return CommandBuilder.of().add("-c").addFile(configuration.getScriptFile());
|
return CommandBuilder.of().add("-c").addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType XTERM = new SimplePathType("app.xterm", "xterm") {
|
ExternalTerminalType XTERM = new SimplePathType("app.xterm", "xterm", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -417,7 +421,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.addFile(configuration.getScriptFile());
|
.addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType DEEPIN_TERMINAL = new SimplePathType("app.deepinTerminal", "deepin-terminal") {
|
ExternalTerminalType DEEPIN_TERMINAL = new SimplePathType("app.deepinTerminal", "deepin-terminal", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -429,7 +433,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
return CommandBuilder.of().add("-C").addFile(configuration.getScriptFile());
|
return CommandBuilder.of().add("-C").addFile(configuration.getScriptFile());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType Q_TERMINAL = new SimplePathType("app.qTerminal", "qterminal") {
|
ExternalTerminalType Q_TERMINAL = new SimplePathType("app.qTerminal", "qterminal", true) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsTabs() {
|
public boolean supportsTabs() {
|
||||||
|
@ -576,10 +580,8 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
.resolve("wezterm-gui")
|
.resolve("wezterm-gui")
|
||||||
.toString())
|
.toString())
|
||||||
.add("start")
|
.add("start")
|
||||||
.addFile(configuration.getScriptFile())
|
.add(configuration.getDialectLaunchCommand());
|
||||||
.buildString(LocalShell.getShell());
|
ExternalApplicationHelper.startAsync(c);
|
||||||
c = ApplicationHelper.createDetachCommand(LocalShell.getShell(), c);
|
|
||||||
LocalShell.getShell().executeSimpleCommand(c);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExternalTerminalType KITTY_MACOS = new MacOsType("app.kitty", "kitty") {
|
ExternalTerminalType KITTY_MACOS = new MacOsType("app.kitty", "kitty") {
|
||||||
|
@ -768,7 +770,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
var format = custom.toLowerCase(Locale.ROOT).contains("$cmd") ? custom : custom + " $CMD";
|
var format = custom.toLowerCase(Locale.ROOT).contains("$cmd") ? custom : custom + " $CMD";
|
||||||
try (var pc = LocalShell.getShell()) {
|
try (var pc = LocalShell.getShell()) {
|
||||||
var toExecute = ApplicationHelper.replaceFileArgument(format, "CMD", configuration.getScriptFile().toString());
|
var toExecute = ExternalApplicationHelper.replaceFileArgument(format, "CMD", configuration.getScriptFile().toString());
|
||||||
// We can't be sure whether the command is blocking or not, so always make it not blocking
|
// We can't be sure whether the command is blocking or not, so always make it not blocking
|
||||||
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
||||||
toExecute = "start \"" + configuration.getCleanTitle() + "\" " + toExecute;
|
toExecute = "start \"" + configuration.getCleanTitle() + "\" " + toExecute;
|
||||||
|
@ -795,21 +797,21 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
@Getter
|
@Getter
|
||||||
abstract class PathCheckType extends ExternalApplicationType.PathApplication implements ExternalTerminalType {
|
abstract class PathCheckType extends ExternalApplicationType.PathApplication implements ExternalTerminalType {
|
||||||
|
|
||||||
public PathCheckType(String id, String executable) {
|
public PathCheckType(String id, String executable, boolean explicitAsync) {
|
||||||
super(id, executable);
|
super(id, executable, explicitAsync);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
abstract class SimplePathType extends PathCheckType {
|
abstract class SimplePathType extends PathCheckType {
|
||||||
|
|
||||||
public SimplePathType(String id, String executable) {
|
public SimplePathType(String id, String executable, boolean explicitAsync) {
|
||||||
super(id, executable);
|
super(id, executable, explicitAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(LaunchConfiguration configuration) throws Exception {
|
public void launch(LaunchConfiguration configuration) throws Exception {
|
||||||
var args = toCommand(configuration).buildCommandBase(LocalShell.getShell());
|
var args = toCommand(configuration);
|
||||||
launch(configuration.getColoredTitle(), args);
|
launch(configuration.getColoredTitle(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package io.xpipe.app.terminal;
|
package io.xpipe.app.terminal;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||||
import io.xpipe.app.util.ApplicationHelper;
|
import io.xpipe.app.util.CommandSupport;
|
||||||
import io.xpipe.app.util.LocalShell;
|
import io.xpipe.app.util.LocalShell;
|
||||||
import io.xpipe.app.util.ShellTemp;
|
import io.xpipe.app.util.ShellTemp;
|
||||||
import io.xpipe.app.util.ThreadHelper;
|
import io.xpipe.app.util.ThreadHelper;
|
||||||
|
@ -44,8 +44,8 @@ public class KittyTerminalType {
|
||||||
private static boolean prepare() throws Exception {
|
private static boolean prepare() throws Exception {
|
||||||
var socket = getSocket();
|
var socket = getSocket();
|
||||||
try (var sc = LocalShell.getShell().start()) {
|
try (var sc = LocalShell.getShell().start()) {
|
||||||
ApplicationHelper.checkIsInPath(sc, "kitty", "Kitty", null);
|
CommandSupport.isInPathOrThrow(sc, "kitty", "Kitty", null);
|
||||||
ApplicationHelper.checkIsInPath(sc, "socat", "socat", null);
|
CommandSupport.isInPathOrThrow(sc, "socat", "socat", null);
|
||||||
|
|
||||||
if (sc.executeSimpleBooleanCommand("test -w " + sc.getShellDialect().fileArgument(socket))) {
|
if (sc.executeSimpleBooleanCommand("test -w " + sc.getShellDialect().fileArgument(socket))) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -11,7 +11,7 @@ import java.nio.file.Path;
|
||||||
public class WindowsTerminalType {
|
public class WindowsTerminalType {
|
||||||
|
|
||||||
public static final ExternalTerminalType WINDOWS_TERMINAL =
|
public static final ExternalTerminalType WINDOWS_TERMINAL =
|
||||||
new ExternalTerminalType.SimplePathType("app.windowsTerminal", "wt.exe") {
|
new ExternalTerminalType.SimplePathType("app.windowsTerminal", "wt.exe", false) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CommandBuilder toCommand(LaunchConfiguration configuration) throws Exception {
|
protected CommandBuilder toCommand(LaunchConfiguration configuration) throws Exception {
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
package io.xpipe.app.util;
|
|
||||||
|
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
|
||||||
import io.xpipe.app.issue.TrackEvent;
|
|
||||||
import io.xpipe.app.storage.DataStoreEntry;
|
|
||||||
import io.xpipe.core.process.CommandBuilder;
|
|
||||||
import io.xpipe.core.process.OsType;
|
|
||||||
import io.xpipe.core.process.ShellControl;
|
|
||||||
import io.xpipe.core.process.ShellDialects;
|
|
||||||
import io.xpipe.core.util.FailableSupplier;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class ApplicationHelper {
|
|
||||||
|
|
||||||
public static String replaceFileArgument(String format, String variable, String file) {
|
|
||||||
// Support for legacy variables that were not upper case
|
|
||||||
variable = variable.toUpperCase(Locale.ROOT);
|
|
||||||
format = format.replace("$" + variable.toLowerCase(Locale.ROOT), "$" + variable.toUpperCase(Locale.ROOT));
|
|
||||||
|
|
||||||
var fileString = file.contains(" ") ? "\"" + file + "\"" : file;
|
|
||||||
// Check if the variable is already quoted
|
|
||||||
return format.replace("\"$" + variable + "\"", fileString).replace("$" + variable, fileString);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void executeLocalApplication(CommandBuilder b, boolean detach) throws Exception {
|
|
||||||
try (var sc = LocalShell.getShell().start()) {
|
|
||||||
var cmd = detach ? createDetachCommand(sc, b.buildString(sc)) : b.buildString(sc);
|
|
||||||
TrackEvent.withDebug("Executing local application")
|
|
||||||
.tag("command", cmd)
|
|
||||||
.handle();
|
|
||||||
try (var c = sc.command(cmd).start()) {
|
|
||||||
c.discardOrThrow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String createDetachCommand(ShellControl pc, String command) {
|
|
||||||
if (ShellDialects.isPowershell(pc)) {
|
|
||||||
var script = ScriptHelper.createExecScript(pc, command);
|
|
||||||
return String.format(
|
|
||||||
"Start-Process -FilePath %s -ArgumentList \"-NoProfile\", \"-File\", %s",
|
|
||||||
pc.getShellDialect().getExecutableName(),
|
|
||||||
pc.getShellDialect().fileArgument(script.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
|
||||||
return "start \"\" " + command;
|
|
||||||
} else {
|
|
||||||
return "nohup " + command + " </dev/null &>/dev/null & disown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isInPath(ShellControl processControl, String executable) throws Exception {
|
|
||||||
return processControl.executeSimpleBooleanCommand(
|
|
||||||
processControl.getShellDialect().getWhichCommand(executable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isInPathSilent(ShellControl processControl, String executable) {
|
|
||||||
try {
|
|
||||||
return processControl.executeSimpleBooleanCommand(
|
|
||||||
processControl.getShellDialect().getWhichCommand(executable));
|
|
||||||
} catch (Exception e) {
|
|
||||||
ErrorEvent.fromThrowable(e).handle();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkIsInPath(
|
|
||||||
ShellControl processControl, String executable, String displayName, DataStoreEntry connection)
|
|
||||||
throws Exception {
|
|
||||||
if (!isInPath(processControl, executable)) {
|
|
||||||
throw ErrorEvent.expected(new IOException(displayName + " executable " + executable + " not found in PATH"
|
|
||||||
+ (connection != null ? " on system " + connection.getName() : "")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void isSupported(FailableSupplier<Boolean> supplier, String displayName, DataStoreEntry connection)
|
|
||||||
throws Exception {
|
|
||||||
if (!supplier.get()) {
|
|
||||||
throw ErrorEvent.expected(new IOException(displayName + " is not supported"
|
|
||||||
+ (connection != null ? " on system " + connection.getName() : "")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
42
app/src/main/java/io/xpipe/app/util/CommandSupport.java
Normal file
42
app/src/main/java/io/xpipe/app/util/CommandSupport.java
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package io.xpipe.app.util;
|
||||||
|
|
||||||
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
|
import io.xpipe.app.storage.DataStoreEntry;
|
||||||
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
import io.xpipe.core.util.FailableSupplier;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class CommandSupport {
|
||||||
|
public static boolean isInPath(ShellControl processControl, String executable) throws Exception {
|
||||||
|
return processControl.executeSimpleBooleanCommand(
|
||||||
|
processControl.getShellDialect().getWhichCommand(executable));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInPathSilent(ShellControl processControl, String executable) {
|
||||||
|
try {
|
||||||
|
return processControl.executeSimpleBooleanCommand(
|
||||||
|
processControl.getShellDialect().getWhichCommand(executable));
|
||||||
|
} catch (Exception e) {
|
||||||
|
ErrorEvent.fromThrowable(e).handle();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void isInPathOrThrow(
|
||||||
|
ShellControl processControl, String executable, String displayName, DataStoreEntry connection)
|
||||||
|
throws Exception {
|
||||||
|
if (!isInPath(processControl, executable)) {
|
||||||
|
throw ErrorEvent.expected(new IOException(displayName + " executable " + executable + " not found in PATH"
|
||||||
|
+ (connection != null ? " on system " + connection.getName() : "")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void isSupported(FailableSupplier<Boolean> supplier, String displayName, DataStoreEntry connection)
|
||||||
|
throws Exception {
|
||||||
|
if (!supplier.get()) {
|
||||||
|
throw ErrorEvent.expected(new IOException(displayName + " is not supported"
|
||||||
|
+ (connection != null ? " on system " + connection.getName() : "")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,7 +46,7 @@ public class ShellControlCache {
|
||||||
public boolean isApplicationInPath(String app) {
|
public boolean isApplicationInPath(String app) {
|
||||||
if (!installedApplications.containsKey(app)) {
|
if (!installedApplications.containsKey(app)) {
|
||||||
try {
|
try {
|
||||||
var b = ApplicationHelper.isInPath(shellControl, app);
|
var b = CommandSupport.isInPath(shellControl, app);
|
||||||
installedApplications.put(app, b);
|
installedApplications.put(app, b);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
installedApplications.put(app, false);
|
installedApplications.put(app, false);
|
||||||
|
|
|
@ -61,9 +61,7 @@ public class TerminalLauncher {
|
||||||
latch.await();
|
latch.await();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw ErrorEvent.expected(new IOException(
|
throw ErrorEvent.expected(new IOException(
|
||||||
"Unable to launch terminal " + type.toTranslatedString().getValue() + ": " + ex.getMessage()
|
"Unable to launch terminal " + type.toTranslatedString().getValue() + ": " + ex.getMessage(), ex));
|
||||||
+ ".\nMaybe try to use a different terminal in the settings.",
|
|
||||||
ex));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,7 +210,11 @@ public class CommandBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String buildCommandBase(ShellControl sc) throws Exception {
|
public String buildBase(ShellControl sc) throws Exception {
|
||||||
|
return String.join(" ", buildBaseParts(sc));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> buildBaseParts(ShellControl sc) throws Exception {
|
||||||
countDown = CountDown.of();
|
countDown = CountDown.of();
|
||||||
uuid = UUID.randomUUID();
|
uuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
@ -227,11 +231,11 @@ public class CommandBuilder {
|
||||||
|
|
||||||
list.add(evaluate);
|
list.add(evaluate);
|
||||||
}
|
}
|
||||||
return String.join(" ", list);
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String buildString(ShellControl sc) throws Exception {
|
public String buildFull(ShellControl sc) throws Exception {
|
||||||
var s = buildCommandBase(sc);
|
var s = buildBase(sc);
|
||||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||||
for (var e : environmentVariables.entrySet()) {
|
for (var e : environmentVariables.entrySet()) {
|
||||||
var v = e.getValue().evaluate(sc);
|
var v = e.getValue().evaluate(sc);
|
||||||
|
@ -239,7 +243,7 @@ public class CommandBuilder {
|
||||||
map.put(e.getKey(), v);
|
map.put(e.getKey(), v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sc.getShellDialect().addInlineVariablesToCommand(map, s);
|
return sc.getShellDialect().assembleCommand(s, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandControl build(ShellControl sc) {
|
public CommandControl build(ShellControl sc) {
|
||||||
|
|
|
@ -16,6 +16,8 @@ import java.util.stream.Stream;
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||||
public interface ShellDialect {
|
public interface ShellDialect {
|
||||||
|
|
||||||
|
CommandBuilder launchAsnyc(CommandBuilder cmd);
|
||||||
|
|
||||||
default String getLicenseFeatureId() {
|
default String getLicenseFeatureId() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +82,7 @@ public interface ShellDialect {
|
||||||
|
|
||||||
String getScriptFileEnding();
|
String getScriptFileEnding();
|
||||||
|
|
||||||
String addInlineVariablesToCommand(Map<String, String> variables, String command);
|
String assembleCommand(String command, Map<String, String> variables);
|
||||||
|
|
||||||
Stream<FileSystem.FileEntry> listFiles(FileSystem fs, ShellControl control, String dir) throws Exception;
|
Stream<FileSystem.FileEntry> listFiles(FileSystem fs, ShellControl control, String dir) throws Exception;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import io.xpipe.app.browser.OpenFileSystemModel;
|
||||||
import io.xpipe.app.browser.action.BrowserActionFormatter;
|
import io.xpipe.app.browser.action.BrowserActionFormatter;
|
||||||
import io.xpipe.app.browser.action.MultiExecuteAction;
|
import io.xpipe.app.browser.action.MultiExecuteAction;
|
||||||
import io.xpipe.app.browser.icon.BrowserIconFileType;
|
import io.xpipe.app.browser.icon.BrowserIconFileType;
|
||||||
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -27,8 +28,8 @@ public class JarAction extends MultiExecuteAction implements JavaAction, FileTyp
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String createCommand(ShellControl sc, OpenFileSystemModel model, BrowserEntry entry) {
|
protected CommandBuilder createCommand(ShellControl sc, OpenFileSystemModel model, BrowserEntry entry) {
|
||||||
return "java -jar " + entry.getOptionallyQuotedFileName();
|
return CommandBuilder.of().add("java", "-jar").addFile(entry.getRawFileEntry().getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.ext.base.browser;
|
||||||
import io.xpipe.app.browser.BrowserEntry;
|
import io.xpipe.app.browser.BrowserEntry;
|
||||||
import io.xpipe.app.browser.OpenFileSystemModel;
|
import io.xpipe.app.browser.OpenFileSystemModel;
|
||||||
import io.xpipe.app.browser.action.MultiExecuteAction;
|
import io.xpipe.app.browser.action.MultiExecuteAction;
|
||||||
|
import io.xpipe.core.process.CommandBuilder;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
import io.xpipe.core.process.ShellDialects;
|
||||||
|
@ -69,8 +70,7 @@ public class RunAction extends MultiExecuteAction {
|
||||||
return entries.stream().allMatch(entry -> isExecutable(entry.getRawFileEntry()));
|
return entries.stream().allMatch(entry -> isExecutable(entry.getRawFileEntry()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected CommandBuilder createCommand(ShellControl sc, OpenFileSystemModel model, BrowserEntry entry) {
|
||||||
protected String createCommand(ShellControl sc, OpenFileSystemModel model, BrowserEntry entry) {
|
return CommandBuilder.of().add(sc.getShellDialect().runScriptCommand(sc, entry.getRawFileEntry().getPath()));
|
||||||
return sc.getShellDialect().runScriptCommand(sc, entry.getRawFileEntry().getPath());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue