feat(config): Reimplemented config backend, much more reliable and extensible now.

This commit is contained in:
FalsePattern 2023-08-18 14:36:41 +02:00 committed by FalsePattern
parent 2d1620f86d
commit bc408b1d50
Signed by: falsepattern
GPG key ID: FDF7126A9E124447
18 changed files with 583 additions and 230 deletions

View file

@ -39,6 +39,10 @@ Changelog structure reference:
- NullPointerException in folding range provider when closing editors quickly
#### Config
- Changes to the ZLS configuration no longer require an IDE restart
## [0.6.0]
### Added

View file

@ -27,6 +27,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
@ -46,7 +47,6 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
public class LSPFoldingRangeProvider extends CustomFoldingBuilder {
private static final Key<Boolean> ASYNC_FOLDING_KEY = new Key<>("ASYNC_FOLDING");
@ -76,7 +76,7 @@ public class LSPFoldingRangeProvider extends CustomFoldingBuilder {
return;
}
var async = async();
var async = async(root.getProject());
if (!async) {
doBuildLanguageFoldRegions((start, end, collapsedText) -> {
if (collapsedText != null) {
@ -186,7 +186,7 @@ public class LSPFoldingRangeProvider extends CustomFoldingBuilder {
}
}
protected boolean async() {
protected boolean async(Project project) {
return true;
}

View file

@ -0,0 +1,71 @@
/*
* Copyright 2023 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.settings;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.util.NlsContexts;
import org.jetbrains.annotations.Nullable;
import javax.swing.JComponent;
import java.lang.invoke.MethodHandles;
import java.util.function.Supplier;
public abstract class AbstractConfigurable<T> implements Configurable {
private final String displayName;
private final Supplier<ConfigurableGui<T>> guiSupplier;
protected ConfigurableGui<T> configurableGui;
public AbstractConfigurable(MethodHandles.Lookup lookup, String displayName, Class<T> holderClass)
throws IllegalAccessException {
this.displayName = displayName;
guiSupplier = ConfigurableGui.create(lookup, holderClass);
}
protected abstract T getHolder();
@Override
public @NlsContexts.ConfigurableName String getDisplayName() {
return displayName;
}
@Override
public @Nullable JComponent createComponent() {
configurableGui = guiSupplier.get();
return configurableGui.getPanel();
}
@Override
public boolean isModified() {
return configurableGui.modified(getHolder());
}
@Override
public void apply() throws ConfigurationException {
configurableGui.guiToConfig(getHolder());
}
@Override
public void reset() {
configurableGui.configToGui(getHolder());
}
@Override
public void disposeUIResources() {
configurableGui = null;
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright 2023 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.settings;
import com.intellij.ui.TextAccessor;
import com.intellij.ui.components.JBCheckBox;
public interface ConfigurableAccessor<T> {
T get();
void set(Object value);
record TextFieldAccessor(TextAccessor accessor) implements ConfigurableAccessor<String> {
@Override
public String get() {
return accessor.getText();
}
@Override
public void set(Object value) {
accessor.setText((String)value);
}
}
record CheckBoxAccessor(JBCheckBox checkBox) implements ConfigurableAccessor<Boolean> {
@Override
public Boolean get() {
return checkBox.isSelected() == Boolean.TRUE;
}
@Override
public void set(Object value) {
checkBox.setSelected(value == Boolean.TRUE);
}
}
}

View file

@ -0,0 +1,186 @@
/*
* Copyright 2023 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.settings;
import com.falsepattern.zigbrains.settings.annotations.Category;
import com.falsepattern.zigbrains.settings.annotations.Config;
import com.falsepattern.zigbrains.settings.annotations.FilePath;
import com.falsepattern.zigbrains.settings.annotations.Label;
import com.falsepattern.zigbrains.settings.annotations.Separator;
import com.falsepattern.zigbrains.settings.annotations.VerticalGap;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.ui.TextBrowseFolderListener;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.ui.TextAccessor;
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 javax.swing.JComponent;
import javax.swing.JPanel;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class ConfigurableGui<T> {
private final JPanel thePanel;
private final Map<String, ConfigurableAccessor<?>> guiProps;
private final Map<String, VarHandle> configProps;
private final Set<String> props;
private ConfigurableGui(JPanel thePanel,
Map<String, ConfigurableAccessor<?>> guiProps,
Map<String, VarHandle> configProps,
Set<String> props) {
this.thePanel = thePanel;
this.guiProps = guiProps;
this.configProps = configProps;
this.props = props;
}
public JPanel getPanel() {
return thePanel;
}
public boolean modified(T holder) {
return modified(holder, false, null);
}
public boolean modified(T holder, boolean exclude, Set<String> mask) {
for (var prop: props) {
if (mask != null && mask.contains(prop) == exclude) {
continue;
}
if (!Objects.equals(guiProps.get(prop).get(), configProps.get(prop).get(holder))) {
return true;
}
}
return false;
}
public void guiToConfig(T holder) {
for (var prop: props) {
configProps.get(prop).set(holder, guiProps.get(prop).get());
}
}
public void configToGui(T holder) {
for (var prop: props) {
guiProps.get(prop).set(configProps.get(prop).get(holder));
}
}
public static <T> Supplier<ConfigurableGui<T>> create(MethodHandles.Lookup lookup, Class<T> dataContainer)
throws IllegalAccessException {
var fields = Arrays.stream(dataContainer.getFields())
.filter(field -> field.isAnnotationPresent(Config.class))
.filter(field -> field.isAnnotationPresent(Label.class))
.sorted(Comparator.comparingInt(field -> field.getAnnotation(Config.class).value()))
.toList();
var configProps = new HashMap<String, VarHandle>();
var props = new HashSet<String>();
var steps = new ArrayList<BiConsumer<HashMap<String, ConfigurableAccessor<?>>, FormBuilder>>();
for (var field: fields) {
var propName = field.getName();
var fieldType = field.getType();
var label = field.getAnnotation(Label.class).value();
if (field.isAnnotationPresent(VerticalGap.class)) {
var gap = field.getAnnotation(VerticalGap.class).value();
steps.add((gp, fb) -> fb.addVerticalGap(gap));
}
if (field.isAnnotationPresent(Separator.class)) {
steps.add((gp, fb) -> fb.addSeparator());
}
if (field.isAnnotationPresent(Category.class)) {
var cat = field.getAnnotation(Category.class).value();
steps.add((gp, fb) -> fb.addSeparator()
.addComponent(new JBLabel(cat))
.addVerticalGap(10));
}
Function<HashMap<String, ConfigurableAccessor<?>>, JComponent> component;
if (fieldType.equals(String.class)) {
var isFilePath = field.isAnnotationPresent(FilePath.class);
if (isFilePath) {
var filePathSpec = field.getAnnotation(FilePath.class);
var descriptor = new FileChooserDescriptor(filePathSpec.files(),
filePathSpec.folders(),
filePathSpec.jars(),
filePathSpec.jarsAsFiles(),
filePathSpec.jarContents(),
filePathSpec.multiple());
component = (guiProps) -> {
var theComponent = new TextFieldWithBrowseButton();
theComponent.addBrowseFolderListener(new TextBrowseFolderListener(descriptor));
guiProps.put(propName, new ConfigurableAccessor.TextFieldAccessor(theComponent));
return theComponent;
};
} else {
component = (guiProps) -> {
var theComponent = new JBTextField();
guiProps.put(propName, new ConfigurableAccessor.TextFieldAccessor(theComponent));
return theComponent;
};
}
} else if (fieldType.equals(boolean.class)) {
component = (guiProps) -> {
var theComponent = new JBCheckBox();
guiProps.put(propName, new ConfigurableAccessor.CheckBoxAccessor(theComponent));
return theComponent;
};
} else {
component = null;
}
if (component != null) {
props.add(propName);
configProps.put(propName, lookup.unreflectVarHandle(field));
}
steps.add((gp, fb) -> {
var comp = component == null ? null : component.apply(gp);
if (comp != null) {
fb.addLabeledComponent(new JBLabel(label), comp, 1, false);
} else {
fb.addComponent(new JBLabel(label));
}
});
}
steps.add((gp, fb) -> fb.addComponentFillVertically(new JPanel(), 0));
return () -> {
var gp = new HashMap<String, ConfigurableAccessor<?>>();
var fb = FormBuilder.createFormBuilder();
for (var step: steps) {
step.accept(gp, fb);
}
for (var prop: props) {
}
return new ConfigurableGui<>(fb.getPanel(), gp, configProps, props);
};
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright 2023 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.settings.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Category {
String value();
}

View file

@ -0,0 +1,25 @@
/*
* Copyright 2023 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.settings.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Config {
int value();
}

View file

@ -0,0 +1,30 @@
/*
* Copyright 2023 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.settings.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface FilePath {
boolean files() default false;
boolean folders() default false;
boolean jars() default false;
boolean jarsAsFiles() default false;
boolean jarContents() default false;
boolean multiple() default false;
}

View file

@ -0,0 +1,25 @@
/*
* Copyright 2023 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.settings.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Label {
String value();
}

View file

@ -0,0 +1,24 @@
/*
* Copyright 2023 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.settings.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Separator {
}

View file

@ -0,0 +1,25 @@
/*
* Copyright 2023 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.settings.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface VerticalGap {
int value();
}

View file

@ -16,7 +16,8 @@
package com.falsepattern.zigbrains.zig.ide;
import com.falsepattern.zigbrains.zig.settings.AppSettingsState;
import com.falsepattern.zigbrains.zig.settings.ZLSSettingsState;
import com.intellij.openapi.project.Project;
import org.eclipse.lsp4j.FoldingRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -40,7 +41,7 @@ public class ZigFoldingRangeProvider extends LSPFoldingRangeProvider {
}
@Override
protected boolean async() {
return AppSettingsState.getInstance().asyncFolding;
protected boolean async(Project project) {
return ZLSSettingsState.getInstance(project).asyncFolding;
}
}

View file

@ -16,7 +16,8 @@
package com.falsepattern.zigbrains.zig.lsp;
import com.falsepattern.zigbrains.zig.settings.AppSettingsState;
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
import com.falsepattern.zigbrains.zig.settings.ZLSSettingsState;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
@ -38,11 +39,19 @@ import java.util.concurrent.locks.ReentrantLock;
public class ZLSStartupActivity implements ProjectActivity {
private static final ReentrantLock lock = new ReentrantLock();
public static void initZLS() {
public static void initZLS(Project project) {
ApplicationManager.getApplication().executeOnPooledThread(() -> {
lock.lock();
try {
var settings = AppSettingsState.getInstance();
var wrappers = IntellijLanguageClient.getAllServerWrappersFor(FileUtils.projectToUri(project));
for (var wrapper : wrappers) {
if (wrapper.serverDefinition.ext.equals("zig")) {
wrapper.stop(false);
wrapper.stop(true);
IntellijLanguageClient.removeWrapper(wrapper);
}
}
var settings = ZLSSettingsState.getInstance(project);
var zlsPath = settings.zlsPath;
if (!validatePath("ZLS Binary", zlsPath, false)) {
return;
@ -90,7 +99,7 @@ public class ZLSStartupActivity implements ProjectActivity {
}
}
}
IntellijLanguageClient.addServerDefinition(new ZLSServerDefinition(cmd.toArray(String[]::new)));
IntellijLanguageClient.addServerDefinition(new ZLSServerDefinition(cmd.toArray(String[]::new)), project);
} finally {
lock.unlock();
}
@ -127,14 +136,14 @@ public class ZLSStartupActivity implements ProjectActivity {
@Nullable
@Override
public Object execute(@NotNull Project project, @NotNull Continuation<? super Unit> continuation) {
var path = AppSettingsState.getInstance().zlsPath;
var path = ZLSSettingsState.getInstance(project).zlsPath;
if ("".equals(path)) {
Notifications.Bus.notify(new Notification("ZigBrains.Nag", "No ZLS binary",
"Please configure the path to the zls executable in the Zig language configuration menu!",
NotificationType.INFORMATION));
return null;
}
initZLS();
initZLS(project);
return null;
}
}

View file

@ -1,115 +0,0 @@
/*
* Copyright 2023 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.util.ui.FormBuilder;
import org.jetbrains.annotations.NotNull;
import javax.swing.JPanel;
public class AppSettingsComponent {
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 debugCheckBox = new JBCheckBox();
private final JBCheckBox messageTraceCheckBox = new JBCheckBox();
private final JBCheckBox increaseTimeouts = new JBCheckBox();
public AppSettingsComponent() {
zlsPathText.addBrowseFolderListener(
new TextBrowseFolderListener(new FileChooserDescriptor(true, false, false, false, false, false)));
myMainPanel = FormBuilder.createFormBuilder()
.addComponent(new JBLabel("Regular settings"))
.addVerticalGap(10)
.addLabeledComponent(new JBLabel("ZLS path: "), zlsPathText, 1, false)
.addLabeledComponent(new JBLabel("ZLS config path (leave empty to use default): "),
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(
"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();
}
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);
}
}

View file

@ -1,87 +0,0 @@
/*
* Copyright 2023 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.zig.lsp.ZLSStartupActivity;
import com.intellij.openapi.options.Configurable;
import org.jetbrains.annotations.Nullable;
import javax.swing.JComponent;
public class AppSettingsConfigurable implements Configurable {
private AppSettingsComponent appSettingsComponent;
@Override
public String getDisplayName() {
return "Zig";
}
@Override
public @Nullable JComponent createComponent() {
appSettingsComponent = new AppSettingsComponent();
return appSettingsComponent.getPanel();
}
@Override
public boolean isModified() {
var settings = AppSettingsState.getInstance();
boolean modified = zlsSettingsModified(settings);
modified |= settings.asyncFolding != appSettingsComponent.getAsyncFolding();
return modified;
}
private boolean zlsSettingsModified(AppSettingsState 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();
return modified;
}
@Override
public void apply() {
var settings = AppSettingsState.getInstance();
boolean reloadZLS = zlsSettingsModified(settings);
settings.zlsPath = appSettingsComponent.getZLSPath();
settings.zlsConfigPath = appSettingsComponent.getZLSConfigPath();
settings.asyncFolding = appSettingsComponent.getAsyncFolding();
settings.debug = appSettingsComponent.getDebug();
settings.messageTrace = appSettingsComponent.getMessageTrace();
settings.increaseTimeouts = appSettingsComponent.getIncreaseTimeouts();
if (reloadZLS) {
ZLSStartupActivity.initZLS();
}
}
@Override
public void reset() {
var settings = AppSettingsState.getInstance();
appSettingsComponent.setZLSPath(settings.zlsPath);
appSettingsComponent.setZLSConfigPath(settings.zlsConfigPath);
appSettingsComponent.setDebug(settings.debug);
appSettingsComponent.setAsyncFolding(settings.asyncFolding);
appSettingsComponent.setMessageTrace(settings.messageTrace);
appSettingsComponent.setIncreaseTimeouts(settings.increaseTimeouts);
appSettingsComponent.setAsyncFolding(settings.asyncFolding);
}
@Override
public void disposeUIResources() {
appSettingsComponent = null;
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright 2023 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.settings.AbstractConfigurable;
import com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import java.lang.invoke.MethodHandles;
import java.util.Set;
public class ZLSSettingsConfigurable extends AbstractConfigurable<ZLSSettingsState> {
private static final MethodHandles.Lookup LOOKUP = MethodHandles.publicLookup();
private static final Set<String> RELOAD_CONFIGS = Set.of("zlsPath", "zlsConfigPath", "increaseTimeouts", "debug", "messageTrace");
private final Project project;
public ZLSSettingsConfigurable(@NotNull Project project) throws IllegalAccessException {
super(LOOKUP, "Zig", ZLSSettingsState.class);
this.project = project;
}
@Override
public void apply() throws ConfigurationException {
boolean reloadZLS = zlsSettingsModified();
super.apply();
if (reloadZLS) {
ZLSStartupActivity.initZLS(project);
}
}
private boolean zlsSettingsModified() {
if (configurableGui != null) {
return configurableGui.modified(getHolder(), false, RELOAD_CONFIGS);
}
return false;
}
@Override
protected ZLSSettingsState getHolder() {
return ZLSSettingsState.getInstance(project);
}
}

View file

@ -16,36 +16,56 @@
package com.falsepattern.zigbrains.zig.settings;
import com.intellij.openapi.application.ApplicationManager;
import com.falsepattern.zigbrains.settings.annotations.Category;
import com.falsepattern.zigbrains.settings.annotations.Config;
import com.falsepattern.zigbrains.settings.annotations.FilePath;
import com.falsepattern.zigbrains.settings.annotations.Label;
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.util.xmlb.XmlSerializerUtil;
import org.jetbrains.annotations.NotNull;
@Service(Service.Level.APP)
@State(name = "com.falsepattern.zigbrains.zig.settings.AppSettingsState",
storages = @Storage("ZigBrainsSettings.xml"))
public final class AppSettingsState implements PersistentStateComponent<AppSettingsState> {
@Service(Service.Level.PROJECT)
@State(name = "ZLSSettings",
storages = @Storage("zigbrains.xml"))
public final class ZLSSettingsState implements PersistentStateComponent<ZLSSettingsState> {
@Category("Regular settings")
@Config(0)
@Label("ZLS path: ")
@FilePath(files = true)
public String zlsPath = "";
@Config(10)
@Label("ZLS config path (leave empty to use default): ")
@FilePath(files = true)
public String zlsConfigPath = "";
public boolean debug = false;
public boolean asyncFolding = true;
public boolean messageTrace = false;
@Config(20)
@Label("Increase timeouts")
public boolean increaseTimeouts = false;
@Config(30)
@Label("Asynchronous code folding ranges: ")
public boolean asyncFolding = true;
@Category("Developer settings (only usable when the IDE was launched with the runIDE gradle task in ZigBrains!)")
@Config(40)
@Label("ZLS debug log: ")
public boolean debug = false;
@Config(50)
@Label("ZLS message trace: ")
public boolean messageTrace = false;
public static AppSettingsState getInstance() {
return ApplicationManager.getApplication().getService(AppSettingsState.class);
public static ZLSSettingsState getInstance(Project project) {
return project.getService(ZLSSettingsState.class);
}
@Override
public AppSettingsState getState() {
public ZLSSettingsState getState() {
return this;
}
@Override
public void loadState(@NotNull AppSettingsState state) {
public void loadState(@NotNull ZLSSettingsState state) {
XmlSerializerUtil.copyBean(state, this);
}
}

View file

@ -86,9 +86,9 @@
<lang.braceMatcher language="Zig"
implementationClass="com.falsepattern.zigbrains.zig.pairing.ZigBraceMatcher"/>
<applicationConfigurable parentId="language"
instance="com.falsepattern.zigbrains.zig.settings.AppSettingsConfigurable"
id="com.falsepattern.zigbrains.zig.settings.AppSettingsConfigurable"
<projectConfigurable parentId="language"
instance="com.falsepattern.zigbrains.zig.settings.ZLSSettingsConfigurable"
id="com.falsepattern.zigbrains.zig.settings.ZLSSettingsConfigurable"
displayName="Zig"/>
<postStartupActivity implementation="com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity"/>