backport: 19.3.0
This commit is contained in:
parent
1567d4456d
commit
ec0acf1e2d
52 changed files with 529 additions and 172 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -17,6 +17,18 @@ Changelog structure reference:
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [19.3.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Toolchains, Run Configurations
|
||||
- [Direnv](https://github.com/direnv/direnv) support
|
||||
|
||||
### Fixed
|
||||
|
||||
- Zig
|
||||
- Missing description for string conversion intentions
|
||||
|
||||
## [19.2.0]
|
||||
|
||||
### Added
|
||||
|
|
|
@ -85,6 +85,14 @@ Adds support for the Zig Language, utilizing the ZLS language server for advance
|
|||
2. Download and compile the ZLS language server, available at https://github.com/zigtools/zls
|
||||
3. Go to `Settings` -> `Languages & Frameworks` -> `Zig`, and point the `Toolchain Location` and `ZLS path` to the correct places
|
||||
|
||||
## Features
|
||||
|
||||
- Integration with the Zig Language Server (ZLS)
|
||||
- Basic syntax highlighting if ZLS is not available
|
||||
- Run and debug configurations for Zig projects (Debugging has some IDE limitations, and on Windows you need to do some extra setup, see below)
|
||||
- Direnv support for loading environment variables from `.envrc` files (requires the direnv executable to be installed system-wide)
|
||||
- Language injection support in Zig strings
|
||||
|
||||
## Debugging
|
||||
|
||||
Debugger settings are available in the `Settings | Build, Execution, Deployment | Debugger` menu, under the `Zig` section.
|
||||
|
|
|
@ -11,7 +11,7 @@ baseIDE=clion
|
|||
ideaVersion=2023.3.8
|
||||
clionVersion=2023.3.6
|
||||
|
||||
pluginVersion=19.2.0
|
||||
pluginVersion=19.3.0
|
||||
|
||||
# Gradle Releases -> https://github.com/gradle/gradle/releases
|
||||
gradleVersion=8.10.2
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
package com.falsepattern.zigbrains.common.direnv;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import lombok.val;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
public class DirenvCmd {
|
||||
private static final String GROUP_DISPLAY_ID = "ZigBrains Direnv";
|
||||
private static final Logger LOG = Logger.getInstance(DirenvCmd.class);
|
||||
|
||||
public static final Key<Boolean> DIRENV_KEY = Key.create("ZIG_DIRENV_KEY");
|
||||
|
||||
private final Path workDir;
|
||||
|
||||
public DirenvCmd(Path workingDirectory) {
|
||||
this.workDir = workingDirectory;
|
||||
}
|
||||
|
||||
public static boolean direnvInstalled() {
|
||||
return FileUtil.findExecutableOnPATH(Map.of(), "direnv").isPresent();
|
||||
}
|
||||
|
||||
public @NotNull Map<String, String> importDirenvSync() {
|
||||
val emptyMap = Map.<String, String>of();
|
||||
if (!direnvInstalled()) {
|
||||
return emptyMap;
|
||||
}
|
||||
try {
|
||||
try {
|
||||
val runOutput = runSync("export", "json");
|
||||
if (runOutput.error()) {
|
||||
if (runOutput.output().contains("is blocked")) {
|
||||
Notifications.Bus.notify(new Notification(GROUP_DISPLAY_ID, "Direnv not allowed",
|
||||
"Run `direnv allow` in a terminal inside the project directory" +
|
||||
" to allow direnv to run", NotificationType.ERROR));
|
||||
} else {
|
||||
Notifications.Bus.notify(new Notification(GROUP_DISPLAY_ID, "Direnv error",
|
||||
"Could not import direnv: " + runOutput.output(),
|
||||
NotificationType.ERROR));
|
||||
return emptyMap;
|
||||
}
|
||||
}
|
||||
|
||||
val type = new TypeToken<Map<String, String>>() {
|
||||
}.getType();
|
||||
if (runOutput.output().isEmpty()) {
|
||||
return emptyMap;
|
||||
}
|
||||
|
||||
return new Gson().fromJson(runOutput.output(), type);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to import direnv", e);
|
||||
return emptyMap;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to import direnv", e);
|
||||
return emptyMap;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull CompletableFuture<@NotNull Map<String, String>> tryGetProjectEnvAsync(@Nullable Project project) {
|
||||
if (project == null)
|
||||
return CompletableFuture.completedFuture(Map.of());
|
||||
val dir = ProjectUtil.guessProjectDir(project);
|
||||
if (dir == null) {
|
||||
return CompletableFuture.completedFuture(Map.of());
|
||||
}
|
||||
val direnv = new DirenvCmd(dir.toNioPath());
|
||||
return direnv.importDirenvAsync();
|
||||
}
|
||||
|
||||
public static @NotNull Map<String, String> tryGetProjectEnvSync(@Nullable Project project) {
|
||||
if (project == null)
|
||||
return Map.of();
|
||||
val dir = ProjectUtil.guessProjectDir(project);
|
||||
if (dir == null) {
|
||||
return Map.of();
|
||||
}
|
||||
val direnv = new DirenvCmd(dir.toNioPath());
|
||||
return direnv.importDirenvSync();
|
||||
}
|
||||
|
||||
public @NotNull CompletableFuture<Map<String, String>> importDirenvAsync() {
|
||||
val emptyMap = Map.<String, String>of();
|
||||
var returnMap = CompletableFuture.completedFuture(emptyMap);
|
||||
if (!direnvInstalled()) {
|
||||
return returnMap;
|
||||
}
|
||||
return runAsync("export", "json").thenApplyAsync(runOutput -> {
|
||||
if (runOutput.error()) {
|
||||
if (runOutput.output().contains("is blocked")) {
|
||||
Notifications.Bus.notify(new Notification(GROUP_DISPLAY_ID, "Direnv not allowed",
|
||||
"Run `direnv allow` in a terminal inside the project directory" +
|
||||
" to allow direnv to run", NotificationType.ERROR));
|
||||
} else {
|
||||
Notifications.Bus.notify(new Notification(GROUP_DISPLAY_ID, "Direnv error",
|
||||
"Could not import direnv: " + runOutput.output(),
|
||||
NotificationType.ERROR));
|
||||
return emptyMap;
|
||||
}
|
||||
}
|
||||
|
||||
val type = new TypeToken<Map<String, String>>() {
|
||||
}.getType();
|
||||
if (runOutput.output().isEmpty()) {
|
||||
return emptyMap;
|
||||
}
|
||||
|
||||
return new Gson().fromJson(runOutput.output(), type);
|
||||
}, AppExecutorUtil.getAppExecutorService()).exceptionallyAsync((e) -> {
|
||||
LOG.error("Failed to import direnv", e);
|
||||
return emptyMap;
|
||||
}, AppExecutorUtil.getAppExecutorService());
|
||||
}
|
||||
|
||||
private CompletableFuture<DirenvOutput> runAsync(String... args) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return runSync(args);
|
||||
} catch (Exception e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, AppExecutorUtil.getAppExecutorService());
|
||||
}
|
||||
|
||||
private DirenvOutput runSync(String... args) throws ExecutionException, InterruptedException, IOException {
|
||||
val commandArgs = new String[args.length + 1];
|
||||
commandArgs[0] = "direnv";
|
||||
System.arraycopy(args, 0, commandArgs, 1, args.length);
|
||||
val cli = new GeneralCommandLine(commandArgs).withWorkDirectory(workDir.toFile());
|
||||
val process = cli.createProcess();
|
||||
|
||||
if (process.waitFor() != 0) {
|
||||
val stdErr = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8);
|
||||
return new DirenvOutput(stdErr, true);
|
||||
}
|
||||
|
||||
val stdOut = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8);
|
||||
|
||||
return new DirenvOutput(stdOut, false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package com.falsepattern.zigbrains.common.direnv;
|
||||
|
||||
public record DirenvOutput(String output, boolean error) {}
|
|
@ -20,6 +20,8 @@ import com.intellij.openapi.diagnostic.Logger;
|
|||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -27,6 +29,7 @@ import java.net.URI;
|
|||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FileUtil {
|
||||
|
@ -132,10 +135,10 @@ public class FileUtil {
|
|||
return path != null ? sanitizeURI(path.toUri().toString()) : null;
|
||||
}
|
||||
|
||||
public static Optional<Path> findExecutableOnPATH(String exe) {
|
||||
var exeName = SystemInfo.isWindows ? exe + ".exe" : exe;
|
||||
var PATH = System.getenv("PATH").split(File.pathSeparator);
|
||||
for (var dir: PATH) {
|
||||
public static @NotNull Optional<Path> findExecutableOnPATH(@NotNull Map<String, String> extraEnv, @NotNull String exe) {
|
||||
val exeName = SystemInfo.isWindows ? exe + ".exe" : exe;
|
||||
val PATH = extraEnv.getOrDefault("PATH", System.getenv("PATH")).split(File.pathSeparator);
|
||||
for (val dir: PATH) {
|
||||
var path = Path.of(dir);
|
||||
try {
|
||||
path = path.toAbsolutePath();
|
||||
|
@ -145,7 +148,7 @@ public class FileUtil {
|
|||
if (!Files.exists(path) || !Files.isDirectory(path)) {
|
||||
continue;
|
||||
}
|
||||
var exePath = path.resolve(exeName).toAbsolutePath();
|
||||
val exePath = path.resolve(exeName).toAbsolutePath();
|
||||
if (!Files.isRegularFile(exePath) || !Files.isExecutable(exePath)) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.jetbrains.annotations.NotNull;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ public class ZigDebugEmitBinaryInstaller<ProfileState extends ProfileStateBase<?
|
|||
cli.addParameters(exeArgs);
|
||||
cli.withCharset(StandardCharsets.UTF_8);
|
||||
cli.withRedirectErrorStream(true);
|
||||
return cli;
|
||||
return profileState.configuration().patchCommandLine(cli, toolchain);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,8 +2,6 @@ package com.falsepattern.zigbrains.debugger.toolchain;
|
|||
|
||||
import com.falsepattern.zigbrains.ZigBundle;
|
||||
import com.falsepattern.zigbrains.common.ZigPathManager;
|
||||
import com.falsepattern.zigbrains.debugger.settings.MSVCDownloadPermission;
|
||||
import com.falsepattern.zigbrains.debugger.settings.ZigDebuggerSettings;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
|
@ -17,7 +15,6 @@ import com.intellij.openapi.util.SystemInfo;
|
|||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.ui.BrowserHyperlinkListener;
|
||||
import com.intellij.ui.HyperlinkLabel;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.ui.components.JBPanel;
|
||||
import com.intellij.util.download.DownloadableFileService;
|
||||
import com.intellij.util.io.Decompressor;
|
||||
|
@ -42,9 +39,6 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@Service(Service.Level.APP)
|
||||
|
|
|
@ -62,7 +62,7 @@ public abstract class ProfileStateBase<T extends ZigExecConfigBase<T>> extends C
|
|||
workingDirectory.getPath().ifPresent(x -> cli.setWorkDirectory(x.toFile()));
|
||||
cli.setCharset(StandardCharsets.UTF_8);
|
||||
cli.addParameters(configuration.buildCommandLineArgs(debug));
|
||||
return cli;
|
||||
return configuration.patchCommandLine(cli, toolchain);
|
||||
}
|
||||
|
||||
public T configuration() {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.execution.base;
|
||||
|
||||
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
|
||||
import com.falsepattern.zigbrains.project.ui.WorkingDirectoryComponent;
|
||||
import com.falsepattern.zigbrains.project.ui.ZigFilePathPanel;
|
||||
import com.falsepattern.zigbrains.project.util.CLIUtil;
|
||||
|
@ -23,6 +24,7 @@ import com.falsepattern.zigbrains.project.util.ElementUtil;
|
|||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.SettingsEditor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
|
@ -358,6 +360,10 @@ public class ZigConfigEditor<T extends ZigExecConfigBase<T>> extends SettingsEdi
|
|||
return new CheckboxConfigurable(serializedName, "Colored terminal", true);
|
||||
}
|
||||
|
||||
public static CheckboxConfigurable direnvConfigurable(String serializedName, Project project) {
|
||||
return new CheckboxConfigurable(serializedName, "Use direnv", ZigProjectSettingsService.getInstance(project).getState().direnv);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class OptimizationConfigurable implements ZigConfigurable<OptimizationConfigurable> {
|
||||
private transient final String serializedName;
|
||||
|
|
|
@ -16,10 +16,13 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.execution.base;
|
||||
|
||||
import com.falsepattern.zigbrains.common.direnv.DirenvCmd;
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.util.ProjectUtil;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.Executor;
|
||||
import com.intellij.execution.configurations.ConfigurationFactory;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.configurations.LocatableConfigurationBase;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
@ -34,13 +37,17 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.falsepattern.zigbrains.project.execution.base.ZigConfigEditor.direnvConfigurable;
|
||||
|
||||
@Getter
|
||||
public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends LocatableConfigurationBase<ProfileStateBase<T>> {
|
||||
private ZigConfigEditor.WorkDirectoryConfigurable workingDirectory = new ZigConfigEditor.WorkDirectoryConfigurable("workingDirectory");
|
||||
private ZigConfigEditor.CheckboxConfigurable pty = new ZigConfigEditor.CheckboxConfigurable("pty", "Emulate Terminal", false);
|
||||
private ZigConfigEditor.CheckboxConfigurable direnv;
|
||||
public ZigExecConfigBase(@NotNull Project project, @NotNull ConfigurationFactory factory, @Nullable String name) {
|
||||
super(project, factory, name);
|
||||
workingDirectory.setPath(getProject().isDefault() ? null : ProjectUtil.guessProjectDir(getProject()));
|
||||
direnv = direnvConfigurable("direnv", project);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,6 +69,13 @@ public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends
|
|||
|
||||
public abstract List<String> buildCommandLineArgs(boolean debug) throws ExecutionException;
|
||||
|
||||
public @NotNull GeneralCommandLine patchCommandLine(@NotNull GeneralCommandLine commandLine, @NotNull AbstractZigToolchain toolchain) {
|
||||
if (direnv.value) {
|
||||
commandLine.withEnvironment(DirenvCmd.tryGetProjectEnvSync(toolchain.getProject()));
|
||||
}
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
public boolean emulateTerminal() {
|
||||
return pty.value;
|
||||
}
|
||||
|
@ -71,6 +85,7 @@ public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends
|
|||
val myClone = (ZigExecConfigBase<?>) super.clone();
|
||||
myClone.workingDirectory = workingDirectory.clone();
|
||||
myClone.pty = pty.clone();
|
||||
myClone.direnv = direnv.clone();
|
||||
return (T) myClone;
|
||||
}
|
||||
|
||||
|
@ -82,6 +97,6 @@ public abstract class ZigExecConfigBase<T extends ZigExecConfigBase<T>> extends
|
|||
throws ExecutionException;
|
||||
|
||||
public @NotNull List<ZigConfigEditor.@NotNull ZigConfigurable<?>> getConfigurables() {
|
||||
return List.of(workingDirectory, pty);
|
||||
return List.of(workingDirectory, pty, direnv);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,8 @@ public class ZigNewProjectPanel implements Disposable {
|
|||
|
||||
public ZigNewProjectPanel(boolean handleGit) {
|
||||
this.handleGit = handleGit;
|
||||
projConf = new ZigProjectSettingsPanel();
|
||||
zlsConf = new ZLSSettingsPanel();
|
||||
projConf = new ZigProjectSettingsPanel(null);
|
||||
zlsConf = new ZLSSettingsPanel(null);
|
||||
}
|
||||
|
||||
public ZigProjectConfigurationData getData() {
|
||||
|
|
|
@ -53,7 +53,7 @@ public record ZigProjectConfigurationData(boolean git, ZigProjectSettings projCo
|
|||
svc.loadState(this.projConf());
|
||||
ZLSProjectSettingsService.getInstance(project).loadState(this.zlsConf());
|
||||
|
||||
val toolchain = svc.getState().getToolchain();
|
||||
val toolchain = svc.getState().getToolchain(project);
|
||||
|
||||
val template = this.selectedTemplate();
|
||||
|
||||
|
@ -65,7 +65,7 @@ public record ZigProjectConfigurationData(boolean git, ZigProjectSettings projCo
|
|||
return;
|
||||
}
|
||||
val zig = toolchain.zig();
|
||||
val resultOpt = zig.callWithArgs(baseDir.toNioPath(), 10000, "init");
|
||||
val resultOpt = zig.callWithArgs(baseDir.toNioPath(), 10000, toolchain.getDataForSelfRuns(), "init");
|
||||
if (resultOpt.isEmpty()) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Project",
|
||||
"Failed to invoke \"zig init\"!",
|
||||
|
|
|
@ -37,7 +37,7 @@ public class ZigProjectConfigurable implements SubConfigurable {
|
|||
|
||||
@Override
|
||||
public void createComponent(JavaPanel panel) {
|
||||
settingsPanel = new ZigProjectSettingsPanel();
|
||||
settingsPanel = new ZigProjectSettingsPanel(project);
|
||||
settingsPanel.attachPanelTo(panel);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.ide.project;
|
||||
|
||||
import com.falsepattern.zigbrains.common.direnv.DirenvCmd;
|
||||
import com.falsepattern.zigbrains.common.util.PathUtil;
|
||||
import com.falsepattern.zigbrains.common.util.StringUtil;
|
||||
import com.falsepattern.zigbrains.common.util.TextFieldUtil;
|
||||
|
@ -27,15 +28,18 @@ import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsS
|
|||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider;
|
||||
import com.falsepattern.zigbrains.project.toolchain.tools.ZigCompilerTool;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.dsl.builder.AlignX;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
@ -44,11 +48,14 @@ import java.util.Optional;
|
|||
import static com.falsepattern.zigbrains.common.util.KtUtil.$f;
|
||||
|
||||
public class ZigProjectSettingsPanel implements MyDisposable {
|
||||
private final @Nullable Project project;
|
||||
@Getter
|
||||
private boolean disposed = false;
|
||||
|
||||
private final UIDebouncer versionUpdateDebouncer = new UIDebouncer(this);
|
||||
|
||||
private final JBCheckBox direnv = new JBCheckBox("Use direnv");
|
||||
|
||||
private final TextFieldWithBrowseButton pathToToolchain = TextFieldUtil.pathToDirectoryTextField(this,
|
||||
"Path to the Zig Toolchain",
|
||||
this::updateUI);
|
||||
|
@ -72,27 +79,44 @@ public class ZigProjectSettingsPanel implements MyDisposable {
|
|||
});
|
||||
}
|
||||
|
||||
public ZigProjectSettingsPanel(@Nullable Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
private void autodetect(ActionEvent e) {
|
||||
autodetect();
|
||||
}
|
||||
|
||||
public void autodetect() {
|
||||
val tc = AbstractZigToolchain.suggest();
|
||||
if (tc != null) {
|
||||
pathToToolchain.setText(PathUtil.stringFromPath(tc.getLocation()));
|
||||
updateUI();
|
||||
val data = new UserDataHolderBase();
|
||||
data.putUserData(DirenvCmd.DIRENV_KEY, direnv.isSelected());
|
||||
if (project != null) {
|
||||
data.putUserData(AbstractZigToolchain.PROJECT_KEY, project);
|
||||
}
|
||||
val tc = AbstractZigToolchain.suggest(data);
|
||||
tc.thenAccept(it -> {
|
||||
if (it != null) {
|
||||
if (!disposed) {
|
||||
pathToToolchain.setText(PathUtil.stringFromPath(it.getLocation()));
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ZigProjectSettings getData() {
|
||||
val toolchain = Optional.of(pathToToolchain.getText())
|
||||
.map(PathUtil::pathFromString)
|
||||
.map(ZigToolchainProvider::findToolchain)
|
||||
.map(path -> ZigToolchainProvider.findToolchain(path, project))
|
||||
.orElse(null);
|
||||
return new ZigProjectSettings(stdFieldOverride.isSelected() ? StringUtil.blankToNull(pathToStdField.getText()) : null, toolchain);
|
||||
return new ZigProjectSettings(direnv.isSelected(),
|
||||
stdFieldOverride.isSelected() ? StringUtil.blankToNull(pathToStdField.getText()) : null,
|
||||
toolchain);
|
||||
}
|
||||
|
||||
public void setData(ZigProjectSettings value) {
|
||||
direnv.setSelected(value.direnv);
|
||||
|
||||
pathToToolchain.setText(Optional.ofNullable(value.getToolchainHomeDirectory())
|
||||
.orElse(""));
|
||||
|
||||
|
@ -112,6 +136,9 @@ public class ZigProjectSettingsPanel implements MyDisposable {
|
|||
p.group("Zig Settings", true, p2 -> {
|
||||
p2.row("Toolchain location", r -> {
|
||||
r.cell(pathToToolchain).resizableColumn().align(AlignX.FILL);
|
||||
if (DirenvCmd.direnvInstalled() && project != null) {
|
||||
r.cell(direnv);
|
||||
}
|
||||
r.button("Autodetect", $f(this::autodetect));
|
||||
});
|
||||
p2.cell("Toolchain version", toolchainVersion);
|
||||
|
@ -133,7 +160,7 @@ public class ZigProjectSettingsPanel implements MyDisposable {
|
|||
|
||||
versionUpdateDebouncer.run(
|
||||
() -> {
|
||||
val toolchain = Optional.ofNullable(pathToToolchain).map(ZigToolchainProvider::findToolchain).orElse(null);
|
||||
val toolchain = Optional.ofNullable(pathToToolchain).map(path -> ZigToolchainProvider.findToolchain(path, project)).orElse(null);
|
||||
val zig = Optional.ofNullable(toolchain).map(AbstractZigToolchain::zig).orElse(null);
|
||||
val version = Optional.ofNullable(zig).flatMap(ZigCompilerTool::queryVersion).orElse(null);
|
||||
val stdPath = Optional.ofNullable(zig).flatMap(ZigCompilerTool::getStdPath).orElse(null);
|
||||
|
|
|
@ -18,34 +18,38 @@ package com.falsepattern.zigbrains.project.openapi.components;
|
|||
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.io.PathKt;
|
||||
import com.intellij.util.xmlb.annotations.Transient;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ZigProjectSettings {
|
||||
public boolean direnv;
|
||||
public boolean overrideStdPath;
|
||||
public String explicitPathToStd;
|
||||
public String toolchainHomeDirectory;
|
||||
|
||||
public ZigProjectSettings(String explicitPathToStd, AbstractZigToolchain toolchain) {
|
||||
this(explicitPathToStd != null, explicitPathToStd, null);
|
||||
public ZigProjectSettings(boolean direnv, String explicitPathToStd, AbstractZigToolchain toolchain) {
|
||||
this(direnv, explicitPathToStd != null, explicitPathToStd, null);
|
||||
setToolchain(toolchain);
|
||||
}
|
||||
|
||||
public ZigProjectSettings() {
|
||||
this(true, false, null, null);
|
||||
}
|
||||
|
||||
@Transient
|
||||
public @Nullable AbstractZigToolchain getToolchain() {
|
||||
public AbstractZigToolchain getToolchain(Project project) {
|
||||
if (toolchainHomeDirectory == null) {
|
||||
return null;
|
||||
}
|
||||
return ZigToolchainProvider.findToolchain(Path.of(toolchainHomeDirectory));
|
||||
return ZigToolchainProvider.findToolchain(Path.of(toolchainHomeDirectory), project);
|
||||
}
|
||||
|
||||
@Transient
|
||||
|
|
|
@ -40,8 +40,9 @@ public final class ZigProjectSettingsService extends AbstractZigProjectSettingsS
|
|||
|
||||
public boolean isModified(ZigProjectSettings otherData) {
|
||||
val myData = getState();
|
||||
return !Objects.equals(myData.toolchainHomeDirectory, otherData.toolchainHomeDirectory) ||
|
||||
return myData.direnv != otherData.direnv ||
|
||||
!Objects.equals(myData.toolchainHomeDirectory, otherData.toolchainHomeDirectory) ||
|
||||
!Objects.equals(myData.explicitPathToStd, otherData.explicitPathToStd) ||
|
||||
!Objects.equals(myData.overrideStdPath, otherData.overrideStdPath);
|
||||
myData.overrideStdPath != otherData.overrideStdPath;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public abstract class ZigProgramRunnerBase<ProfileState extends ProfileStateBase
|
|||
if (state == null)
|
||||
return Promises.resolvedPromise();
|
||||
|
||||
val toolchain = ZigProjectSettingsService.getInstance(environment.getProject()).getState().getToolchain();
|
||||
val toolchain = ZigProjectSettingsService.getInstance(environment.getProject()).getState().getToolchain(environment.getProject());
|
||||
if (toolchain == null) {
|
||||
return Promises.resolvedPromise();
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public final class ZigStepDiscoveryService {
|
|||
val zig = toolchain.zig();
|
||||
val result = zig.callWithArgs(
|
||||
ProjectUtil.guessProjectDir(project), CURRENT_TIMEOUT_SEC * 1000,
|
||||
toolchain.getDataForSelfRuns(),
|
||||
"build", "-l");
|
||||
if (result.isPresent()) {
|
||||
val res = result.get();
|
||||
|
|
|
@ -20,32 +20,57 @@ import com.falsepattern.zigbrains.common.util.Lazy;
|
|||
import com.falsepattern.zigbrains.project.toolchain.flavours.AbstractZigToolchainFlavour;
|
||||
import com.falsepattern.zigbrains.project.toolchain.tools.ZigCompilerTool;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public abstract class AbstractZigToolchain {
|
||||
private final Path location;
|
||||
private final @Nullable Project project;
|
||||
|
||||
private final Lazy<ZigCompilerTool> zig = new Lazy<>(() -> new ZigCompilerTool(this));
|
||||
public static final Key<Path> WORK_DIR_KEY = Key.create("ZIG_TOOLCHAIN_WORK_DIR");
|
||||
public static final Key<Project> PROJECT_KEY = Key.create("ZIG_TOOLCHAIN_PROJECT");
|
||||
|
||||
public static @Nullable AbstractZigToolchain suggest() {
|
||||
return suggest(null);
|
||||
}
|
||||
|
||||
public static @Nullable AbstractZigToolchain suggest(@Nullable Path projectDir) {
|
||||
return AbstractZigToolchainFlavour.getApplicableFlavours()
|
||||
.stream()
|
||||
.flatMap(it -> it.suggestHomePaths().stream())
|
||||
.filter(Objects::nonNull)
|
||||
.map(it -> ZigToolchainProvider.findToolchain(it.toAbsolutePath()))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst().orElse(null);
|
||||
public static @NotNull CompletableFuture<@Nullable AbstractZigToolchain> suggest(@NotNull UserDataHolder workDir) {
|
||||
val project = workDir.getUserData(PROJECT_KEY);
|
||||
if (workDir.getUserData(WORK_DIR_KEY) == null) {
|
||||
if (project != null) {
|
||||
val projectDir = ProjectUtil.guessProjectDir(project);
|
||||
if (projectDir != null) {
|
||||
workDir.putUserData(WORK_DIR_KEY, projectDir.toNioPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
val exec = AppExecutorUtil.getAppExecutorService();
|
||||
val homePathFutures = AbstractZigToolchainFlavour.getApplicableFlavours()
|
||||
.stream()
|
||||
.map(it -> it.suggestHomePaths(workDir))
|
||||
.toList();
|
||||
return CompletableFuture.allOf(homePathFutures.toArray(CompletableFuture[]::new))
|
||||
.thenApplyAsync(it -> homePathFutures.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(Objects::nonNull)
|
||||
.map((dir) -> ZigToolchainProvider.findToolchain(dir, project))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null), exec);
|
||||
}
|
||||
|
||||
public ZigCompilerTool zig() {
|
||||
|
@ -54,10 +79,14 @@ public abstract class AbstractZigToolchain {
|
|||
|
||||
public abstract int executionTimeoutInMilliseconds();
|
||||
|
||||
public abstract GeneralCommandLine patchCommandLine(GeneralCommandLine commandLine);
|
||||
public abstract @NotNull GeneralCommandLine patchCommandLine(@NotNull GeneralCommandLine commandLine, @NotNull UserDataHolder data);
|
||||
|
||||
public abstract Path pathToExecutable(String toolName);
|
||||
|
||||
public @NotNull UserDataHolder getDataForSelfRuns() {
|
||||
return new UserDataHolderBase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof AbstractZigToolchain azt) {
|
||||
|
|
|
@ -16,15 +16,22 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.toolchain;
|
||||
|
||||
import com.falsepattern.zigbrains.common.direnv.DirenvCmd;
|
||||
import com.falsepattern.zigbrains.common.util.PathUtil;
|
||||
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class LocalZigToolchain extends AbstractZigToolchain{
|
||||
public LocalZigToolchain(Path location) {
|
||||
super(location);
|
||||
public LocalZigToolchain(Path location, @Nullable Project project) {
|
||||
super(location, project);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,7 +40,12 @@ public class LocalZigToolchain extends AbstractZigToolchain{
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeneralCommandLine patchCommandLine(GeneralCommandLine commandLine) {
|
||||
public @NotNull GeneralCommandLine patchCommandLine(@NotNull GeneralCommandLine commandLine, @NotNull UserDataHolder data) {
|
||||
val project = getProject();
|
||||
val direnv = data.getUserData(DirenvCmd.DIRENV_KEY);
|
||||
if (direnv != null && direnv) {
|
||||
commandLine.withEnvironment(DirenvCmd.tryGetProjectEnvSync(project));
|
||||
}
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
|
@ -42,6 +54,16 @@ public class LocalZigToolchain extends AbstractZigToolchain{
|
|||
return PathUtil.pathToExecutable(getLocation(), toolName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UserDataHolder getDataForSelfRuns() {
|
||||
val data = super.getDataForSelfRuns();
|
||||
val project = getProject();
|
||||
if (project != null) {
|
||||
data.putUserData(DirenvCmd.DIRENV_KEY, ZigProjectSettingsService.getInstance(project).getState().direnv);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static LocalZigToolchain ensureLocal(AbstractZigToolchain toolchain) throws ExecutionException {
|
||||
if (!(toolchain instanceof LocalZigToolchain $toolchain)) {
|
||||
throw new ExecutionException("The debugger only supports local zig toolchains!");
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package com.falsepattern.zigbrains.project.toolchain;
|
||||
|
||||
import com.intellij.execution.wsl.WslPath;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -27,11 +28,11 @@ import java.util.Map;
|
|||
public class LocalZigToolchainProvider implements ZigToolchainProvider {
|
||||
private static final Map<Path, LocalZigToolchain> tcCache = ContainerUtil.createWeakKeyWeakValueMap();
|
||||
@Override
|
||||
public @Nullable AbstractZigToolchain getToolchain(Path homePath) {
|
||||
public @Nullable AbstractZigToolchain getToolchain(Path homePath, @Nullable Project project) {
|
||||
if (SystemInfo.isWindows && WslPath.isWslUncPath(homePath.toString())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tcCache.computeIfAbsent(homePath, LocalZigToolchain::new);
|
||||
return tcCache.computeIfAbsent(homePath, (path) -> new LocalZigToolchain(path, project));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.toolchain;
|
||||
|
||||
import com.falsepattern.zigbrains.common.direnv.DirenvCmd;
|
||||
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
|
||||
import com.falsepattern.zigbrains.zig.environment.ZLSConfig;
|
||||
import com.falsepattern.zigbrains.zig.environment.ZLSConfigProvider;
|
||||
|
@ -24,6 +25,7 @@ import com.intellij.notification.NotificationType;
|
|||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import lombok.val;
|
||||
|
||||
import java.nio.file.Files;
|
||||
|
@ -35,16 +37,20 @@ public class ToolchainZLSConfigProvider implements ZLSConfigProvider {
|
|||
public void getEnvironment(Project project, ZLSConfig.ZLSConfigBuilder builder) {
|
||||
val svc = ZigProjectSettingsService.getInstance(project);
|
||||
val state = svc.getState();
|
||||
var toolchain = state.getToolchain();
|
||||
var toolchain = state.getToolchain(project);
|
||||
if (toolchain == null) {
|
||||
toolchain = AbstractZigToolchain.suggest();
|
||||
val data = new UserDataHolderBase();
|
||||
|
||||
data.putUserData(AbstractZigToolchain.PROJECT_KEY, project);
|
||||
data.putUserData(DirenvCmd.DIRENV_KEY, svc.getState().direnv);
|
||||
toolchain = AbstractZigToolchain.suggest(data).join();
|
||||
if (toolchain == null) {
|
||||
return;
|
||||
}
|
||||
state.setToolchain(toolchain);
|
||||
}
|
||||
val projectDir = ProjectUtil.guessProjectDir(project);
|
||||
val oEnv = toolchain.zig().getEnv(projectDir == null ? null : projectDir.toNioPath());
|
||||
val oEnv = toolchain.zig().getEnv();
|
||||
if (oEnv.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package com.falsepattern.zigbrains.project.toolchain;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
@ -26,14 +27,14 @@ public interface ZigToolchainProvider {
|
|||
ExtensionPointName<ZigToolchainProvider> EXTENSION_POINT_NAME =
|
||||
ExtensionPointName.create("com.falsepattern.zigbrains.toolchainProvider");
|
||||
|
||||
static @Nullable AbstractZigToolchain findToolchain(Path homePath) {
|
||||
static @Nullable AbstractZigToolchain findToolchain(Path homePath, @Nullable Project project) {
|
||||
return EXTENSION_POINT_NAME.getExtensionList()
|
||||
.stream()
|
||||
.map(it -> it.getToolchain(homePath))
|
||||
.map(it -> it.getToolchain(homePath, project))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Nullable AbstractZigToolchain getToolchain(Path homePath);
|
||||
@Nullable AbstractZigToolchain getToolchain(Path homePath, @Nullable Project project);
|
||||
}
|
||||
|
|
|
@ -19,12 +19,17 @@ package com.falsepattern.zigbrains.project.toolchain.flavours;
|
|||
import com.falsepattern.zigbrains.common.util.PathUtil;
|
||||
import com.falsepattern.zigbrains.project.toolchain.tools.ZigCompilerTool;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public abstract class AbstractZigToolchainFlavour {
|
||||
private static final ExtensionPointName<AbstractZigToolchainFlavour> EXTENSION_POINT_NAME =
|
||||
|
@ -44,13 +49,14 @@ public abstract class AbstractZigToolchainFlavour {
|
|||
.orElse(null);
|
||||
}
|
||||
|
||||
public List<Path> suggestHomePaths() {
|
||||
return getHomePathCandidates().stream()
|
||||
.filter(this::isValidToolchainPath)
|
||||
.toList();
|
||||
public CompletableFuture<List<Path>> suggestHomePaths(@NotNull UserDataHolder data) {
|
||||
return getHomePathCandidates(data).thenApplyAsync(it -> it.stream()
|
||||
.filter(this::isValidToolchainPath)
|
||||
.toList(),
|
||||
AppExecutorUtil.getAppExecutorService());
|
||||
}
|
||||
|
||||
protected abstract List<Path> getHomePathCandidates();
|
||||
protected abstract CompletableFuture<List<Path>> getHomePathCandidates(@NotNull UserDataHolder data);
|
||||
|
||||
protected boolean isApplicable() {
|
||||
return true;
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.toolchain.flavours;
|
||||
|
||||
import com.falsepattern.zigbrains.common.direnv.DirenvCmd;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
|
@ -24,18 +27,33 @@ import java.nio.file.Path;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain.WORK_DIR_KEY;
|
||||
|
||||
public class ZigSystemPathToolchainFlavour extends AbstractZigToolchainFlavour{
|
||||
@Override
|
||||
protected List<Path> getHomePathCandidates() {
|
||||
val PATH = System.getenv("PATH");
|
||||
if (PATH == null) {
|
||||
return Collections.emptyList();
|
||||
protected CompletableFuture<List<Path>> getHomePathCandidates(@NotNull UserDataHolder data) {
|
||||
val workDir = data.getUserData(WORK_DIR_KEY);
|
||||
val direnv = data.getUserData(DirenvCmd.DIRENV_KEY);
|
||||
CompletableFuture<Map<String, String>> direnvFuture;
|
||||
if (direnv == Boolean.TRUE && DirenvCmd.direnvInstalled() && workDir != null) {
|
||||
val cmd = new DirenvCmd(workDir);
|
||||
direnvFuture = cmd.importDirenvAsync();
|
||||
} else {
|
||||
direnvFuture = CompletableFuture.completedFuture(Map.of());
|
||||
}
|
||||
return Arrays.stream(PATH.split(File.pathSeparator))
|
||||
.filter(it -> !it.isEmpty())
|
||||
.map(Path::of)
|
||||
.filter(Files::isDirectory)
|
||||
.toList();
|
||||
return direnvFuture.thenApplyAsync(env -> {
|
||||
val PATH = env.getOrDefault("PATH", System.getenv("PATH"));
|
||||
if (PATH == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.stream(PATH.split(File.pathSeparator))
|
||||
.filter(it -> !it.isEmpty())
|
||||
.map(Path::of)
|
||||
.filter(Files::isDirectory)
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package com.falsepattern.zigbrains.project.toolchain.stdlib;
|
||||
|
||||
import com.falsepattern.zigbrains.common.util.PathUtil;
|
||||
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
|
||||
import com.falsepattern.zigbrains.zig.Icons;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
|
@ -51,7 +50,7 @@ public class ZigSyntheticLibrary extends SyntheticLibrary implements ItemPresent
|
|||
val service = ZigProjectSettingsService.getInstance(project);
|
||||
val state = service.getState();
|
||||
this.roots = ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||
val toolchain = state.getToolchain();
|
||||
val toolchain = state.getToolchain(project);
|
||||
blk: try {
|
||||
val ePathStr = state.getExplicitPathToStd();
|
||||
if (ePathStr == null) {
|
||||
|
@ -84,7 +83,7 @@ public class ZigSyntheticLibrary extends SyntheticLibrary implements ItemPresent
|
|||
});
|
||||
|
||||
this.name = ApplicationManager.getApplication()
|
||||
.executeOnPooledThread(() -> Optional.ofNullable(state.getToolchain())
|
||||
.executeOnPooledThread(() -> Optional.ofNullable(state.getToolchain(project))
|
||||
.flatMap(tc -> tc.zig().queryVersion())
|
||||
.map(version -> "Zig " + version)
|
||||
.orElse("Zig"));
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
|
|||
import com.falsepattern.zigbrains.project.util.CLIUtil;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import kotlin.text.Charsets;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.val;
|
||||
|
@ -41,35 +42,39 @@ public abstract class AbstractZigTool {
|
|||
return toolchain.pathToExecutable(toolName);
|
||||
}
|
||||
|
||||
public final Optional<ProcessOutput> callWithArgs(@Nullable Path workingDirectory, int timeoutMillis, String... parameters) {
|
||||
return CLIUtil.execute(createBaseCommandLine(workingDirectory, parameters),
|
||||
public final Optional<ProcessOutput> callWithArgs(@Nullable Path workingDirectory, int timeoutMillis, @NotNull UserDataHolder data, String @NotNull... parameters) {
|
||||
return CLIUtil.execute(createBaseCommandLine(workingDirectory, data, parameters),
|
||||
timeoutMillis);
|
||||
}
|
||||
|
||||
protected final GeneralCommandLine createBaseCommandLine(@Nullable Path workingDirectory,
|
||||
@NotNull UserDataHolder data,
|
||||
String @NotNull... parameters) {
|
||||
return createBaseCommandLine(workingDirectory, Collections.emptyMap(), parameters);
|
||||
return createBaseCommandLine(workingDirectory, Collections.emptyMap(), data, parameters);
|
||||
}
|
||||
|
||||
protected final GeneralCommandLine createBaseCommandLine(@Nullable Path workingDirectory,
|
||||
@NotNull Map<String, String> environment,
|
||||
@NotNull UserDataHolder data,
|
||||
String @NotNull... parameters) {
|
||||
return createBaseCommandLine(workingDirectory, environment, List.of(parameters));
|
||||
return createBaseCommandLine(workingDirectory, environment, data, List.of(parameters));
|
||||
}
|
||||
|
||||
protected final GeneralCommandLine createBaseCommandLine(@Nullable Path workingDirectory,
|
||||
@NotNull UserDataHolder data,
|
||||
@NotNull List<String> parameters) {
|
||||
return createBaseCommandLine(workingDirectory, Collections.emptyMap(), parameters);
|
||||
return createBaseCommandLine(workingDirectory, Collections.emptyMap(), data, parameters);
|
||||
}
|
||||
|
||||
protected GeneralCommandLine createBaseCommandLine(@Nullable Path workingDirectory,
|
||||
@NotNull Map<String, String> environment,
|
||||
@NotNull UserDataHolder data,
|
||||
@NotNull List<String> parameters) {
|
||||
val cli = new GeneralCommandLine(executable().toString())
|
||||
.withWorkDirectory(workingDirectory == null ? null : workingDirectory.toString())
|
||||
.withParameters(parameters)
|
||||
.withEnvironment(environment)
|
||||
.withCharset(Charsets.UTF_8);
|
||||
return toolchain.patchCommandLine(cli);
|
||||
return toolchain.patchCommandLine(cli, data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import com.google.gson.Gson;
|
|||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
|
@ -38,7 +37,7 @@ public class ZigCompilerTool extends AbstractZigTool{
|
|||
public ZigCompilerTool(AbstractZigToolchain toolchain) {
|
||||
super(toolchain, TOOL_NAME);
|
||||
val app = ApplicationManager.getApplication();
|
||||
val baseFuture = app.executeOnPooledThread(() -> getEnv(toolchain.getLocation()));
|
||||
val baseFuture = app.executeOnPooledThread(() -> getEnv());
|
||||
version = new Lazy<>(() -> {
|
||||
try {
|
||||
return baseFuture.get().map(ZigToolchainEnvironmentSerializable::version);
|
||||
|
@ -70,8 +69,8 @@ public class ZigCompilerTool extends AbstractZigTool{
|
|||
});
|
||||
}
|
||||
|
||||
public Optional<ZigToolchainEnvironmentSerializable> getEnv(@Nullable Path workingDirectory) {
|
||||
return callWithArgs(workingDirectory, toolchain.executionTimeoutInMilliseconds(), "env")
|
||||
public Optional<ZigToolchainEnvironmentSerializable> getEnv() {
|
||||
return callWithArgs(toolchain.getLocation(), toolchain.executionTimeoutInMilliseconds(), toolchain.getDataForSelfRuns(), "env")
|
||||
.map(ProcessOutput::getStdoutLines)
|
||||
.map(lines -> new Gson().fromJson(String.join(" ", lines), ZigToolchainEnvironmentSerializable.class));
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.nio.file.Path;
|
|||
|
||||
public class ProjectUtil {
|
||||
public static @Nullable AbstractZigToolchain getToolchain(Project project) {
|
||||
return ZigProjectSettingsService.getInstance(project).getState().getToolchain();
|
||||
return ZigProjectSettingsService.getInstance(project).getState().getToolchain(project);
|
||||
}
|
||||
|
||||
public static @Nullable Path guessProjectDir(Project project) {
|
||||
|
|
|
@ -3,35 +3,18 @@ package com.falsepattern.zigbrains.zig.editing;
|
|||
import com.falsepattern.zigbrains.zig.parser.ZigFile;
|
||||
import com.falsepattern.zigbrains.zig.psi.ZigStringLiteral;
|
||||
import com.falsepattern.zigbrains.zig.psi.ZigTypes;
|
||||
import com.falsepattern.zigbrains.zig.stringlexer.ZigStringLexer;
|
||||
import com.falsepattern.zigbrains.zig.util.PsiTextUtil;
|
||||
import com.falsepattern.zigbrains.zig.util.ZigStringUtil;
|
||||
import com.intellij.application.options.CodeStyle;
|
||||
import com.intellij.codeInsight.editorActions.JavaLikeQuoteHandler;
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lexer.StringLiteralLexer;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.StringEscapesTokenTypes;
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ZigEnterInQuotedStringHandler extends EnterHandlerDelegateAdapter {
|
||||
@Override
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package com.falsepattern.zigbrains.zig.highlighter;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.lexer.ZigHighlightingLexer;
|
||||
import com.falsepattern.zigbrains.zig.lexer.ZigLexerAdapter;
|
||||
import com.falsepattern.zigbrains.zig.psi.ZigTypes;
|
||||
import com.intellij.lexer.Lexer;
|
||||
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.falsepattern.zigbrains.zig.intentions;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.psi.ZigStringLiteral;
|
||||
import com.falsepattern.zigbrains.zig.psi.ZigTypes;
|
||||
import com.falsepattern.zigbrains.zig.util.PsiTextUtil;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
|
||||
|
@ -10,7 +9,6 @@ import com.intellij.codeInspection.util.IntentionName;
|
|||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import lombok.val;
|
||||
|
|
|
@ -1,24 +1,17 @@
|
|||
package com.falsepattern.zigbrains.zig.intentions;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.psi.ZigStringLiteral;
|
||||
import com.falsepattern.zigbrains.zig.util.PsiTextUtil;
|
||||
import com.falsepattern.zigbrains.zig.util.ZigStringUtil;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName;
|
||||
import com.intellij.codeInspection.util.IntentionName;
|
||||
import com.intellij.formatting.CoreFormatterUtil;
|
||||
import com.intellij.formatting.FormatterEx;
|
||||
import com.intellij.formatting.FormattingModel;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.MathUtil;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package com.falsepattern.zigbrains.zig.lsp;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.settings.ZLSProjectSettingsService;
|
||||
import com.falsepattern.zigbrains.zig.settings.ZLSSettings;
|
||||
import com.falsepattern.zigbrains.zig.settings.ZLSSettingsConfigProvider;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.redhat.devtools.lsp4ij.LanguageServerEnablementSupport;
|
||||
|
@ -12,7 +10,6 @@ import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures;
|
|||
import com.redhat.devtools.lsp4ij.client.features.LSPFormattingFeature;
|
||||
import com.redhat.devtools.lsp4ij.client.features.LSPInlayHintFeature;
|
||||
import com.redhat.devtools.lsp4ij.server.StreamConnectionProvider;
|
||||
import com.redhat.devtools.lsp4ij.server.capabilities.InlayHintCapabilityRegistry;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -54,7 +51,7 @@ public class ZLSLanguageServerFactory implements LanguageServerFactory, Language
|
|||
|
||||
@Override
|
||||
public boolean isEnabled(@NotNull Project project) {
|
||||
return enabled && ZLSStreamConnectionProvider.doGetCommand(project, false) != null;
|
||||
return enabled && ZLSStreamConnectionProvider.getCommandSync(project, false) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,8 +10,6 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.util.List;
|
||||
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.BUILTIN;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.COMMENT;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.COMMENT_DOC;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.ENUM_DECL;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.ENUM_MEMBER_DECL;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.ENUM_MEMBER_REF;
|
||||
|
@ -36,7 +34,6 @@ import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.OP
|
|||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.PARAMETER;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.PROPERTY_DECL;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.PROPERTY_REF;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.STRING;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.STRUCT_DECL;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.STRUCT_REF;
|
||||
import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.TYPE_DECL;
|
||||
|
@ -52,7 +49,6 @@ import static com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.VA
|
|||
import static com.falsepattern.zigbrains.zig.lsp.ZLSSemanticTokenModifiers.Declaration;
|
||||
import static com.falsepattern.zigbrains.zig.lsp.ZLSSemanticTokenModifiers.Definition;
|
||||
import static com.falsepattern.zigbrains.zig.lsp.ZLSSemanticTokenModifiers.Deprecated;
|
||||
import static com.falsepattern.zigbrains.zig.lsp.ZLSSemanticTokenModifiers.Documentation;
|
||||
import static com.falsepattern.zigbrains.zig.lsp.ZLSSemanticTokenModifiers.Generic;
|
||||
import static com.falsepattern.zigbrains.zig.lsp.ZLSSemanticTokenTypes.Builtin;
|
||||
import static com.falsepattern.zigbrains.zig.lsp.ZLSSemanticTokenTypes.Comment;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.falsepattern.zigbrains.zig.lsp;
|
||||
|
||||
import com.falsepattern.zigbrains.common.direnv.DirenvCmd;
|
||||
import com.falsepattern.zigbrains.common.util.StringUtil;
|
||||
import com.falsepattern.zigbrains.zig.environment.ZLSConfigProvider;
|
||||
import com.falsepattern.zigbrains.zig.settings.ZLSProjectSettingsService;
|
||||
|
@ -14,13 +15,15 @@ import com.intellij.openapi.project.Project;
|
|||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import com.redhat.devtools.lsp4ij.server.OSProcessStreamConnectionProvider;
|
||||
import com.redhat.devtools.lsp4ij.server.ProcessStreamConnectionProvider;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.InlayHint;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Message;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage;
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
@ -29,10 +32,9 @@ import java.nio.file.Path;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvider {
|
||||
|
@ -40,7 +42,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
|
|||
private final Project project;
|
||||
public ZLSStreamConnectionProvider(Project project) {
|
||||
this.project = project;
|
||||
val command = getCommand(project);
|
||||
val command = getCommandAsync(project, true);
|
||||
val projectDir = ProjectUtil.guessProjectDir(project);
|
||||
GeneralCommandLine commandLine = null;
|
||||
try {
|
||||
|
@ -88,35 +90,62 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
|
|||
}
|
||||
}
|
||||
|
||||
public static List<String> doGetCommand(Project project, boolean safe) {
|
||||
private static @Nullable Path tryWorkDirFromProject(Project project) {
|
||||
val projectDir = ProjectUtil.guessProjectDir(project);
|
||||
if (projectDir == null) {
|
||||
return null;
|
||||
}
|
||||
return projectDir.toNioPath();
|
||||
}
|
||||
|
||||
private static @NotNull CompletableFuture<Map<String, String>> getDirenv(Project project) {
|
||||
val workDir = tryWorkDirFromProject(project);
|
||||
if (workDir == null)
|
||||
return CompletableFuture.completedFuture(Map.of());
|
||||
val direnvCmd = new DirenvCmd(workDir);
|
||||
//Async if in dispatch thread, sync otherwise
|
||||
return ApplicationManager.getApplication().isDispatchThread() ? direnvCmd.importDirenvAsync() : CompletableFuture.completedFuture(direnvCmd.importDirenvSync());
|
||||
}
|
||||
|
||||
public static List<String> getCommandSync(Project project, boolean full) {
|
||||
var svc = ZLSProjectSettingsService.getInstance(project);
|
||||
val state = svc.getState();
|
||||
var zlsPath = state.zlsPath;
|
||||
if (StringUtil.isEmpty(zlsPath)) {
|
||||
zlsPath = com.falsepattern.zigbrains.common.util.FileUtil.findExecutableOnPATH("zls").map(Path::toString).orElse(null);
|
||||
Map<String, String> direnv = Map.of();
|
||||
if (state.direnv && DirenvCmd.direnvInstalled()) {
|
||||
try {
|
||||
direnv = getDirenv(project).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
LOG.warn(e);
|
||||
direnv = Map.of();
|
||||
}
|
||||
}
|
||||
zlsPath = com.falsepattern.zigbrains.common.util.FileUtil.findExecutableOnPATH(direnv, "zls").map(Path::toString).orElse(null);
|
||||
if (zlsPath == null) {
|
||||
if (safe) {
|
||||
if (full) {
|
||||
Notifications.Bus.notify(
|
||||
new Notification("ZigBrains.ZLS", "Could not detect ZLS binary! Please configure it!",
|
||||
NotificationType.ERROR));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
state.setZlsPath(zlsPath);
|
||||
val zlsPathFinal = zlsPath;
|
||||
ApplicationManager.getApplication().invokeLater(() -> state.setZlsPath(zlsPathFinal));
|
||||
}
|
||||
if (!validatePath("ZLS Binary", zlsPath, false, safe)) {
|
||||
if (!validatePath("ZLS Binary", zlsPath, false, full)) {
|
||||
return null;
|
||||
}
|
||||
var configPath = state.zlsConfigPath;
|
||||
boolean configOK = true;
|
||||
if (!configPath.isBlank() && !validatePath("ZLS Config", configPath, false, safe)) {
|
||||
if (safe) {
|
||||
if (!configPath.isBlank() && !validatePath("ZLS Config", configPath, false, full)) {
|
||||
if (full) {
|
||||
Notifications.Bus.notify(
|
||||
new Notification("ZigBrains.ZLS", "Using default config path.", NotificationType.INFORMATION));
|
||||
}
|
||||
configPath = null;
|
||||
}
|
||||
if ((configPath == null || configPath.isBlank()) && safe) {
|
||||
if ((configPath == null || configPath.isBlank()) && full) {
|
||||
blk:
|
||||
try {
|
||||
val tmpFile = FileUtil.createTempFile("zigbrains-zls-autoconf", ".json", true).toPath();
|
||||
|
@ -133,7 +162,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
|
|||
}
|
||||
configPath = tmpFile.toAbsolutePath().toString();
|
||||
} catch (IOException e) {
|
||||
if (safe) {
|
||||
if (full) {
|
||||
Notifications.Bus.notify(
|
||||
new Notification("ZigBrains.ZLS", "Failed to create automatic zls config file",
|
||||
NotificationType.WARNING));
|
||||
|
@ -166,19 +195,11 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
|
|||
return cmd;
|
||||
}
|
||||
|
||||
public static Future<List<String>> getCommand(Project project) {
|
||||
val future = new CompletableFuture<List<String>>();
|
||||
ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||
try {
|
||||
future.complete(doGetCommand(project, true));
|
||||
} catch (Throwable t) {
|
||||
future.completeExceptionally(t);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
public static CompletableFuture<List<String>> getCommandAsync(Project project, boolean full) {
|
||||
return CompletableFuture.supplyAsync(() -> getCommandSync(project, full), AppExecutorUtil.getAppExecutorService());
|
||||
}
|
||||
|
||||
private static boolean validatePath(String name, String pathTxt, boolean dir, boolean safe) {
|
||||
private static boolean validatePath(String name, String pathTxt, boolean dir, boolean full) {
|
||||
if (pathTxt == null || pathTxt.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -186,7 +207,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
|
|||
try {
|
||||
path = Path.of(pathTxt);
|
||||
} catch (InvalidPathException e) {
|
||||
if (safe) {
|
||||
if (full) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name,
|
||||
"Invalid " + name + " at path \"" + pathTxt + "\"",
|
||||
NotificationType.ERROR));
|
||||
|
@ -194,7 +215,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
|
|||
return false;
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
if (safe) {
|
||||
if (full) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name,
|
||||
"The " + name + " at \"" + pathTxt + "\" doesn't exist!",
|
||||
NotificationType.ERROR));
|
||||
|
@ -202,7 +223,7 @@ public class ZLSStreamConnectionProvider extends OSProcessStreamConnectionProvid
|
|||
return false;
|
||||
}
|
||||
if (Files.isDirectory(path) != dir) {
|
||||
if (safe) {
|
||||
if (full) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name,
|
||||
"The " + name + " at \"" + pathTxt + "\" is a " +
|
||||
(Files.isDirectory(path) ? "directory" : "file") +
|
||||
|
|
|
@ -11,9 +11,6 @@ import lombok.val;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ZigStringElementManipulator extends AbstractElementManipulator<ZigStringLiteral> {
|
||||
private enum InjectTriState {
|
||||
NotYet,
|
||||
|
|
|
@ -13,7 +13,6 @@ import com.intellij.psi.impl.source.tree.LeafElement;
|
|||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ZigStringLiteralMixinImpl extends ASTWrapperPsiElement implements ZigStringLiteral {
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
package com.falsepattern.zigbrains.zig.psi.mixins;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.psi.ZigStringLiteral;
|
||||
import com.intellij.extapi.psi.ASTWrapperPsiElement;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.LiteralTextEscaper;
|
||||
import com.intellij.psi.PsiLanguageInjectionHost;
|
||||
import com.intellij.psi.impl.source.tree.LeafElement;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
|
|
@ -49,6 +49,7 @@ public final class ZLSProjectSettingsService extends WrappingStateComponent<ZLSS
|
|||
modified |= myData.dangerousComptimeExperimentsDoNotEnable != otherData.dangerousComptimeExperimentsDoNotEnable;
|
||||
modified |= myData.inlayHints != otherData.inlayHints;
|
||||
modified |= myData.inlayHintsCompact != otherData.inlayHintsCompact;
|
||||
modified |= myData.direnv != otherData.direnv;
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
@Data
|
||||
@AllArgsConstructor
|
||||
public final class ZLSSettings {
|
||||
public boolean direnv;
|
||||
public @Nullable String zlsPath;
|
||||
public @NotNull String zlsConfigPath;
|
||||
public boolean debug;
|
||||
|
@ -36,6 +37,6 @@ public final class ZLSSettings {
|
|||
public boolean inlayHintsCompact;
|
||||
|
||||
public ZLSSettings() {
|
||||
this(null, "", false, false, false, "install", false, false, true, true);
|
||||
this(true, null, "", false, false, false, "install", false, false, true, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public class ZLSSettingsConfigurable implements SubConfigurable {
|
|||
|
||||
@Override
|
||||
public void createComponent(JavaPanel panel) {
|
||||
appSettingsComponent = new ZLSSettingsPanel();
|
||||
appSettingsComponent = new ZLSSettingsPanel(project);
|
||||
appSettingsComponent.attachPanelTo(panel);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,26 +16,37 @@
|
|||
|
||||
package com.falsepattern.zigbrains.zig.settings;
|
||||
|
||||
import com.falsepattern.zigbrains.common.direnv.DirenvCmd;
|
||||
import com.falsepattern.zigbrains.common.util.FileUtil;
|
||||
import com.falsepattern.zigbrains.common.util.TextFieldUtil;
|
||||
import com.falsepattern.zigbrains.common.util.dsl.JavaPanel;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.ui.TextBrowseFolderListener;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
import com.intellij.ui.components.fields.ExtendableTextField;
|
||||
import com.intellij.ui.dsl.builder.AlignX;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.falsepattern.zigbrains.common.util.KtUtil.$f;
|
||||
|
||||
public class ZLSSettingsPanel implements Disposable {
|
||||
private final @Nullable Project project;
|
||||
|
||||
private final TextFieldWithBrowseButton zlsPath = TextFieldUtil.pathToFileTextField(this,
|
||||
"Path to the ZLS Binary",
|
||||
() -> {});
|
||||
|
@ -52,6 +63,7 @@ public class ZLSSettingsPanel implements Disposable {
|
|||
|
||||
private final JBCheckBox messageTrace = new JBCheckBox();
|
||||
private final JBCheckBox debug = new JBCheckBox();
|
||||
private final JBCheckBox direnv = new JBCheckBox("Use direnv");
|
||||
|
||||
{
|
||||
buildOnSave.setToolTipText("Whether to enable build-on-save diagnostics");
|
||||
|
@ -66,12 +78,36 @@ public class ZLSSettingsPanel implements Disposable {
|
|||
autodetect();
|
||||
}
|
||||
|
||||
public void autodetect() {
|
||||
FileUtil.findExecutableOnPATH("zls").map(Path::toString).ifPresent(zlsPath::setText);
|
||||
private @Nullable Path tryWorkDirFromProject() {
|
||||
if (project == null)
|
||||
return null;
|
||||
val projectDir = ProjectUtil.guessProjectDir(project);
|
||||
if (projectDir == null) {
|
||||
return null;
|
||||
}
|
||||
return projectDir.toNioPath();
|
||||
}
|
||||
|
||||
public ZLSSettingsPanel() {
|
||||
private @NotNull CompletableFuture<Map<String, String>> tryGetDirenv() {
|
||||
if (!DirenvCmd.direnvInstalled() || !direnv.isSelected()) {
|
||||
return CompletableFuture.completedFuture(Map.of());
|
||||
}
|
||||
val workDir = tryWorkDirFromProject();
|
||||
if (workDir == null)
|
||||
return CompletableFuture.completedFuture(Map.of());
|
||||
val direnvCmd = new DirenvCmd(workDir);
|
||||
return direnvCmd.importDirenvAsync();
|
||||
}
|
||||
|
||||
public void autodetect() {
|
||||
tryGetDirenv().thenAcceptAsync((env) -> {
|
||||
FileUtil.findExecutableOnPATH(env, "zls").map(Path::toString).ifPresent(zlsPath::setText);
|
||||
}, AppExecutorUtil.getAppExecutorService());
|
||||
}
|
||||
|
||||
public ZLSSettingsPanel(@Nullable Project project) {
|
||||
zlsPath.addBrowseFolderListener(new TextBrowseFolderListener(new FileChooserDescriptor(true, false, false, false, false, false)));
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public void attachPanelTo(JavaPanel panel) {
|
||||
|
@ -81,6 +117,9 @@ public class ZLSSettingsPanel implements Disposable {
|
|||
panel.group("ZLS Settings", true, p -> {
|
||||
p.row("Executable path", r -> {
|
||||
r.cell(zlsPath).resizableColumn().align(AlignX.FILL);
|
||||
if (DirenvCmd.direnvInstalled() && project != null) {
|
||||
r.cell(direnv);
|
||||
}
|
||||
r.button("Autodetect", $f(this::autodetect));
|
||||
});
|
||||
p.cell("Config path (leave empty to use built-in config)", zlsConfigPath, AlignX.FILL);
|
||||
|
@ -100,7 +139,8 @@ public class ZLSSettingsPanel implements Disposable {
|
|||
}
|
||||
|
||||
public ZLSSettings getData() {
|
||||
return new ZLSSettings(zlsPath.getText(),
|
||||
return new ZLSSettings(direnv.isSelected(),
|
||||
zlsPath.getText(),
|
||||
zlsConfigPath.getText(),
|
||||
debug.isSelected(),
|
||||
messageTrace.isSelected(),
|
||||
|
@ -113,6 +153,7 @@ public class ZLSSettingsPanel implements Disposable {
|
|||
}
|
||||
|
||||
public void setData(ZLSSettings value) {
|
||||
direnv.setSelected(value.direnv);
|
||||
zlsPath.setText(value.zlsPath == null ? "" : value.zlsPath);
|
||||
zlsConfigPath.setText(value.zlsConfigPath);
|
||||
debug.setSelected(value.debug);
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
package com.falsepattern.zigbrains.zig.util;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.stringlexer.ZigStringLexer;
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lexer.FlexAdapter;
|
||||
import com.intellij.lexer.FlexLexer;
|
||||
import com.intellij.lexer.Lexer;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.StringEscapesTokenTypes;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.MathUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
const myText =
|
||||
\\Hello
|
||||
\\World!💯
|
||||
;
|
|
@ -0,0 +1 @@
|
|||
const myText = <spot>"Hello\nWorld!\u{1f4af}"</spot>;
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
Converts quoted strings into multi-line strings, un-escaping any escape sequences.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
const myText = "Hello\nWorld!\u{1f4af}";
|
|
@ -0,0 +1,4 @@
|
|||
const myText =
|
||||
<spot>\\Hello
|
||||
\\World!💯</spot>
|
||||
;
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
Converts multi-line strings into quoted strings, escaping characters if necessary.
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue