backport: 13.1.0
ci: 13.1.0 feat: Grammar error recovery (cherry picked from commit1ff76b15a7
) fix: Update to latest zig grammar (cherry picked from commit4b2e9b44f6
) fix: NPE in executeCommands (cherry picked from commit99a0e7811d
) fix: Autocomplete wasn't being done by intellij (cherry picked from commit7754e8f2c8
) fix: LSP id collision (cherry picked from commitb8aa355392
) docs: Update changelog (cherry picked from commit6cb8ebebb7
) feat: Integrated parameter info popup (cherry picked from commit868a83326a
) fix: Better indenting logic (cherry picked from commita266ff94c3
)
This commit is contained in:
parent
a0eb491f2c
commit
94a52ac944
7 changed files with 220 additions and 130 deletions
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -18,6 +18,24 @@ Changelog structure reference:
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [13.1.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Zig
|
||||||
|
- Parameter info (CTRL + P) is now properly integrated
|
||||||
|
- Parser error recovery (completion will still work even with missing semicolons in a statement)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- LSP
|
||||||
|
- The registry IDs of some of the LSP handlers were colliding with the Rust intellij plugin
|
||||||
|
- Autocompletion insertion is now fully handled by intellij, this should fix some of the weirdness
|
||||||
|
|
||||||
|
- Zig
|
||||||
|
- Indent support for function parameters and struct initializers
|
||||||
|
- Updated to latest grammar spec (https://github.com/ziglang/zig-spec/commit/78c2e2e5cfa7090965deaf631cb8ca6f405b7c42)
|
||||||
|
|
||||||
## [13.0.1]
|
## [13.0.1]
|
||||||
|
|
||||||
### HOTFIX CHANGES
|
### HOTFIX CHANGES
|
||||||
|
|
|
@ -35,7 +35,6 @@ import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||||
import com.falsepattern.zigbrains.lsp.utils.GUIUtils;
|
import com.falsepattern.zigbrains.lsp.utils.GUIUtils;
|
||||||
import com.intellij.codeInsight.completion.InsertionContext;
|
import com.intellij.codeInsight.completion.InsertionContext;
|
||||||
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
|
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
|
||||||
import com.intellij.codeInsight.hint.HintManager;
|
|
||||||
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
|
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
|
||||||
import com.intellij.codeInsight.lookup.LookupElement;
|
import com.intellij.codeInsight.lookup.LookupElement;
|
||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||||
|
@ -96,7 +95,6 @@ import org.eclipse.lsp4j.InsertReplaceEdit;
|
||||||
import org.eclipse.lsp4j.InsertTextFormat;
|
import org.eclipse.lsp4j.InsertTextFormat;
|
||||||
import org.eclipse.lsp4j.Location;
|
import org.eclipse.lsp4j.Location;
|
||||||
import org.eclipse.lsp4j.LocationLink;
|
import org.eclipse.lsp4j.LocationLink;
|
||||||
import org.eclipse.lsp4j.MarkupContent;
|
|
||||||
import org.eclipse.lsp4j.Position;
|
import org.eclipse.lsp4j.Position;
|
||||||
import org.eclipse.lsp4j.Range;
|
import org.eclipse.lsp4j.Range;
|
||||||
import org.eclipse.lsp4j.ReferenceContext;
|
import org.eclipse.lsp4j.ReferenceContext;
|
||||||
|
@ -144,7 +142,6 @@ import static com.falsepattern.zigbrains.common.util.ApplicationUtil.invokeLater
|
||||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.pool;
|
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.pool;
|
||||||
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.writeAction;
|
import static com.falsepattern.zigbrains.common.util.ApplicationUtil.writeAction;
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.DocumentUtils.toEither;
|
import static com.falsepattern.zigbrains.lsp.utils.DocumentUtils.toEither;
|
||||||
import static com.falsepattern.zigbrains.lsp.utils.GUIUtils.createAndShowEditorHint;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class handling events related to an Editor (a Document)
|
* Class handling events related to an Editor (a Document)
|
||||||
|
@ -236,17 +233,6 @@ public class EditorEventManager {
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls onTypeFormatting or signatureHelp if the character typed was a trigger character
|
|
||||||
*
|
|
||||||
* @param c The character just typed
|
|
||||||
*/
|
|
||||||
public void characterTyped(char c) {
|
|
||||||
if (signatureTriggers.contains(Character.toString(c))) {
|
|
||||||
signatureHelp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSupportedLanguageFile(PsiFile file) {
|
private boolean isSupportedLanguageFile(PsiFile file) {
|
||||||
return file.getLanguage().isKindOf(PlainTextLanguage.INSTANCE)
|
return file.getLanguage().isKindOf(PlainTextLanguage.INSTANCE)
|
||||||
|| FileUtils.isFileSupported(file.getVirtualFile());
|
|| FileUtils.isFileSupported(file.getVirtualFile());
|
||||||
|
@ -518,70 +504,40 @@ public class EditorEventManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public CompletableFuture<SignatureHelp> getSignatureHelp(int offset) {
|
||||||
* Calls signatureHelp at the current editor caret position
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public void signatureHelp() {
|
|
||||||
if (editor.isDisposed()) {
|
if (editor.isDisposed()) {
|
||||||
return;
|
return CompletableFuture.failedFuture(new RuntimeException("Editor is disposed"));
|
||||||
}
|
}
|
||||||
LogicalPosition lPos = editor.getCaretModel().getCurrentCaret().getLogicalPosition();
|
SignatureHelpParams params = new SignatureHelpParams(identifier, DocumentUtils.offsetToLSPPos(editor, offset));
|
||||||
Point point = editor.logicalPositionToXY(lPos);
|
val result = new CompletableFuture<SignatureHelp>();
|
||||||
SignatureHelpParams params = new SignatureHelpParams(identifier, DocumentUtils.logicalToLSPPos(lPos, editor));
|
|
||||||
pool(() -> {
|
pool(() -> {
|
||||||
CompletableFuture<SignatureHelp> future = wrapper.getRequestManager().signatureHelp(params);
|
val future = wrapper.getRequestManager().signatureHelp(params);
|
||||||
if (future == null) {
|
if (future == null) {
|
||||||
|
result.complete(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
SignatureHelp signatureResp = future.get(Timeout.getTimeout(Timeouts.SIGNATURE), TimeUnit.MILLISECONDS);
|
val signatureResp = future.get(Timeout.getTimeout(Timeouts.SIGNATURE), TimeUnit.MILLISECONDS);
|
||||||
wrapper.notifySuccess(Timeouts.SIGNATURE);
|
wrapper.notifySuccess(Timeouts.SIGNATURE);
|
||||||
if (signatureResp == null) {
|
if (signatureResp == null) {
|
||||||
|
result.complete(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<SignatureInformation> signatures = signatureResp.getSignatures();
|
result.complete(signatureResp);
|
||||||
if (signatures == null || signatures.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int activeSignatureIndex = signatureResp.getActiveSignature();
|
|
||||||
int activeParameterIndex = signatureResp.getActiveParameter();
|
|
||||||
|
|
||||||
String activeParameter = signatures.get(activeSignatureIndex).getParameters().size() > activeParameterIndex ?
|
|
||||||
extractLabel(signatures.get(activeSignatureIndex), signatures.get(activeSignatureIndex).getParameters().get(activeParameterIndex).getLabel()) : "";
|
|
||||||
Either<String, MarkupContent> signatureDescription = signatures.get(activeSignatureIndex).getDocumentation();
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
builder.append("<html>");
|
|
||||||
if (signatureDescription == null) {
|
|
||||||
builder.append("<b>").append(signatures.get(activeSignatureIndex).getLabel().
|
|
||||||
replace(" " + activeParameter, String.format("<font color=\"orange\"> %s</font>",
|
|
||||||
activeParameter))).append("</b>");
|
|
||||||
} else if (signatureDescription.isLeft()) {
|
|
||||||
// Todo - Add parameter Documentation
|
|
||||||
String descriptionLeft = signatureDescription.getLeft().replace(System.lineSeparator(), "<br />");
|
|
||||||
builder.append("<b>").append(signatures.get(activeSignatureIndex).getLabel()
|
|
||||||
.replace(" " + activeParameter, String.format("<font color=\"orange\"> %s</font>",
|
|
||||||
activeParameter))).append("</b>");
|
|
||||||
builder.append("<div>").append(descriptionLeft).append("</div>");
|
|
||||||
} else if (signatureDescription.isRight()) {
|
|
||||||
// Todo - Add marked content parsing
|
|
||||||
builder.append("<b>").append(signatures.get(activeSignatureIndex).getLabel()).append("</b>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.append("</html>");
|
|
||||||
invokeLater(() -> currentHint = createAndShowEditorHint(editor, builder.toString(), point, HintManager.UNDER, HintManager.HIDE_BY_OTHER_HINT));
|
|
||||||
|
|
||||||
} catch (TimeoutException e) {
|
} catch (TimeoutException e) {
|
||||||
LOG.warn(e);
|
LOG.warn(e);
|
||||||
wrapper.notifyFailure(Timeouts.SIGNATURE);
|
wrapper.notifyFailure(Timeouts.SIGNATURE);
|
||||||
|
result.completeExceptionally(e);
|
||||||
} catch (JsonRpcException | ExecutionException | InterruptedException e) {
|
} catch (JsonRpcException | ExecutionException | InterruptedException e) {
|
||||||
LOG.warn(e);
|
LOG.warn(e);
|
||||||
wrapper.crashed(e);
|
wrapper.crashed(e);
|
||||||
|
result.completeExceptionally(e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("Internal error occurred when processing signature help");
|
LOG.warn("Internal error occurred when processing signature help");
|
||||||
|
result.completeExceptionally(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractLabel(SignatureInformation signatureInformation, Either<String, Tuple.Two<Integer, Integer>> label) {
|
private String extractLabel(SignatureInformation signatureInformation, Either<String, Tuple.Two<Integer, Integer>> label) {
|
||||||
|
@ -808,8 +764,6 @@ public class EditorEventManager {
|
||||||
|
|
||||||
if (addTextEdits != null) {
|
if (addTextEdits != null) {
|
||||||
builder = builder.withInsertHandler((InsertionContext context, LookupElement lookupElement) -> invokeLater(() -> {
|
builder = builder.withInsertHandler((InsertionContext context, LookupElement lookupElement) -> invokeLater(() -> {
|
||||||
applyInitialTextEdit(item, context, lookupString);
|
|
||||||
|
|
||||||
if (format == InsertTextFormat.Snippet) {
|
if (format == InsertTextFormat.Snippet) {
|
||||||
context.commitDocument();
|
context.commitDocument();
|
||||||
prepareAndRunSnippet(lookupString);
|
prepareAndRunSnippet(lookupString);
|
||||||
|
@ -823,8 +777,6 @@ public class EditorEventManager {
|
||||||
}));
|
}));
|
||||||
} else if (command != null) {
|
} else if (command != null) {
|
||||||
builder = builder.withInsertHandler((InsertionContext context, LookupElement lookupElement) -> {
|
builder = builder.withInsertHandler((InsertionContext context, LookupElement lookupElement) -> {
|
||||||
applyInitialTextEdit(item, context, lookupString);
|
|
||||||
|
|
||||||
if (format == InsertTextFormat.Snippet) {
|
if (format == InsertTextFormat.Snippet) {
|
||||||
context.commitDocument();
|
context.commitDocument();
|
||||||
prepareAndRunSnippet(lookupString);
|
prepareAndRunSnippet(lookupString);
|
||||||
|
@ -834,8 +786,6 @@ public class EditorEventManager {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
builder = builder.withInsertHandler((InsertionContext context, LookupElement lookupElement) -> {
|
builder = builder.withInsertHandler((InsertionContext context, LookupElement lookupElement) -> {
|
||||||
applyInitialTextEdit(item, context, lookupString);
|
|
||||||
|
|
||||||
if (format == InsertTextFormat.Snippet) {
|
if (format == InsertTextFormat.Snippet) {
|
||||||
context.commitDocument();
|
context.commitDocument();
|
||||||
prepareAndRunSnippet(lookupString);
|
prepareAndRunSnippet(lookupString);
|
||||||
|
@ -846,37 +796,6 @@ public class EditorEventManager {
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyInitialTextEdit(CompletionItem item, InsertionContext context, String lookupString) {
|
|
||||||
if (item.getTextEdit() != null) {
|
|
||||||
// remove intellij edit, server is controlling insertion
|
|
||||||
writeAction(() -> {
|
|
||||||
Runnable runnable = () -> this.editor.getDocument().deleteString(context.getStartOffset(), context.getTailOffset());
|
|
||||||
|
|
||||||
CommandProcessor.getInstance()
|
|
||||||
.executeCommand(project, runnable, "Removing Intellij Completion", "LSPPlugin", editor.getDocument());
|
|
||||||
});
|
|
||||||
context.commitDocument();
|
|
||||||
|
|
||||||
if(item.getTextEdit().isLeft()) {
|
|
||||||
item.getTextEdit().getLeft().setNewText(getLookupStringWithoutPlaceholders(item, lookupString));
|
|
||||||
}
|
|
||||||
|
|
||||||
applyEdit(Integer.MAX_VALUE, Collections.singletonList(item.getTextEdit()), "text edit", false, true);
|
|
||||||
} else {
|
|
||||||
// client handles insertion, determine a prefix (to allow completions of partially matching items)
|
|
||||||
int prefixLength = getCompletionPrefixLength(context.getStartOffset());
|
|
||||||
|
|
||||||
writeAction(() -> {
|
|
||||||
Runnable runnable = () -> this.editor.getDocument().deleteString(context.getStartOffset() - prefixLength, context.getStartOffset());
|
|
||||||
|
|
||||||
CommandProcessor.getInstance()
|
|
||||||
.executeCommand(project, runnable, "Removing Prefix", "LSPPlugin", editor.getDocument());
|
|
||||||
});
|
|
||||||
context.commitDocument();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getCompletionPrefixLength(int offset) {
|
private int getCompletionPrefixLength(int offset) {
|
||||||
return getCompletionPrefix(this.editor, offset).length();
|
return getCompletionPrefix(this.editor, offset).length();
|
||||||
}
|
}
|
||||||
|
@ -1107,6 +1026,8 @@ public class EditorEventManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
commands.stream().map(c -> {
|
commands.stream().map(c -> {
|
||||||
|
if (c == null)
|
||||||
|
return null;
|
||||||
ExecuteCommandParams params = new ExecuteCommandParams();
|
ExecuteCommandParams params = new ExecuteCommandParams();
|
||||||
params.setArguments(c.getArguments());
|
params.setArguments(c.getArguments());
|
||||||
params.setCommand(c.getCommand());
|
params.setCommand(c.getCommand());
|
||||||
|
|
|
@ -29,22 +29,8 @@ import org.jetbrains.annotations.NotNull;
|
||||||
* This class notifies an EditorEventManager that a character has been typed in the editor
|
* This class notifies an EditorEventManager that a character has been typed in the editor
|
||||||
*/
|
*/
|
||||||
public class LSPTypedHandler extends TypedHandlerDelegate {
|
public class LSPTypedHandler extends TypedHandlerDelegate {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result charTyped(char c, Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
public @NotNull Result checkAutoPopup(char charTyped, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||||
if (!FileUtils.isFileSupported(file.getVirtualFile())) {
|
|
||||||
return Result.CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorEventManager eventManager = EditorEventManagerBase.forEditor(editor);
|
|
||||||
if (eventManager != null) {
|
|
||||||
eventManager.characterTyped(c);
|
|
||||||
}
|
|
||||||
return Result.CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Result checkAutoPopup(char charTyped, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
|
||||||
if (!FileUtils.isFileSupported(file.getVirtualFile())) {
|
if (!FileUtils.isFileSupported(file.getVirtualFile())) {
|
||||||
return Result.CONTINUE;
|
return Result.CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,13 +188,11 @@ VarDeclProto ::= (KEYWORD_CONST | KEYWORD_VAR) IDENTIFIER (COLON TypeExpr)? Byte
|
||||||
|
|
||||||
GlobalVarDecl ::= VarDeclProto (EQUAL Expr)? SEMICOLON
|
GlobalVarDecl ::= VarDeclProto (EQUAL Expr)? SEMICOLON
|
||||||
|
|
||||||
ContainerField
|
ContainerField ::= DOC_COMMENT? KEYWORD_COMPTUME? !KEYWORD_FN (IDENTIFIER COLON)? TypeExpr ByteAlign? (EQUAL Expr)?
|
||||||
::= DOC_COMMENT? KEYWORD_COMPTIME? IDENTIFIER (COLON (KEYWORD_ANYTYPE | TypeExpr) ByteAlign?)? (EQUAL Expr)?
|
|
||||||
| DOC_COMMENT? KEYWORD_COMPTIME? (IDENTIFIER COLON)? !KEYWORD_FN TypeExpr ByteAlign? (EQUAL Expr)?
|
|
||||||
|
|
||||||
// *** Block Level ***
|
// *** Block Level ***
|
||||||
Statement
|
Statement
|
||||||
::= KEYWORD_COMPTIME? ComptimeStatement
|
::= KEYWORD_COMPTIME ComptimeStatement
|
||||||
| KEYWORD_NOSUSPEND BlockExprStatement
|
| KEYWORD_NOSUSPEND BlockExprStatement
|
||||||
| KEYWORD_DEFER BlockExprStatement
|
| KEYWORD_DEFER BlockExprStatement
|
||||||
| KEYWORD_ERRDEFER Payload? BlockExprStatement
|
| KEYWORD_ERRDEFER Payload? BlockExprStatement
|
||||||
|
@ -274,7 +272,9 @@ PrimaryExpr
|
||||||
|
|
||||||
IfExpr ::= IfPrefix Expr (KEYWORD_ELSE Payload? Expr)?
|
IfExpr ::= IfPrefix Expr (KEYWORD_ELSE Payload? Expr)?
|
||||||
|
|
||||||
Block ::= LBRACE Statement* RBRACE
|
Block ::= LBRACE ZB_Block_Statement* RBRACE {pin(".*")=1}
|
||||||
|
|
||||||
|
private ZB_Block_Statement ::= Statement {recoverWhile="#auto"}
|
||||||
|
|
||||||
LoopExpr ::= KEYWORD_INLINE? (ForExpr | WhileExpr)
|
LoopExpr ::= KEYWORD_INLINE? (ForExpr | WhileExpr)
|
||||||
|
|
||||||
|
@ -285,9 +285,12 @@ WhileExpr ::= WhilePrefix Expr (KEYWORD_ELSE Payload? Expr)?
|
||||||
CurlySuffixExpr ::= TypeExpr InitList?
|
CurlySuffixExpr ::= TypeExpr InitList?
|
||||||
|
|
||||||
InitList
|
InitList
|
||||||
::= LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
|
::= LBRACE FieldInit (COMMA ZB_InitList_FieldInit)* COMMA? RBRACE
|
||||||
| LBRACE Expr (COMMA Expr)* COMMA? RBRACE
|
| LBRACE Expr (COMMA ZB_InitList_Expr)* COMMA? RBRACE
|
||||||
| LBRACE RBRACE
|
| LBRACE RBRACE {pin=1}
|
||||||
|
|
||||||
|
private ZB_InitList_FieldInit ::= FieldInit {recoverWhile="#auto"}
|
||||||
|
private ZB_InitList_Expr ::= Expr {recoverWhile="#auto"}
|
||||||
|
|
||||||
TypeExpr ::= PrefixTypeOp* ErrorUnionExpr
|
TypeExpr ::= PrefixTypeOp* ErrorUnionExpr
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* 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.completion;
|
||||||
|
|
||||||
|
import com.falsepattern.zigbrains.lsp.editor.EditorEventManagerBase;
|
||||||
|
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||||
|
import com.falsepattern.zigbrains.zig.psi.ZigExprList;
|
||||||
|
import com.intellij.lang.parameterInfo.CreateParameterInfoContext;
|
||||||
|
import com.intellij.lang.parameterInfo.ParameterInfoHandler;
|
||||||
|
import com.intellij.lang.parameterInfo.ParameterInfoUIContext;
|
||||||
|
import com.intellij.lang.parameterInfo.UpdateParameterInfoContext;
|
||||||
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
|
import com.intellij.psi.PsiFile;
|
||||||
|
import com.intellij.psi.PsiWhiteSpace;
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil;
|
||||||
|
import lombok.val;
|
||||||
|
import org.eclipse.lsp4j.ParameterInformation;
|
||||||
|
import org.eclipse.lsp4j.SignatureHelp;
|
||||||
|
import org.eclipse.lsp4j.SignatureInformation;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
public class ZigParameterInfoHandler implements ParameterInfoHandler<PsiElement, ZigParameterInfoHandler.IndexedSignatureInformation> {
|
||||||
|
private final WeakHashMap<PsiElement, CompletableFuture<SignatureHelp>> requests = new WeakHashMap<>();
|
||||||
|
private final Logger LOG = Logger.getInstance(ZigParameterInfoHandler.class);
|
||||||
|
|
||||||
|
public record IndexedSignatureInformation(SignatureInformation information, boolean active) {}
|
||||||
|
|
||||||
|
private @Nullable PsiElement fetchQuery(@NotNull PsiFile file, int offset) {
|
||||||
|
val sourceElem = file.findElementAt(offset);
|
||||||
|
if (sourceElem == null)
|
||||||
|
return null;
|
||||||
|
PsiElement element = PsiTreeUtil.getParentOfType(sourceElem, ZigExprList.class);
|
||||||
|
if (element == null) {
|
||||||
|
element = sourceElem.getPrevSibling();
|
||||||
|
while (element instanceof PsiWhiteSpace)
|
||||||
|
element = element.getPrevSibling();
|
||||||
|
if (!(element instanceof ZigExprList))
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
val editor = FileUtils.editorFromPsiFile(file);
|
||||||
|
if (editor == null)
|
||||||
|
return null;
|
||||||
|
val manager = EditorEventManagerBase.forEditor(editor);
|
||||||
|
if (manager == null)
|
||||||
|
return null;
|
||||||
|
val request = manager.getSignatureHelp(offset);
|
||||||
|
requests.put(element, request);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable SignatureHelp getResponse(PsiElement element) {
|
||||||
|
val request = requests.get(element);
|
||||||
|
if (request == null)
|
||||||
|
return null;
|
||||||
|
final SignatureHelp response;
|
||||||
|
try {
|
||||||
|
response = request.get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOG.warn(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable PsiElement findElementForParameterInfo(@NotNull CreateParameterInfoContext context) {
|
||||||
|
return fetchQuery(context.getFile(), context.getOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showParameterInfo(@NotNull PsiElement element, @NotNull CreateParameterInfoContext context) {
|
||||||
|
val response = getResponse(element);
|
||||||
|
if (response == null)
|
||||||
|
return;
|
||||||
|
val signatures = response.getSignatures();
|
||||||
|
val indexedSignatures = new IndexedSignatureInformation[signatures.size()];
|
||||||
|
val active = response.getActiveSignature();
|
||||||
|
for (int i = 0; i < indexedSignatures.length; i++) {
|
||||||
|
indexedSignatures[i] = new IndexedSignatureInformation(signatures.get(i), i == active);
|
||||||
|
}
|
||||||
|
context.setItemsToShow(indexedSignatures);
|
||||||
|
context.showHint(element, 0, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable PsiElement findElementForUpdatingParameterInfo(@NotNull UpdateParameterInfoContext context) {
|
||||||
|
return fetchQuery(context.getFile(), context.getOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateParameterInfo(@NotNull PsiElement psiElement, @NotNull UpdateParameterInfoContext context) {
|
||||||
|
val response = getResponse(psiElement);
|
||||||
|
if (response == null)
|
||||||
|
return;
|
||||||
|
context.setCurrentParameter(response.getActiveParameter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateUI(IndexedSignatureInformation p, @NotNull ParameterInfoUIContext context) {
|
||||||
|
if (p == null) {
|
||||||
|
context.setUIComponentEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
val txt = new StringBuilder();
|
||||||
|
int hStart = 0;
|
||||||
|
int hEnd = 0;
|
||||||
|
List<ParameterInformation> parameters = p.information.getParameters();
|
||||||
|
val active = context.getCurrentParameterIndex();
|
||||||
|
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
|
||||||
|
var param = parameters.get(i);
|
||||||
|
if (i != 0) {
|
||||||
|
txt.append(", ");
|
||||||
|
}
|
||||||
|
if (i == active) {
|
||||||
|
hStart = txt.length();
|
||||||
|
}
|
||||||
|
txt.append(param.getLabel().getLeft());
|
||||||
|
if (i == active) {
|
||||||
|
hEnd = txt.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.setupUIComponentPresentation(txt.toString(), hStart, hEnd, !p.active, false, true, context.getDefaultParameterColor());
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,12 +26,18 @@ import com.intellij.formatting.Wrap;
|
||||||
import com.intellij.lang.ASTNode;
|
import com.intellij.lang.ASTNode;
|
||||||
import com.intellij.psi.TokenType;
|
import com.intellij.psi.TokenType;
|
||||||
import com.intellij.psi.formatter.common.AbstractBlock;
|
import com.intellij.psi.formatter.common.AbstractBlock;
|
||||||
|
import com.intellij.psi.tree.IElementType;
|
||||||
|
import lombok.val;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.falsepattern.zigbrains.zig.psi.ZigTypes.BLOCK;
|
||||||
|
import static com.falsepattern.zigbrains.zig.psi.ZigTypes.EXPR_LIST;
|
||||||
|
import static com.falsepattern.zigbrains.zig.psi.ZigTypes.INIT_LIST;
|
||||||
|
|
||||||
public class ZigBlock extends AbstractBlock {
|
public class ZigBlock extends AbstractBlock {
|
||||||
|
|
||||||
private final SpacingBuilder spacingBuilder;
|
private final SpacingBuilder spacingBuilder;
|
||||||
|
@ -69,16 +75,22 @@ public class ZigBlock extends AbstractBlock {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected @Nullable Indent getChildIndent() {
|
protected @Nullable Indent getChildIndent() {
|
||||||
if (myNode.getElementType() == ZigTypes.BLOCK) {
|
return getIndentBasedOnParentType(getNode().getElementType());
|
||||||
return Indent.getNormalIndent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Indent.getNoneIndent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Indent getIndent() {
|
public Indent getIndent() {
|
||||||
if (myNode.getElementType() == ZigTypes.STATEMENT) {
|
val parent = getNode().getTreeParent();
|
||||||
|
if (parent != null) {
|
||||||
|
return getIndentBasedOnParentType(parent.getElementType());
|
||||||
|
}
|
||||||
|
return Indent.getNoneIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Indent getIndentBasedOnParentType(IElementType elementType) {
|
||||||
|
if (elementType == BLOCK ||
|
||||||
|
elementType == INIT_LIST ||
|
||||||
|
elementType == EXPR_LIST) {
|
||||||
return Indent.getNormalIndent();
|
return Indent.getNormalIndent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,29 +38,29 @@
|
||||||
language="any"/>
|
language="any"/>
|
||||||
<!-- needed for completion as well as signature help -->
|
<!-- needed for completion as well as signature help -->
|
||||||
<typedHandler implementation="com.falsepattern.zigbrains.lsp.listeners.LSPTypedHandler"
|
<typedHandler implementation="com.falsepattern.zigbrains.lsp.listeners.LSPTypedHandler"
|
||||||
id="LSPTypedHandler"/>
|
id="ZBLSPTypedHandler"/>
|
||||||
|
|
||||||
<!-- needed for code diagnostics -->
|
<!-- needed for code diagnostics -->
|
||||||
<externalAnnotator id="LSPAnnotator"
|
<externalAnnotator id="ZBLSPAnnotator"
|
||||||
language="Zig"
|
language="Zig"
|
||||||
implementationClass="com.falsepattern.zigbrains.lsp.contributors.annotator.LSPAnnotator"/>
|
implementationClass="com.falsepattern.zigbrains.lsp.contributors.annotator.LSPAnnotator"/>
|
||||||
|
|
||||||
<!-- needed for Workspace Symbols -->
|
<!-- needed for Workspace Symbols -->
|
||||||
<gotoSymbolContributor implementation="com.falsepattern.zigbrains.lsp.contributors.symbol.LSPSymbolContributor"
|
<gotoSymbolContributor implementation="com.falsepattern.zigbrains.lsp.contributors.symbol.LSPSymbolContributor"
|
||||||
id="LSPSymbolContributor"/>
|
id="ZBLSPSymbolContributor"/>
|
||||||
|
|
||||||
<!-- needed for renaming -->
|
<!-- needed for renaming -->
|
||||||
<renameHandler implementation="com.falsepattern.zigbrains.lsp.contributors.rename.LSPRenameHandler"
|
<renameHandler implementation="com.falsepattern.zigbrains.lsp.contributors.rename.LSPRenameHandler"
|
||||||
id="LSPRenameHandler"
|
id="ZBLSPRenameHandler"
|
||||||
order="first"/>
|
order="first"/>
|
||||||
<renamePsiElementProcessor implementation="com.falsepattern.zigbrains.lsp.contributors.rename.LSPRenameProcessor"
|
<renamePsiElementProcessor implementation="com.falsepattern.zigbrains.lsp.contributors.rename.LSPRenameProcessor"
|
||||||
id="LSPRenameProcessor"
|
id="ZBLSPRenameProcessor"
|
||||||
order="first"/>
|
order="first"/>
|
||||||
|
|
||||||
<!-- needed for folding support -->
|
<!-- needed for folding support -->
|
||||||
<lang.foldingBuilder language="Zig"
|
<lang.foldingBuilder language="Zig"
|
||||||
implementationClass="com.falsepattern.zigbrains.zig.ide.ZigFoldingRangeProvider"
|
implementationClass="com.falsepattern.zigbrains.zig.ide.ZigFoldingRangeProvider"
|
||||||
id="LSPFoldingRangeProvider"
|
id="ZBLSPFoldingRangeProvider"
|
||||||
order="first"/>
|
order="first"/>
|
||||||
|
|
||||||
<!-- needed for documentation -->
|
<!-- needed for documentation -->
|
||||||
|
@ -109,6 +109,9 @@
|
||||||
key="notif-zls"
|
key="notif-zls"
|
||||||
id="ZigBrains.ZLS"/>
|
id="ZigBrains.ZLS"/>
|
||||||
|
|
||||||
|
<codeInsight.parameterInfo language="Zig"
|
||||||
|
implementationClass="com.falsepattern.zigbrains.zig.completion.ZigParameterInfoHandler"/>
|
||||||
|
|
||||||
<platform.backend.documentation.linkHandler implementation="com.falsepattern.zigbrains.lsp.contributors.LSPDocumentationLinkHandler"/>
|
<platform.backend.documentation.linkHandler implementation="com.falsepattern.zigbrains.lsp.contributors.LSPDocumentationLinkHandler"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
|
@ -123,7 +126,7 @@
|
||||||
|
|
||||||
<!-- needed for find references -->
|
<!-- needed for find references -->
|
||||||
<action class="com.falsepattern.zigbrains.lsp.actions.LSPReferencesAction"
|
<action class="com.falsepattern.zigbrains.lsp.actions.LSPReferencesAction"
|
||||||
id="LSPFindUsages">
|
id="ZBLSPFindUsages">
|
||||||
<keyboard-shortcut first-keystroke="shift alt F7"
|
<keyboard-shortcut first-keystroke="shift alt F7"
|
||||||
keymap="$default"/>
|
keymap="$default"/>
|
||||||
</action>
|
</action>
|
||||||
|
|
Loading…
Add table
Reference in a new issue