feat: External Libraries and some toolchain perf tweaks

This commit is contained in:
FalsePattern 2024-05-12 04:27:40 +02:00
parent cac455b460
commit 6514f65980
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
9 changed files with 253 additions and 9 deletions

View file

@ -17,6 +17,11 @@ Changelog structure reference:
## [Unreleased] ## [Unreleased]
### Added
- Zig
- External Libraries support for zig stdlib
## [14.1.0] ## [14.1.0]
### Fixed ### Fixed

View file

@ -0,0 +1,39 @@
/*
* Copyright 2023-2024 FalsePattern
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.falsepattern.zigbrains.common.util;
import lombok.RequiredArgsConstructor;
import java.util.function.Supplier;
@RequiredArgsConstructor
public class Lazy<T> {
private T value;
private final Supplier<T> initializer;
public T get() {
if (value != null)
return value;
synchronized (this) {
if (value != null)
return value;
return value = initializer.get();
}
}
}

View file

@ -26,6 +26,7 @@ import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettings;
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService; import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider; import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider;
import com.falsepattern.zigbrains.project.toolchain.tools.ZigCompilerTool;
import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Disposer;
@ -114,8 +115,8 @@ public class ZigProjectSettingsPanel implements MyDisposable {
() -> { () -> {
val toolchain = Optional.ofNullable(pathToToolchain).map(ZigToolchainProvider::findToolchain).orElse(null); val toolchain = Optional.ofNullable(pathToToolchain).map(ZigToolchainProvider::findToolchain).orElse(null);
val zig = Optional.ofNullable(toolchain).map(AbstractZigToolchain::zig).orElse(null); val zig = Optional.ofNullable(toolchain).map(AbstractZigToolchain::zig).orElse(null);
val version = Optional.ofNullable(zig).flatMap(z -> z.queryVersion(Path.of("."))).orElse(null); val version = Optional.ofNullable(zig).flatMap(ZigCompilerTool::queryVersion).orElse(null);
val stdPath = Optional.ofNullable(zig).flatMap(z -> z.getStdPath(Path.of("."))).orElse(null); val stdPath = Optional.ofNullable(zig).flatMap(ZigCompilerTool::getStdPath).orElse(null);
return new Pair<>(version, stdPath); return new Pair<>(version, stdPath);
}, },

View file

@ -16,6 +16,7 @@
package com.falsepattern.zigbrains.project.toolchain; package com.falsepattern.zigbrains.project.toolchain;
import com.falsepattern.zigbrains.common.util.Lazy;
import com.falsepattern.zigbrains.project.toolchain.flavours.AbstractZigToolchainFlavour; import com.falsepattern.zigbrains.project.toolchain.flavours.AbstractZigToolchainFlavour;
import com.falsepattern.zigbrains.project.toolchain.tools.ZigCompilerTool; import com.falsepattern.zigbrains.project.toolchain.tools.ZigCompilerTool;
import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.GeneralCommandLine;
@ -31,6 +32,8 @@ import java.util.Objects;
public abstract class AbstractZigToolchain { public abstract class AbstractZigToolchain {
private final Path location; private final Path location;
private final Lazy<ZigCompilerTool> zig = new Lazy<>(() -> new ZigCompilerTool(this));
public static @Nullable AbstractZigToolchain suggest() { public static @Nullable AbstractZigToolchain suggest() {
return suggest(null); return suggest(null);
} }
@ -46,7 +49,7 @@ public abstract class AbstractZigToolchain {
} }
public ZigCompilerTool zig() { public ZigCompilerTool zig() {
return new ZigCompilerTool(this); return zig.get();
} }
public abstract int executionTimeoutInMilliseconds(); public abstract int executionTimeoutInMilliseconds();

View file

@ -18,17 +18,23 @@ package com.falsepattern.zigbrains.project.toolchain;
import com.intellij.execution.wsl.WslPath; import com.intellij.execution.wsl.WslPath;
import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.lang.ref.WeakReference;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
public class LocalZigToolchainProvider implements ZigToolchainProvider { public class LocalZigToolchainProvider implements ZigToolchainProvider {
private static final Map<Path, LocalZigToolchain> tcCache = ContainerUtil.createWeakKeyWeakValueMap();
@Override @Override
public @Nullable AbstractZigToolchain getToolchain(Path homePath) { public @Nullable AbstractZigToolchain getToolchain(Path homePath) {
if (SystemInfo.isWindows && WslPath.isWslUncPath(homePath.toString())) { if (SystemInfo.isWindows && WslPath.isWslUncPath(homePath.toString())) {
return null; return null;
} }
return new LocalZigToolchain(homePath); return tcCache.computeIfAbsent(homePath, LocalZigToolchain::new);
} }
} }

View file

@ -0,0 +1,32 @@
/*
* Copyright 2023-2024 FalsePattern
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.falsepattern.zigbrains.project.toolchain.stdlib;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.AdditionalLibraryRootsProvider;
import com.intellij.openapi.roots.SyntheticLibrary;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
public class ZigLibraryRootProvider extends AdditionalLibraryRootsProvider {
@Override
public @NotNull Collection<SyntheticLibrary> getAdditionalProjectLibraries(@NotNull Project project) {
return Collections.singleton(new ZigSyntheticLibrary(project));
}
}

View file

@ -0,0 +1,131 @@
/*
* Copyright 2023-2024 FalsePattern
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.falsepattern.zigbrains.project.toolchain.stdlib;
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
import com.falsepattern.zigbrains.common.util.PathUtil;
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
import com.falsepattern.zigbrains.zig.Icons;
import com.falsepattern.zigbrains.zig.parser.ZigFile;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.roots.SyntheticLibrary;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@RequiredArgsConstructor
public class ZigSyntheticLibrary extends SyntheticLibrary implements ItemPresentation {
private final Future<Collection<VirtualFile>> roots;
private final Future<String> name;
public ZigSyntheticLibrary(Project project) {
val service = ZigProjectSettingsService.getInstance(project);
val state = service.getState();
this.roots = ApplicationManager.getApplication().executeOnPooledThread(() -> {
var roots = pathToVFS(state.getExplicitPathToStd());
if (roots != null) {
return roots;
}
val toolchain = state.getToolchain();
if (toolchain != null) {
val stdPath =
toolchain.zig().getStdPath().orElse(null);
return pathToVFS(stdPath);
}
return Collections.emptySet();
});
this.name = ApplicationManager.getApplication()
.executeOnPooledThread(() -> Optional.ofNullable(state.getToolchain())
.flatMap(tc -> tc.zig().queryVersion())
.map(version -> "Zig " + version)
.orElse("Zig"));
}
private static @Nullable Collection<VirtualFile> pathToVFS(String path) {
if (path != null && !path.isEmpty()) {
val thePath = PathUtil.pathFromString(path);
if (thePath != null) {
val file = VfsUtil.findFile(thePath, true);
if (file != null) {
val children = file.getChildren();
if (children != null && children.length > 0)
return Arrays.asList(children);
}
}
}
return null;
}
@Override
public @NotNull Collection<VirtualFile> getSourceRoots() {
try {
return roots.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return Collections.emptySet();
}
}
@Override
public @NotNull Collection<VirtualFile> getBinaryRoots() {
return super.getBinaryRoots();
}
@SneakyThrows
@Override
public boolean equals(Object o) {
return o instanceof ZigSyntheticLibrary other && Objects.equals(roots.get(), other.roots.get());
}
@SneakyThrows
@Override
public int hashCode() {
return Objects.hash(roots.get());
}
@Override
public @NlsSafe @Nullable String getPresentableText() {
try {
return name.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return "Zig";
}
}
@Override
public @Nullable Icon getIcon(boolean unused) {
return Icons.ZIG;
}
}

View file

@ -16,20 +16,45 @@
package com.falsepattern.zigbrains.project.toolchain.tools; package com.falsepattern.zigbrains.project.toolchain.tools;
import com.falsepattern.zigbrains.common.util.Lazy;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainEnvironmentSerializable; import com.falsepattern.zigbrains.project.toolchain.ZigToolchainEnvironmentSerializable;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.intellij.execution.process.ProcessOutput; import com.intellij.execution.process.ProcessOutput;
import com.intellij.openapi.application.ApplicationManager;
import lombok.val;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ZigCompilerTool extends AbstractZigTool{ public class ZigCompilerTool extends AbstractZigTool{
public static final String TOOL_NAME = "zig"; public static final String TOOL_NAME = "zig";
private final Lazy<Optional<String>> version;
private final Lazy<Optional<String>> stdPath;
public ZigCompilerTool(AbstractZigToolchain toolchain) { public ZigCompilerTool(AbstractZigToolchain toolchain) {
super(toolchain, TOOL_NAME); super(toolchain, TOOL_NAME);
val app = ApplicationManager.getApplication();
val baseFuture = app.executeOnPooledThread(() -> getEnv(null));
version = new Lazy<>(() -> {
try {
return baseFuture.get().map(ZigToolchainEnvironmentSerializable::version);
} catch (InterruptedException | ExecutionException e) {
return Optional.empty();
}
});
stdPath = new Lazy<>(() -> {
try {
return baseFuture.get().map(ZigToolchainEnvironmentSerializable::stdDirectory);
} catch (InterruptedException | ExecutionException e) {
return Optional.empty();
}
});
} }
public Optional<ZigToolchainEnvironmentSerializable> getEnv(@Nullable Path workingDirectory) { public Optional<ZigToolchainEnvironmentSerializable> getEnv(@Nullable Path workingDirectory) {
@ -39,11 +64,11 @@ public class ZigCompilerTool extends AbstractZigTool{
} }
public Optional<String> getStdPath(@Nullable Path workingDirectory) { public Optional<String> getStdPath() {
return getEnv(workingDirectory).map(ZigToolchainEnvironmentSerializable::stdDirectory); return stdPath.get();
} }
public Optional<String> queryVersion(@Nullable Path workingDirectory) { public Optional<String> queryVersion() {
return getEnv(workingDirectory).map(ZigToolchainEnvironmentSerializable::version); return version.get();
} }
} }

View file

@ -52,6 +52,8 @@
<consoleFilterProvider implementation="com.falsepattern.zigbrains.project.console.ZigConsoleFilterProvider"/> <consoleFilterProvider implementation="com.falsepattern.zigbrains.project.console.ZigConsoleFilterProvider"/>
<analyzeStacktraceFilter implementation="com.falsepattern.zigbrains.project.console.ZigSourceFileFilter"/> <analyzeStacktraceFilter implementation="com.falsepattern.zigbrains.project.console.ZigSourceFileFilter"/>
<additionalLibraryRootsProvider implementation="com.falsepattern.zigbrains.project.toolchain.stdlib.ZigLibraryRootProvider"/>
</extensions> </extensions>
<extensions defaultExtensionNs="com.falsepattern.zigbrains"> <extensions defaultExtensionNs="com.falsepattern.zigbrains">