feat: Gutter launch buttons

This commit is contained in:
FalsePattern 2023-08-21 18:52:53 +02:00
parent d6afb01d08
commit c3145edc5c
Signed by: falsepattern
GPG key ID: FDF7126A9E124447
11 changed files with 386 additions and 17 deletions

View file

@ -23,6 +23,12 @@ Changelog structure reference:
- Toolchain
- Debugging Support
- Editor
- Gutter icons for:
- Launching a file with a `main` top level function
- Launching a file with tests in it
- Running `zig build` from a build.zig file
### Fixed
- Toolchain

View file

@ -28,10 +28,11 @@ and the Eclipse LSP4J project
A multifunctional Zig Programming Language plugin for the IDEA platform.
Core features:
- Uses ZLS (Zig Language Server) for code assistance, syntax highlighting, and anything to do with smart coding
- 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
## Setting up the language server
@ -60,19 +61,20 @@ Currently, the debugger only works with the bundled LLDB debugger, so make sure
## Feature tracker:
### .zig files:
- Working:
- Code completion
- Code folding
- Syntax highlighting
- Inlay hints
- Basic error diagnostics
- Go to definition
- Rename symbol
- Hover documentation
- Go to implementations / find usages
- Brace/Parenthesis/Bracket matching
- Breakpoints (CLion/IDEA Ultimate)
- File creation prompt
- Code completion
- Code folding
- Syntax highlighting
- Inlay hints
- Basic error diagnostics
- Go to definition
- Rename symbol
- Hover documentation
- Go to implementations / find usages
- Brace/Parenthesis/Bracket matching
- Breakpoints (CLion/IDEA Ultimate)
- File creation prompt
- Gutter launch buttons
- TODO:
- Workspace Symbols

View file

@ -19,11 +19,15 @@ package com.falsepattern.zigbrains.project.execution.actions;
import com.falsepattern.zigbrains.project.execution.configurations.ZigRunExecutionConfiguration;
import com.falsepattern.zigbrains.project.execution.configurations.ZigRunExecutionConfigurationType;
import com.falsepattern.zigbrains.zig.parser.ZigFile;
import com.falsepattern.zigbrains.zig.psi.ZigTypes;
import com.falsepattern.zigbrains.zig.util.PsiUtil;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.execution.actions.ConfigurationFromContext;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.ConfigurationTypeUtil;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
@ -38,6 +42,27 @@ public class ZigRunExecutionConfigurationProducer extends AbstractZigRunExecutio
@Override
protected boolean setupConfigurationFromContext(@NotNull ZigRunExecutionConfiguration configuration, @NotNull ConfigurationContext context, @NotNull Ref<PsiElement> sourceElement) {
var loc = context.getLocation();
if (loc == null) {
return false;
}
var element = loc.getPsiElement();
var psiFile = element.getContainingFile();
if (psiFile == null) {
return false;
}
var theFile = psiFile.getVirtualFile();
var filePath = theFile.getPath();
if (PsiUtil.getElementType(element) == ZigTypes.KEYWORD_TEST) {
configuration.command = "test " + filePath;
configuration.setName("Test " + theFile.getPresentableName());
} else if ("build.zig".equals(theFile.getName())) {
configuration.command = "build";
configuration.setName("Build");
} else {
configuration.command = "run " + filePath;
configuration.setName(theFile.getPresentableName());
}
return true;
}
@ -46,10 +71,24 @@ public class ZigRunExecutionConfigurationProducer extends AbstractZigRunExecutio
if (context.getLocation() == null) {
return false;
}
var element = context.getLocation().getPsiElement();
if (element.getContainingFile() == null) {
val element = context.getLocation().getPsiElement();
val file = element.getContainingFile();
if (file == null) {
return false;
}
return element.getContainingFile() instanceof ZigFile;
if (!(file instanceof ZigFile)) {
return false;
}
val vFile = file.getVirtualFile();
val filePath = vFile.getPath();
if (!configuration.command.contains(filePath)) {
return configuration.command.startsWith("build") && vFile.getName().equals("build.zig");
}
return (PsiUtil.getElementType(element) == ZigTypes.KEYWORD_TEST) == configuration.command.startsWith("test ");
}
@Override
public boolean shouldReplace(@NotNull ConfigurationFromContext self, @NotNull ConfigurationFromContext other) {
return self.getConfigurationType() instanceof ZigRunExecutionConfigurationType;
}
}

View file

@ -0,0 +1,42 @@
/*
* 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.project.execution.linemarker;
import com.intellij.execution.lineMarker.RunLineMarkerContributor;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class ZigAggregateLineMarkerContributor extends RunLineMarkerContributor {
private final List<RunLineMarkerContributor> contributors = List.of(
new ZigExecutableRunLineMarkerContributor(),
new ZigTestLineMarkerContributor(),
new ZigBuildLineMarkerContributor()
);
@Override
public @Nullable Info getInfo(@NotNull PsiElement element) {
for (var contributor: contributors) {
var info = contributor.getInfo(element);
if (info != null) {
return info;
}
}
return null;
}
}

View file

@ -0,0 +1,57 @@
/*
* 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.project.execution.linemarker;
import com.falsepattern.zigbrains.zig.psi.ZigTypes;
import com.falsepattern.zigbrains.zig.util.PsiUtil;
import com.intellij.icons.AllIcons;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
public class ZigBuildLineMarkerContributor extends ZigTopLevelDeclarationLineMarkerContributorBase {
@Override
protected @Nullable PsiElement getDeclaration(@NotNull PsiElement element) {
if (PsiUtil.getElementType(element) != ZigTypes.IDENTIFIER) {
return null;
}
if (!element.textMatches("build")) {
return null;
}
var parent = element.getParent();
if (PsiUtil.getElementType(parent) != ZigTypes.FN_PROTO) {
return null;
}
var file = element.getContainingFile();
if (file == null) {
return null;
}
var fileName = file.getVirtualFile().getName();
if (!"build.zig".equals(fileName)) {
return null;
}
return parent.getParent();
}
@Override
protected @NotNull Icon getIcon(@NotNull PsiElement element) {
return AllIcons.RunConfigurations.TestState.Run;
}
}

View file

@ -0,0 +1,48 @@
/*
* 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.project.execution.linemarker;
import com.falsepattern.zigbrains.zig.psi.ZigTypes;
import com.falsepattern.zigbrains.zig.util.PsiUtil;
import com.intellij.icons.AllIcons;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
public class ZigExecutableRunLineMarkerContributor extends ZigTopLevelDeclarationLineMarkerContributorBase {
@Override
protected @Nullable PsiElement getDeclaration(@NotNull PsiElement element) {
if (PsiUtil.getElementType(element) != ZigTypes.IDENTIFIER) {
return null;
}
if (!element.textMatches("main")) {
return null;
}
var parent = element.getParent();
if (PsiUtil.getElementType(parent) != ZigTypes.FN_PROTO) {
return null;
}
return parent.getParent();
}
@Override
protected @NotNull Icon getIcon(@NotNull PsiElement element) {
return AllIcons.RunConfigurations.TestState.Run;
}
}

View file

@ -0,0 +1,45 @@
/*
* 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.project.execution.linemarker;
import com.falsepattern.zigbrains.zig.psi.ZigTypes;
import com.falsepattern.zigbrains.zig.util.PsiUtil;
import com.intellij.icons.AllIcons;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
public class ZigTestLineMarkerContributor extends ZigTopLevelDeclarationLineMarkerContributorBase {
@Override
protected @Nullable PsiElement getDeclaration(@NotNull PsiElement element) {
if (PsiUtil.getElementType(element) != ZigTypes.KEYWORD_TEST) {
return null;
}
var parent = element.getParent();
if (PsiUtil.getElementType(parent) != ZigTypes.TEST_DECL) {
return null;
}
return parent;
}
@Override
protected @NotNull Icon getIcon(@NotNull PsiElement element) {
return AllIcons.RunConfigurations.TestState.Run;
}
}

View file

@ -0,0 +1,55 @@
/*
* 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.project.execution.linemarker;
import com.falsepattern.zigbrains.zig.psi.ZigTypes;
import com.falsepattern.zigbrains.zig.util.PsiUtil;
import com.intellij.execution.lineMarker.ExecutorAction;
import com.intellij.execution.lineMarker.RunLineMarkerContributor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
public abstract class ZigTopLevelDeclarationLineMarkerContributorBase extends RunLineMarkerContributor {
@Override
public @Nullable Info getInfo(@NotNull PsiElement element) {
PsiElement parent = getDeclaration(element);
int nestingLevel = 0;
while (parent != null && !(parent instanceof PsiFile)) {
if (PsiUtil.getElementType(parent) == ZigTypes.CONTAINER_DECLARATION) {
nestingLevel++;
}
parent = parent.getParent();
}
if (nestingLevel != 1) {
return null;
}
val actions = ExecutorAction.getActions(0);
return new Info(getIcon(element), actions, null);
}
protected abstract @Nullable PsiElement getDeclaration(@NotNull PsiElement element);
protected abstract @NotNull Icon getIcon(@NotNull PsiElement element);
}

View file

@ -0,0 +1,32 @@
/*
* 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.project.runconfig;
import com.intellij.execution.executors.DefaultRunExecutor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class ZigRegularRunner extends ZigExecutableRunner{
public ZigRegularRunner() {
super(DefaultRunExecutor.EXECUTOR_ID, "Unable to run zig");
}
@Override
public @NotNull @NonNls String getRunnerId() {
return "ZigRegularRunner";
}
}

View file

@ -11,6 +11,12 @@
instance="com.falsepattern.zigbrains.project.ide.project.ZigProjectConfigurable"
id="com.falsepattern.zigbrains.project.ide.project.ZigProjectConfigurable"
displayName="Zig"/>
<runLineMarkerContributor language="Zig"
implementationClass="com.falsepattern.zigbrains.project.execution.linemarker.ZigAggregateLineMarkerContributor"/>
<programRunner implementation="com.falsepattern.zigbrains.project.runconfig.ZigRegularRunner"
id="ZigDebugRunner"/>
</extensions>
<extensions defaultExtensionNs="com.falsepattern.zigbrains">

View file

@ -0,0 +1,37 @@
/*
* 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.util;
import com.falsepattern.zigbrains.zig.psi.ZigFnProto;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
public class PsiUtil {
@Contract("null -> null")
public static @Nullable IElementType getElementType(@Nullable PsiElement element) {
if (element instanceof ASTWrapperPsiElement ast) {
return ast.getNode().getElementType();
} else if (element instanceof ASTNode ast) {
return ast.getElementType();
}
return null;
}
}