backport: 14.2.0

This commit is contained in:
FalsePattern 2024-05-13 00:07:08 +02:00
parent 6b21a75113
commit c3babed8c4
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
13 changed files with 327 additions and 26 deletions

View file

@ -17,6 +17,18 @@ Changelog structure reference:
## [Unreleased]
## [14.2.0]
### Added
- Zig
- External Libraries support for zig stdlib
### Fixed
- Debugging (Windows)
- Variables sometimes don't show up in the variable inspector when in breakpoint state
## [14.1.0]
### Fixed

View file

@ -1,8 +1,26 @@
# ZigBrains
### [Website](https://falsepattern.com/zigbrains)
Zig language support for IntelliJ IDEA, CLion, and other JetBrains IDEs.
### [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/22456-zigbrains)
## Installing
You can either install this plugin from the [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/22456-zigbrains), or from FalsePattern's [website](https://falsepattern.com/zigbrains).
See [the quick setup guide](#quick-setup-guide-for-zig-and-zls) for how to set up language server integration.
Note: marketplace updates are usually delayed by a few days from the actual release, so if you want to always have the
latest builds of ZigBrains, you can set up your IDE to download signed releases directly from FalsePattern's website
through the built-in plugin browser:
1. Go to `Settings -> Plugins`
2. To the right of the `Installed` button at the top, click on the `...` dropdown menu, then select `Manage Plugin Repositories...`
3. Click the add button, and then enter the ZigBrains updater URL, based on your IDE version:
- `2024.1.*`: https://falsepattern.com/zigbrains/updatePlugins-241.xml
- `2023.3.*`: https://falsepattern.com/zigbrains/updatePlugins-233.xml
- `2023.2.*`: https://falsepattern.com/zigbrains/updatePlugins-232.xml
- `2023.1.*`: https://falsepattern.com/zigbrains/updatePlugins-231.xml
4. Click `OK`, and your IDE should now automatically detect the latest version
(both in the Installed tab and in the Marketplace tab), even if it's not yet verified on the official JetBrains marketplace yet.
## Developer guide

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

@ -755,16 +755,11 @@ public abstract class DAPDriver<
*/
@Override
public @NotNull LLValueData getData(@NotNull LLValue value) throws ExecutionException, DebuggerCommandException {
String result;
String result = "";
int childrenRef = 0;
boolean failed = false;
if (value.getReferenceExpression().isBlank()) {
val known = value.getUserData(LLVALUE_DATA);
if (known != null)
return known;
val cRef = value.getUserData(LLVALUE_CHILDREN_REF);
if (cRef != null)
childrenRef = cRef;
result = "";
failed = true;
} else {
val args = new EvaluateArguments();
args.setContext(EvaluateArgumentsContext.VARIABLES);
@ -774,8 +769,27 @@ public abstract class DAPDriver<
childrenRef = res.getVariablesReference();
if (childrenRef > 0)
value.putUserData(LLVALUE_CHILDREN_REF, childrenRef);
val hint = res.getPresentationHint();
if (hint != null) {
val attribs = hint.getAttributes();
if (attribs != null) {
for (val attrib: attribs) {
if ("failedEvaluation".equals(attrib)) {
failed = true;
}
}
}
}
result = res.getResult();
}
if (failed) {
val known = value.getUserData(LLVALUE_DATA);
if (known != null)
return known;
val cRef = value.getUserData(LLVALUE_CHILDREN_REF);
if (cRef != null)
childrenRef = cRef;
}
return new LLValueData(result, null, false, childrenRef > 0, false);
}

View file

@ -36,6 +36,7 @@ import com.falsepattern.zigbrains.lsp.statusbar.LSPServerStatusWidget;
import com.falsepattern.zigbrains.lsp.statusbar.LSPServerStatusWidgetFactory;
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
import com.falsepattern.zigbrains.lsp.utils.LSPException;
import com.google.gson.GsonBuilder;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.diagnostic.Logger;
@ -514,16 +515,28 @@ public class LanguageServerWrapper {
Class<? extends LanguageServer> remoteServerInterFace = extManager.getExtendedServerInterface();
client = extManager.getExtendedClientFor(new ServerWrapperBaseClientContext(this));
Launcher<? extends LanguageServer> launcher = Launcher
.createLauncher(client, remoteServerInterFace, inputStream, outputStream, executorService,
messageHandler);
val launcher = new Launcher.Builder<LanguageServer>()
.setLocalService(client)
.setRemoteInterface(remoteServerInterFace)
.setInput(inputStream)
.setOutput(outputStream)
.setExecutorService(executorService)
.wrapMessages(messageHandler)
.configureGson(GsonBuilder::disableHtmlEscaping)
.create();
languageServer = launcher.getRemoteProxy();
launcherFuture = launcher.startListening();
} else {
client = new DefaultLanguageClient(new ServerWrapperBaseClientContext(this));
Launcher<LanguageServer> launcher = Launcher
.createLauncher(client, LanguageServer.class, inputStream, outputStream, executorService,
messageHandler);
val launcher = new Launcher.Builder<LanguageServer>()
.setLocalService(client)
.setRemoteInterface(LanguageServer.class)
.setInput(inputStream)
.setOutput(outputStream)
.setExecutorService(executorService)
.wrapMessages(messageHandler)
.configureGson(GsonBuilder::disableHtmlEscaping)
.create();
languageServer = launcher.getRemoteProxy();
launcherFuture = launcher.startListening();
}
@ -559,8 +572,13 @@ public class LanguageServerWrapper {
private InitializeParams getInitParams() throws URISyntaxException {
InitializeParams initParams = new InitializeParams();
String projectRootUri = FileUtil.pathToUri(projectRootPath);
if (projectRootUri.endsWith("/")) {
projectRootUri = projectRootUri.substring(0, projectRootUri.length() - 1);
}
WorkspaceFolder workspaceFolder = new WorkspaceFolder(projectRootUri, this.project.getName());
initParams.setWorkspaceFolders(Collections.singletonList(workspaceFolder));
initParams.setProcessId((int) ProcessHandle.current().pid());
initParams.setLocale("en-us");
// workspace capabilities
WorkspaceClientCapabilities workspaceClientCapabilities = new WorkspaceClientCapabilities();

View file

@ -44,7 +44,7 @@ public class DocumentEventManager {
private final TextDocumentSyncKind syncKind;
private final LanguageServerWrapper wrapper;
private final TextDocumentIdentifier identifier;
private int version = -1;
private int version = 0;
protected Logger LOG = Logger.getInstance(EditorEventManager.class);
private final Set<Document> openDocuments = new HashSet<>();

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.toolchain.AbstractZigToolchain;
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.ui.TextFieldWithBrowseButton;
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 zig = Optional.ofNullable(toolchain).map(AbstractZigToolchain::zig).orElse(null);
val version = Optional.ofNullable(zig).flatMap(z -> z.queryVersion(Path.of("."))).orElse(null);
val stdPath = Optional.ofNullable(zig).flatMap(z -> z.getStdPath(Path.of("."))).orElse(null);
val version = Optional.ofNullable(zig).flatMap(ZigCompilerTool::queryVersion).orElse(null);
val stdPath = Optional.ofNullable(zig).flatMap(ZigCompilerTool::getStdPath).orElse(null);
return new Pair<>(version, stdPath);
},

View file

@ -16,6 +16,7 @@
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.tools.ZigCompilerTool;
import com.intellij.execution.configurations.GeneralCommandLine;
@ -31,6 +32,8 @@ import java.util.Objects;
public abstract class AbstractZigToolchain {
private final Path location;
private final Lazy<ZigCompilerTool> zig = new Lazy<>(() -> new ZigCompilerTool(this));
public static @Nullable AbstractZigToolchain suggest() {
return suggest(null);
}
@ -46,7 +49,7 @@ public abstract class AbstractZigToolchain {
}
public ZigCompilerTool zig() {
return new ZigCompilerTool(this);
return zig.get();
}
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.openapi.util.SystemInfo;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.WeakReference;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
public class LocalZigToolchainProvider implements ZigToolchainProvider {
private static final Map<Path, LocalZigToolchain> tcCache = ContainerUtil.createWeakKeyWeakValueMap();
@Override
public @Nullable AbstractZigToolchain getToolchain(Path homePath) {
if (SystemInfo.isWindows && WslPath.isWslUncPath(homePath.toString())) {
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;
import com.falsepattern.zigbrains.common.util.Lazy;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainEnvironmentSerializable;
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.Path;
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 static final String TOOL_NAME = "zig";
private final Lazy<Optional<String>> version;
private final Lazy<Optional<String>> stdPath;
public ZigCompilerTool(AbstractZigToolchain toolchain) {
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) {
@ -39,11 +64,11 @@ public class ZigCompilerTool extends AbstractZigTool{
}
public Optional<String> getStdPath(@Nullable Path workingDirectory) {
return getEnv(workingDirectory).map(ZigToolchainEnvironmentSerializable::stdDirectory);
public Optional<String> getStdPath() {
return stdPath.get();
}
public Optional<String> queryVersion(@Nullable Path workingDirectory) {
return getEnv(workingDirectory).map(ZigToolchainEnvironmentSerializable::version);
public Optional<String> queryVersion() {
return version.get();
}
}

View file

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