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 - NullPointerException in folding range provider when closing editors quickly
#### Config
- Changes to the ZLS configuration no longer require an IDE restart
## [0.6.0] ## [0.6.0]
### Added ### Added

View file

@ -27,6 +27,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
@ -46,7 +47,6 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
public class LSPFoldingRangeProvider extends CustomFoldingBuilder { public class LSPFoldingRangeProvider extends CustomFoldingBuilder {
private static final Key<Boolean> ASYNC_FOLDING_KEY = new Key<>("ASYNC_FOLDING"); private static final Key<Boolean> ASYNC_FOLDING_KEY = new Key<>("ASYNC_FOLDING");
@ -76,7 +76,7 @@ public class LSPFoldingRangeProvider extends CustomFoldingBuilder {
return; return;
} }
var async = async(); var async = async(root.getProject());
if (!async) { if (!async) {
doBuildLanguageFoldRegions((start, end, collapsedText) -> { doBuildLanguageFoldRegions((start, end, collapsedText) -> {
if (collapsedText != null) { if (collapsedText != null) {
@ -186,7 +186,7 @@ public class LSPFoldingRangeProvider extends CustomFoldingBuilder {
} }
} }
protected boolean async() { protected boolean async(Project project) {
return true; 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; 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.eclipse.lsp4j.FoldingRange;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -40,7 +41,7 @@ public class ZigFoldingRangeProvider extends LSPFoldingRangeProvider {
} }
@Override @Override
protected boolean async() { protected boolean async(Project project) {
return AppSettingsState.getInstance().asyncFolding; return ZLSSettingsState.getInstance(project).asyncFolding;
} }
} }

View file

@ -16,7 +16,8 @@
package com.falsepattern.zigbrains.zig.lsp; 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.Notification;
import com.intellij.notification.NotificationType; import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications; import com.intellij.notification.Notifications;
@ -38,11 +39,19 @@ import java.util.concurrent.locks.ReentrantLock;
public class ZLSStartupActivity implements ProjectActivity { public class ZLSStartupActivity implements ProjectActivity {
private static final ReentrantLock lock = new ReentrantLock(); private static final ReentrantLock lock = new ReentrantLock();
public static void initZLS() { public static void initZLS(Project project) {
ApplicationManager.getApplication().executeOnPooledThread(() -> { ApplicationManager.getApplication().executeOnPooledThread(() -> {
lock.lock(); lock.lock();
try { 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; var zlsPath = settings.zlsPath;
if (!validatePath("ZLS Binary", zlsPath, false)) { if (!validatePath("ZLS Binary", zlsPath, false)) {
return; 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 { } finally {
lock.unlock(); lock.unlock();
} }
@ -127,14 +136,14 @@ 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 path = AppSettingsState.getInstance().zlsPath; var path = ZLSSettingsState.getInstance(project).zlsPath;
if ("".equals(path)) { if ("".equals(path)) {
Notifications.Bus.notify(new Notification("ZigBrains.Nag", "No ZLS binary", Notifications.Bus.notify(new Notification("ZigBrains.Nag", "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!",
NotificationType.INFORMATION)); NotificationType.INFORMATION));
return null; return null;
} }
initZLS(); initZLS(project);
return null; 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; 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.PersistentStateComponent;
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.util.xmlb.XmlSerializerUtil; import com.intellij.util.xmlb.XmlSerializerUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@Service(Service.Level.APP) @Service(Service.Level.PROJECT)
@State(name = "com.falsepattern.zigbrains.zig.settings.AppSettingsState", @State(name = "ZLSSettings",
storages = @Storage("ZigBrainsSettings.xml")) storages = @Storage("zigbrains.xml"))
public final class AppSettingsState implements PersistentStateComponent<AppSettingsState> { public final class ZLSSettingsState implements PersistentStateComponent<ZLSSettingsState> {
@Category("Regular settings")
@Config(0)
@Label("ZLS path: ")
@FilePath(files = true)
public String zlsPath = ""; public String zlsPath = "";
@Config(10)
@Label("ZLS config path (leave empty to use default): ")
@FilePath(files = true)
public String zlsConfigPath = ""; public String zlsConfigPath = "";
public boolean debug = false; @Config(20)
public boolean asyncFolding = true; @Label("Increase timeouts")
public boolean messageTrace = false;
public boolean increaseTimeouts = false; 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() { public static ZLSSettingsState getInstance(Project project) {
return ApplicationManager.getApplication().getService(AppSettingsState.class); return project.getService(ZLSSettingsState.class);
} }
@Override @Override
public AppSettingsState getState() { public ZLSSettingsState getState() {
return this; return this;
} }
@Override @Override
public void loadState(@NotNull AppSettingsState state) { public void loadState(@NotNull ZLSSettingsState state) {
XmlSerializerUtil.copyBean(state, this); XmlSerializerUtil.copyBean(state, this);
} }
} }

View file

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