backport: 16.1.3

This commit is contained in:
FalsePattern 2024-07-27 01:35:28 +02:00
parent 560653fd47
commit 62d3771a75
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
8 changed files with 160 additions and 59 deletions

View file

@ -17,6 +17,15 @@ Changelog structure reference:
## [Unreleased] ## [Unreleased]
## [16.1.3]
### Changed
- Debugger (Windows)
- MSVC debugger metadata download now requires consent from the user
- Metadata download is now cached after the first fetch
- Metadata download timeout has been set to 3 seconds, after which it reverts to the fallback file
## [16.1.2] ## [16.1.2]
### Fixed ### Fixed

View file

@ -11,7 +11,7 @@ baseIDE=clion
ideaVersion=2024.1.4 ideaVersion=2024.1.4
clionVersion=2024.1.4 clionVersion=2024.1.4
pluginVersion=16.1.2 pluginVersion=16.1.3
# Gradle Releases -> https://github.com/gradle/gradle/releases # Gradle Releases -> https://github.com/gradle/gradle/releases
gradleVersion=8.9 gradleVersion=8.9

View file

@ -13,7 +13,7 @@ notification.nativedebug.browser=Open in Browser
notification.title.debugger=Debugger notification.title.debugger=Debugger
notification.content.debugger.successfully.downloaded=Debugger successfully downloaded notification.content.debugger.successfully.downloaded=Debugger successfully downloaded
notification.content.debugger.downloading.failed=Debugger downloading failed notification.content.debugger.downloading.failed=Debugger downloading failed
notification.content.debugger.metadata.downloading.failed=Debugger metadata downloading failed notification.content.debugger.metadata.downloading.failed=Debugger metadata downloading failed, switching to fallback
notification.content.debugger.metadata.fallback.fetch.failed=Debugger fallback metadata fetch failed notification.content.debugger.metadata.fallback.fetch.failed=Debugger fallback metadata fetch failed
notification.content.debugger.metadata.fallback.parse.failed=Debugger fallback metadata parse failed notification.content.debugger.metadata.fallback.parse.failed=Debugger fallback metadata parse failed
settings.debugger.toolchain.download.debugger.automatically.checkbox=Download and update the debugger automatically settings.debugger.toolchain.download.debugger.automatically.checkbox=Download and update the debugger automatically

View file

@ -0,0 +1,7 @@
package com.falsepattern.zigbrains.debugger.settings;
public enum MSVCDownloadPermission {
AskMe,
Allow,
Deny
}

View file

@ -22,6 +22,7 @@ public class ZigDebuggerSettings extends XDebuggerSettings<ZigDebuggerSettings>
public boolean downloadAutomatically = false; public boolean downloadAutomatically = false;
public boolean useClion = true; public boolean useClion = true;
public MSVCDownloadPermission msvcConsent = MSVCDownloadPermission.AskMe;
protected ZigDebuggerSettings() { protected ZigDebuggerSettings() {
super("Zig"); super("Zig");

View file

@ -10,6 +10,7 @@ import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.observable.util.ListenerUiUtil; import com.intellij.openapi.observable.util.ListenerUiUtil;
import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBCheckBox;
import lombok.val; import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -79,7 +80,7 @@ public class ZigDebuggerToolchainConfigurableUi extends ZigDebuggerUiComponent {
panel.row(r -> { panel.row(r -> {
r.cell(downloadAutomaticallyCheckBox); r.cell(downloadAutomaticallyCheckBox);
}); });
if (PluginManager.isPluginInstalled(PluginId.getId("com.intellij.modules.clion"))) { if (PluginManager.isPluginInstalled(PluginId.getId("com.intellij.modules.clion")) && !SystemInfo.isWindows) {
panel.row(r -> { panel.row(r -> {
r.cell(useClion); r.cell(useClion);
}); });

View file

@ -0,0 +1,130 @@
package com.falsepattern.zigbrains.debugger.toolchain;
import com.falsepattern.zigbrains.ZigBundle;
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;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.DialogBuilder;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBPanel;
import com.intellij.util.download.DownloadableFileService;
import lombok.Cleanup;
import lombok.val;
import javax.swing.BoxLayout;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import static com.falsepattern.zigbrains.debugger.toolchain.ZigDebuggerToolchainService.downloadPath;
class MSVCMetadataProvider {
private Properties cached;
private Future<Properties> downloadMSVCProps() {
val future = new CompletableFuture<Properties>();
ApplicationManager.getApplication().executeOnPooledThread(() -> {
try {
val service = DownloadableFileService.getInstance();
val desc = service.createFileDescription("https://falsepattern.com/zigbrains/msvc.properties", "msvc.properties");
val downloader = service.createDownloader(List.of(desc), "Debugger metadata downloading");
val downloadDirectory = downloadPath().toFile();
val prop = new Properties();
val downloadResults = downloader.download(downloadDirectory);
for (val result : downloadResults) {
if (Objects.equals(result.second.getDefaultFileName(), "msvc.properties")) {
@Cleanup val reader = new FileReader(result.first);
prop.load(reader);
}
}
future.complete(prop);
} catch (Throwable t) {
future.completeExceptionally(t);
}
});
return future;
}
private Properties fetchBuiltinMSVCProps() {
val prop = new Properties();
try {
@Cleanup val resource = ZigDebuggerToolchainService.class.getResourceAsStream("/msvc.properties");
if (resource == null) {
Notifications.Bus.notify(new Notification(
"ZigBrains.Debugger.Error",
ZigBundle.message("notification.title.debugger"),
ZigBundle.message("notification.content.debugger.metadata.fallback.fetch.failed"),
NotificationType.ERROR
));
return prop;
}
val reader = new InputStreamReader(resource);
prop.load(reader);
} catch (IOException ex) {
ex.printStackTrace();
Notifications.Bus.notify(new Notification(
"ZigBrains.Debugger.Error",
ZigBundle.message("notification.title.debugger"),
ZigBundle.message("notification.content.debugger.metadata.fallback.parse.failed"),
NotificationType.ERROR
));
}
return prop;
}
Properties msvcProperties() {
if (cached != null)
return cached;
val settings = ZigDebuggerSettings.getInstance();
var permission = settings.msvcConsent;
if (permission == MSVCDownloadPermission.AskMe) {
AtomicReference<Boolean> accepted = new AtomicReference<>(false);
ApplicationManager.getApplication().invokeAndWait(() -> {
val dialog = new DialogBuilder();
dialog.setTitle("Network Request Consent");
dialog.addCancelAction().setText("Deny");
dialog.addOkAction().setText("Allow");
val centerPanel = new JBPanel<>();
centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS));
centerPanel.add(new JBLabel("ZigBrains needs to download some metadata from the internet for debugging on Windows."));
centerPanel.add(new JBLabel("ZigBrains will use the fallback metadata shipped inside the plugin if the request is denied."));
centerPanel.add(new JBLabel("Would you like to allow this network request?"));
dialog.centerPanel(centerPanel);
accepted.set(dialog.showAndGet());
});
permission = settings.msvcConsent = accepted.get() ? MSVCDownloadPermission.Allow : MSVCDownloadPermission.Deny;
}
return switch (permission) {
//noinspection DataFlowIssue
case AskMe, Deny -> cached = fetchBuiltinMSVCProps();
case Allow -> {
val future = downloadMSVCProps();
try {
yield future.get(3, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
e.printStackTrace();
Notifications.Bus.notify(new Notification(
"ZigBrains.Debugger.Error",
ZigBundle.message("notification.title.debugger"),
ZigBundle.message("notification.content.debugger.metadata.downloading.failed"),
NotificationType.ERROR
));
settings.msvcConsent = MSVCDownloadPermission.Deny;
yield cached = fetchBuiltinMSVCProps();
}
}
};
}
}

View file

@ -2,6 +2,8 @@ package com.falsepattern.zigbrains.debugger.toolchain;
import com.falsepattern.zigbrains.ZigBundle; import com.falsepattern.zigbrains.ZigBundle;
import com.falsepattern.zigbrains.common.ZigPathManager; 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.Notification;
import com.intellij.notification.NotificationType; import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications; import com.intellij.notification.Notifications;
@ -15,6 +17,7 @@ import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtil;
import com.intellij.ui.BrowserHyperlinkListener; import com.intellij.ui.BrowserHyperlinkListener;
import com.intellij.ui.HyperlinkLabel; import com.intellij.ui.HyperlinkLabel;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.JBPanel;
import com.intellij.util.download.DownloadableFileService; import com.intellij.util.download.DownloadableFileService;
import com.intellij.util.io.Decompressor; import com.intellij.util.io.Decompressor;
@ -24,17 +27,13 @@ import com.jetbrains.cidr.execution.debugger.CidrDebuggerPathManager;
import com.jetbrains.cidr.execution.debugger.backend.bin.UrlProvider; import com.jetbrains.cidr.execution.debugger.backend.bin.UrlProvider;
import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguration; import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguration;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Cleanup;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.val; import lombok.val;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@ -43,10 +42,14 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Properties; 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; import java.util.concurrent.atomic.AtomicReference;
@Service(Service.Level.APP) @Service(Service.Level.APP)
public final class ZigDebuggerToolchainService { public final class ZigDebuggerToolchainService {
private MSVCMetadataProvider msvcProvider = new MSVCMetadataProvider();
public static ZigDebuggerToolchainService getInstance() { public static ZigDebuggerToolchainService getInstance() {
return ApplicationManager.getApplication().getService(ZigDebuggerToolchainService.class); return ApplicationManager.getApplication().getService(ZigDebuggerToolchainService.class);
} }
@ -320,7 +323,7 @@ public final class ZigDebuggerToolchainService {
}; };
} }
private static Path downloadPath() { static Path downloadPath() {
return Paths.get(PathManager.getTempPath()); return Paths.get(PathManager.getTempPath());
} }
@ -354,56 +357,6 @@ public final class ZigDebuggerToolchainService {
return UrlProvider.gdb(OS.CURRENT, CpuArch.CURRENT); return UrlProvider.gdb(OS.CURRENT, CpuArch.CURRENT);
} }
private Properties msvcProperties() {
val service = DownloadableFileService.getInstance();
val desc = service.createFileDescription("https://falsepattern.com/zigbrains/msvc.properties", "msvc.properties");
val downloader = service.createDownloader(List.of(desc), "Debugger metadata downloading");
val downloadDirectory = downloadPath().toFile();
val prop = new Properties();
try {
val downloadResults = downloader.download(downloadDirectory);
for (val result : downloadResults) {
if (Objects.equals(result.second.getDefaultFileName(), "msvc.properties")) {
@Cleanup val reader = new FileReader(result.first);
prop.load(reader);
}
}
} catch (IOException e) {
//TODO logging
e.printStackTrace();
Notifications.Bus.notify(new Notification(
"ZigBrains.Debugger.Error",
ZigBundle.message("notification.title.debugger"),
ZigBundle.message("notification.content.debugger.metadata.downloading.failed"),
NotificationType.ERROR
));
//Try to load fallback file
try {
@Cleanup val resource = ZigDebuggerToolchainService.class.getResourceAsStream("msvc.properties");
if (resource == null) {
Notifications.Bus.notify(new Notification(
"ZigBrains.Debugger.Error",
ZigBundle.message("notification.title.debugger"),
ZigBundle.message("notification.content.debugger.metadata.fallback.fetch.failed"),
NotificationType.ERROR
));
return prop;
}
val reader = new InputStreamReader(resource);
prop.load(reader);
} catch (IOException ex) {
ex.printStackTrace();
Notifications.Bus.notify(new Notification(
"ZigBrains.Debugger.Error",
ZigBundle.message("notification.title.debugger"),
ZigBundle.message("notification.content.debugger.metadata.fallback.parse.failed"),
NotificationType.ERROR
));
}
}
return prop;
}
private MSVCUrl msvcUrl() { private MSVCUrl msvcUrl() {
String dlKey = switch (CpuArch.CURRENT) { String dlKey = switch (CpuArch.CURRENT) {
case X86 -> "downloadX86"; case X86 -> "downloadX86";
@ -414,7 +367,7 @@ public final class ZigDebuggerToolchainService {
if (dlKey == null) if (dlKey == null)
return null; return null;
val props = msvcProperties(); val props = msvcProvider.msvcProperties();
val version = props.getProperty("version"); val version = props.getProperty("version");
val url = props.getProperty(dlKey); val url = props.getProperty(dlKey);
if (url == null || version == null) if (url == null || version == null)