backport: 12.0.0
ci: 12.0.0 (cherry picked from commit 2f80528cb46ee5a13dd5cb960d361c41d62c7e28) (cherry picked from commit 87d7db94410dd30be154e585138498c67c262db8) chore: Isolate C++ toolchain into separate package to fix verifier error (cherry picked from commit c393120bf24c40d5fc5e8ce41dacc560bcb29ae8) chore: Forgot to bump gradle version in properties (cherry picked from commit b8639b0e8dc7d4e8177339d5466d98af9b87c900) fix(zig): Make go to references non-blocking (cherry picked from commit a1cee2b1ea399776f5d4bbf33c2403a9c4bf9b03) feat(zig)!: Go to declaration/usages and go to definition are now separate actions (cherry picked from commit 18e130cc52e78b69dfdd08e5f160e82d3215deb2) fix(zig): Refresh syntax highlighting after running code edits (cherry picked from commit 64eba369d61073f1dd57c999449e9ee7b914bd49) chore: Cleanup dependencies (cherry picked from commit baabbb030dc8ad729f5a1f82c523c1d6da27489b) docs: Extra information about module tree (cherry picked from commit 12ad175f510124353fd9cc6b8994355e44965161) feat(zig)!: Autogenerate zls config if not specified, based on project toolchain (cherry picked from commit 59a56b67646b0253734130a84d8d9d825effe114) feat(debugger): Library frame filter (cherry picked from commit 4e3336add808801097acc792fa3e899b26cbdaee) docs: update changelog (cherry picked from commit 7db1c621288ba1cac25e85b682d981fd8cc2d4b8) feat!: Reimplemented go to declaration/usages to replace the built-in action Also removed mouse handling, no longer needed (cherry picked from commit 6f481ac844f80701f22d9383a3ff228ea3ee440d) feat(debugger): Detect C++ debugger toolchains (cherry picked from commit 6df7cb6dc059c622a0e06ace38558c8b495bd91e) fix(zig): Add proper lang key for notification group (cherry picked from commit b8f64ac0062847279b6c85b00083711900e8f7cf) chore(buildscript): Update gradle and gradle plugins (cherry picked from commit 45ab2d9bb7834be9bc2914f962d3a21bef494fbd) fix(lsp): Force always creating a new DocumentEventManager34b29ee729
(cherry picked from commit 59c1f4612d353de3230419d005206dada4900a7f) fix(lsp): unregisterManager method's cleanupbc9c5ea31c
(cherry picked from commit 14a1a9d79f09573d3d7c8cfb27bb008cb63ca38b) chore(lsp): Extract shared logic75a5fd8919
(cherry picked from commit 857a0224897e27968fa03f93b0f9a02bc109f0b0) fix(lsp): Remove duplicated changedConfiguration calls9b2b0557c9
(cherry picked from commit ceb347d8723130c8ee4097a78fed5f14615805ee) chore(lsp): Small code cleanup8c1e6736df
(cherry picked from commit 1e1b4aaaeafabfe76abad22cc18aa51629098012) fix(lsp): Code action annotations lose range00bbd6ff45
(cherry picked from commit d920fa37f32549d7d7419fa07e478b94cc8ae8ba) fix(lsp): Request code actions immediately after diagnostics arrive0fe2cf98fe
(cherry picked from commit 36138213ba878eaf97e9c7f0642b9927672d2e59)
This commit is contained in:
parent
a026396072
commit
96163ae81e
44 changed files with 847 additions and 601 deletions
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -18,6 +18,30 @@ Changelog structure reference:
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [12.0.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Debugger
|
||||
- Now uses the toolchains you set in Settings | Build, Execution, Deployment | Toolchains
|
||||
- Standard library stack frames are now automatically filtered from the debug stack trace
|
||||
|
||||
- Zig
|
||||
- Go to Declaration/Usages now functions as expected, taking you to the declaration of a symbol instead of its resolved
|
||||
implementation.
|
||||
- For the time being, the "Quick Definition" (CTRL+Shift+I) action has been repurposed as Go To Definition. This will be
|
||||
replaced with a properly integrated solution once a way to couple the PSI symbol system and the LSP has been found.
|
||||
|
||||
### Fixed
|
||||
|
||||
- LSP
|
||||
- Diagnostics race condition
|
||||
- Code action annotations no longer lose range
|
||||
|
||||
- Zig
|
||||
- Syntax highlighting no longer breaks after refactoring or reformatting
|
||||
- Go to Usages no longer freezes the IDE
|
||||
|
||||
## [11.1.0]
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -2,8 +2,18 @@ For now this project is still pretty small, but here are some general guidelines
|
|||
|
||||
- This project ships with a code style config in .idea, your IDE should automatically apply it when you pull the repo.
|
||||
When making pull requests, please try to keep to this style as much as possible.
|
||||
|
||||
- Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), and scope as much as you can.
|
||||
- The project has support for the PSI tree, but try to keep "intelligent" behaviour out of it, all code inspection and
|
||||
other semantics-aware help should come from ZLS where possible.
|
||||
- Generally, if the answer to "Can i write some glue code that can get this info from ZLS?" is YES, do not use the PSI
|
||||
tree.
|
||||
|
||||
- Do not edit the relationship tree between modules without unanimous agreement from the project lead.
|
||||
|
||||
If you want an "upstream" module (for instance, the LSP) receive data from a "downstream" module
|
||||
(for instance, the project module), you should use service providers, dependency injections, or other ways to implement
|
||||
the dataflow in a way where the upstream module is not directly aware of downstream modules.
|
||||
|
||||
The main purpose of this is to avoid any circular dependencies, which could cause proprietary IDE-only features
|
||||
(for instance, the CLion debugger module) to be depended on by FOSS modules. This restriction is non-negotiable.
|
||||
|
||||
- Any complex language inspection, syntax action, or similar, should be done by ZLS, and ZigBrains will just act as an
|
||||
adapter for these features. This notion of "complexity" is determined by the project maintainer. Open an issue
|
||||
that explains the feature before you start implementing anything!
|
44
MODULE_TREE.md
Normal file
44
MODULE_TREE.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
## What is this?
|
||||
This document describes the relationships between the different "modules" of ZigBrains.
|
||||
|
||||
These relationships are strictly enforced, and modules that do not directly (or indirectly) depend on another
|
||||
module cannot access their classes.
|
||||
|
||||
The primary purpose of this strictly enforced module system is to avoid code compatible with open source IDEs (IDEA/PyCharm Community)
|
||||
from depending on code that only works on proprietary IDEs (IDEA Ultimate/CLion/...).
|
||||
|
||||
NOTE: These "modules" are in no way related to the module system introduced in Java 9. They're just called the same.
|
||||
|
||||
The suffix after the module name signifies which IDE it depends on.
|
||||
|
||||
- IC: IDEA Community
|
||||
- CL: CLion
|
||||
- EXT: External maven library
|
||||
|
||||
IC modules MUST NOT depend on CL modules, as this violates the restrictions set forth above.
|
||||
|
||||
## Modules
|
||||
|
||||
### Common (IC)
|
||||
|
||||
### LSP (IC)
|
||||
- LSP4J (EXT)
|
||||
- Flexmark (EXT)
|
||||
- Apache Commons Lang 3 (EXT)
|
||||
|
||||
### Zig (IC)
|
||||
- Grammarkit (EXT)
|
||||
- Common (IC)
|
||||
- LSP (IC)
|
||||
|
||||
### Project (IC)
|
||||
- Common (IC)
|
||||
- Zig (IC)
|
||||
|
||||
### Zon (IC)
|
||||
- Grammarkit (EXT)
|
||||
- Common (IC)
|
||||
|
||||
### Debugger (CL)
|
||||
- Zig (IC)
|
||||
- Project (IC)
|
44
README.md
44
README.md
|
@ -80,7 +80,17 @@ LSP server is running.
|
|||
|
||||
## Debugging
|
||||
|
||||
Currently, the debugger only works with the bundled LLDB debugger, so make sure you have that.
|
||||
ZigBrains uses the CLion C++ toolchains (Settings | Build, Execution, Deployment | Toolchains) for debugging purposes,
|
||||
and it is fully compatible with both GDB and LLDB debuggers.
|
||||
|
||||
Additionally, ZigBrains will prioritize a toolchain if it is called `Zig`, otherwise it will use the default toolchain.
|
||||
|
||||
If no toolchain is available, ZigBrains will attempt to use the bundled LLDB debugger, and if that is not available either,
|
||||
an error popup will be shown when you try to run with debugging.
|
||||
|
||||
Note: There is a small issue with the LLDB debugger which does not happen with GDB: The debugger will pause on the first
|
||||
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.
|
||||
|
||||
## Feature tracker:
|
||||
|
||||
|
@ -96,7 +106,7 @@ Currently, the debugger only works with the bundled LLDB debugger, so make sure
|
|||
- Hover documentation
|
||||
- Go to implementations / find usages
|
||||
- Brace/Parenthesis/Bracket matching
|
||||
- Breakpoints (CLion/IDEA Ultimate)
|
||||
- Debugging (CLion/CLion Nova)
|
||||
- File creation prompt
|
||||
- Gutter launch buttons
|
||||
- Commenter (thanks @MarioAriasC !)
|
||||
|
@ -117,36 +127,6 @@ Currently, the debugger only works with the bundled LLDB debugger, so make sure
|
|||
- Debugging (CLion/IDEA Ultimate)
|
||||
- Project generation (thanks @JensvandeWiel !)
|
||||
|
||||
## The motivation
|
||||
The other existing Zig language plugins for IntelliJ rely a lot on the PSI tree.
|
||||
This seems correct in theory, until
|
||||
the sheer power of Zig's comptime is taken into consideration.
|
||||
|
||||
The comptime makes any sort of contextual help implemented with the PSI tree a lot more restrictive,
|
||||
and adding LSP integration at that point is an uphill battle.
|
||||
|
||||
## Current state of the project
|
||||
This project takes the opposite approach: The initial implementation *completely* relies on ZLS, with no lexer or parser
|
||||
in sight.
|
||||
Using a language server immediately gives us access to advanced features such as refactoring, go to definition,
|
||||
semantics-based highlighting, and so on.
|
||||
|
||||
However, this also restricts the amount of IDE integration the language plugin can achieve,
|
||||
and things like live previews, peek definition, go to usage previews, and many other features that deeply integrate with
|
||||
the PSI system just don't work at all.
|
||||
|
||||
## Long-term plans
|
||||
The first and foremost goal of this project is deeply integrating ZLS into the IDE,
|
||||
and LSP-provided information *always* takes the first seat.
|
||||
|
||||
However, we must also not completely reject the PSI tree,
|
||||
as it has its own merits when used wisely, such as basic "dumb mode" syntax highlighting,
|
||||
proper caret placements with go to usages, and so on.
|
||||
|
||||
Thus, this project will still use PSI trees and the IntelliJ lexer/parser system, but with heavy moderation, and any
|
||||
sort of "smart inspection" *shall not* be implemented in the PSI, but instead retrieved from the language server.
|
||||
|
||||
|
||||
## Licenses
|
||||
|
||||
<p>
|
||||
|
|
|
@ -12,9 +12,9 @@ fun environment(key: String) = providers.environmentVariable(key)
|
|||
plugins {
|
||||
id("java") // Java support
|
||||
id("java-library")
|
||||
id("org.jetbrains.intellij") version("1.17.0")
|
||||
id("org.jetbrains.intellij") version("1.17.2")
|
||||
id("org.jetbrains.changelog") version("2.2.0")
|
||||
id("org.jetbrains.grammarkit") version("2022.3.2.1")
|
||||
id("org.jetbrains.grammarkit") version("2022.3.2.2")
|
||||
}
|
||||
|
||||
val grammarKitGenDir = "build/generated/sources/grammarkit/java"
|
||||
|
@ -58,8 +58,6 @@ allprojects {
|
|||
maven("https://cache-redirector.jetbrains.com/intellij-dependencies")
|
||||
}
|
||||
dependencies {
|
||||
compileOnly("org.jetbrains:annotations:24.1.0")
|
||||
|
||||
compileOnly("org.projectlombok:lombok:1.18.30")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.30")
|
||||
}
|
||||
|
@ -116,7 +114,7 @@ allprojects {
|
|||
purgeOldFiles = true
|
||||
}
|
||||
generateParser {
|
||||
targetRoot = "${grammarKitGenDir}/parser"
|
||||
targetRootOutputDir = file("${grammarKitGenDir}/parser")
|
||||
purgeOldFiles = true
|
||||
}
|
||||
|
||||
|
@ -171,7 +169,7 @@ project(":lsp") {
|
|||
plugin("java-library")
|
||||
}
|
||||
dependencies {
|
||||
api("org.eclipse.lsp4j:org.eclipse.lsp4j:0.21.2")
|
||||
api("org.eclipse.lsp4j:org.eclipse.lsp4j:0.22.0")
|
||||
implementation("com.vladsch.flexmark:flexmark:0.64.8")
|
||||
api("org.apache.commons:commons-lang3:3.14.0")
|
||||
}
|
||||
|
@ -186,8 +184,7 @@ project(":zig") {
|
|||
generateLexer {
|
||||
enabled = true
|
||||
sourceFile = file("src/main/grammar/Zig.flex")
|
||||
targetDir = "${grammarKitGenDir}/lexer/${rootPackagePath}/zig/lexer"
|
||||
targetClass = "ZigFlexLexer"
|
||||
targetOutputDir = file("${grammarKitGenDir}/lexer/${rootPackagePath}/zig/lexer")
|
||||
}
|
||||
|
||||
generateParser {
|
||||
|
@ -219,8 +216,7 @@ project(":zon") {
|
|||
generateLexer {
|
||||
enabled = true
|
||||
sourceFile = file("src/main/grammar/Zon.flex")
|
||||
targetDir = "${grammarKitGenDir}/lexer/${rootPackagePath}/zon/lexer"
|
||||
targetClass = "ZonFlexLexer"
|
||||
targetOutputDir = file("${grammarKitGenDir}/lexer/${rootPackagePath}/zon/lexer")
|
||||
}
|
||||
|
||||
generateParser {
|
||||
|
|
|
@ -2,7 +2,7 @@ pluginGroup = com.falsepattern.zigbrains
|
|||
pluginName = ZigBrains
|
||||
pluginRepositoryUrl = https://github.com/FalsePattern/ZigBrains
|
||||
# SemVer format -> https://semver.org
|
||||
pluginVersion = 11.1.0
|
||||
pluginVersion = 12.0.0
|
||||
|
||||
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
|
||||
pluginSinceBuild = 231
|
||||
|
@ -14,7 +14,7 @@ ideaVersion = IC-2023.1.5
|
|||
clionVersion = CL-2023.1.5
|
||||
|
||||
# Gradle Releases -> https://github.com/gradle/gradle/releases
|
||||
gradleVersion = 8.2.1
|
||||
gradleVersion = 8.6
|
||||
|
||||
# Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html
|
||||
org.gradle.caching = true
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.cpp;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.debugbridge.DebuggerDriverProvider;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.jetbrains.cidr.cpp.execution.debugger.backend.CLionGDBDriverConfiguration;
|
||||
import com.jetbrains.cidr.cpp.execution.debugger.backend.CLionLLDBDriverConfiguration;
|
||||
import com.jetbrains.cidr.cpp.toolchains.CPPToolchains;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class CPPDebuggerDriverProvider implements DebuggerDriverProvider {
|
||||
private static final Logger LOG = Logger.getInstance(CPPDebuggerDriverProvider.class);
|
||||
@Override
|
||||
public @Nullable DebuggerDriverConfiguration getDebuggerConfiguration(Project project) {
|
||||
val toolchains = CPPToolchains.getInstance();
|
||||
var toolchain = toolchains.getToolchainByNameOrDefault("Zig");
|
||||
if (toolchain == null || !isDebuggerSupported(toolchain)) {
|
||||
LOG.info("Couldn't find debug-compatible C++ toolchain with name \"Zig\"");
|
||||
toolchain = toolchains.getDefaultToolchain();
|
||||
}
|
||||
if (toolchain == null || !isDebuggerSupported(toolchain)) {
|
||||
LOG.info("Couldn't find debug-compatible C++ default toolchain");
|
||||
return null;
|
||||
}
|
||||
|
||||
return switch (toolchain.getDebuggerKind()) {
|
||||
case CUSTOM_GDB, BUNDLED_GDB -> new CLionGDBDriverConfiguration(project, toolchain);
|
||||
case BUNDLED_LLDB -> new CLionLLDBDriverConfiguration(project, toolchain);
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isDebuggerSupported(CPPToolchains.Toolchain toolchain) {
|
||||
try {
|
||||
toolchain.getDebugger();
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.debugbridge;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* getDebuggerConfiguration should return com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration, it's UserDataHolder here to
|
||||
* avoid a plugin verifier error.
|
||||
*/
|
||||
public interface DebuggerDriverProvider {
|
||||
ExtensionPointName<DebuggerDriverProvider> EXTENSION_POINT_NAME = ExtensionPointName.create("com.falsepattern.zigbrains.debuggerDriverProvider");
|
||||
|
||||
static @NotNull Stream<UserDataHolder> findDebuggerConfigurations(Project project) {
|
||||
return EXTENSION_POINT_NAME.getExtensionList()
|
||||
.stream()
|
||||
.map(it -> it.getDebuggerConfiguration(project))
|
||||
.filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
@Nullable UserDataHolder getDebuggerConfiguration(Project project);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.debugger;
|
||||
|
||||
import com.falsepattern.zigbrains.zig.debugbridge.DebuggerDriverProvider;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguration;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Utils {
|
||||
public static @Nullable DebuggerDriverConfiguration getDebuggerConfiguration(Project project) {
|
||||
val providedDebugger = DebuggerDriverProvider.findDebuggerConfigurations(project)
|
||||
.filter(x -> x instanceof DebuggerDriverConfiguration)
|
||||
.map(x -> (DebuggerDriverConfiguration)x)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (providedDebugger != null)
|
||||
return providedDebugger;
|
||||
|
||||
|
||||
if (LLDBDriverConfiguration.hasBundledLLDB()) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Warn",
|
||||
"Couldn't find a working debug toolchain, using bundled LLDB debugger!",
|
||||
NotificationType.WARNING));
|
||||
return new LLDBDriverConfiguration();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,14 +21,16 @@ import com.jetbrains.cidr.execution.Installer;
|
|||
import com.jetbrains.cidr.execution.RunParameters;
|
||||
import com.jetbrains.cidr.execution.TrivialInstaller;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration;
|
||||
import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguration;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ZigDebugRunParameters extends RunParameters {
|
||||
private final GeneralCommandLine cmd;
|
||||
private final DebuggerDriverConfiguration driverConfiguration;
|
||||
|
||||
@Override
|
||||
public @NotNull Installer getInstaller() {
|
||||
return new TrivialInstaller(cmd);
|
||||
|
@ -36,11 +38,7 @@ public class ZigDebugRunParameters extends RunParameters {
|
|||
|
||||
@Override
|
||||
public @NotNull DebuggerDriverConfiguration getDebuggerDriverConfiguration() {
|
||||
if (LLDBDriverConfiguration.hasBundledLLDB()) {
|
||||
return new LLDBDriverConfiguration();
|
||||
} else {
|
||||
throw new IllegalStateException("The bundled LLDB debugger is missing from your IDE!");
|
||||
}
|
||||
return driverConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,6 +24,9 @@ import com.intellij.execution.executors.DefaultDebugExecutor;
|
|||
import com.intellij.execution.process.ProcessTerminatedListener;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.xdebugger.XDebugProcess;
|
||||
import com.intellij.xdebugger.XDebugProcessStarter;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
|
@ -32,6 +35,8 @@ import lombok.val;
|
|||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ZigDebugRunnerBase extends ZigExecutableRunner {
|
||||
public ZigDebugRunnerBase() {
|
||||
super(DefaultDebugExecutor.EXECUTOR_ID, "Unable to run Zig debugger");
|
||||
|
@ -40,9 +45,16 @@ public class ZigDebugRunnerBase extends ZigExecutableRunner {
|
|||
@Override
|
||||
protected RunContentDescriptor showRunContent(ZigRunExecutionConfigurationRunProfileState state, ExecutionEnvironment environment, GeneralCommandLine runExecutable)
|
||||
throws ExecutionException {
|
||||
val runParameters = new ZigDebugRunParameters(runExecutable);
|
||||
return XDebuggerManager.getInstance(environment.getProject())
|
||||
.startSession(environment, new XDebugProcessStarter() {
|
||||
val project = environment.getProject();
|
||||
val debuggerDriver = Utils.getDebuggerConfiguration(project);
|
||||
if (debuggerDriver == null) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Debugger.Error", "Couldn't find a working GDB or LLDB debugger! Please check your Toolchains! (Settings | Build, Execution, Deployment | Toolchains)", NotificationType.ERROR));
|
||||
return null;
|
||||
}
|
||||
val runParameters = new ZigDebugRunParameters(runExecutable, debuggerDriver);
|
||||
val manager = XDebuggerManager.getInstance(project);
|
||||
return manager.startSession(environment,
|
||||
new XDebugProcessStarter() {
|
||||
@Override
|
||||
public @NotNull XDebugProcess start(@NotNull XDebugSession session) throws ExecutionException {
|
||||
val process = new ZigLocalDebugProcess(runParameters, session, state.getConsoleBuilder());
|
||||
|
|
|
@ -29,11 +29,4 @@ public class ZigLocalDebugProcess extends CidrLocalDebugProcess {
|
|||
throws ExecutionException {
|
||||
super(parameters, session, consoleBuilder, (project) -> Filter.EMPTY_ARRAY, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLibraryFrameFilterSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<idea-plugin package="com.falsepattern.zigbrains.zig.cpp">
|
||||
<depends>com.intellij.modules.clion</depends>
|
||||
<extensions defaultExtensionNs="com.falsepattern.zigbrains">
|
||||
<debuggerDriverProvider implementation="com.falsepattern.zigbrains.zig.cpp.CPPDebuggerDriverProvider"/>
|
||||
</extensions>
|
||||
|
||||
</idea-plugin>
|
|
@ -16,10 +16,20 @@
|
|||
|
||||
<idea-plugin package="com.falsepattern.zigbrains.zig.debugger">
|
||||
<depends>com.intellij.modules.cidr.debugger</depends>
|
||||
<resource-bundle>zigbrains.zig.debugger.Bundle</resource-bundle>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<programRunner implementation="com.falsepattern.zigbrains.zig.debugger.ZigDebugRunner"
|
||||
id="ZigDebugRunner"/>
|
||||
|
||||
<notificationGroup displayType="BALLOON"
|
||||
bundle="zigbrains.zig.debugger.Bundle"
|
||||
key="notif-debug-error"
|
||||
id="ZigBrains.Debugger.Error"/>
|
||||
<notificationGroup displayType="BALLOON"
|
||||
bundle="zigbrains.zig.debugger.Bundle"
|
||||
key="notif-debug-warn"
|
||||
id="ZigBrains.Debugger.Warn"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="cidr.debugger">
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
notif-debug-warn=ZigBrains debugger warning
|
||||
notif-debug-error=ZigBrains debugger error
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.lsp.actions;
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.falsepattern.zigbrains.lsp.requests.ReformatHandler;
|
||||
import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LSPGotoDeclarationAction extends GotoDeclarationAction {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getData(CommonDataKeys.PROJECT);
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
if (editor == null || project == null) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
if (file == null || !IntellijLanguageClient.isExtensionSupported(file.getVirtualFile())) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
EditorEventManager manager = EditorEventManagerBase.forEditor(editor);
|
||||
if (manager == null) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
val offset = editor.getCaretModel().getOffset();
|
||||
val psiElement = file.findElementAt(offset);
|
||||
if (psiElement == null) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
manager.gotoDeclarationOrUsages(psiElement);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.lsp.actions;
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.intellij.codeInsight.hint.actions.ShowImplementationsAction;
|
||||
import com.intellij.codeInsight.navigation.CtrlMouseAction;
|
||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LSPGotoDefinitionAction extends ShowImplementationsAction {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getData(CommonDataKeys.PROJECT);
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
if (editor == null || project == null) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
if (file == null || !IntellijLanguageClient.isExtensionSupported(file.getVirtualFile())) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
EditorEventManager manager = EditorEventManagerBase.forEditor(editor);
|
||||
if (manager == null) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
val offset = editor.getCaretModel().getOffset();
|
||||
val psiElement = file.findElementAt(offset);
|
||||
if (psiElement == null) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
manager.gotoDefinition(psiElement);
|
||||
}
|
||||
}
|
|
@ -24,6 +24,8 @@ import com.intellij.find.findUsages.FindUsagesOptions;
|
|||
import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
|
@ -41,6 +43,8 @@ import com.intellij.usages.UsageInfo2UsageAdapter;
|
|||
import com.intellij.usages.UsageTarget;
|
||||
import com.intellij.usages.UsageViewManager;
|
||||
import com.intellij.usages.UsageViewPresentation;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import lombok.val;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import java.awt.Color;
|
||||
|
@ -63,8 +67,8 @@ public class LSPReferencesAction extends DumbAwareAction {
|
|||
}
|
||||
List<PsiElement2UsageTargetAdapter> targets = new ArrayList<>();
|
||||
Pair<List<PsiElement>, List<VirtualFile>> references = eventManager
|
||||
.references(editor.getCaretModel().getCurrentCaret().getOffset());
|
||||
if (references.first != null && references.second != null) {
|
||||
.references(editor.getCaretModel().getCurrentCaret().getOffset(), true, true);
|
||||
if (references.first != null) {
|
||||
references.first.forEach(element -> targets.add(new PsiElement2UsageTargetAdapter(element, true)));
|
||||
}
|
||||
showReferences(editor, targets, editor.getCaretModel().getCurrentCaret().getLogicalPosition());
|
||||
|
@ -72,13 +76,22 @@ public class LSPReferencesAction extends DumbAwareAction {
|
|||
}
|
||||
|
||||
public void forManagerAndOffset(EditorEventManager manager, int offset) {
|
||||
List<PsiElement2UsageTargetAdapter> targets = new ArrayList<>();
|
||||
Pair<List<PsiElement>, List<VirtualFile>> references = manager.references(offset);
|
||||
if (references.first != null && references.second != null) {
|
||||
ReadAction.nonBlocking(() -> {
|
||||
val references = manager.references(offset, true, true);
|
||||
val targets = new ArrayList<PsiElement2UsageTargetAdapter>();
|
||||
if (references.first != null) {
|
||||
references.first.forEach(element -> targets.add(new PsiElement2UsageTargetAdapter(element, true)));
|
||||
}
|
||||
Editor editor = manager.editor;
|
||||
return targets;
|
||||
})
|
||||
.expireWhen(() -> manager.editor.isDisposed())
|
||||
.finishOnUiThread(ModalityState.NON_MODAL, targets -> {
|
||||
val editor = manager.editor;
|
||||
if (editor.isDisposed())
|
||||
return;
|
||||
showReferences(editor, targets, editor.offsetToLogicalPosition(offset));
|
||||
})
|
||||
.submit(AppExecutorUtil.getAppExecutorService());
|
||||
}
|
||||
|
||||
private void showReferences(Editor editor, List<PsiElement2UsageTargetAdapter> targets, LogicalPosition position) {
|
||||
|
@ -108,7 +121,7 @@ public class LSPReferencesAction extends DumbAwareAction {
|
|||
UsageViewPresentation presentation = createPresentation(targets.get(0).getElement(),
|
||||
new FindUsagesOptions(editor.getProject()), false);
|
||||
UsageViewManager.getInstance(project)
|
||||
.showUsages(new UsageTarget[] { targets.get(0) }, usages.toArray(new Usage[usages.size()]),
|
||||
.showUsages(new UsageTarget[0], usages.toArray(new Usage[0]),
|
||||
presentation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,10 +43,14 @@ public class LSPReformatAction extends ReformatCodeAction implements DumbAware {
|
|||
Project project = e.getData(CommonDataKeys.PROJECT);
|
||||
Editor editor = e.getData(CommonDataKeys.EDITOR);
|
||||
if (editor == null || project == null) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
if (IntellijLanguageClient.isExtensionSupported(file.getVirtualFile())) {
|
||||
if (file == null || !IntellijLanguageClient.isExtensionSupported(file.getVirtualFile())) {
|
||||
super.actionPerformed(e);
|
||||
return;
|
||||
}
|
||||
ApplicationUtils.writeAction(() -> FileDocumentManager.getInstance().saveDocument(editor.getDocument()));
|
||||
// if editor hasSelection, only reformat selection, not reformat the whole file
|
||||
if (editor.getSelectionModel().hasSelection()) {
|
||||
|
@ -54,9 +58,6 @@ public class LSPReformatAction extends ReformatCodeAction implements DumbAware {
|
|||
} else {
|
||||
ReformatHandler.reformatFile(editor);
|
||||
}
|
||||
} else {
|
||||
super.actionPerformed(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.eclipse.lsp4j.CompletionItem;
|
|||
import org.eclipse.lsp4j.CompletionList;
|
||||
import org.eclipse.lsp4j.CompletionOptions;
|
||||
import org.eclipse.lsp4j.CompletionParams;
|
||||
import org.eclipse.lsp4j.DeclarationParams;
|
||||
import org.eclipse.lsp4j.DefinitionParams;
|
||||
import org.eclipse.lsp4j.DidChangeConfigurationParams;
|
||||
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
|
||||
|
@ -572,6 +573,21 @@ public class DefaultRequestManager implements RequestManager {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> declaration(DeclarationParams params) {
|
||||
if (checkStatus()) {
|
||||
try {
|
||||
return Optional.ofNullable(serverCapabilities.getDefinitionProvider())
|
||||
.map(e -> e.getLeft() || e.getRight() != null).orElse(false) ?
|
||||
textDocumentService.declaration(params) : null;
|
||||
} catch (Exception e) {
|
||||
crashed(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<Either<Command, CodeAction>>> codeAction(CodeActionParams params) {
|
||||
if (checkStatus()) {
|
||||
|
|
|
@ -28,8 +28,6 @@ import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
|||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||
import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.DocumentListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.EditorMouseListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.EditorMouseMotionListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.LSPCaretListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.requests.Timeout;
|
||||
import com.falsepattern.zigbrains.lsp.requests.Timeouts;
|
||||
|
@ -67,6 +65,7 @@ import org.eclipse.lsp4j.DidChangeWatchedFilesCapabilities;
|
|||
import org.eclipse.lsp4j.DocumentHighlightCapabilities;
|
||||
import org.eclipse.lsp4j.ExecuteCommandCapabilities;
|
||||
import org.eclipse.lsp4j.FoldingRangeCapabilities;
|
||||
import org.eclipse.lsp4j.FoldingRangeKind;
|
||||
import org.eclipse.lsp4j.FoldingRangeKindSupportCapabilities;
|
||||
import org.eclipse.lsp4j.FoldingRangeSupportCapabilities;
|
||||
import org.eclipse.lsp4j.FormattingCapabilities;
|
||||
|
@ -355,30 +354,24 @@ public class LanguageServerWrapper {
|
|||
//Todo - Implement
|
||||
// SelectionListenerImpl selectionListener = new SelectionListenerImpl();
|
||||
DocumentListenerImpl documentListener = new DocumentListenerImpl();
|
||||
EditorMouseListenerImpl mouseListener = new EditorMouseListenerImpl();
|
||||
EditorMouseMotionListenerImpl mouseMotionListener = new EditorMouseMotionListenerImpl();
|
||||
LSPCaretListenerImpl caretListener = new LSPCaretListenerImpl();
|
||||
|
||||
ServerOptions serverOptions = new ServerOptions(capabilities);
|
||||
EditorEventManager manager;
|
||||
if (extManager != null) {
|
||||
manager = extManager.getExtendedEditorEventManagerFor(editor, documentListener,
|
||||
mouseListener, mouseMotionListener, caretListener, requestManager, serverOptions,
|
||||
caretListener, requestManager, serverOptions,
|
||||
this);
|
||||
if (manager == null) {
|
||||
manager = new EditorEventManager(editor, documentListener, mouseListener,
|
||||
mouseMotionListener, caretListener,
|
||||
manager = new EditorEventManager(editor, documentListener, caretListener,
|
||||
requestManager, serverOptions, this);
|
||||
}
|
||||
} else {
|
||||
manager = new EditorEventManager(editor, documentListener, mouseListener,
|
||||
mouseMotionListener, caretListener,
|
||||
manager = new EditorEventManager(editor, documentListener, caretListener,
|
||||
requestManager, serverOptions, this);
|
||||
}
|
||||
// selectionListener.setManager(manager);
|
||||
documentListener.setManager(manager);
|
||||
mouseListener.setManager(manager);
|
||||
mouseMotionListener.setManager(manager);
|
||||
caretListener.setManager(manager);
|
||||
manager.registerListeners();
|
||||
if (!urisUnderLspControl.contains(uri)) {
|
||||
|
@ -464,7 +457,6 @@ public class LanguageServerWrapper {
|
|||
|
||||
// sadly this whole editor closing stuff runs asynchronously, so we cannot be sure the state is really clean here...
|
||||
// therefore clear the mapping from here as it should be empty by now.
|
||||
DocumentEventManager.clearState();
|
||||
uriToEditorManagers.clear();
|
||||
urisUnderLspControl.clear();
|
||||
launcherFuture = null;
|
||||
|
@ -590,7 +582,8 @@ public class LanguageServerWrapper {
|
|||
textDocumentClientCapabilities.setSynchronization(new SynchronizationCapabilities(true, true, true));
|
||||
|
||||
FoldingRangeCapabilities foldingRangeCapabilities = new FoldingRangeCapabilities();
|
||||
foldingRangeCapabilities.setFoldingRangeKind(new FoldingRangeKindSupportCapabilities(List.of("comment", "region", "imports")));
|
||||
foldingRangeCapabilities.setFoldingRangeKind(new FoldingRangeKindSupportCapabilities(List.of(
|
||||
FoldingRangeKind.Comment, FoldingRangeKind.Imports, FoldingRangeKind.Region)));
|
||||
foldingRangeCapabilities.setFoldingRange(new FoldingRangeSupportCapabilities(true));
|
||||
textDocumentClientCapabilities.setFoldingRange(foldingRangeCapabilities);
|
||||
|
||||
|
|
|
@ -109,16 +109,7 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
|
|||
if (eventManager == null) {
|
||||
return;
|
||||
}
|
||||
if (eventManager.isCodeActionSyncRequired()) {
|
||||
try {
|
||||
updateAnnotations(holder, eventManager);
|
||||
} catch (ConcurrentModificationException e) {
|
||||
// Todo - Add proper fix to handle concurrent modifications gracefully.
|
||||
LOG.warn("Error occurred when updating LSP diagnostics due to concurrent modifications.", e);
|
||||
} catch (Throwable t) {
|
||||
LOG.warn("Error occurred when updating LSP diagnostics.", t);
|
||||
}
|
||||
} else if (eventManager.isDiagnosticSyncRequired()) {
|
||||
if (eventManager.isDiagnosticSyncRequired()) {
|
||||
try {
|
||||
createAnnotations(holder, eventManager);
|
||||
} catch (ConcurrentModificationException e) {
|
||||
|
@ -127,6 +118,15 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
|
|||
} catch (Throwable t) {
|
||||
LOG.warn("Error occurred when updating LSP code actions.", t);
|
||||
}
|
||||
} else if (eventManager.isCodeActionSyncRequired()) {
|
||||
try {
|
||||
updateAnnotations(holder, eventManager);
|
||||
} catch (ConcurrentModificationException e) {
|
||||
// Todo - Add proper fix to handle concurrent modifications gracefully.
|
||||
LOG.warn("Error occurred when updating LSP diagnostics due to concurrent modifications.", e);
|
||||
} catch (Throwable t) {
|
||||
LOG.warn("Error occurred when updating LSP diagnostics.", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,12 @@ public class LSPAnnotator extends ExternalAnnotator<Object, Object> {
|
|||
annotations.forEach(annotation -> {
|
||||
if (annotation.getQuickFixes() != null && !annotation.getQuickFixes().isEmpty()) {
|
||||
AnnotationBuilder builder = holder.newAnnotation(annotation.getSeverity(), annotation.getMessage());
|
||||
boolean range = true;
|
||||
for (Annotation.QuickFixInfo quickFixInfo : annotation.getQuickFixes()) {
|
||||
if (range) {
|
||||
builder = builder.range(quickFixInfo.textRange);
|
||||
range = false;
|
||||
}
|
||||
builder = builder.withFix(quickFixInfo.quickFix);
|
||||
}
|
||||
builder.create();
|
||||
|
|
|
@ -39,9 +39,7 @@ import org.eclipse.lsp4j.TextDocumentSyncKind;
|
|||
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class DocumentEventManager {
|
||||
|
@ -52,7 +50,6 @@ public class DocumentEventManager {
|
|||
private final TextDocumentIdentifier identifier;
|
||||
private int version = -1;
|
||||
protected Logger LOG = Logger.getInstance(EditorEventManager.class);
|
||||
private static final Map<String, DocumentEventManager> uriToDocumentEventManager = new HashMap<>();
|
||||
|
||||
private final Set<Document> openDocuments = new HashSet<>();
|
||||
|
||||
|
@ -64,22 +61,6 @@ public class DocumentEventManager {
|
|||
this.identifier = new TextDocumentIdentifier(FileUtils.documentToUri(document));
|
||||
}
|
||||
|
||||
public static void clearState() {
|
||||
uriToDocumentEventManager.clear();
|
||||
}
|
||||
|
||||
public static DocumentEventManager getOrCreateDocumentManager(Document document, DocumentListener listener, TextDocumentSyncKind syncKind, LanguageServerWrapper wrapper) {
|
||||
DocumentEventManager manager = uriToDocumentEventManager.get(FileUtils.documentToUri(document));
|
||||
if (manager != null) {
|
||||
return manager;
|
||||
}
|
||||
|
||||
manager = new DocumentEventManager(document, listener, syncKind, wrapper);
|
||||
|
||||
uriToDocumentEventManager.put(FileUtils.documentToUri(document), manager);
|
||||
return manager;
|
||||
}
|
||||
|
||||
public void removeListeners() {
|
||||
document.removeDocumentListener(documentListener);
|
||||
}
|
||||
|
|
|
@ -72,11 +72,13 @@ import com.intellij.openapi.util.Pair;
|
|||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.ui.Hint;
|
||||
import com.intellij.util.SmartList;
|
||||
import lombok.val;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.lsp4j.CodeAction;
|
||||
import org.eclipse.lsp4j.CodeActionContext;
|
||||
|
@ -86,6 +88,7 @@ import org.eclipse.lsp4j.CompletionItem;
|
|||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.eclipse.lsp4j.CompletionList;
|
||||
import org.eclipse.lsp4j.CompletionParams;
|
||||
import org.eclipse.lsp4j.DeclarationParams;
|
||||
import org.eclipse.lsp4j.DefinitionParams;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
|
||||
|
@ -119,7 +122,9 @@ import org.eclipse.lsp4j.WorkspaceEdit;
|
|||
import org.eclipse.lsp4j.jsonrpc.JsonRpcException;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Tuple;
|
||||
import org.eclipse.lsp4j.services.TextDocumentService;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import java.awt.Point;
|
||||
|
@ -138,12 +143,11 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase.getCtrlRange;
|
||||
import static com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase.getIsCtrlDown;
|
||||
import static com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase.setCtrlRange;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.computableReadAction;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.computableWriteAction;
|
||||
import static com.falsepattern.zigbrains.lsp.utils.ApplicationUtils.invokeLater;
|
||||
|
@ -173,8 +177,6 @@ public class EditorEventManager {
|
|||
public LanguageServerWrapper wrapper;
|
||||
private Project project;
|
||||
private TextDocumentIdentifier identifier;
|
||||
private EditorMouseListener mouseListener;
|
||||
private EditorMouseMotionListener mouseMotionListener;
|
||||
private LSPCaretListenerImpl caretListener;
|
||||
|
||||
public List<String> completionTriggers;
|
||||
|
@ -200,13 +202,10 @@ public class EditorEventManager {
|
|||
public static final String SNIPPET_PLACEHOLDER_REGEX = "(\\$\\{\\d+:?([^{^}]*)}|\\$\\d+)";
|
||||
|
||||
//Todo - Revisit arguments order and add remaining listeners
|
||||
public EditorEventManager(Editor editor, DocumentListener documentListener, EditorMouseListener mouseListener,
|
||||
EditorMouseMotionListener mouseMotionListener, LSPCaretListenerImpl caretListener,
|
||||
public EditorEventManager(Editor editor, DocumentListener documentListener, LSPCaretListenerImpl caretListener,
|
||||
RequestManager requestmanager, ServerOptions serverOptions, LanguageServerWrapper wrapper) {
|
||||
|
||||
this.editor = editor;
|
||||
this.mouseListener = mouseListener;
|
||||
this.mouseMotionListener = mouseMotionListener;
|
||||
this.wrapper = wrapper;
|
||||
this.caretListener = caretListener;
|
||||
|
||||
|
@ -229,7 +228,7 @@ public class EditorEventManager {
|
|||
|
||||
this.currentHint = null;
|
||||
|
||||
this.documentEventManager = DocumentEventManager.getOrCreateDocumentManager(editor.getDocument(), documentListener, syncKind, wrapper);
|
||||
this.documentEventManager = new DocumentEventManager(editor.getDocument(), documentListener, syncKind, wrapper);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -258,159 +257,53 @@ public class EditorEventManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the manager that the mouse is in the editor
|
||||
*/
|
||||
public void mouseEntered() {
|
||||
mouseInEditor = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the manager that the mouse is not in the editor
|
||||
*/
|
||||
public void mouseExited() {
|
||||
mouseInEditor = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will show documentation if the mouse doesn't move for a given time (Hover)
|
||||
*
|
||||
* @param e the event
|
||||
*/
|
||||
public void mouseMoved(EditorMouseEvent e) {
|
||||
// if (e.getEditor() != editor) {
|
||||
// LOG.error("Wrong editor for EditorEventManager");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
// if (psiFile == null) {
|
||||
// return;
|
||||
// }
|
||||
// Language language = psiFile.getLanguage();
|
||||
// if ((!LanguageDocumentation.INSTANCE.allForLanguage(language).isEmpty() && !isSupportedLanguageFile(psiFile))
|
||||
// || (!getIsCtrlDown() && !EditorSettingsExternalizable.getInstance().isShowQuickDocOnMouseOverElement())) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// long curTime = System.nanoTime();
|
||||
// if (predTime == (-1L) || ctrlTime == (-1L)) {
|
||||
// predTime = curTime;
|
||||
// ctrlTime = curTime;
|
||||
// } else {
|
||||
// LogicalPosition lPos = getPos(e);
|
||||
// if (lPos == null || getIsKeyPressed() && !getIsCtrlDown()) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// int offset = editor.logicalPositionToOffset(lPos);
|
||||
// if ((getIsCtrlDown() || EditorSettingsExternalizable.getInstance().isShowQuickDocOnMouseOverElement())
|
||||
// && curTime - ctrlTime > CTRL_THRESH) {
|
||||
// if (getCtrlRange() == null || !getCtrlRange().highlightContainsOffset(offset)) {
|
||||
// if (currentHint != null) {
|
||||
// currentHint.hide();
|
||||
// }
|
||||
// currentHint = null;
|
||||
// if (getCtrlRange() != null) {
|
||||
// getCtrlRange().dispose();
|
||||
// }
|
||||
// setCtrlRange(null);
|
||||
// pool(() -> requestAndShowDoc(lPos, e.getMouseEvent().getPoint()));
|
||||
// } else if (getCtrlRange().definitionContainsOffset(offset)) {
|
||||
// createAndShowEditorHint(editor, "Click to show usages", editor.offsetToXY(offset));
|
||||
// } else {
|
||||
// editor.getContentComponent().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
// }
|
||||
// ctrlTime = curTime;
|
||||
// }
|
||||
// predTime = curTime;
|
||||
// }
|
||||
}
|
||||
|
||||
private boolean isSupportedLanguageFile(PsiFile file) {
|
||||
return file.getLanguage().isKindOf(PlainTextLanguage.INSTANCE)
|
||||
|| FileUtils.isFileSupported(file.getVirtualFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mouse is clicked
|
||||
* At the moment, is used by CTRL+click to see references / goto definition
|
||||
*
|
||||
* @param e The mouse event
|
||||
*/
|
||||
public void mouseClicked(EditorMouseEvent e) {
|
||||
if (e.getEditor() != editor) {
|
||||
LOG.error("Wrong editor for EditorEventManager");
|
||||
return;
|
||||
}
|
||||
|
||||
if (getIsCtrlDown()) {
|
||||
// If CTRL/CMD key is pressed, triggers goto definition/references and hover.
|
||||
try {
|
||||
trySourceNavigationAndHover(e);
|
||||
} catch (Exception err) {
|
||||
LOG.warn("Error occurred when trying source navigation", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createCtrlRange(Position logicalPos, Range range) {
|
||||
Location location = requestDefinition(logicalPos);
|
||||
if (location == null || location.getRange() == null || editor.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
Range corRange;
|
||||
if (range == null) {
|
||||
corRange = new Range(logicalPos, logicalPos);
|
||||
} else {
|
||||
corRange = range;
|
||||
}
|
||||
int startOffset = DocumentUtils.LSPPosToOffset(editor, corRange.getStart());
|
||||
int endOffset = DocumentUtils.LSPPosToOffset(editor, corRange.getEnd());
|
||||
boolean isDefinition = DocumentUtils.LSPPosToOffset(editor, location.getRange().getStart()) == startOffset;
|
||||
|
||||
CtrlRangeMarker ctrlRange = getCtrlRange();
|
||||
if (!editor.isDisposed()) {
|
||||
if (ctrlRange != null) {
|
||||
ctrlRange.dispose();
|
||||
}
|
||||
setCtrlRange(new CtrlRangeMarker(location, editor, !isDefinition ?
|
||||
(editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, HighlighterLayer.HYPERLINK,
|
||||
editor.getColorsScheme().getAttributes(EditorColors.REFERENCE_HYPERLINK_COLOR),
|
||||
HighlighterTargetArea.EXACT_RANGE)) : null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the definition given a position in the editor
|
||||
*
|
||||
* @param position The position
|
||||
* @return The location of the definition
|
||||
*/
|
||||
private Location requestDeclaration(Position position) {
|
||||
return requestDefinitionOrDeclaration(position, Timeouts.DECLARATION, DeclarationParams::new, TextDocumentService::declaration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the declaration given a position in the editor
|
||||
*
|
||||
* @param position The position
|
||||
* @return The location of the declaration
|
||||
*/
|
||||
private Location requestDefinition(Position position) {
|
||||
DefinitionParams params = new DefinitionParams(identifier, position);
|
||||
CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> request =
|
||||
wrapper.getRequestManager().definition(params);
|
||||
return requestDefinitionOrDeclaration(position, Timeouts.DEFINITION, DefinitionParams::new, TextDocumentService::definition);
|
||||
}
|
||||
|
||||
private <T> Location requestDefinitionOrDeclaration(Position position, Timeouts timeout, BiFunction<TextDocumentIdentifier, Position, T> paramsConstructor, BiFunction<TextDocumentService, T, CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>>> requester) {
|
||||
val params = paramsConstructor.apply(identifier, position);
|
||||
val request = requester.apply(wrapper.getRequestManager(), params);
|
||||
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Either<List<? extends Location>, List<? extends LocationLink>> definition =
|
||||
request.get(Timeout.getTimeout(Timeouts.DEFINITION), TimeUnit.MILLISECONDS);
|
||||
wrapper.notifySuccess(Timeouts.DEFINITION);
|
||||
if (definition == null) {
|
||||
val result = request.get(Timeout.getTimeout(timeout), TimeUnit.MILLISECONDS);
|
||||
wrapper.notifySuccess(timeout);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
if (definition.isLeft() && !definition.getLeft().isEmpty()) {
|
||||
return definition.getLeft().get(0);
|
||||
} else if (definition.isRight() && !definition.getRight().isEmpty()) {
|
||||
var def = definition.getRight().get(0);
|
||||
if (result.isLeft() && !result.getLeft().isEmpty()) {
|
||||
return result.getLeft().get(0);
|
||||
} else if (result.isRight() && !result.getRight().isEmpty()) {
|
||||
var def = result.getRight().get(0);
|
||||
return new Location(def.getTargetUri(), def.getTargetRange());
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
LOG.warn(e);
|
||||
wrapper.notifyFailure(Timeouts.DEFINITION);
|
||||
wrapper.notifyFailure(timeout);
|
||||
return null;
|
||||
} catch (InterruptedException | JsonRpcException | ExecutionException e) {
|
||||
LOG.warn(e);
|
||||
|
@ -459,7 +352,7 @@ public class EditorEventManager {
|
|||
* @param offset The offset in the editor
|
||||
* @return An array of PsiElement
|
||||
*/
|
||||
public Pair<List<PsiElement>, List<VirtualFile>> references(int offset, boolean getOriginalElement, boolean close) {
|
||||
public Pair<List<PsiElement>, List<VirtualFile>> references(int offset, boolean getOriginalElement, boolean fast) {
|
||||
renameCache = null;
|
||||
Position lspPos = DocumentUtils.offsetToLSPPos(editor, offset);
|
||||
TextDocumentIdentifier textDocumentIdentifier = new TextDocumentIdentifier(FileUtils.editorToURIString(editor));
|
||||
|
@ -471,17 +364,32 @@ public class EditorEventManager {
|
|||
try {
|
||||
List<? extends Location> res = request.get(Timeout.getTimeout(Timeouts.REFERENCES), TimeUnit.MILLISECONDS);
|
||||
wrapper.notifySuccess(Timeouts.REFERENCES);
|
||||
if (res != null && res.size() > 0) {
|
||||
List<VirtualFile> openedEditors = new ArrayList<>();
|
||||
if (res != null && !res.isEmpty()) {
|
||||
List<VirtualFile> openedEditors = fast ? null : new ArrayList<>();
|
||||
List<PsiElement> elements = new ArrayList<>();
|
||||
res.forEach(l -> {
|
||||
Position start = l.getRange().getStart();
|
||||
Position end = l.getRange().getEnd();
|
||||
String uri = FileUtils.sanitizeURI(l.getUri());
|
||||
VirtualFile file = FileUtils.virtualFileFromURI(uri);
|
||||
if (fast) {
|
||||
if (file == null)
|
||||
return;
|
||||
val document = FileDocumentManager.getInstance().getDocument(file);
|
||||
if (document == null)
|
||||
return;
|
||||
val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
|
||||
if (psiFile == null)
|
||||
return;
|
||||
val element = psiFile.findElementAt(DocumentUtils.LSPPosToOffset(document, start));
|
||||
if (element == null)
|
||||
return;
|
||||
elements.add(element);
|
||||
} else {
|
||||
Editor curEditor = FileUtils.editorFromUri(uri, project);
|
||||
if (curEditor == null && file != null) {
|
||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file, start.getLine(), start.getCharacter());
|
||||
OpenFileDescriptor descriptor =
|
||||
new OpenFileDescriptor(project, file, start.getLine(), start.getCharacter());
|
||||
curEditor = computableWriteAction(
|
||||
() -> FileEditorManager.getInstance(project).openTextEditor(descriptor, false));
|
||||
openedEditors.add(file);
|
||||
|
@ -494,13 +402,10 @@ public class EditorEventManager {
|
|||
int logicalEnd = DocumentUtils.LSPPosToOffset(curEditor, end);
|
||||
String name = curEditor.getDocument().getText(new TextRange(logicalStart, logicalEnd));
|
||||
elements.add(new LSPPsiElement(name, project, logicalStart, logicalEnd,
|
||||
PsiDocumentManager.getInstance(project).getPsiFile(curEditor.getDocument())));
|
||||
});
|
||||
if (close) {
|
||||
writeAction(
|
||||
() -> openedEditors.forEach(f -> FileEditorManager.getInstance(project).closeFile(f)));
|
||||
openedEditors.clear();
|
||||
PsiDocumentManager.getInstance(project)
|
||||
.getPsiFile(curEditor.getDocument())));
|
||||
}
|
||||
});
|
||||
return new Pair<>(elements, openedEditors);
|
||||
} else {
|
||||
return new Pair<>(null, null);
|
||||
|
@ -797,57 +702,6 @@ public class EditorEventManager {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hover request and shows it
|
||||
*
|
||||
* @param editorPos The editor position
|
||||
* @param point The point at which to show the hint
|
||||
*/
|
||||
private void requestAndShowDoc(LogicalPosition editorPos, Point point) {
|
||||
Position serverPos = computableReadAction(() -> DocumentUtils.logicalToLSPPos(editorPos, editor));
|
||||
CompletableFuture<Hover> request = wrapper.getRequestManager().hover(new HoverParams(identifier, serverPos));
|
||||
if (request == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Hover hover = request.get(Timeout.getTimeout(Timeouts.HOVER), TimeUnit.MILLISECONDS);
|
||||
wrapper.notifySuccess(Timeouts.HOVER);
|
||||
|
||||
if (hover == null) {
|
||||
LOG.debug(String.format("Hover is null for file %s and pos (%d;%d)", identifier.getUri(),
|
||||
serverPos.getLine(), serverPos.getCharacter()));
|
||||
return;
|
||||
}
|
||||
|
||||
String string = HoverHandler.getHoverString(hover);
|
||||
if (StringUtils.isEmpty(string)) {
|
||||
LOG.warn(String.format("Hover string returned is empty for file %s and pos (%d;%d)",
|
||||
identifier.getUri(), serverPos.getLine(), serverPos.getCharacter()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (getIsCtrlDown()) {
|
||||
invokeLater(() -> {
|
||||
if (!editor.isDisposed()) {
|
||||
currentHint = createAndShowEditorHint(editor, string, point, HintManager.HIDE_BY_OTHER_HINT);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
invokeLater(() -> {
|
||||
if (!editor.isDisposed()) {
|
||||
currentHint = createAndShowEditorHint(editor, string, point);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
LOG.warn(e);
|
||||
wrapper.notifyFailure(Timeouts.HOVER);
|
||||
} catch (InterruptedException | JsonRpcException | ExecutionException e) {
|
||||
LOG.warn(e);
|
||||
wrapper.crashed(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the completion suggestions given a position
|
||||
*
|
||||
|
@ -1290,8 +1144,6 @@ public class EditorEventManager {
|
|||
* Adds all the listeners
|
||||
*/
|
||||
public void registerListeners() {
|
||||
editor.addEditorMouseListener(mouseListener);
|
||||
editor.addEditorMouseMotionListener(mouseMotionListener);
|
||||
editor.getCaretModel().addCaretListener(caretListener);
|
||||
// Todo - Implement
|
||||
// editor.getSelectionModel.addSelectionListener(selectionListener)
|
||||
|
@ -1301,8 +1153,6 @@ public class EditorEventManager {
|
|||
* Removes all the listeners
|
||||
*/
|
||||
public void removeListeners() {
|
||||
editor.removeEditorMouseListener(mouseListener);
|
||||
editor.removeEditorMouseMotionListener(mouseMotionListener);
|
||||
editor.getCaretModel().removeCaretListener(caretListener);
|
||||
// Todo - Implement
|
||||
// editor.getSelectionModel.removeSelectionListener(selectionListener)
|
||||
|
@ -1444,50 +1294,58 @@ public class EditorEventManager {
|
|||
saveDocument();
|
||||
}
|
||||
}
|
||||
|
||||
// Tries to go to definition / show usages based on the element which is
|
||||
private void trySourceNavigationAndHover(EditorMouseEvent e) {
|
||||
// Tries to go to definition
|
||||
public void gotoDefinition(PsiElement element) {
|
||||
if (editor.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
val sourceOffset = element.getTextOffset();
|
||||
val loc = requestDefinition(DocumentUtils.offsetToLSPPos(editor, sourceOffset));
|
||||
|
||||
createCtrlRange(DocumentUtils.logicalToLSPPos(editor.xyToLogicalPosition(e.getMouseEvent().getPoint()), editor),
|
||||
null);
|
||||
final CtrlRangeMarker ctrlRange = getCtrlRange();
|
||||
|
||||
if (ctrlRange == null) {
|
||||
int offset = editor.logicalPositionToOffset(editor.xyToLogicalPosition(e.getMouseEvent().getPoint()));
|
||||
LSPReferencesAction referencesAction = (LSPReferencesAction) ActionManager.getInstance()
|
||||
.getAction("LSPFindUsages");
|
||||
if (referencesAction != null) {
|
||||
referencesAction.forManagerAndOffset(this, offset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Location loc = ctrlRange.location;
|
||||
invokeLater(() -> {
|
||||
if (editor.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = editor.logicalPositionToOffset(editor.xyToLogicalPosition(e.getMouseEvent().getPoint()));
|
||||
String locUri = FileUtils.sanitizeURI(loc.getUri());
|
||||
gotoLocation(loc);
|
||||
});
|
||||
}
|
||||
|
||||
if (identifier.getUri().equals(locUri)
|
||||
&& offset >= DocumentUtils.LSPPosToOffset(editor, loc.getRange().getStart())
|
||||
&& offset <= DocumentUtils.LSPPosToOffset(editor, loc.getRange().getEnd())) {
|
||||
// Tries to go to declaration / show usages based on the element which is
|
||||
public void gotoDeclarationOrUsages(PsiElement element) {
|
||||
if (editor.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
val sourceOffset = element.getTextOffset();
|
||||
val loc = requestDeclaration(DocumentUtils.offsetToLSPPos(editor, sourceOffset));
|
||||
|
||||
if (loc == null) {
|
||||
LSPReferencesAction referencesAction = (LSPReferencesAction) ActionManager.getInstance()
|
||||
.getAction("LSPFindUsages");
|
||||
if (referencesAction != null) {
|
||||
referencesAction.forManagerAndOffset(this, offset);
|
||||
referencesAction.forManagerAndOffset(this, sourceOffset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
invokeLater(() -> {
|
||||
if (editor.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String locUri = FileUtils.sanitizeURI(loc.getUri());
|
||||
|
||||
if (identifier.getUri().equals(locUri)
|
||||
&& sourceOffset >= DocumentUtils.LSPPosToOffset(editor, loc.getRange().getStart())
|
||||
&& sourceOffset <= DocumentUtils.LSPPosToOffset(editor, loc.getRange().getEnd())) {
|
||||
LSPReferencesAction referencesAction = (LSPReferencesAction) ActionManager.getInstance()
|
||||
.getAction("LSPFindUsages");
|
||||
if (referencesAction != null) {
|
||||
referencesAction.forManagerAndOffset(this, sourceOffset);
|
||||
}
|
||||
} else {
|
||||
gotoLocation(loc);
|
||||
}
|
||||
|
||||
ctrlRange.dispose();
|
||||
setCtrlRange(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1599,7 +1457,8 @@ public class EditorEventManager {
|
|||
});
|
||||
// If code actions are updated, forcefully triggers the inspection tool.
|
||||
if (codeActionSyncRequired) {
|
||||
updateErrorAnnotations();
|
||||
// double-delay the update to ensure that the code analyzer finishes.
|
||||
invokeLater(this::updateErrorAnnotations);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.falsepattern.zigbrains.lsp.utils.ApplicationUtils;
|
|||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.OSUtils;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
|
||||
import java.awt.KeyboardFocusManager;
|
||||
|
@ -36,56 +37,6 @@ public class EditorEventManagerBase {
|
|||
|
||||
private static final Map<String, Set<EditorEventManager>> uriToManagers = new ConcurrentHashMap<>();
|
||||
private static final Map<Editor, EditorEventManager> editorToManager = new ConcurrentHashMap<>();
|
||||
private static final int CTRL_KEY_CODE = OSUtils.isMac() ? KeyEvent.VK_META : KeyEvent.VK_CONTROL;
|
||||
private volatile static boolean isKeyPressed = false;
|
||||
private volatile static boolean isCtrlDown = false;
|
||||
private volatile static CtrlRangeMarker ctrlRange;
|
||||
|
||||
static {
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher((KeyEvent e) -> {
|
||||
int eventId = e.getID();
|
||||
if (eventId == KeyEvent.KEY_PRESSED) {
|
||||
setIsKeyPressed(true);
|
||||
if (e.getKeyCode() == CTRL_KEY_CODE) {
|
||||
setIsCtrlDown(true);
|
||||
}
|
||||
} else if (eventId == KeyEvent.KEY_RELEASED) {
|
||||
setIsKeyPressed(false);
|
||||
if (e.getKeyCode() == CTRL_KEY_CODE) {
|
||||
setIsCtrlDown(false);
|
||||
if (getCtrlRange() != null) {
|
||||
getCtrlRange().dispose();
|
||||
setCtrlRange(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
static synchronized CtrlRangeMarker getCtrlRange() {
|
||||
return ctrlRange;
|
||||
}
|
||||
|
||||
static synchronized void setCtrlRange(CtrlRangeMarker ctrlRange) {
|
||||
EditorEventManagerBase.ctrlRange = ctrlRange;
|
||||
}
|
||||
|
||||
static synchronized boolean getIsCtrlDown() {
|
||||
return isCtrlDown;
|
||||
}
|
||||
|
||||
static synchronized void setIsCtrlDown(boolean isCtrlDown) {
|
||||
EditorEventManagerBase.isCtrlDown = isCtrlDown;
|
||||
}
|
||||
|
||||
static synchronized boolean getIsKeyPressed() {
|
||||
return isKeyPressed;
|
||||
}
|
||||
|
||||
static synchronized void setIsKeyPressed(boolean isKeyPressed) {
|
||||
EditorEventManagerBase.isKeyPressed = isKeyPressed;
|
||||
}
|
||||
|
||||
private static void prune() {
|
||||
pruneEditorManagerMap();
|
||||
|
@ -159,12 +110,15 @@ public class EditorEventManagerBase {
|
|||
|
||||
String uri = FileUtils.editorToURIString(manager.editor);
|
||||
synchronized (uriToManagers) {
|
||||
Set<EditorEventManager> set = getEditorEventManagerCopy(uri);
|
||||
if (set.isEmpty()) {
|
||||
val managers = uriToManagers.get(uri);
|
||||
if (managers != null) {
|
||||
managers.remove(manager);
|
||||
if (managers.isEmpty()) {
|
||||
uriToManagers.remove(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param editor An editor
|
||||
|
|
|
@ -26,8 +26,6 @@ import com.falsepattern.zigbrains.lsp.contributors.icon.LSPIconProvider;
|
|||
import com.falsepattern.zigbrains.lsp.contributors.label.LSPDefaultLabelProvider;
|
||||
import com.falsepattern.zigbrains.lsp.contributors.label.LSPLabelProvider;
|
||||
import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.EditorMouseListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.EditorMouseMotionListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.LSPCaretListenerImpl;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
|
@ -69,8 +67,6 @@ public interface LSPExtensionManager {
|
|||
*/
|
||||
EditorEventManager getExtendedEditorEventManagerFor(Editor editor,
|
||||
DocumentListener documentListener,
|
||||
EditorMouseListenerImpl mouseListener,
|
||||
EditorMouseMotionListenerImpl mouseMotionListener,
|
||||
LSPCaretListenerImpl caretListener,
|
||||
RequestManager requestManager,
|
||||
ServerOptions serverOptions,
|
||||
|
|
|
@ -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.lsp.listeners;
|
||||
|
||||
import com.intellij.openapi.editor.event.EditorMouseEvent;
|
||||
import com.intellij.openapi.editor.event.EditorMouseListener;
|
||||
|
||||
/**
|
||||
* An EditorMouseListener implementation which listens to mouseExited, mouseEntered and mouseClicked events.
|
||||
*/
|
||||
public class EditorMouseListenerImpl extends LSPListener implements EditorMouseListener {
|
||||
@Override
|
||||
public void mousePressed(EditorMouseEvent editorMouseEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(EditorMouseEvent editorMouseEvent) {
|
||||
if (checkEnabled()) {
|
||||
manager.mouseClicked(editorMouseEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(EditorMouseEvent editorMouseEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(EditorMouseEvent editorMouseEvent) {
|
||||
if (checkEnabled()) {
|
||||
manager.mouseEntered();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(EditorMouseEvent editorMouseEvent) {
|
||||
if (checkEnabled()) {
|
||||
manager.mouseExited();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,37 +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.lsp.listeners;
|
||||
|
||||
import com.intellij.openapi.editor.event.EditorMouseEvent;
|
||||
import com.intellij.openapi.editor.event.EditorMouseMotionListener;
|
||||
|
||||
/**
|
||||
* Class listening for mouse movement in an editor (used for hover)
|
||||
*/
|
||||
public class EditorMouseMotionListenerImpl extends LSPListener implements EditorMouseMotionListener {
|
||||
|
||||
@Override
|
||||
public void mouseMoved(EditorMouseEvent e) {
|
||||
if (checkEnabled()) {
|
||||
manager.mouseMoved(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(EditorMouseEvent editorMouseEvent) {
|
||||
|
||||
}
|
||||
}
|
|
@ -102,28 +102,7 @@ class LSPFileEventManager {
|
|||
return;
|
||||
}
|
||||
String oldFileUri = String.format("%s/%s", oldParentUri, event.getFileName());
|
||||
|
||||
ApplicationUtils.invokeAfterPsiEvents(() -> {
|
||||
// Notifies the language server.
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(oldFileUri,
|
||||
FileUtils.projectToUri(p), FileChangeType.Deleted));
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(newFileUri,
|
||||
FileUtils.projectToUri(p), FileChangeType.Created));
|
||||
|
||||
FileUtils.findProjectsFor(file).forEach(p -> {
|
||||
// Detaches old file from the wrappers.
|
||||
Set<LanguageServerWrapper> wrappers = IntellijLanguageClient.getAllServerWrappersFor(FileUtils.projectToUri(p));
|
||||
if (wrappers != null) {
|
||||
wrappers.forEach(wrapper -> wrapper.disconnect(oldFileUri, FileUtils.projectToUri(p)));
|
||||
}
|
||||
// Re-open file to so that the new editor will be connected to the language server.
|
||||
FileEditorManager fileEditorManager = FileEditorManager.getInstance(p);
|
||||
ApplicationUtils.invokeLater(() -> {
|
||||
fileEditorManager.closeFile(file);
|
||||
fileEditorManager.openFile(file, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
closeAndReopenAffectedFile(file, oldFileUri);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("LSP file move event failed due to :", e);
|
||||
}
|
||||
|
@ -169,6 +148,16 @@ class LSPFileEventManager {
|
|||
}
|
||||
String newFileUri = FileUtils.VFSToURI(file);
|
||||
String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
|
||||
closeAndReopenAffectedFile(file, oldFileUri);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("LSP file rename event failed due to : ", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void closeAndReopenAffectedFile(VirtualFile file, String oldFileUri) {
|
||||
String newFileUri = FileUtils.VFSToURI(file);
|
||||
|
||||
// Notifies the language server.
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(oldFileUri,
|
||||
|
@ -179,17 +168,7 @@ class LSPFileEventManager {
|
|||
FileUtils.findProjectsFor(file).forEach(p -> {
|
||||
// Detaches old file from the wrappers.
|
||||
Set<LanguageServerWrapper> wrappers = IntellijLanguageClient.getAllServerWrappersFor(FileUtils.projectToUri(p));
|
||||
if (wrappers != null) {
|
||||
wrappers.forEach(wrapper -> {
|
||||
// make these calls first since the disconnect might stop the LS client if its last file.
|
||||
wrapper.getRequestManager().didChangeWatchedFiles(
|
||||
getDidChangeWatchedFilesParams(oldFileUri, FileChangeType.Deleted));
|
||||
wrapper.getRequestManager().didChangeWatchedFiles(
|
||||
getDidChangeWatchedFilesParams(newFileUri, FileChangeType.Created));
|
||||
|
||||
wrapper.disconnect(oldFileUri, FileUtils.projectToUri(p));
|
||||
});
|
||||
}
|
||||
wrappers.forEach(wrapper -> wrapper.disconnect(oldFileUri, FileUtils.projectToUri(p)));
|
||||
if (!newFileUri.equals(oldFileUri)) {
|
||||
// Re-open file to so that the new editor will be connected to the language server.
|
||||
FileEditorManager fileEditorManager = FileEditorManager.getInstance(p);
|
||||
|
@ -200,11 +179,6 @@ class LSPFileEventManager {
|
|||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("LSP file rename event failed due to : ", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a file is created. Notifies the server if needed.
|
||||
|
|
|
@ -19,7 +19,7 @@ package com.falsepattern.zigbrains.lsp.requests;
|
|||
* Enumeration for the timeouts
|
||||
*/
|
||||
public enum Timeouts {
|
||||
CODEACTION(2000), CODELENS(2000), COMPLETION(1000), DEFINITION(2000), DOC_HIGHLIGHT(1000), EXECUTE_COMMAND(
|
||||
CODEACTION(2000), CODELENS(2000), COMPLETION(1000), DECLARATION(2000), DEFINITION(2000), DOC_HIGHLIGHT(1000), EXECUTE_COMMAND(
|
||||
2000), FORMATTING(2000), HOVER(2000), INIT(10000), REFERENCES(2000), SIGNATURE(1000), SHUTDOWN(
|
||||
5000), SYMBOLS(2000), WILLSAVE(2000), FOLDING(1000), HIGHLIGHTING(1000), INLAY_HINTS(2000);
|
||||
|
||||
|
|
|
@ -90,6 +90,10 @@ public class DocumentUtils {
|
|||
});
|
||||
}
|
||||
|
||||
public static int LSPPosToOffset(Document document, Position pos) {
|
||||
return document.getLineStartOffset(pos.getLine()) + pos.getCharacter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an LSP position to an editor offset
|
||||
*
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package com.falsepattern.zigbrains.project.ide.project;
|
||||
|
||||
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
|
||||
import com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
@ -66,10 +67,14 @@ public class ZigProjectConfigurable implements Configurable {
|
|||
public void apply() throws ConfigurationException {
|
||||
val zigSettings = ZigProjectSettingsService.getInstance(project);
|
||||
val settingsData = settingsPanel.getData();
|
||||
boolean modified = isModified();
|
||||
zigSettings.modify((settings) -> {
|
||||
settings.setToolchain(settingsData.toolchain());
|
||||
settings.setExplicitPathToStd(settingsData.explicitPathToStd());
|
||||
});
|
||||
if (modified) {
|
||||
ZLSStartupActivity.initZLS(project);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.toolchain;
|
||||
|
||||
import com.falsepattern.zigbrains.project.openapi.components.ZigProjectSettingsService;
|
||||
import com.falsepattern.zigbrains.zig.environment.ZLSConfig;
|
||||
import com.falsepattern.zigbrains.zig.environment.ZLSConfigProvider;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ToolchainZLSConfigProvider implements ZLSConfigProvider {
|
||||
@Override
|
||||
public @NotNull ZLSConfig getEnvironment(Project project) {
|
||||
val projectSettings = ZigProjectSettingsService.getInstance(project);
|
||||
val toolchain = projectSettings.getToolchain();
|
||||
if (toolchain == null)
|
||||
return ZLSConfig.EMPTY;
|
||||
val projectDir = ProjectUtil.guessProjectDir(project);
|
||||
val env = toolchain.zig().getEnv(projectDir == null ? Path.of(".") : projectDir.toNioPath());
|
||||
return env.map(e -> new ZLSConfig(e.zigExecutable(), e.libDirectory())).orElse(ZLSConfig.EMPTY);
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@
|
|||
<extensions defaultExtensionNs="com.falsepattern.zigbrains">
|
||||
<toolchainFlavour implementation="com.falsepattern.zigbrains.project.toolchain.flavours.ZigSystemPathToolchainFlavour"/>
|
||||
<toolchainProvider implementation="com.falsepattern.zigbrains.project.toolchain.LocalZigToolchainProvider"/>
|
||||
<zlsConfigProvider implementation="com.falsepattern.zigbrains.project.toolchain.ToolchainZLSConfigProvider"/>
|
||||
</extensions>
|
||||
|
||||
<actions>
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.environment;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public record ZLSConfig(@NotNull Optional<String> zigExePath,
|
||||
@NotNull Optional<String> zigLibPath) {
|
||||
public ZLSConfig(String zigExePath, String zigLibPath) {
|
||||
this(Optional.ofNullable(zigExePath), Optional.ofNullable(zigLibPath));
|
||||
}
|
||||
|
||||
public ZLSConfig overrideWith(ZLSConfig other) {
|
||||
return new ZLSConfig(other.zigExePath.or(() -> zigExePath),
|
||||
other.zigLibPath.or(() -> zigLibPath));
|
||||
}
|
||||
|
||||
public static final ZLSConfig EMPTY = new ZLSConfig(Optional.empty(), Optional.empty());
|
||||
|
||||
public JsonObject toJson() {
|
||||
val result = new JsonObject();
|
||||
zigExePath.ifPresent(s -> result.addProperty("zig_exe_path", s));
|
||||
zigLibPath.ifPresent(s -> result.addProperty("zig_lib_path", s));
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -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.zig.environment;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface ZLSConfigProvider {
|
||||
ExtensionPointName<ZLSConfigProvider> EXTENSION_POINT_NAME = ExtensionPointName.create("com.falsepattern.zigbrains.zlsConfigProvider");
|
||||
|
||||
static @NotNull ZLSConfig findEnvironment(Project project) {
|
||||
return EXTENSION_POINT_NAME.getExtensionList()
|
||||
.stream()
|
||||
.map(it -> it.getEnvironment(project))
|
||||
.reduce(ZLSConfig.EMPTY, ZLSConfig::overrideWith);
|
||||
}
|
||||
|
||||
@NotNull ZLSConfig getEnvironment(Project project);
|
||||
}
|
|
@ -23,17 +23,20 @@ import com.falsepattern.zigbrains.lsp.editor.EditorEventManager;
|
|||
import com.falsepattern.zigbrains.lsp.listeners.LSPCaretListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.requests.Timeouts;
|
||||
import com.falsepattern.zigbrains.zig.ide.SemaEdit;
|
||||
import com.falsepattern.zigbrains.zig.util.HighlightingUtil;
|
||||
import com.falsepattern.zigbrains.zig.util.TokenDecoder;
|
||||
import com.intellij.lang.annotation.Annotation;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.event.EditorMouseListener;
|
||||
import com.intellij.openapi.editor.event.EditorMouseMotionListener;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.InsertReplaceEdit;
|
||||
import org.eclipse.lsp4j.SemanticTokens;
|
||||
import org.eclipse.lsp4j.SemanticTokensDelta;
|
||||
import org.eclipse.lsp4j.SemanticTokensDeltaParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensEdit;
|
||||
import org.eclipse.lsp4j.SemanticTokensParams;
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.eclipse.lsp4j.jsonrpc.JsonRpcException;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
|
||||
|
@ -49,8 +52,8 @@ import static com.falsepattern.zigbrains.lsp.requests.Timeout.getTimeout;
|
|||
public class ZLSEditorEventManager extends EditorEventManager {
|
||||
private static String previousResultID = null;
|
||||
|
||||
public ZLSEditorEventManager(Editor editor, DocumentListener documentListener, EditorMouseListener mouseListener, EditorMouseMotionListener mouseMotionListener, LSPCaretListenerImpl caretListener, RequestManager requestmanager, ServerOptions serverOptions, LanguageServerWrapper wrapper) {
|
||||
super(editor, documentListener, mouseListener, mouseMotionListener, caretListener, requestmanager,
|
||||
public ZLSEditorEventManager(Editor editor, DocumentListener documentListener, LSPCaretListenerImpl caretListener, RequestManager requestmanager, ServerOptions serverOptions, LanguageServerWrapper wrapper) {
|
||||
super(editor, documentListener, caretListener, requestmanager,
|
||||
serverOptions, wrapper);
|
||||
}
|
||||
|
||||
|
@ -108,4 +111,15 @@ public class ZLSEditorEventManager extends EditorEventManager {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable getEditsRunnable(int version, List<Either<TextEdit, InsertReplaceEdit>> edits, String name, boolean setCaret) {
|
||||
val run = super.getEditsRunnable(version, edits, name, setCaret);
|
||||
return () -> {
|
||||
run.run();
|
||||
if (!editor.isDisposed()) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> HighlightingUtil.refreshHighlighting(this));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ import com.falsepattern.zigbrains.lsp.client.languageserver.ServerOptions;
|
|||
import com.falsepattern.zigbrains.lsp.client.languageserver.requestmanager.RequestManager;
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.wrapper.LanguageServerWrapper;
|
||||
import com.falsepattern.zigbrains.lsp.extensions.LSPExtensionManager;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.EditorMouseListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.EditorMouseMotionListenerImpl;
|
||||
import com.falsepattern.zigbrains.lsp.listeners.LSPCaretListenerImpl;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
|
@ -37,8 +35,8 @@ public class ZLSExtensionManager implements LSPExtensionManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ZLSEditorEventManager getExtendedEditorEventManagerFor(Editor editor, DocumentListener documentListener, EditorMouseListenerImpl mouseListener, EditorMouseMotionListenerImpl mouseMotionListener, LSPCaretListenerImpl caretListener, RequestManager requestManager, ServerOptions serverOptions, LanguageServerWrapper wrapper) {
|
||||
return new ZLSEditorEventManager(editor, documentListener, mouseListener, mouseMotionListener,
|
||||
public ZLSEditorEventManager getExtendedEditorEventManagerFor(Editor editor, DocumentListener documentListener, LSPCaretListenerImpl caretListener, RequestManager requestManager, ServerOptions serverOptions, LanguageServerWrapper wrapper) {
|
||||
return new ZLSEditorEventManager(editor, documentListener,
|
||||
caretListener, requestManager, serverOptions, wrapper);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,18 +18,23 @@ package com.falsepattern.zigbrains.zig.lsp;
|
|||
|
||||
import com.falsepattern.zigbrains.lsp.IntellijLanguageClient;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.falsepattern.zigbrains.zig.environment.ZLSConfigProvider;
|
||||
import com.falsepattern.zigbrains.zig.settings.ZLSSettingsState;
|
||||
import com.google.gson.Gson;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.startup.ProjectActivity;
|
||||
import kotlin.Unit;
|
||||
import kotlin.coroutines.Continuation;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
|
@ -37,6 +42,7 @@ import java.util.ArrayList;
|
|||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class ZLSStartupActivity implements ProjectActivity {
|
||||
private static final Logger LOG = Logger.getInstance(ZLSStartupActivity.class);
|
||||
private static final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public static void initZLS(Project project) {
|
||||
|
@ -59,12 +65,31 @@ public class ZLSStartupActivity implements ProjectActivity {
|
|||
var configPath = settings.zlsConfigPath;
|
||||
boolean configOK = true;
|
||||
if (!"".equals(configPath) && !validatePath("ZLS Config", configPath, false)) {
|
||||
configOK = false;
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Nag", "Using default config path.",
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "Using default config path.",
|
||||
NotificationType.INFORMATION));
|
||||
configPath = "";
|
||||
}
|
||||
if ("".equals(configPath)) {
|
||||
blk:
|
||||
try {
|
||||
val tmpFile = Files.createTempFile("zigbrains-zls-autoconf", ".json");
|
||||
val config = ZLSConfigProvider.findEnvironment(project);
|
||||
if (config.zigExePath().isEmpty() && config.zigLibPath().isEmpty()) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "(ZLS) Failed to detect zig path from project toolchain", NotificationType.WARNING));
|
||||
configOK = false;
|
||||
break blk;
|
||||
}
|
||||
try (val writer = Files.newBufferedWriter(tmpFile)) {
|
||||
val gson = new Gson();
|
||||
gson.toJson(config.toJson(), writer);
|
||||
}
|
||||
configPath = tmpFile.toAbsolutePath().toString();
|
||||
} catch (IOException e) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "Failed to create automatic zls config file",
|
||||
NotificationType.WARNING));
|
||||
LOG.warn(e);
|
||||
configOK = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (IntellijLanguageClient.getExtensionManagerFor("zig") == null) {
|
||||
|
@ -112,18 +137,18 @@ public class ZLSStartupActivity implements ProjectActivity {
|
|||
path = Path.of(pathTxt);
|
||||
} catch (InvalidPathException e) {
|
||||
Notifications.Bus.notify(
|
||||
new Notification("ZigBrains.Nag", "No " + name, "Invalid " + name + " path \"" + pathTxt + "\"",
|
||||
new Notification("ZigBrains.ZLS", "No " + name, "Invalid " + name + " path \"" + pathTxt + "\"",
|
||||
NotificationType.ERROR));
|
||||
return false;
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Nag", "No " + name,
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name,
|
||||
"The " + name + " at \"" + pathTxt + "\" doesn't exist!",
|
||||
NotificationType.ERROR));
|
||||
return false;
|
||||
}
|
||||
if (Files.isDirectory(path) != dir) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Nag", "No " + name,
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No " + name,
|
||||
"The " + name + " at \"" + pathTxt + "\" is a " +
|
||||
(Files.isDirectory(path) ? "directory" : "file") +
|
||||
" , expected a " + (dir ? "directory" : "file"),
|
||||
|
@ -146,8 +171,8 @@ public class ZLSStartupActivity implements ProjectActivity {
|
|||
zlsPath = settings.zlsPath = thePath.get();
|
||||
}
|
||||
}
|
||||
if ("".equals(zlsPath)) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.Nag", "No ZLS binary",
|
||||
if (zlsPath.isEmpty()) {
|
||||
Notifications.Bus.notify(new Notification("ZigBrains.ZLS", "No ZLS binary",
|
||||
"Please configure the path to the zls executable in the Zig language configuration menu!",
|
||||
NotificationType.INFORMATION));
|
||||
return null;
|
||||
|
|
|
@ -46,7 +46,7 @@ public class ZLSSettingsComponent {
|
|||
.addVerticalGap(10)
|
||||
.addLabeledComponent(new JBLabel("ZLS path: "), zlsPathText, 1, false)
|
||||
.addComponent(autodetectZls)
|
||||
.addLabeledComponent(new JBLabel("ZLS config path (leave empty to use default): "),
|
||||
.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: "),
|
||||
|
|
|
@ -105,7 +105,9 @@
|
|||
|
||||
<postStartupActivity implementation="com.falsepattern.zigbrains.zig.lsp.ZLSStartupActivity"/>
|
||||
<notificationGroup displayType="BALLOON"
|
||||
id="ZigBrains.Nag"/>
|
||||
bundle="zigbrains.zig.Bundle"
|
||||
key="notif-zls"
|
||||
id="ZigBrains.ZLS"/>
|
||||
</extensions>
|
||||
|
||||
|
||||
|
@ -121,6 +123,10 @@
|
|||
</action>
|
||||
<action class="com.falsepattern.zigbrains.lsp.actions.LSPReformatAction" id="ReformatCode" use-shortcut-of="ReformatCode"
|
||||
overrides="true" text="Reformat Code"/>
|
||||
<action class="com.falsepattern.zigbrains.lsp.actions.LSPGotoDeclarationAction" id="GotoDeclaration" use-shortcut-of="GotoDeclaration"
|
||||
overrides="true" text="Go to Declaration or Usages"/>
|
||||
<action class="com.falsepattern.zigbrains.lsp.actions.LSPGotoDefinitionAction" id="QuickImplementations" use-shortcut-of="QuickImplementations"
|
||||
overrides="true" text="View Implementations"/>
|
||||
<action class="com.falsepattern.zigbrains.lsp.actions.LSPShowReformatDialogAction" id="ShowReformatFileDialog"
|
||||
use-shortcut-of="ShowReformatFileDialog" overrides="true" text="Show Reformat File Dialog"/>
|
||||
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
inlayprovider=ZLS Inlay Provider
|
||||
notif-zls=ZLS Error Notification
|
|
@ -11,6 +11,7 @@
|
|||
<xi:include href="/META-INF/zigbrains-project.xml"/>
|
||||
<!--suppress PluginXmlValidity -->
|
||||
<depends optional="true" config-file="zigbrains-zig-debugger.xml">com.intellij.modules.cidr.debugger</depends>
|
||||
<depends optional="true" config-file="zigbrains-zig-cpp.xml">com.intellij.modules.clion</depends>
|
||||
|
||||
<extensionPoints>
|
||||
<!-- region zigbrains-project -->
|
||||
|
@ -20,6 +21,12 @@
|
|||
<extensionPoint
|
||||
interface="com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider" dynamic="true"
|
||||
name="toolchainProvider"/>
|
||||
<extensionPoint
|
||||
interface="com.falsepattern.zigbrains.zig.environment.ZLSConfigProvider" dynamic="true"
|
||||
name="zlsConfigProvider"/>
|
||||
<extensionPoint
|
||||
interface="com.falsepattern.zigbrains.zig.debugbridge.DebuggerDriverProvider" dynamic="true"
|
||||
name="debuggerDriverProvider"/>
|
||||
<!-- endregion zigbrains-project -->
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
|
|
Loading…
Add table
Reference in a new issue