feat!!: Overhauled configs and project creation

This commit is contained in:
FalsePattern 2024-04-19 23:06:19 +02:00
parent 125b97fb3b
commit 1f042fad34
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
43 changed files with 1059 additions and 861 deletions

View file

@ -18,12 +18,22 @@ Changelog structure reference:
## [Unreleased] ## [Unreleased]
### Added
- Project
- Completely overhauled the configuration system and the new project creation window. All the configs have been unified
into a single screen, and project creation has been fully integrated as a mainline feature, instead of just a "nice to have".
### Changed ### Changed
- LSP - LSP
- The injection of the various language actions (Go to declaration/implementation, reformat, etc.) has been - The injection of the various language actions (Go to declaration/implementation, reformat, etc.) has been
reimplemented from the ground up to be much more reliable and compatible in the presence of other languages and plugins. reimplemented from the ground up to be much more reliable and compatible in the presence of other languages and plugins.
- Zig, ZLS
- The configurations have been unified into a single cohesive interface
- Improved auto-detection for both Zig and ZLS
### Fixed ### Fixed
- Project - Project

View file

@ -54,24 +54,15 @@ and might as well utilize the full semver string for extra information.
# Description # Description
<!-- Plugin description --> <!-- Plugin description -->
A multifunctional Zig Programming Language plugin for the IDEA platform. Adds support for the Zig Language, utilizing the ZLS language server for advanced coding assistance.
Core features: ## Quick setup guide for Zig and ZLS
- Uses ZLS (Zig Language Server) for code assistance, syntax highlighting, and anything to do with coding assistance
- Supports build.zig.zon files with autocomplete
- Per-project Zig toolchain integration
- Debugging support for CLion (builtin), and IDEA Ultimate [With this plugin](https://plugins.jetbrains.com/plugin/12775-native-debugging-support)
- Gutter icon for running main(), tests, and build
1. Download the latest version of Zig from https://ziglang.org/download
## Setting up the language server 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
If you have `zls` available on PATH, ZigBrains will automatically discover it. If not, follow this guide: 4. Open a .zig file, and wait for the circle in the bottom status bar to turn Green (empty).
See below (`LSP status icon explanation`) for an explanation on what the circle means.
1. Download or compile the ZLS language server, available at https://github.com/zigtools/zls
2. Go to `Settings` -> `Languages & Frameworks` -> `ZLS` -> `ZLS path` -> set the path to the `zls` executable you downloaded or compiled
3. Open a .zig file, and wait for the circle in the bottom status bar to turn Green (empty).
See below for an explanation on what the circle means.
### LSP status icon explanation ### LSP status icon explanation
Red (X symbol): Red (X symbol):
@ -85,6 +76,13 @@ LSP server is running.
## Debugging ## Debugging
### Note
Debugging on Linux/MacOS/Unix is only available in CLion, as ZigBrains depends on the C++ toolchains system.
On Windows, debugging is also available with the help of the
[Native Debugging Support](https://plugins.jetbrains.com/plugin/12775-native-debugging-support), which is unfortunately
only compatible with paid IDEs.
### Windows ### Windows
Due to technical limitations, the C++ toolchains cannot be used for debugging zig code on windows. Due to technical limitations, the C++ toolchains cannot be used for debugging zig code on windows.
@ -106,39 +104,4 @@ Note: There is a small issue with the LLDB debugger which does not happen with G
instruction (usually, deep inside the zig standard library's startup code). Unfortunately, we have not found a fix for instruction (usually, deep inside the zig standard library's startup code). Unfortunately, we have not found a fix for
this yet, but fortunately it doesn't break anything, just a bit of inconvenience. this yet, but fortunately it doesn't break anything, just a bit of inconvenience.
## Feature tracker:
### .zig files:
- Code completion
- Code folding
- Code formatting
- Syntax highlighting
- Inlay hints
- Basic error diagnostics
- Go to definition
- Rename symbol
- Hover documentation
- Go to implementations / find usages
- Brace/Parenthesis/Bracket matching
- Debugging (CLion/CLion Nova)
- File creation prompt
- Gutter launch buttons
- Commenter (thanks @MarioAriasC !)
- TODO:
- Workspace Symbols
### .zon files:
- Syntax highlighting
- Formatting and indentation
- Code completion
- Brace folding
- Automatic brace and quote pairing
### Toolchain:
- Basic per-project toolchain management
- Run configurations
- Debugging (CLion/IDEA Ultimate)
- Project generation (thanks @JensvandeWiel !)
<!-- Plugin description end --> <!-- Plugin description end -->

View file

@ -0,0 +1,74 @@
/*
* 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;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.ui.JBColor;
import lombok.val;
import org.jetbrains.annotations.Nullable;
import javax.swing.JComponent;
import java.util.Arrays;
import static com.falsepattern.zigbrains.common.util.dsl.JavaPanel.newPanel;
public abstract class MultiConfigurable implements Configurable {
private final SubConfigurable[] configurables;
protected MultiConfigurable(SubConfigurable... configurables) {
this.configurables = Arrays.copyOf(configurables, configurables.length);
}
@Override
public @Nullable JComponent createComponent() {
return newPanel(p -> {
for (val configurable: configurables) {
configurable.createComponent(p);
}
});
}
@Override
public boolean isModified() {
for (val configurable: configurables) {
if (configurable.isModified())
return true;
}
return false;
}
@Override
public void apply() throws ConfigurationException {
for (val config: configurables) {
config.apply();
}
}
@Override
public void reset() {
for (val config: configurables) {
config.reset();
}
}
@Override
public void disposeUIResources() {
for (val config: configurables) {
config.disposeUIResources();
}
}
}

View file

@ -0,0 +1,28 @@
/*
* 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;
import com.falsepattern.zigbrains.common.util.dsl.JavaPanel;
import com.intellij.openapi.options.ConfigurationException;
public interface SubConfigurable {
void createComponent(JavaPanel panel);
boolean isModified();
void apply() throws ConfigurationException;
void reset();
void disposeUIResources();
}

View file

@ -0,0 +1,37 @@
/*
* 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;
import com.intellij.openapi.components.PersistentStateComponent;
import org.jetbrains.annotations.NotNull;
public abstract class WrappingStateComponent<T> implements PersistentStateComponent<T> {
private T state;
public WrappingStateComponent(@NotNull T initialState) {
this.state = initialState;
}
@Override
public @NotNull T getState() {
return state;
}
@Override
public void loadState(@NotNull T state) {
this.state = state;
}
}

View file

@ -17,6 +17,7 @@
package com.falsepattern.zigbrains.common.util; package com.falsepattern.zigbrains.common.util;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -24,7 +25,9 @@ import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Optional;
public class FileUtil { public class FileUtil {
private static final Logger LOG = Logger.getInstance(FileUtil.class); private static final Logger LOG = Logger.getInstance(FileUtil.class);
@ -96,6 +99,10 @@ public class FileUtil {
return LocalFileSystem.getInstance().findFileByIoFile(new File(uri)); return LocalFileSystem.getInstance().findFileByIoFile(new File(uri));
} }
public static VirtualFile virtualFileFromPath(Path path) {
return LocalFileSystem.getInstance().findFileByNioFile(path);
}
/** /**
* Transforms an URI string into a VFS file * Transforms an URI string into a VFS file
* *
@ -125,6 +132,28 @@ public class FileUtil {
return path != null ? sanitizeURI(path.toUri().toString()) : null; 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) {
var path = Path.of(dir);
try {
path = path.toAbsolutePath();
} catch (Exception ignored) {
continue;
}
if (!Files.exists(path) || !Files.isDirectory(path)) {
continue;
}
var exePath = path.resolve(exeName).toAbsolutePath();
if (!Files.isRegularFile(exePath) || !Files.isExecutable(exePath)) {
continue;
}
return Optional.of(exePath);
}
return Optional.empty();
}
/** /**
* Object representing the OS type (Windows or Unix) * Object representing the OS type (Windows or Unix)
*/ */

View file

@ -0,0 +1,31 @@
/*
* 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 kotlin.Unit;
import kotlin.jvm.functions.Function1;
import java.util.function.Consumer;
public class KtUtil {
public static <T> Function1<T, Unit> $f(Consumer<T> f) {
return (x) -> {
f.accept(x);
return null;
};
}
}

View file

@ -17,8 +17,11 @@
package com.falsepattern.zigbrains.common.util; package com.falsepattern.zigbrains.common.util;
import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
public class PathUtil { public class PathUtil {
@ -30,4 +33,22 @@ public class PathUtil {
var exeName = SystemInfo.isWindows ? toolName + ".exe" : toolName; var exeName = SystemInfo.isWindows ? toolName + ".exe" : toolName;
return path.resolve(exeName).toAbsolutePath(); return path.resolve(exeName).toAbsolutePath();
} }
public static @Nullable Path pathFromString(@Nullable String pathString) {
if (pathString == null || pathString.isBlank()) {
return null;
}
try {
return Path.of(pathString);
} catch (InvalidPathException e) {
return null;
}
}
public static @NotNull String stringFromPath(@Nullable Path path) {
if (path == null) {
return "";
}
return path.toString();
}
} }

View file

@ -23,6 +23,7 @@ import com.intellij.openapi.ui.TextComponentAccessor;
import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.NlsContexts; import com.intellij.openapi.util.NlsContexts;
import com.intellij.ui.DocumentAdapter; import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.components.fields.ExtendableTextField;
import lombok.val; import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -53,7 +54,7 @@ public class TextFieldUtil {
Disposable disposable, Disposable disposable,
@NlsContexts.DialogTitle String dialogTitle, @NlsContexts.DialogTitle String dialogTitle,
Runnable onTextChanged) { Runnable onTextChanged) {
val component = new TextFieldWithBrowseButton(null, disposable); val component = new TextFieldWithBrowseButton(new ExtendableTextField(), null, disposable);
component.addBrowseFolderListener(dialogTitle, null, null, fileChooserDescriptor, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT); component.addBrowseFolderListener(dialogTitle, null, null, fileChooserDescriptor, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
addTextChangeListener(component.getChildComponent(), ignored -> onTextChanged.run()); addTextChangeListener(component.getChildComponent(), ignored -> onTextChanged.run());

View file

@ -0,0 +1,103 @@
/*
* 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.dsl;
import com.falsepattern.zigbrains.common.util.KtUtil;
import com.intellij.openapi.ui.DialogPanel;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.dsl.builder.Align;
import com.intellij.ui.dsl.builder.AlignX;
import com.intellij.ui.dsl.builder.BuilderKt;
import com.intellij.ui.dsl.builder.Panel;
import com.intellij.ui.dsl.builder.RightGap;
import com.intellij.ui.dsl.builder.Row;
import com.intellij.ui.dsl.builder.RowsRange;
import lombok.RequiredArgsConstructor;
import javax.swing.JComponent;
import javax.swing.JLabel;
import java.awt.Color;
import java.util.function.Consumer;
import static com.falsepattern.zigbrains.common.util.KtUtil.$f;
public class JavaPanel {
private final Panel panel;
public JavaPanel(Panel p) {
this.panel = p;
}
public void cell(String label, JComponent component, Align align) {
row(label, row -> row.cell(component).align(align));
}
public void cell(String label, JComponent component) {
row(label, row -> row.cell(component));
}
public void cell(JComponent component) {
cell("", component);
}
public void label(String label) {
panel.row((JLabel) null, $f(r -> r.label(label)));
}
public void gap() {
panel.gap(RightGap.SMALL);
}
public void row(Consumer<Row> row) {
panel.row((JLabel) null, (r) -> {
row.accept(r);
return null;
});
}
public void row(String text, Consumer<Row> row) {
panel.row(text, $f(row));
}
public void separator() {
panel.separator(null);
}
public void separator(Color color) {
panel.separator(color);
}
public void panel(Consumer<JavaPanel> c) {
panel.panel($f(p -> {
c.accept(new JavaPanel(p));
}));
}
public static DialogPanel newPanel(Consumer<JavaPanel> c) {
return BuilderKt.panel((p) -> {
c.accept(new JavaPanel(p));
return null;
});
}
public void group(String title, Consumer<JavaPanel> c) {
panel.groupRowsRange(title, false, null, null, (p) -> {
c.accept(new JavaPanel(p));
return null;
});
}
}

View file

@ -0,0 +1,34 @@
/*
* 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.ide.config;
import com.falsepattern.zigbrains.common.MultiConfigurable;
import com.falsepattern.zigbrains.project.ide.project.ZigProjectConfigurable;
import com.falsepattern.zigbrains.zig.settings.ZLSSettingsConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsContexts;
import org.jetbrains.annotations.NotNull;
public class ZigConfigurable extends MultiConfigurable {
public ZigConfigurable(@NotNull Project project) {
super(new ZigProjectConfigurable(project), new ZLSSettingsConfigurable(project));
}
@Override
public @NlsContexts.ConfigurableName String getDisplayName() {
return "Zig";
}
}

View file

@ -0,0 +1,82 @@
/*
* 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.ide.newproject;
import com.falsepattern.zigbrains.project.ide.newproject.ZigProjectConfigurationData;
import com.falsepattern.zigbrains.project.ide.newproject.ZigProjectGeneratorPeer;
import com.falsepattern.zigbrains.project.ide.project.ZigDefaultTemplate;
import com.falsepattern.zigbrains.project.ide.newproject.ZigProjectSettingsStep;
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
import com.falsepattern.zigbrains.zig.Icons;
import com.falsepattern.zigbrains.zig.settings.ZLSProjectSettingsService;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.ide.util.projectWizard.AbstractNewProjectStep;
import com.intellij.ide.util.projectWizard.CustomStepProjectGenerator;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.impl.welcomeScreen.AbstractActionWithPanel;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.platform.ProjectGeneratorPeer;
import com.intellij.util.ResourceUtil;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ZigDirectoryProjectGenerator implements DirectoryProjectGenerator<ZigProjectConfigurationData>,
CustomStepProjectGenerator<ZigProjectConfigurationData> {
@Override
public @NotNull @NlsContexts.Label String getName() {
return "Zig";
}
@Override
public @Nullable Icon getLogo() {
return Icons.ZIG;
}
@Override
public @NotNull ProjectGeneratorPeer<ZigProjectConfigurationData> createPeer() {
return new ZigProjectGeneratorPeer(true);
}
@Override
public @NotNull ValidationResult validate(@NotNull String baseDirPath) {
return ValidationResult.OK;
}
@Override
public void generateProject(@NotNull Project project, @NotNull VirtualFile baseDir, @NotNull ZigProjectConfigurationData data, @NotNull Module module) {
data.generateProject(this, project, baseDir, false);
}
@Override
public AbstractActionWithPanel createStep(DirectoryProjectGenerator<ZigProjectConfigurationData> projectGenerator, AbstractNewProjectStep.AbstractCallback<ZigProjectConfigurationData> callback) {
return new ZigProjectSettingsStep(projectGenerator);
}
}

View file

@ -14,23 +14,28 @@
* limitations under the License. * limitations under the License.
*/ */
package com.falsepattern.zigbrains.project.ide.util.projectwizard; package com.falsepattern.zigbrains.project.ide.newproject;
import com.falsepattern.zigbrains.project.ide.newproject.ZigProjectConfigurationData;
import com.falsepattern.zigbrains.project.openapi.module.ZigModuleType; import com.falsepattern.zigbrains.project.openapi.module.ZigModuleType;
import com.falsepattern.zigbrains.project.util.ExperimentUtil;
import com.intellij.ide.util.projectWizard.ModuleBuilder; import com.intellij.ide.util.projectWizard.ModuleBuilder;
import com.intellij.ide.util.projectWizard.ModuleWizardStep; import com.intellij.ide.util.projectWizard.ModuleWizardStep;
import com.intellij.ide.util.projectWizard.WizardContext; import com.intellij.ide.util.projectWizard.WizardContext;
import com.intellij.ide.wizard.CommitStepException;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Disposer;
import com.intellij.util.ui.JBUI;
import lombok.val; import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import javax.swing.JComponent;
public class ZigModuleBuilder extends ModuleBuilder { public class ZigModuleBuilder extends ModuleBuilder {
public @Nullable ZigProjectConfigurationData configurationData = null; public @Nullable ZigProjectConfigurationData configurationData = null;
public boolean forceGitignore = false;
@Override @Override
public ModuleType<?> getModuleType() { public ModuleType<?> getModuleType() {
@ -39,7 +44,7 @@ public class ZigModuleBuilder extends ModuleBuilder {
@Override @Override
public void setupRootModel(@NotNull ModifiableRootModel modifiableRootModel) { public void setupRootModel(@NotNull ModifiableRootModel modifiableRootModel) {
createProject(modifiableRootModel, "git"); createProject(modifiableRootModel);
} }
@Override @Override
@ -49,8 +54,8 @@ public class ZigModuleBuilder extends ModuleBuilder {
return step; return step;
} }
public void createProject(ModifiableRootModel modifiableRootModel, @Nullable String vcs) { public void createProject(ModifiableRootModel rootModel) {
val contentEntry = doAddContentEntry(modifiableRootModel); val contentEntry = doAddContentEntry(rootModel);
if (contentEntry == null) { if (contentEntry == null) {
return; return;
} }
@ -58,7 +63,36 @@ public class ZigModuleBuilder extends ModuleBuilder {
if (root == null) { if (root == null) {
return; return;
} }
modifiableRootModel.inheritSdk(); if (configurationData == null) {
return;
}
configurationData.generateProject(this, rootModel.getProject(), root, forceGitignore);
root.refresh(false, true); root.refresh(false, true);
} }
public class ZigModuleWizardStep extends ModuleWizardStep {
private final ZigProjectGeneratorPeer peer = new ZigProjectGeneratorPeer(true);
@Override
public JComponent getComponent() {
return withBorderIfNeeded(peer.getComponent());
}
@Override
public void disposeUIResources() {
peer.dispose();
}
@Override
public void updateDataModel() {
ZigModuleBuilder.this.configurationData = peer.getSettings();
}
private <T extends JComponent> T withBorderIfNeeded(T component) {
if (ExperimentUtil.isNewWizard()) {
component.setBorder(JBUI.Borders.empty(14, 20));
}
return component;
}
}
} }

View file

@ -16,19 +16,23 @@
package com.falsepattern.zigbrains.project.ide.newproject; package com.falsepattern.zigbrains.project.ide.newproject;
import com.falsepattern.zigbrains.common.util.dsl.JavaPanel;
import com.falsepattern.zigbrains.project.ide.project.ZigDefaultTemplate; import com.falsepattern.zigbrains.project.ide.project.ZigDefaultTemplate;
import com.falsepattern.zigbrains.project.ide.project.ZigProjectSettingsPanel; import com.falsepattern.zigbrains.project.ide.project.ZigProjectSettingsPanel;
import com.falsepattern.zigbrains.project.ide.project.ZigProjectTemplate; import com.falsepattern.zigbrains.project.ide.project.ZigProjectTemplate;
import com.falsepattern.zigbrains.zig.settings.ZLSSettingsPanel;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionToolbarPosition; import com.intellij.openapi.actionSystem.ActionToolbarPosition;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.ColoredListCellRenderer; import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.JBColor;
import com.intellij.ui.ToolbarDecorator; import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBList;
import com.intellij.ui.dsl.builder.AlignX; import com.intellij.ui.dsl.builder.AlignX;
import com.intellij.ui.dsl.builder.AlignY; import com.intellij.ui.dsl.builder.AlignY;
import com.intellij.ui.dsl.builder.Panel; import com.intellij.ui.dsl.builder.Panel;
import com.intellij.util.ui.JBUI; import com.intellij.util.ui.JBUI;
import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
@ -40,11 +44,20 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
public class ZigNewProjectPanel implements Disposable { public class ZigNewProjectPanel implements Disposable {
private final ZigProjectSettingsPanel projectSettingsPanel = new ZigProjectSettingsPanel(); private boolean handleGit;
private JBCheckBox git = new JBCheckBox();
private ZigProjectSettingsPanel projConf;
private ZLSSettingsPanel zlsConf;
public ZigNewProjectPanel(boolean handleGit) {
this.handleGit = handleGit;
projConf = new ZigProjectSettingsPanel();
zlsConf = new ZLSSettingsPanel();
}
public ZigProjectConfigurationData getData() { public ZigProjectConfigurationData getData() {
ZigProjectTemplate selectedTemplate = templateList.getSelectedValue(); ZigProjectTemplate selectedTemplate = templateList.getSelectedValue();
return new ZigProjectConfigurationData(projectSettingsPanel.getData(), selectedTemplate); return new ZigProjectConfigurationData(handleGit && git.isSelected(), projConf.getData(), zlsConf.getData(), selectedTemplate);
} }
private final List<ZigProjectTemplate> defaultTemplates = Arrays.asList( private final List<ZigProjectTemplate> defaultTemplates = Arrays.asList(
@ -75,23 +88,27 @@ public class ZigNewProjectPanel implements Disposable {
.disableAddAction() .disableAddAction()
.disableRemoveAction(); .disableRemoveAction();
public void attachPanelTo(Panel panel) { public void attachPanelTo(JavaPanel p) {
projectSettingsPanel.attachPanelTo(panel); if (handleGit) {
p.row("Create Git repository", r -> r.cell(git));
panel.groupRowsRange("Zig Project Template", false, null, null, (p) -> { }
p.row((JLabel) null, (r) -> { p.group("Zig Project Template", (p2) -> {
p2.row((r) -> {
r.resizableRow(); r.resizableRow();
r.cell(templateToolbar.createPanel()) r.cell(templateToolbar.createPanel())
.align(AlignX.FILL) .align(AlignX.FILL)
.align(AlignY.FILL); .align(AlignY.FILL);
return null;
}); });
return null;
}); });
projConf.attachPanelTo(p);
zlsConf.attachPanelTo(p);
projConf.autodetect();
zlsConf.autodetect();
} }
@Override @Override
public void dispose() { public void dispose() {
Disposer.dispose(projectSettingsPanel); projConf = null;
zlsConf = null;
} }
} }

View file

@ -16,18 +16,14 @@
package com.falsepattern.zigbrains.project.ide.newproject; package com.falsepattern.zigbrains.project.ide.newproject;
import com.falsepattern.zigbrains.common.util.ApplicationUtil; import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.project.ide.util.projectwizard.ZigModuleBuilder;
import com.falsepattern.zigbrains.project.platform.ZigProjectGeneratorPeer;
import com.falsepattern.zigbrains.zig.Icons; import com.falsepattern.zigbrains.zig.Icons;
import com.intellij.ide.wizard.AbstractNewProjectWizardStep; import com.intellij.ide.wizard.AbstractNewProjectWizardStep;
import com.intellij.ide.wizard.GitNewProjectWizardData; import com.intellij.ide.wizard.GitNewProjectWizardData;
import com.intellij.ide.wizard.NewProjectWizardStep; import com.intellij.ide.wizard.NewProjectWizardStep;
import com.intellij.ide.wizard.language.LanguageGeneratorNewProjectWizard; import com.intellij.ide.wizard.language.LanguageGeneratorNewProjectWizard;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.ui.dsl.builder.AlignX; import com.intellij.ui.dsl.builder.AlignX;
import com.intellij.ui.dsl.builder.Panel; import com.intellij.ui.dsl.builder.Panel;
import lombok.val; import lombok.val;
@ -35,8 +31,6 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JLabel; import javax.swing.JLabel;
import java.io.IOException;
import java.nio.file.Path;
public class ZigNewProjectWizard implements LanguageGeneratorNewProjectWizard { public class ZigNewProjectWizard implements LanguageGeneratorNewProjectWizard {
@NotNull @NotNull
@ -63,7 +57,7 @@ public class ZigNewProjectWizard implements LanguageGeneratorNewProjectWizard {
} }
private static class ZigNewProjectWizardStep extends AbstractNewProjectWizardStep { private static class ZigNewProjectWizardStep extends AbstractNewProjectWizardStep {
private final ZigProjectGeneratorPeer peer = new ZigProjectGeneratorPeer(); private final ZigProjectGeneratorPeer peer = new ZigProjectGeneratorPeer(false);
public ZigNewProjectWizardStep(@NotNull NewProjectWizardStep parentStep) { public ZigNewProjectWizardStep(@NotNull NewProjectWizardStep parentStep) {
super(parentStep); super(parentStep);
@ -81,41 +75,10 @@ public class ZigNewProjectWizard implements LanguageGeneratorNewProjectWizard {
@Override @Override
public void setupProject(@NotNull Project project) { public void setupProject(@NotNull Project project) {
val builder = new ZigModuleBuilder(); val builder = new ZigModuleBuilder();
val modList = builder.commit(project);
if (modList == null || modList.isEmpty()) {
return;
}
val module = modList.get(0);
ModuleRootModificationUtil.updateModel(module, rootModel -> {
builder.configurationData = peer.getSettings(); builder.configurationData = peer.getSettings();
builder.createProject(rootModel, "none");
var gitData = GitNewProjectWizardData.Companion.getGitData(this); var gitData = GitNewProjectWizardData.Companion.getGitData(this);
if (gitData == null) { builder.forceGitignore = gitData != null && gitData.getGit();
return; builder.commit(project);
}
if (gitData.getGit()) {
ApplicationUtil.writeAction(() -> createGitIgnoreFile(getContext().getProjectDirectory(), module));
}
});
}
private static final String GITIGNORE = ".gitignore";
private static void createGitIgnoreFile(Path projectDir, Module module) {
try {
val directory = VfsUtil.createDirectoryIfMissing(projectDir.toString());
if (directory == null) {
return;
}
val existingFile = directory.findChild(GITIGNORE);
if (existingFile != null) {
return;
}
directory.createChildData(module, GITIGNORE);
} catch (IOException e) {
e.printStackTrace();
}
} }
} }
} }

View file

@ -16,8 +16,132 @@
package com.falsepattern.zigbrains.project.ide.newproject; package com.falsepattern.zigbrains.project.ide.newproject;
import com.falsepattern.zigbrains.project.ide.project.ZigProjectSettingsPanel; import com.falsepattern.zigbrains.common.util.ApplicationUtil;
import com.falsepattern.zigbrains.project.ide.project.ZigDefaultTemplate;
import com.falsepattern.zigbrains.project.ide.project.ZigProjectTemplate; import com.falsepattern.zigbrains.project.ide.project.ZigProjectTemplate;
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettings;
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
import com.falsepattern.zigbrains.zig.settings.ZLSProjectSettingsService;
import com.falsepattern.zigbrains.zig.settings.ZLSSettings;
import com.intellij.ide.wizard.GitNewProjectWizardData;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.GitRepositoryInitializer;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ResourceUtil;
import lombok.Cleanup;
import lombok.val;
import org.jetbrains.annotations.NotNull;
public record ZigProjectConfigurationData(ZigProjectSettingsPanel.SettingsData settings, ZigProjectTemplate selectedTemplate) { import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
public record ZigProjectConfigurationData(boolean git, ZigProjectSettings projConf, ZLSSettings zlsConf, ZigProjectTemplate selectedTemplate) {
private static String getResourceString(String path) throws IOException {
byte[] data = ResourceUtil.getResourceAsBytes(path, ZigDirectoryProjectGenerator.class.getClassLoader());
if (data == null)
throw new IOException("Could not find resource " + path + "!");
return new String(data, StandardCharsets.UTF_8);
}
public void generateProject(@NotNull Object requestor, @NotNull Project project, @NotNull VirtualFile baseDir, boolean forceGitignore) {
val svc = ZigProjectSettingsService.getInstance(project);
svc.loadState(this.projConf());
ZLSProjectSettingsService.getInstance(project).loadState(this.zlsConf());
val toolchain = svc.getState().getToolchain();
val template = this.selectedTemplate();
if (template instanceof ZigDefaultTemplate.ZigInitTemplate) {
if (toolchain == null) {
Notifications.Bus.notify(new Notification("ZigBrains.Project",
"Tried to generate project with zig init, but zig toolchain is invalid!",
NotificationType.ERROR));
return;
}
val zig = toolchain.zig();
val resultOpt = zig.callWithArgs(baseDir.toNioPath(), 10000, "init");
if (resultOpt.isEmpty()) {
Notifications.Bus.notify(new Notification("ZigBrains.Project",
"Failed to invoke \"zig init\"!",
NotificationType.ERROR));
return;
}
val result = resultOpt.get();
if (result.getExitCode() != 0) {
Notifications.Bus.notify(new Notification("ZigBrains.Project",
"\"zig init\" failed with exit code " + result.getExitCode() + "! Check the IDE log files!",
NotificationType.ERROR));
System.err.println(result.getStderr());
}
} else {
try {
val projectName = project.getName();
WriteAction.run(() -> {
for (val fileTemplate : template.fileTemplates().entrySet()) {
var fileName = fileTemplate.getKey();
VirtualFile parentDir;
if (fileName.contains("/")) {
val slashIndex = fileName.indexOf('/');
parentDir = baseDir.createChildDirectory(requestor, fileName.substring(0, slashIndex));
fileName = fileName.substring(slashIndex + 1);
} else {
parentDir = baseDir;
}
val templateDir = fileTemplate.getValue();
val resourceData = getResourceString("project-gen/" + templateDir + "/" + fileName + ".template").replace("@@PROJECT_NAME@@", projectName);
val targetFile = parentDir.createChildData(requestor, fileName);
VfsUtil.saveText(targetFile, resourceData);
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (git) {
ApplicationManager.getApplication().executeOnPooledThread(() -> {
val initializer = GitRepositoryInitializer.getInstance();
if (initializer != null) {
initializer.initRepository(project, baseDir);
}
});
}
if (git || forceGitignore) {
createGitIgnoreFile(baseDir, this);
}
}
private static final String GITIGNORE = ".gitignore";
private static void createGitIgnoreFile(VirtualFile projectDir, Object requestor) {
val existingFile = projectDir.findChild(GITIGNORE);
if (existingFile != null) {
return;
}
WriteAction.run(() -> {
VirtualFile file = null;
try {
file = projectDir.createChildData(requestor, GITIGNORE);
@Cleanup val res = ZigProjectConfigurationData.class.getResourceAsStream("/fileTemplates/internal/gitignore");
if (res == null)
return;
file.setCharset(StandardCharsets.UTF_8);
file.setBinaryContent(res.readAllBytes());
} catch (IOException e) {
e.printStackTrace();
}
});
}
} }

View file

@ -14,19 +14,22 @@
* limitations under the License. * limitations under the License.
*/ */
package com.falsepattern.zigbrains.project.platform; package com.falsepattern.zigbrains.project.ide.newproject;
import com.falsepattern.zigbrains.project.ide.newproject.ZigNewProjectPanel; import com.intellij.openapi.util.Disposer;
import com.falsepattern.zigbrains.project.ide.newproject.ZigProjectConfigurationData;
import com.intellij.platform.GeneratorPeerImpl; import com.intellij.platform.GeneratorPeerImpl;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.swing.JComponent; import javax.swing.JComponent;
import static com.intellij.ui.dsl.builder.BuilderKt.panel; import static com.falsepattern.zigbrains.common.util.dsl.JavaPanel.newPanel;
public class ZigProjectGeneratorPeer extends GeneratorPeerImpl<ZigProjectConfigurationData> { public class ZigProjectGeneratorPeer extends GeneratorPeerImpl<ZigProjectConfigurationData> {
private final ZigNewProjectPanel newProjectPanel = new ZigNewProjectPanel(); private final ZigNewProjectPanel newProjectPanel;
public ZigProjectGeneratorPeer(boolean handleGit) {
newProjectPanel = new ZigNewProjectPanel(handleGit);
}
@Override @Override
public @NotNull ZigProjectConfigurationData getSettings() { public @NotNull ZigProjectConfigurationData getSettings() {
@ -35,9 +38,10 @@ public class ZigProjectGeneratorPeer extends GeneratorPeerImpl<ZigProjectConfigu
@Override @Override
public @NotNull JComponent getComponent() { public @NotNull JComponent getComponent() {
return panel((p) -> { return newPanel(newProjectPanel::attachPanelTo);
newProjectPanel.attachPanelTo(p); }
return null;
}); public void dispose() {
Disposer.dispose(newProjectPanel);
} }
} }

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.falsepattern.zigbrains.project.ide.util.projectwizard; package com.falsepattern.zigbrains.project.ide.newproject;
import com.falsepattern.zigbrains.project.ide.newproject.ZigProjectConfigurationData; import com.falsepattern.zigbrains.project.ide.newproject.ZigProjectConfigurationData;
import com.intellij.ide.util.projectWizard.AbstractNewProjectStep; import com.intellij.ide.util.projectWizard.AbstractNewProjectStep;

View file

@ -16,23 +16,17 @@
package com.falsepattern.zigbrains.project.ide.project; package com.falsepattern.zigbrains.project.ide.project;
import com.falsepattern.zigbrains.common.SubConfigurable;
import com.falsepattern.zigbrains.common.util.dsl.JavaPanel;
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService; import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
import com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity; import com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.NlsContexts;
import lombok.val; import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.JComponent; public class ZigProjectConfigurable implements SubConfigurable {
import java.util.Objects;
import static com.intellij.ui.dsl.builder.BuilderKt.panel;
public class ZigProjectConfigurable implements Configurable {
private ZigProjectSettingsPanel settingsPanel; private ZigProjectSettingsPanel settingsPanel;
private final Project project; private final Project project;
@ -42,36 +36,22 @@ public class ZigProjectConfigurable implements Configurable {
} }
@Override @Override
public @NlsContexts.ConfigurableName String getDisplayName() { public void createComponent(JavaPanel panel) {
return "Zig";
}
@Override
public @Nullable JComponent createComponent() {
settingsPanel = new ZigProjectSettingsPanel(); settingsPanel = new ZigProjectSettingsPanel();
return panel((p) -> { settingsPanel.attachPanelTo(panel);
settingsPanel.attachPanelTo(p);
return null;
});
} }
@Override @Override
public boolean isModified() { public boolean isModified() {
var zigSettings = ZigProjectSettingsService.getInstance(project); return ZigProjectSettingsService.getInstance(project).isModified(settingsPanel.getData());
var settingsData = settingsPanel.getData();
return !Objects.equals(settingsData.toolchain(), zigSettings.getToolchain()) ||
!Objects.equals(settingsData.explicitPathToStd(), zigSettings.getExplicitPathToStd());
} }
@Override @Override
public void apply() throws ConfigurationException { public void apply() throws ConfigurationException {
val zigSettings = ZigProjectSettingsService.getInstance(project); val service = ZigProjectSettingsService.getInstance(project);
val settingsData = settingsPanel.getData(); val data = settingsPanel.getData();
boolean modified = isModified(); val modified = service.isModified(data);
zigSettings.modify((settings) -> { service.loadState(data);
settings.setToolchain(settingsData.toolchain());
settings.setExplicitPathToStd(settingsData.explicitPathToStd());
});
if (modified) { if (modified) {
ZLSStartupActivity.initZLS(project); ZLSStartupActivity.initZLS(project);
} }
@ -80,10 +60,7 @@ public class ZigProjectConfigurable implements Configurable {
@Override @Override
public void reset() { public void reset() {
val zigSettings = ZigProjectSettingsService.getInstance(project); val zigSettings = ZigProjectSettingsService.getInstance(project);
settingsPanel.setData(new ZigProjectSettingsPanel.SettingsData( settingsPanel.setData(zigSettings.getState());
zigSettings.getExplicitPathToStd(),
zigSettings.getToolchain()
));
} }
@Override @Override

View file

@ -16,10 +16,13 @@
package com.falsepattern.zigbrains.project.ide.project; package com.falsepattern.zigbrains.project.ide.project;
import com.falsepattern.zigbrains.common.util.PathUtil;
import com.falsepattern.zigbrains.common.util.StringUtil; import com.falsepattern.zigbrains.common.util.StringUtil;
import com.falsepattern.zigbrains.common.util.TextFieldUtil; import com.falsepattern.zigbrains.common.util.TextFieldUtil;
import com.falsepattern.zigbrains.common.util.dsl.JavaPanel;
import com.falsepattern.zigbrains.project.openapi.MyDisposable; import com.falsepattern.zigbrains.project.openapi.MyDisposable;
import com.falsepattern.zigbrains.project.openapi.UIDebouncer; import com.falsepattern.zigbrains.project.openapi.UIDebouncer;
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;
@ -29,26 +32,25 @@ import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pair;
import com.intellij.ui.JBColor; import com.intellij.ui.JBColor;
import com.intellij.ui.dsl.builder.AlignX; import com.intellij.ui.dsl.builder.AlignX;
import com.intellij.ui.dsl.builder.Panel;
import lombok.Getter; import lombok.Getter;
import lombok.val; import lombok.val;
import org.jetbrains.annotations.Nullable;
import javax.swing.JLabel; import javax.swing.JLabel;
import java.awt.event.ActionEvent;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional; import java.util.Optional;
import static com.falsepattern.zigbrains.common.util.KtUtil.$f;
public class ZigProjectSettingsPanel implements MyDisposable { public class ZigProjectSettingsPanel implements MyDisposable {
@Getter @Getter
private boolean disposed = false; private boolean disposed = false;
public record SettingsData(@Nullable String explicitPathToStd,
@Nullable AbstractZigToolchain toolchain) {}
private final UIDebouncer versionUpdateDebouncer = new UIDebouncer(this); private final UIDebouncer versionUpdateDebouncer = new UIDebouncer(this);
private final ZigToolchainPathChooserComboBox toolchainPathChooserComboBox = new ZigToolchainPathChooserComboBox(this::updateUI); private final TextFieldWithBrowseButton pathToToolchain = TextFieldUtil.pathToDirectoryTextField(this,
"Path to the Zig Toolchain",
this::updateUI);
private final JLabel toolchainVersion = new JLabel(); private final JLabel toolchainVersion = new JLabel();
@ -56,56 +58,57 @@ public class ZigProjectSettingsPanel implements MyDisposable {
"Path to Standard Library", "Path to Standard Library",
() -> {}); () -> {});
public SettingsData getData() { private void autodetect(ActionEvent e) {
val toolchain = Optional.ofNullable(toolchainPathChooserComboBox.getSelectedPath()) autodetect();
.map(ZigToolchainProvider::findToolchain)
.orElse(null);
return new SettingsData(StringUtil.blankToNull(pathToStdField.getText()), toolchain);
} }
public void setData(SettingsData value) { public void autodetect() {
toolchainPathChooserComboBox.setSelectedPath(Optional.ofNullable(value.toolchain()).map(tc -> tc.location).orElse(null)); val tc = AbstractZigToolchain.suggest();
if (tc != null) {
pathToToolchain.setText(PathUtil.stringFromPath(tc.getLocation()));
updateUI();
}
}
pathToStdField.setText(Optional.ofNullable(value.explicitPathToStd()).orElse("")); public ZigProjectSettings getData() {
val toolchain = Optional.of(pathToToolchain.getText())
.map(PathUtil::pathFromString)
.map(ZigToolchainProvider::findToolchain)
.orElse(null);
return new ZigProjectSettings(StringUtil.blankToNull(pathToStdField.getText()), toolchain);
}
public void setData(ZigProjectSettings value) {
pathToToolchain.setText(Optional.ofNullable(value.getToolchainHomeDirectory())
.orElse(""));
pathToStdField.setText(Optional.ofNullable(value.getExplicitPathToStd()).orElse(""));
updateUI(); updateUI();
} }
public void attachPanelTo(Panel panel) { public void attachPanelTo(JavaPanel p) {
setData(new SettingsData(null, Optional.ofNullable(ZigProjectSettingsService.getInstance(ProjectManager.getInstance().getDefaultProject()))
Optional.ofNullable(ProjectManager.getInstance() .map(ZigProjectSettingsService::getState)
.getDefaultProject() .ifPresent(this::setData);
.getService(ZigProjectSettingsService.class)) p.group("Zig Settings", p2 -> {
.map(ZigProjectSettingsService::getToolchain) p2.row("Toolchain location", r -> {
.orElse(AbstractZigToolchain.suggest(Paths.get("."))))); r.cell(pathToToolchain).resizableColumn().align(AlignX.FILL);
r.button("Autodetect", $f(this::autodetect));
panel.row("Toolchain Location", (r) -> {
r.cell(toolchainPathChooserComboBox)
.align(AlignX.FILL);
return null;
}); });
p2.cell("Toolchain version", toolchainVersion);
panel.row("Toolchain Version", (r) -> { p2.cell("Standard library location", pathToStdField, AlignX.FILL);
r.cell(toolchainVersion);
return null;
});
panel.row("Standard Library Location", (r) -> {
r.cell(pathToStdField)
.align(AlignX.FILL);
return null;
}); });
} }
@Override @Override
public void dispose() { public void dispose() {
disposed = true; disposed = true;
Disposer.dispose(toolchainPathChooserComboBox); Disposer.dispose(pathToToolchain);
} }
private void updateUI() { private void updateUI() {
val pathToToolchain = toolchainPathChooserComboBox.getSelectedPath(); val pathToToolchain = PathUtil.pathFromString(this.pathToToolchain.getText());
versionUpdateDebouncer.run( versionUpdateDebouncer.run(
() -> { () -> {

View file

@ -1,78 +0,0 @@
/*
* 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.ide.project;
import com.falsepattern.zigbrains.common.util.TextFieldUtil;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.ui.ComboBoxWithWidePopup;
import com.intellij.openapi.ui.ComponentWithBrowseButton;
import com.intellij.ui.AnimatedIcon;
import com.intellij.ui.ComboboxSpeedSearch;
import com.intellij.ui.components.fields.ExtendableTextComponent;
import com.intellij.ui.components.fields.ExtendableTextField;
import lombok.val;
import javax.swing.JTextField;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import java.nio.file.Path;
public class ZigToolchainPathChooserComboBox extends ComponentWithBrowseButton<ComboBoxWithWidePopup<Path>> {
public Runnable onTextChanged;
private final BasicComboBoxEditor comboBoxEditor = new BasicComboBoxEditor() {
@Override
protected JTextField createEditorComponent() {
return new ExtendableTextField();
}
};
private ExtendableTextField getPathTextField() {
return (ExtendableTextField) getChildComponent().getEditor().getEditorComponent();
}
private final ExtendableTextComponent.Extension busyIconExtension = hovered -> AnimatedIcon.Default.INSTANCE;
public Path getSelectedPath() {
return Path.of(getPathTextField().getText());
}
public void setSelectedPath(Path path) {
if (path == null) {
getPathTextField().setText("");
return;
}
getPathTextField().setText(path.toString());
}
public ZigToolchainPathChooserComboBox(Runnable onTextChanged) {
super(new ComboBoxWithWidePopup<>(), null);
this.onTextChanged = onTextChanged;
ComboboxSpeedSearch.installOn(getChildComponent());
getChildComponent().setEditor(comboBoxEditor);
getChildComponent().setEditable(true);
addActionListener(e -> {
val descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
//noinspection UsagesOfObsoleteApi
FileChooser.chooseFile(descriptor, null, null, (file) -> getChildComponent().setSelectedItem(file.toNioPath()));
});
TextFieldUtil.addTextChangeListener(getPathTextField(), ignored -> onTextChanged.run());
}
}

View file

@ -1,56 +0,0 @@
/*
* 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.ide.util.projectwizard;
import com.falsepattern.zigbrains.project.ide.newproject.ZigNewProjectPanel;
import com.falsepattern.zigbrains.project.util.ExperimentUtil;
import com.intellij.ide.util.projectWizard.ModuleWizardStep;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.ui.JBUI;
import javax.swing.JComponent;
import static com.intellij.ui.dsl.builder.BuilderKt.panel;
public class ZigModuleWizardStep extends ModuleWizardStep {
private final ZigNewProjectPanel newProjectPanel = new ZigNewProjectPanel();
@Override
public JComponent getComponent() {
return withBorderIfNeeded(panel((p) -> {
newProjectPanel.attachPanelTo(p);
return null;
}));
}
@Override
public void disposeUIResources() {
Disposer.dispose(newProjectPanel);
}
@Override
public void updateDataModel() {
throw new UnsupportedOperationException("Not yet implemented");
}
private <T extends JComponent> T withBorderIfNeeded(T component) {
if (ExperimentUtil.isNewWizard()) {
component.setBorder(JBUI.Borders.empty(14, 20));
}
return component;
}
}

View file

@ -16,26 +16,16 @@
package com.falsepattern.zigbrains.project.openapi.components; package com.falsepattern.zigbrains.project.openapi.components;
import com.falsepattern.zigbrains.common.WrappingStateComponent;
import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.util.xmlb.XmlSerializerUtil; import com.intellij.util.xmlb.XmlSerializerUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public abstract class AbstractZigProjectSettingsService<T> implements PersistentStateComponent<T> { public abstract class AbstractZigProjectSettingsService<T> extends WrappingStateComponent<T> {
public final transient Project project; public final transient Project project;
private final T state;
public AbstractZigProjectSettingsService(Project project, @NotNull T initialState) { public AbstractZigProjectSettingsService(Project project, @NotNull T initialState) {
super(initialState);
this.project = project; this.project = project;
this.state = initialState;
}
@Override
public @NotNull T getState() {
return state;
}
@Override
public void loadState(@NotNull T state) {
XmlSerializerUtil.copyBean(state, this.state);
} }
} }

View file

@ -20,20 +20,23 @@ import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider; import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider;
import com.intellij.util.io.PathKt; import com.intellij.util.io.PathKt;
import com.intellij.util.xmlb.annotations.Transient; import com.intellij.util.xmlb.annotations.Transient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.nio.file.Path; import java.nio.file.Path;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ZigProjectSettings { public class ZigProjectSettings {
public String toolchainHomeDirectory = null; public String explicitPathToStd;
public String explicitPathToStd = null; public String toolchainHomeDirectory;
public String getExplicitPathToStd() { public ZigProjectSettings(String explicitPathToStd, AbstractZigToolchain toolchain) {
return explicitPathToStd; this(explicitPathToStd, (String)null);
} setToolchain(toolchain);
public void setExplicitPathToStd(String value) {
explicitPathToStd = value;
} }
@Transient @Transient
@ -50,7 +53,7 @@ public class ZigProjectSettings {
toolchainHomeDirectory = null; toolchainHomeDirectory = null;
return; return;
} }
var loc = value.location; var loc = value.getLocation();
if (loc == null) { if (loc == null) {
toolchainHomeDirectory = null; toolchainHomeDirectory = null;
return; return;

View file

@ -16,13 +16,17 @@
package com.falsepattern.zigbrains.project.openapi.components; package com.falsepattern.zigbrains.project.openapi.components;
import com.falsepattern.zigbrains.project.ide.project.ZigProjectSettingsPanel;
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain; import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain;
import com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity;
import com.intellij.openapi.components.Service; import com.intellij.openapi.components.Service;
import com.intellij.openapi.components.State; import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage; import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import lombok.val;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
@Service(Service.Level.PROJECT) @Service(Service.Level.PROJECT)
@ -39,16 +43,9 @@ public final class ZigProjectSettingsService extends AbstractZigProjectSettingsS
return project.getService(ZigProjectSettingsService.class); return project.getService(ZigProjectSettingsService.class);
} }
public @Nullable AbstractZigToolchain getToolchain() { public boolean isModified(ZigProjectSettings otherData) {
return getState().getToolchain(); val myData = getState();
} return !Objects.equals(myData.toolchainHomeDirectory, otherData.toolchainHomeDirectory) ||
!Objects.equals(myData.explicitPathToStd, otherData.explicitPathToStd);
public @Nullable String getExplicitPathToStd() {
return getState().getExplicitPathToStd();
}
public void modify(Consumer<ZigProjectSettings> modifier) {
var state = getState();
modifier.accept(state);
} }
} }

View file

@ -16,7 +16,7 @@
package com.falsepattern.zigbrains.project.openapi.module; package com.falsepattern.zigbrains.project.openapi.module;
import com.falsepattern.zigbrains.project.ide.util.projectwizard.ZigModuleBuilder; import com.falsepattern.zigbrains.project.ide.newproject.ZigModuleBuilder;
import com.falsepattern.zigbrains.zig.Icons; import com.falsepattern.zigbrains.zig.Icons;
import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.module.ModuleType;
import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nls;

View file

@ -1,140 +0,0 @@
/*
* 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.platform;
import com.falsepattern.zigbrains.project.ide.newproject.ZigProjectConfigurationData;
import com.falsepattern.zigbrains.project.ide.project.ZigDefaultTemplate;
import com.falsepattern.zigbrains.project.ide.util.projectwizard.ZigProjectSettingsStep;
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
import com.falsepattern.zigbrains.zig.Icons;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.ide.util.projectWizard.AbstractNewProjectStep;
import com.intellij.ide.util.projectWizard.CustomStepProjectGenerator;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.impl.welcomeScreen.AbstractActionWithPanel;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.platform.ProjectGeneratorPeer;
import com.intellij.util.ResourceUtil;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ZigDirectoryProjectGenerator implements DirectoryProjectGenerator<ZigProjectConfigurationData>,
CustomStepProjectGenerator<ZigProjectConfigurationData> {
@Override
public @NotNull @NlsContexts.Label String getName() {
return "Zig";
}
@Override
public @Nullable Icon getLogo() {
return Icons.ZIG;
}
@Override
public @NotNull ProjectGeneratorPeer<ZigProjectConfigurationData> createPeer() {
return new ZigProjectGeneratorPeer();
}
@Override
public @NotNull ValidationResult validate(@NotNull String baseDirPath) {
return ValidationResult.OK;
}
private static String getResourceString(String path) throws IOException {
byte[] data = ResourceUtil.getResourceAsBytes(path, ZigDirectoryProjectGenerator.class.getClassLoader());
if (data == null)
throw new IOException("Could not find resource " + path + "!");
return new String(data, StandardCharsets.UTF_8);
}
@Override
public void generateProject(@NotNull Project project, @NotNull VirtualFile baseDir, @NotNull ZigProjectConfigurationData data, @NotNull Module module) {
val settings = data.settings();
var svc = ZigProjectSettingsService.getInstance(project);
val toolchain = settings.toolchain();
svc.getState().setToolchain(toolchain);
val template = data.selectedTemplate();
if (template instanceof ZigDefaultTemplate.ZigInitTemplate) {
if (toolchain == null) {
Notifications.Bus.notify(new Notification("ZigBrains.Project",
"Tried to generate project with zig init, but zig toolchain is invalid!",
NotificationType.ERROR));
return;
}
val zig = toolchain.zig();
val resultOpt = zig.callWithArgs(baseDir.toNioPath(), 10000, "init");
if (resultOpt.isEmpty()) {
Notifications.Bus.notify(new Notification("ZigBrains.Project",
"Failed to invoke \"zig init\"!",
NotificationType.ERROR));
return;
}
val result = resultOpt.get();
if (result.getExitCode() != 0) {
Notifications.Bus.notify(new Notification("ZigBrains.Project",
"\"zig init\" failed with exit code " + result.getExitCode() + "! Check the IDE log files!",
NotificationType.ERROR));
System.err.println(result.getStderr());
}
} else {
try {
val projectName = project.getName();
WriteAction.run(() -> {
for (val fileTemplate : template.fileTemplates().entrySet()) {
var fileName = fileTemplate.getKey();
VirtualFile parentDir;
if (fileName.contains("/")) {
val slashIndex = fileName.indexOf('/');
parentDir = baseDir.createChildDirectory(this, fileName.substring(0, slashIndex));
fileName = fileName.substring(slashIndex + 1);
} else {
parentDir = baseDir;
}
val templateDir = fileTemplate.getValue();
val resourceData = getResourceString("project-gen/" + templateDir + "/" + fileName + ".template").replace("@@PROJECT_NAME@@", projectName);
val targetFile = parentDir.createChildData(this, fileName);
VfsUtil.saveText(targetFile, resourceData);
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public AbstractActionWithPanel createStep(DirectoryProjectGenerator<ZigProjectConfigurationData> projectGenerator, AbstractNewProjectStep.AbstractCallback<ZigProjectConfigurationData> callback) {
return new ZigProjectSettingsStep(projectGenerator);
}
}

View file

@ -52,7 +52,7 @@ public abstract class ZigProgramRunnerBase<ProfileState extends ProfileStateBase
if (state == null) if (state == null)
return null; return null;
val toolchain = ZigProjectSettingsService.getInstance(environment.getProject()).getToolchain(); val toolchain = ZigProjectSettingsService.getInstance(environment.getProject()).getState().getToolchain();
if (toolchain == null) { if (toolchain == null) {
return null; return null;
} }

View file

@ -19,6 +19,7 @@ package com.falsepattern.zigbrains.project.toolchain;
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;
import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -26,8 +27,9 @@ import java.nio.file.Path;
import java.util.Objects; import java.util.Objects;
@RequiredArgsConstructor @RequiredArgsConstructor
@Getter
public abstract class AbstractZigToolchain { public abstract class AbstractZigToolchain {
public final Path location; private final Path location;
public static @Nullable AbstractZigToolchain suggest() { public static @Nullable AbstractZigToolchain suggest() {
return suggest(null); return suggest(null);

View file

@ -38,6 +38,6 @@ public class LocalZigToolchain extends AbstractZigToolchain{
@Override @Override
public Path pathToExecutable(String toolName) { public Path pathToExecutable(String toolName) {
return PathUtil.pathToExecutable(location, toolName); return PathUtil.pathToExecutable(getLocation(), toolName);
} }
} }

View file

@ -28,8 +28,9 @@ import java.nio.file.Path;
public class ToolchainZLSConfigProvider implements ZLSConfigProvider { public class ToolchainZLSConfigProvider implements ZLSConfigProvider {
@Override @Override
public void getEnvironment(Project project, ZLSConfig.ZLSConfigBuilder builder) { public void getEnvironment(Project project, ZLSConfig.ZLSConfigBuilder builder) {
val projectSettings = ZigProjectSettingsService.getInstance(project); val svc = ZigProjectSettingsService.getInstance(project);
val toolchain = projectSettings.getToolchain(); val state = svc.getState();
val toolchain = state.getToolchain();
if (toolchain == null) if (toolchain == null)
return; return;
val projectDir = ProjectUtil.guessProjectDir(project); val projectDir = ProjectUtil.guessProjectDir(project);

View file

@ -32,13 +32,13 @@
<runLineMarkerContributor language="Zig" <runLineMarkerContributor language="Zig"
implementationClass="com.falsepattern.zigbrains.project.execution.build.ZigLineMarkerBuild"/> implementationClass="com.falsepattern.zigbrains.project.execution.build.ZigLineMarkerBuild"/>
<directoryProjectGenerator implementation="com.falsepattern.zigbrains.project.platform.ZigDirectoryProjectGenerator"/> <directoryProjectGenerator implementation="com.falsepattern.zigbrains.project.ide.newproject.ZigDirectoryProjectGenerator"/>
<newProjectWizard.languageGenerator implementation="com.falsepattern.zigbrains.project.ide.newproject.ZigNewProjectWizard"/> <newProjectWizard.languageGenerator implementation="com.falsepattern.zigbrains.project.ide.newproject.ZigNewProjectWizard"/>
<moduleBuilder builderClass="com.falsepattern.zigbrains.project.ide.util.projectwizard.ZigModuleBuilder"/> <moduleBuilder builderClass="com.falsepattern.zigbrains.project.ide.util.projectwizard.ZigModuleBuilder"/>
<projectConfigurable parentId="language" <projectConfigurable parentId="language"
instance="com.falsepattern.zigbrains.project.ide.project.ZigProjectConfigurable" instance="com.falsepattern.zigbrains.project.ide.config.ZigConfigurable"
id="com.falsepattern.zigbrains.project.ide.project.ZigProjectConfigurable" id="com.falsepattern.zigbrains.project.ide.config.ZigConfigurable"
displayName="Zig"/> displayName="Zig"/>

View file

@ -0,0 +1,5 @@
zig-cache/
zig-out/
build/
build-*/
docgen_tmp/

View file

@ -17,7 +17,7 @@
package com.falsepattern.zigbrains.zig.ide; package com.falsepattern.zigbrains.zig.ide;
import com.falsepattern.zigbrains.lsp.contributors.LSPFoldingRangeProvider; import com.falsepattern.zigbrains.lsp.contributors.LSPFoldingRangeProvider;
import com.falsepattern.zigbrains.zig.settings.ZLSSettingsState; import com.falsepattern.zigbrains.zig.settings.ZLSProjectSettingsService;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import org.eclipse.lsp4j.FoldingRange; import org.eclipse.lsp4j.FoldingRange;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -42,6 +42,6 @@ public class ZigFoldingRangeProvider extends LSPFoldingRangeProvider {
@Override @Override
protected boolean async(Project project) { protected boolean async(Project project) {
return ZLSSettingsState.getInstance(project).asyncFolding; return ZLSProjectSettingsService.getInstance(project).getState().asyncFolding;
} }
} }

View file

@ -16,11 +16,12 @@
package com.falsepattern.zigbrains.zig.lsp; package com.falsepattern.zigbrains.zig.lsp;
import com.falsepattern.zigbrains.common.util.FileUtil;
import com.falsepattern.zigbrains.common.util.StringUtil; import com.falsepattern.zigbrains.common.util.StringUtil;
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient; import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
import com.falsepattern.zigbrains.lsp.utils.FileUtils; import com.falsepattern.zigbrains.lsp.utils.FileUtils;
import com.falsepattern.zigbrains.zig.environment.ZLSConfigProvider; import com.falsepattern.zigbrains.zig.environment.ZLSConfigProvider;
import com.falsepattern.zigbrains.zig.settings.ZLSSettingsState; import com.falsepattern.zigbrains.zig.settings.ZLSProjectSettingsService;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.intellij.notification.Notification; import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType; import com.intellij.notification.NotificationType;
@ -54,23 +55,23 @@ public class ZLSStartupActivity implements ProjectActivity {
for (var wrapper : wrappers) { for (var wrapper : wrappers) {
if (wrapper.serverDefinition.ext.equals("zig")) { if (wrapper.serverDefinition.ext.equals("zig")) {
wrapper.stop(false); wrapper.stop(false);
wrapper.removeWidget();
IntellijLanguageClient.removeWrapper(wrapper); IntellijLanguageClient.removeWrapper(wrapper);
} }
} }
var settings = ZLSSettingsState.getInstance(project); var svc = ZLSProjectSettingsService.getInstance(project);
var zlsPath = settings.zlsPath; val state = svc.getState();
var zlsPath = state.zlsPath;
if (!validatePath("ZLS Binary", zlsPath, false)) { if (!validatePath("ZLS Binary", zlsPath, false)) {
return; return;
} }
var configPath = settings.zlsConfigPath; var configPath = state.zlsConfigPath;
boolean configOK = true; boolean configOK = true;
if (!"".equals(configPath) && !validatePath("ZLS Config", configPath, false)) { if (!configPath.isEmpty() && !validatePath("ZLS Config", configPath, false)) {
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "Using default config path.", Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "Using default config path.",
NotificationType.INFORMATION)); NotificationType.INFORMATION));
configPath = ""; configPath = null;
} }
if ("".equals(configPath)) { if (configPath == null || configPath.isBlank()) {
blk: blk:
try { try {
val tmpFile = Files.createTempFile("zigbrains-zls-autoconf", ".json"); val tmpFile = Files.createTempFile("zigbrains-zls-autoconf", ".json");
@ -103,16 +104,16 @@ public class ZLSStartupActivity implements ProjectActivity {
cmd.add(configPath); cmd.add(configPath);
} }
// TODO make this properly configurable // TODO make this properly configurable
if (settings.increaseTimeouts) { if (state.increaseTimeouts) {
for (var timeout : IntellijLanguageClient.getTimeouts().keySet()) { for (var timeout : IntellijLanguageClient.getTimeouts().keySet()) {
IntellijLanguageClient.setTimeout(timeout, 15000); IntellijLanguageClient.setTimeout(timeout, 15000);
} }
} }
if (settings.debug) { if (state.debug) {
cmd.add("--enable-debug-log"); cmd.add("--enable-debug-log");
} }
if (settings.messageTrace) { if (state.messageTrace) {
cmd.add("--enable-message-tracing"); cmd.add("--enable-message-tracing");
} }
for (var wrapper : IntellijLanguageClient.getAllServerWrappersFor("zig")) { for (var wrapper : IntellijLanguageClient.getAllServerWrappersFor("zig")) {
@ -133,12 +134,16 @@ public class ZLSStartupActivity implements ProjectActivity {
} }
private static boolean validatePath(String name, String pathTxt, boolean dir) { private static boolean validatePath(String name, String pathTxt, boolean dir) {
if (pathTxt == null || pathTxt.isBlank()) {
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "Missing " + name, "No path was specified", NotificationType.WARNING));
return false;
}
Path path; Path path;
try { try {
path = Path.of(pathTxt); path = Path.of(pathTxt);
} catch (InvalidPathException e) { } catch (InvalidPathException e) {
Notifications.Bus.notify( Notifications.Bus.notify(
new Notification("ZigBrains.ZLS", "No " + name, "Invalid " + name + " path \"" + pathTxt + "\"", new Notification("ZigBrains.ZLS", "No " + name, "Invalid " + name + " at path \"" + pathTxt + "\"",
NotificationType.ERROR)); NotificationType.ERROR));
return false; return false;
} }
@ -152,7 +157,7 @@ public class ZLSStartupActivity implements ProjectActivity {
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name, Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name,
"The " + name + " at \"" + pathTxt + "\" is a " + "The " + name + " at \"" + pathTxt + "\" is a " +
(Files.isDirectory(path) ? "directory" : "file") + (Files.isDirectory(path) ? "directory" : "file") +
" , expected a " + (dir ? "directory" : "file"), ", expected a " + (dir ? "directory" : "file"),
NotificationType.ERROR)); NotificationType.ERROR));
return false; return false;
} }
@ -162,16 +167,15 @@ public class ZLSStartupActivity implements ProjectActivity {
@Nullable @Nullable
@Override @Override
public Object execute(@NotNull Project project, @NotNull Continuation<? super Unit> continuation) { public Object execute(@NotNull Project project, @NotNull Continuation<? super Unit> continuation) {
var settings = ZLSSettingsState.getInstance(project); val svc = ZLSProjectSettingsService.getInstance(project);
var zlsPath = settings.zlsPath; val state = svc.getState();
var zlsPath = state.zlsPath;
if (zlsPath.isEmpty() && !settings.initialAutodetectHasBeenDone) { if (zlsPath == null) {
settings.initialAutodetectHasBeenDone = true; //Project creation
var thePath = ZLSSettingsState.executablePathFinder("zls"); return null;
if (thePath.isPresent()) {
zlsPath = settings.zlsPath = thePath.get();
}
} }
if (zlsPath.isEmpty()) { if (zlsPath.isEmpty()) {
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No ZLS binary", Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No ZLS binary",
"Please configure the path to the zls executable in the Zig language configuration menu!", "Please configure the path to the zls executable in the Zig language configuration menu!",

View file

@ -0,0 +1,65 @@
/*
* 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.zig.settings;
import com.falsepattern.zigbrains.common.WrappingStateComponent;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import lombok.val;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
@Service(Service.Level.PROJECT)
@State(name = "ZLSSettings",
storages = @Storage("zigbrains.xml"))
public final class ZLSProjectSettingsService extends WrappingStateComponent<ZLSSettings> {
public ZLSProjectSettingsService() {
super(new ZLSSettings());
}
public static ZLSProjectSettingsService getInstance(Project project) {
return project.getService(ZLSProjectSettingsService.class);
}
public boolean isModified(ZLSSettings otherData) {
val myData = this.getState();
boolean modified = zlsSettingsModified(otherData);
modified |= myData.asyncFolding != otherData.asyncFolding;
return modified;
}
public boolean zlsSettingsModified(ZLSSettings otherData) {
val myData = this.getState();
boolean modified = !Objects.equals(myData.zlsPath, otherData.zlsPath);
modified |= !Objects.equals(myData.zlsConfigPath, otherData.zlsConfigPath);
modified |= myData.debug != otherData.debug;
modified |= myData.messageTrace != otherData.messageTrace;
modified |= myData.increaseTimeouts != otherData.increaseTimeouts;
modified |= myData.buildOnSave != otherData.buildOnSave;
modified |= !Objects.equals(myData.buildOnSaveStep, otherData.buildOnSaveStep);
modified |= myData.highlightGlobalVarDeclarations != otherData.highlightGlobalVarDeclarations;
modified |= myData.dangerousComptimeExperimentsDoNotEnable != otherData.dangerousComptimeExperimentsDoNotEnable;
return modified;
}
}

View file

@ -0,0 +1,41 @@
/*
* 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.zig.settings;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Data
@AllArgsConstructor
public final class ZLSSettings {
public @Nullable String zlsPath;
public @NotNull String zlsConfigPath;
public boolean increaseTimeouts;
public boolean asyncFolding;
public boolean debug;
public boolean messageTrace;
public boolean buildOnSave;
public @NotNull String buildOnSaveStep;
public boolean highlightGlobalVarDeclarations;
public boolean dangerousComptimeExperimentsDoNotEnable;
public ZLSSettings() {
this(null, "", false, true, false, false, false, "install", false, false);
}
}

View file

@ -1,174 +0,0 @@
/*
* 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.zig.settings;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.ui.TextBrowseFolderListener;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.ui.FormBuilder;
import org.jetbrains.annotations.NotNull;
import javax.swing.JButton;
import javax.swing.JPanel;
public class ZLSSettingsComponent {
private final JPanel myMainPanel;
private final TextFieldWithBrowseButton zlsPathText = new TextFieldWithBrowseButton();
private final TextFieldWithBrowseButton zlsConfigPathText = new TextFieldWithBrowseButton();
private final JBCheckBox asyncFoldingCheckBox = new JBCheckBox();
private final JBCheckBox increaseTimeouts = new JBCheckBox();
private final JBCheckBox buildOnSave = new JBCheckBox();
private final JBTextField buildOnSaveStep = new JBTextField();
private final JBCheckBox highlightGlobalVarDeclarations = new JBCheckBox();
private final JBCheckBox dangerousComptimeExperimentsDoNotEnable = new JBCheckBox();
private final JBCheckBox messageTraceCheckBox = new JBCheckBox();
private final JBCheckBox debugCheckBox = new JBCheckBox();
private final JButton autodetectZls = new JButton("Autodetect");
{
buildOnSave.setToolTipText("Whether to enable build-on-save diagnostics");
buildOnSaveStep.setToolTipText("Select which step should be executed on build-on-save");
highlightGlobalVarDeclarations.setToolTipText("Whether to highlight global var declarations");
dangerousComptimeExperimentsDoNotEnable.setToolTipText("Whether to use the comptime interpreter");
}
public ZLSSettingsComponent() {
zlsPathText.addBrowseFolderListener(
new TextBrowseFolderListener(new FileChooserDescriptor(true, false, false, false, false, false)));
myMainPanel = FormBuilder.createFormBuilder()
.addComponent(new JBLabel("ZLS launch settings"))
.addVerticalGap(10)
.addLabeledComponent(new JBLabel("ZLS path: "), zlsPathText, 1, false)
.addComponent(autodetectZls)
.addLabeledComponent(new JBLabel("ZLS config path (leave empty to use built-in config): "),
zlsConfigPathText, 1, false)
.addLabeledComponent(new JBLabel("Increase timeouts"), increaseTimeouts, 1, false)
.addLabeledComponent(new JBLabel("Asynchronous code folding ranges: "),
asyncFoldingCheckBox, 1, false)
.addSeparator()
.addComponent(new JBLabel("ZLS configuration"))
.addVerticalGap(10)
.addLabeledComponent("Build on save", buildOnSave)
.addLabeledComponent("Build on save step", buildOnSaveStep)
.addLabeledComponent("Highlight global variable declarations", highlightGlobalVarDeclarations)
.addLabeledComponent("Dangerous comptime experiments (do not enable)", dangerousComptimeExperimentsDoNotEnable)
.addSeparator()
.addComponent(new JBLabel(
"Developer settings (only usable when the IDE was launched with " +
"the runIDE gradle task in ZigBrains!)"))
.addVerticalGap(10)
.addLabeledComponent(new JBLabel("ZLS debug log: "), debugCheckBox, 1, false)
.addLabeledComponent(new JBLabel("ZLS message trace: "), messageTraceCheckBox, 1,
false)
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
autodetectZls.addActionListener(e -> {
ZLSSettingsState.executablePathFinder("zls").ifPresent(this::setZLSPath);
});
}
public JPanel getPanel() {
return myMainPanel;
}
@NotNull
public String getZLSPath() {
return zlsPathText.getText();
}
public void setZLSPath(@NotNull String newText) {
zlsPathText.setText(newText);
}
@NotNull
public String getZLSConfigPath() {
return zlsConfigPathText.getText();
}
public void setZLSConfigPath(@NotNull String newText) {
zlsConfigPathText.setText(newText);
}
public boolean getIncreaseTimeouts() {
return increaseTimeouts.isSelected();
}
public void setIncreaseTimeouts(boolean state) {
increaseTimeouts.setSelected(state);
}
public boolean getAsyncFolding() {
return asyncFoldingCheckBox.isSelected();
}
public void setAsyncFolding(boolean state) {
asyncFoldingCheckBox.setSelected(state);
}
public boolean getDebug() {
return debugCheckBox.isSelected();
}
public void setDebug(boolean state) {
debugCheckBox.setSelected(state);
}
public boolean getMessageTrace() {
return messageTraceCheckBox.isSelected();
}
public void setMessageTrace(boolean state) {
messageTraceCheckBox.setSelected(state);
}
public boolean getBuildOnSave() {
return buildOnSave.isSelected();
}
public void setBuildOnSave(boolean state) {
buildOnSave.setSelected(state);
}
public String getBuildOnSaveStep() {
return buildOnSaveStep.getText();
}
public void setBuildOnSaveStep(String value) {
buildOnSaveStep.setText(value);
}
public boolean getHighlightGlobalVarDeclarations() {
return highlightGlobalVarDeclarations.isSelected();
}
public void setHighlightGlobalVarDeclarations(boolean state) {
highlightGlobalVarDeclarations.setSelected(state);
}
public boolean getDangerousComptimeExperimentsDoNotEnable() {
return dangerousComptimeExperimentsDoNotEnable.isSelected();
}
public void setDangerousComptimeExperimentsDoNotEnable(boolean state) {
dangerousComptimeExperimentsDoNotEnable.setSelected(state);
}
}

View file

@ -24,7 +24,7 @@ import lombok.val;
public class ZLSSettingsConfigProvider implements ZLSConfigProvider { public class ZLSSettingsConfigProvider implements ZLSConfigProvider {
@Override @Override
public void getEnvironment(Project project, ZLSConfig.ZLSConfigBuilder builder) { public void getEnvironment(Project project, ZLSConfig.ZLSConfigBuilder builder) {
val state = ZLSSettingsState.getInstance(project); val state = ZLSProjectSettingsService.getInstance(project).getState();
builder.enable_build_on_save(state.buildOnSave); builder.enable_build_on_save(state.buildOnSave);
builder.build_on_save_step(state.buildOnSaveStep); builder.build_on_save_step(state.buildOnSaveStep);
builder.highlight_global_var_declarations(state.highlightGlobalVarDeclarations); builder.highlight_global_var_declarations(state.highlightGlobalVarDeclarations);

View file

@ -16,16 +16,15 @@
package com.falsepattern.zigbrains.zig.settings; package com.falsepattern.zigbrains.zig.settings;
import com.falsepattern.zigbrains.common.SubConfigurable;
import com.falsepattern.zigbrains.common.util.dsl.JavaPanel;
import com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity; import com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.JComponent; public class ZLSSettingsConfigurable implements SubConfigurable {
private ZLSSettingsPanel appSettingsComponent;
public class ZLSSettingsConfigurable implements Configurable {
private ZLSSettingsComponent appSettingsComponent;
private final Project project; private final Project project;
@ -34,52 +33,24 @@ public class ZLSSettingsConfigurable implements Configurable {
} }
@Override @Override
public String getDisplayName() { public void createComponent(JavaPanel panel) {
return "Zig"; appSettingsComponent = new ZLSSettingsPanel();
} appSettingsComponent.attachPanelTo(panel);
@Override
public @Nullable JComponent createComponent() {
appSettingsComponent = new ZLSSettingsComponent();
return appSettingsComponent.getPanel();
} }
@Override @Override
public boolean isModified() { public boolean isModified() {
var settings = ZLSSettingsState.getInstance(project); var settings = ZLSProjectSettingsService.getInstance(project);
boolean modified = zlsSettingsModified(settings); val data = appSettingsComponent.getData();
modified |= settings.asyncFolding != appSettingsComponent.getAsyncFolding(); return settings.isModified(data);
return modified;
}
private boolean zlsSettingsModified(ZLSSettingsState settings) {
boolean modified = !settings.zlsPath.equals(appSettingsComponent.getZLSPath());
modified |= !settings.zlsConfigPath.equals(appSettingsComponent.getZLSConfigPath());
modified |= settings.debug != appSettingsComponent.getDebug();
modified |= settings.messageTrace != appSettingsComponent.getMessageTrace();
modified |= settings.increaseTimeouts != appSettingsComponent.getIncreaseTimeouts();
modified |= settings.buildOnSave != appSettingsComponent.getBuildOnSave();
modified |= !settings.buildOnSaveStep.equals(appSettingsComponent.getBuildOnSaveStep());
modified |= settings.highlightGlobalVarDeclarations != appSettingsComponent.getHighlightGlobalVarDeclarations();
modified |= settings.dangerousComptimeExperimentsDoNotEnable != appSettingsComponent.getDangerousComptimeExperimentsDoNotEnable();
return modified;
} }
@Override @Override
public void apply() { public void apply() {
var settings = ZLSSettingsState.getInstance(project); var settings = ZLSProjectSettingsService.getInstance(project);
boolean reloadZLS = zlsSettingsModified(settings); val data = appSettingsComponent.getData();
settings.zlsPath = appSettingsComponent.getZLSPath(); boolean reloadZLS = settings.zlsSettingsModified(data);
settings.zlsConfigPath = appSettingsComponent.getZLSConfigPath(); settings.loadState(data);
settings.asyncFolding = appSettingsComponent.getAsyncFolding();
settings.debug = appSettingsComponent.getDebug();
settings.messageTrace = appSettingsComponent.getMessageTrace();
settings.increaseTimeouts = appSettingsComponent.getIncreaseTimeouts();
settings.buildOnSave = appSettingsComponent.getBuildOnSave();
settings.buildOnSaveStep = appSettingsComponent.getBuildOnSaveStep();
settings.highlightGlobalVarDeclarations = appSettingsComponent.getHighlightGlobalVarDeclarations();
settings.dangerousComptimeExperimentsDoNotEnable = appSettingsComponent.getDangerousComptimeExperimentsDoNotEnable();
if (reloadZLS) { if (reloadZLS) {
ZLSStartupActivity.initZLS(project); ZLSStartupActivity.initZLS(project);
} }
@ -87,19 +58,8 @@ public class ZLSSettingsConfigurable implements Configurable {
@Override @Override
public void reset() { public void reset() {
var settings = ZLSSettingsState.getInstance(project); var settings = ZLSProjectSettingsService.getInstance(project);
appSettingsComponent.setZLSPath(settings.zlsPath); appSettingsComponent.setData(settings.getState());
appSettingsComponent.setZLSConfigPath(settings.zlsConfigPath);
appSettingsComponent.setDebug(settings.debug);
appSettingsComponent.setAsyncFolding(settings.asyncFolding);
appSettingsComponent.setMessageTrace(settings.messageTrace);
appSettingsComponent.setIncreaseTimeouts(settings.increaseTimeouts);
appSettingsComponent.setAsyncFolding(settings.asyncFolding);
appSettingsComponent.setBuildOnSave(settings.buildOnSave);
appSettingsComponent.setBuildOnSaveStep(settings.buildOnSaveStep);
appSettingsComponent.setHighlightGlobalVarDeclarations(settings.highlightGlobalVarDeclarations);
appSettingsComponent.setDangerousComptimeExperimentsDoNotEnable(settings.dangerousComptimeExperimentsDoNotEnable);
} }
@Override @Override

View file

@ -0,0 +1,133 @@
/*
* 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.zig.settings;
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.ProjectManager;
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 java.awt.event.ActionEvent;
import java.nio.file.Path;
import java.util.Optional;
import static com.falsepattern.zigbrains.common.util.KtUtil.$f;
public class ZLSSettingsPanel implements Disposable {
private final TextFieldWithBrowseButton zlsPath = TextFieldUtil.pathToFileTextField(this,
"Path to the ZLS Binary",
() -> {});
private final TextFieldWithBrowseButton zlsConfigPath = TextFieldUtil.pathToFileTextField(this,
"Path to the Custom ZLS Config File (Optional)",
() -> {});
private final JBCheckBox asyncFolding = new JBCheckBox();
private final JBCheckBox increaseTimeouts = new JBCheckBox();
private final JBCheckBox buildOnSave = new JBCheckBox();
private final JBTextField buildOnSaveStep = new ExtendableTextField();
private final JBCheckBox highlightGlobalVarDeclarations = new JBCheckBox();
private final JBCheckBox dangerousComptimeExperimentsDoNotEnable = new JBCheckBox();
private final JBCheckBox messageTrace = new JBCheckBox();
private final JBCheckBox debug = new JBCheckBox();
{
buildOnSave.setToolTipText("Whether to enable build-on-save diagnostics");
buildOnSaveStep.setToolTipText("Select which step should be executed on build-on-save");
highlightGlobalVarDeclarations.setToolTipText("Whether to highlight global var declarations");
dangerousComptimeExperimentsDoNotEnable.setToolTipText("Whether to use the comptime interpreter");
}
private void autodetect(ActionEvent e) {
autodetect();
}
public void autodetect() {
FileUtil.findExecutableOnPATH("zls").map(Path::toString).ifPresent(zlsPath::setText);
}
public ZLSSettingsPanel() {
zlsPath.addBrowseFolderListener(new TextBrowseFolderListener(new FileChooserDescriptor(true, false, false, false, false, false)));
}
public void attachPanelTo(JavaPanel panel) {
Optional.ofNullable(ZLSProjectSettingsService.getInstance(ProjectManager.getInstance().getDefaultProject()))
.map(ZLSProjectSettingsService::getState)
.ifPresent(this::setData);
panel.group("ZLS launch settings", p -> {
p.row("Executable path", r -> {
r.cell(zlsPath).resizableColumn().align(AlignX.FILL);
r.button("Autodetect", $f(this::autodetect));
});
p.cell("Config path (leave empty to use built-in config)", zlsConfigPath, AlignX.FILL);
p.cell("Increase timeouts", increaseTimeouts);
p.cell("Asynchronous code folding ranges", asyncFolding);
});
panel.group("ZLS Configuration", p -> {
p.cell("Build on save", buildOnSave);
p.row("Build on save step", r -> {
r.cell(buildOnSaveStep).resizableColumn().align(AlignX.FILL);
});
p.cell("Highlight global variable declarations", highlightGlobalVarDeclarations);
p.cell("Dangerous comptime experiments (do not enable)", dangerousComptimeExperimentsDoNotEnable);
});
panel.group("ZLS Developer settings", p -> {
p.cell("Debug log", debug);
p.cell("Message trace", messageTrace);
});
}
public ZLSSettings getData() {
return new ZLSSettings(zlsPath.getText(),
zlsConfigPath.getText(),
increaseTimeouts.isSelected(),
asyncFolding.isSelected(),
debug.isSelected(),
messageTrace.isSelected(),
buildOnSave.isSelected(),
buildOnSaveStep.getText(),
highlightGlobalVarDeclarations.isSelected(),
dangerousComptimeExperimentsDoNotEnable.isSelected());
}
public void setData(ZLSSettings value) {
zlsPath.setText(value.zlsPath == null ? "" : value.zlsPath);
zlsConfigPath.setText(value.zlsConfigPath);
increaseTimeouts.setSelected(value.increaseTimeouts);
asyncFolding.setSelected(value.asyncFolding);
debug.setSelected(value.debug);
messageTrace.setSelected(value.messageTrace);
buildOnSave.setSelected(value.buildOnSave);
buildOnSaveStep.setText(value.buildOnSaveStep);
highlightGlobalVarDeclarations.setSelected(value.highlightGlobalVarDeclarations);
dangerousComptimeExperimentsDoNotEnable.setSelected(value.dangerousComptimeExperimentsDoNotEnable);
}
@Override
public void dispose() {
zlsPath.dispose();
zlsConfigPath.dispose();
}
}

View file

@ -1,85 +0,0 @@
/*
* 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.zig.settings;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.xmlb.XmlSerializerUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
@Service(Service.Level.PROJECT)
@State(name = "ZLSSettings",
storages = @Storage("zigbrains.xml"))
public final class ZLSSettingsState implements PersistentStateComponent<ZLSSettingsState> {
public String zlsPath = "";
public String zlsConfigPath = "";
public boolean initialAutodetectHasBeenDone = false;
public boolean increaseTimeouts = false;
public boolean asyncFolding = true;
public boolean debug = false;
public boolean messageTrace = false;
public boolean buildOnSave = false;
public String buildOnSaveStep = "install";
public boolean dangerousComptimeExperimentsDoNotEnable = false;
public boolean highlightGlobalVarDeclarations = false;
public static Optional<String> executablePathFinder(String exe) {
var exeName = SystemInfo.isWindows ? exe + ".exe" : exe;
var PATH = System.getenv("PATH").split(File.pathSeparator);
for (var dir: PATH) {
var path = Path.of(dir);
try {
path = path.toAbsolutePath();
} catch (Exception ignored) {
continue;
}
if (!Files.exists(path) || !Files.isDirectory(path)) {
continue;
}
var exePath = path.resolve(exeName).toAbsolutePath();
if (!Files.isRegularFile(exePath) || !Files.isExecutable(exePath)) {
continue;
}
return Optional.of(exePath.toString());
}
return Optional.empty();
}
public static ZLSSettingsState getInstance(Project project) {
return project.getService(ZLSSettingsState.class);
}
@Override
public ZLSSettingsState getState() {
return this;
}
@Override
public void loadState(@NotNull ZLSSettingsState state) {
XmlSerializerUtil.copyBean(state, this);
}
}

View file

@ -98,11 +98,6 @@
<lang.formatter language="Zig" implementationClass="com.falsepattern.zigbrains.zig.formatter.ZigFormattingModelBuilder"/> <lang.formatter language="Zig" implementationClass="com.falsepattern.zigbrains.zig.formatter.ZigFormattingModelBuilder"/>
<projectConfigurable parentId="language"
instance="com.falsepattern.zigbrains.zig.settings.ZLSSettingsConfigurable"
id="com.falsepattern.zigbrains.zig.settings.ZLSSettingsConfigurable"
displayName="ZLS"/>
<postStartupActivity implementation="com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity"/> <postStartupActivity implementation="com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity"/>
<notificationGroup displayType="BALLOON" <notificationGroup displayType="BALLOON"
bundle="zigbrains.zig.Bundle" bundle="zigbrains.zig.Bundle"