feat: Improved docs, more reliable file sync

This commit is contained in:
FalsePattern 2024-03-08 16:23:24 +01:00
parent 8a0c862446
commit 23b72086bc
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
8 changed files with 129 additions and 129 deletions

View file

@ -16,10 +16,9 @@
package com.falsepattern.zigbrains.common.util; package com.falsepattern.zigbrains.common.util;
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.project.NoAccessDuringPsiEvents; import com.intellij.openapi.project.NoAccessDuringPsiEvents;
import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition; import lombok.val;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -50,16 +49,7 @@ public class ApplicationUtil {
} }
static public <T> T computableReadAction(Computable<T> computable) { static public <T> T computableReadAction(Computable<T> computable) {
if (ApplicationManager.getApplication().isDispatchThread() ||
ApplicationManagerEx.getApplicationEx().holdsReadLock()) {
return ApplicationManager.getApplication().runReadAction(computable); return ApplicationManager.getApplication().runReadAction(computable);
} else {
var result = new Object() {
T value = null;
};
ApplicationManager.getApplication().invokeAndWait(() -> result.value = ApplicationManager.getApplication().runReadAction(computable));
return result.value;
}
} }
static public void writeAction(Runnable runnable) { static public void writeAction(Runnable runnable) {
@ -70,15 +60,15 @@ public class ApplicationUtil {
return ApplicationManager.getApplication().runWriteAction(computable); return ApplicationManager.getApplication().runWriteAction(computable);
} }
static public void invokeAfterPsiEvents(Runnable runnable) { static public void invokeAfterPsiEvents(Runnable runnable, boolean readLock, boolean writeLock) {
Runnable wrapper = () -> { Runnable wrapper = () -> {
if (NoAccessDuringPsiEvents.isInsideEventProcessing()) { if (NoAccessDuringPsiEvents.isInsideEventProcessing()) {
invokeAfterPsiEvents(runnable); invokeAfterPsiEvents(runnable, readLock, writeLock);
} else { } else {
runnable.run(); runnable.run();
} }
}; };
val app = ApplicationManager.getApplication();
ApplicationManager.getApplication().invokeLater(wrapper, (Condition<Void>) value -> false); app.invokeLater(writeLock ? wrapper : () -> app.executeOnPooledThread(readLock ? () -> app.runReadAction(wrapper) : wrapper));
} }
} }

View file

@ -23,6 +23,7 @@ import com.falsepattern.zigbrains.lsp.requests.Timeouts;
import com.falsepattern.zigbrains.common.util.ApplicationUtil; import com.falsepattern.zigbrains.common.util.ApplicationUtil;
import com.falsepattern.zigbrains.lsp.utils.DocumentUtils; import com.falsepattern.zigbrains.lsp.utils.DocumentUtils;
import com.falsepattern.zigbrains.lsp.utils.FileUtils; import com.falsepattern.zigbrains.lsp.utils.FileUtils;
import com.intellij.markdown.utils.doc.DocMarkdownToHtmlConverter;
import com.intellij.model.Pointer; import com.intellij.model.Pointer;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.TextRange;
@ -33,6 +34,7 @@ import com.intellij.platform.backend.presentation.TargetPresentation;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.psi.SmartPointerManager; import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.SmartPsiFileRange; import com.intellij.psi.SmartPsiFileRange;
import lombok.val;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.lsp4j.HoverParams; import org.eclipse.lsp4j.HoverParams;
import org.eclipse.lsp4j.jsonrpc.JsonRpcException; import org.eclipse.lsp4j.jsonrpc.JsonRpcException;
@ -102,13 +104,15 @@ public class LSPDocumentationTargetProvider implements DocumentationTargetProvid
return null; return null;
} }
String string = HoverHandler.getHoverString(hover); val markdown = HoverHandler.getHoverString(hover);
val string = ApplicationUtil.computableReadAction(() -> DocMarkdownToHtmlConverter
.convert(manager.getProject(), markdown));
if (StringUtils.isEmpty(string)) { if (StringUtils.isEmpty(string)) {
LOG.warn(String.format("Hover string returned is empty for file %s and pos (%d;%d)", LOG.warn(String.format("Hover string returned is empty for file %s and pos (%d;%d)",
identifier.getUri(), serverPos.getLine(), serverPos.getCharacter())); identifier.getUri(), serverPos.getLine(), serverPos.getCharacter()));
return null; return null;
} }
return DocumentationResult.documentation(string.lines().collect(Collectors.joining("<br>\n"))); return DocumentationResult.documentation(string);
} catch (TimeoutException e) { } catch (TimeoutException e) {
LOG.warn(e); LOG.warn(e);
wrapper.notifyFailure(Timeouts.HOVER); wrapper.notifyFailure(Timeouts.HOVER);

View file

@ -78,48 +78,49 @@ public class DocumentEventManager {
DidChangeTextDocumentParams changesParams = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(), DidChangeTextDocumentParams changesParams = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(),
Collections.singletonList(new TextDocumentContentChangeEvent())); Collections.singletonList(new TextDocumentContentChangeEvent()));
changesParams.getTextDocument().setUri(identifier.getUri()); changesParams.getTextDocument().setUri(identifier.getUri());
changesParams.getTextDocument().setVersion(++version); changesParams.getTextDocument().setVersion(++version);
if (syncKind == TextDocumentSyncKind.Incremental) { // TODO this incremental update logic is kinda broken, investigate later...
TextDocumentContentChangeEvent changeEvent = changesParams.getContentChanges().get(0); // if (syncKind == TextDocumentSyncKind.Incremental) {
CharSequence newText = event.getNewFragment(); // TextDocumentContentChangeEvent changeEvent = changesParams.getContentChanges().get(0);
int offset = event.getOffset(); // CharSequence newText = event.getNewFragment();
int newTextLength = event.getNewLength(); // int offset = event.getOffset();
// int newTextLength = event.getNewLength();
EditorEventManager editorEventManager = EditorEventManagerBase.forUri(FileUtils.documentToUri(document)); //
if (editorEventManager == null) { // EditorEventManager editorEventManager = EditorEventManagerBase.forUri(FileUtils.documentToUri(document));
LOG.warn("no editor associated with document"); // if (editorEventManager == null) {
return; // LOG.warn("no editor associated with document");
} // return;
Editor editor = editorEventManager.editor; // }
Position lspPosition = DocumentUtils.offsetToLSPPos(editor, offset); // Editor editor = editorEventManager.editor;
if (lspPosition == null) { // Position lspPosition = DocumentUtils.offsetToLSPPos(editor, offset);
return; // if (lspPosition == null) {
} // return;
int startLine = lspPosition.getLine(); // }
int startColumn = lspPosition.getCharacter(); // int startLine = lspPosition.getLine();
CharSequence oldText = event.getOldFragment(); // int startColumn = lspPosition.getCharacter();
// CharSequence oldText = event.getOldFragment();
//if text was deleted/replaced, calculate the end position of inserted/deleted text //
int endLine, endColumn; // //if text was deleted/replaced, calculate the end position of inserted/deleted text
if (oldText.length() > 0) { // int endLine, endColumn;
endLine = startLine + StringUtil.countNewLines(oldText); // if (oldText.length() > 0) {
String content = oldText.toString(); // endLine = startLine + StringUtil.countNewLines(oldText);
String[] oldLines = content.split("\n"); // String content = oldText.toString();
int oldTextLength = oldLines.length == 0 ? 0 : oldLines[oldLines.length - 1].length(); // String[] oldLines = content.split("\n");
endColumn = content.endsWith("\n") ? 0 : oldLines.length == 1 ? startColumn + oldTextLength : oldTextLength; // int oldTextLength = oldLines.length == 0 ? 0 : oldLines[oldLines.length - 1].length();
} else { //if insert or no text change, the end position is the same // endColumn = content.endsWith("\n") ? 0 : oldLines.length == 1 ? startColumn + oldTextLength : oldTextLength;
endLine = startLine; // } else { //if insert or no text change, the end position is the same
endColumn = startColumn; // endLine = startLine;
} // endColumn = startColumn;
Range range = new Range(new Position(startLine, startColumn), new Position(endLine, endColumn)); // }
changeEvent.setRange(range); // Range range = new Range(new Position(startLine, startColumn), new Position(endLine, endColumn));
changeEvent.setText(newText.toString()); // changeEvent.setRange(range);
} else if (syncKind == TextDocumentSyncKind.Full) { // changeEvent.setText(newText.toString());
// } else if (syncKind == TextDocumentSyncKind.Full) {
if (syncKind != TextDocumentSyncKind.None) {
changesParams.getContentChanges().get(0).setText(document.getText()); changesParams.getContentChanges().get(0).setText(document.getText());
} }
// }
ApplicationUtil.pool(() -> wrapper.getRequestManager().didChange(changesParams)); ApplicationUtil.pool(() -> wrapper.getRequestManager().didChange(changesParams));
} }

View file

@ -28,7 +28,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileMoveEvent; import com.intellij.openapi.vfs.newvfs.events.VFileMoveEvent;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams; import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.eclipse.lsp4j.FileChangeType; import org.eclipse.lsp4j.FileChangeType;
import org.eclipse.lsp4j.FileEvent; import org.eclipse.lsp4j.FileEvent;
@ -80,9 +80,9 @@ class LSPFileEventManager {
ApplicationUtil.invokeAfterPsiEvents(() -> { ApplicationUtil.invokeAfterPsiEvents(() -> {
EditorEventManagerBase.documentSaved(uri); EditorEventManagerBase.documentSaved(uri);
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri, FileUtils.findProjectsFor(file)
FileUtils.projectToUri(p), FileChangeType.Changed)); .forEach(p -> changedConfiguration(uri, FileUtils.projectToUri(p), FileChangeType.Changed));
}); }, false, false);
} }
/** /**
@ -90,7 +90,7 @@ class LSPFileEventManager {
* *
* @param event The file move event * @param event The file move event
*/ */
static void fileMoved(VirtualFileMoveEvent event) { static void fileMoved(VFileMoveEvent event) {
try { try {
VirtualFile file = event.getFile(); VirtualFile file = event.getFile();
if (!FileUtils.isFileSupported(file)) { if (!FileUtils.isFileSupported(file)) {
@ -102,7 +102,7 @@ class LSPFileEventManager {
if (newFileUri == null || oldParentUri == null) { if (newFileUri == null || oldParentUri == null) {
return; return;
} }
String oldFileUri = String.format("%s/%s", oldParentUri, event.getFileName()); String oldFileUri = String.format("%s/%s", oldParentUri, event.getFile().getName());
closeAndReopenAffectedFile(file, oldFileUri); closeAndReopenAffectedFile(file, oldFileUri);
} catch (Exception e) { } catch (Exception e) {
LOG.warn("LSP file move event failed due to :", e); LOG.warn("LSP file move event failed due to :", e);
@ -125,7 +125,7 @@ class LSPFileEventManager {
ApplicationUtil.invokeAfterPsiEvents(() -> { ApplicationUtil.invokeAfterPsiEvents(() -> {
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri, FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
FileUtils.projectToUri(p), FileChangeType.Deleted)); FileUtils.projectToUri(p), FileChangeType.Deleted));
}); }, true, true);
} }
/** /**
@ -143,6 +143,7 @@ class LSPFileEventManager {
.flatMap(p -> searchFiles(newFileName, p).stream()) .flatMap(p -> searchFiles(newFileName, p).stream())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
ApplicationUtil.invokeLater(() -> {
for (VirtualFile file : files) { for (VirtualFile file : files) {
if (!FileUtils.isFileSupported(file)) { if (!FileUtils.isFileSupported(file)) {
continue; continue;
@ -151,10 +152,11 @@ class LSPFileEventManager {
String oldFileUri = newFileUri.replace(file.getName(), oldFileName); String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
closeAndReopenAffectedFile(file, oldFileUri); closeAndReopenAffectedFile(file, oldFileUri);
} }
});
} catch (Exception e) { } catch (Exception e) {
LOG.warn("LSP file rename event failed due to : ", e); LOG.warn("LSP file rename event failed due to : ", e);
} }
}); }, true, false);
} }
private static void closeAndReopenAffectedFile(VirtualFile file, String oldFileUri) { private static void closeAndReopenAffectedFile(VirtualFile file, String oldFileUri) {
@ -195,7 +197,7 @@ class LSPFileEventManager {
ApplicationUtil.invokeAfterPsiEvents(() -> { ApplicationUtil.invokeAfterPsiEvents(() -> {
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri, FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
FileUtils.projectToUri(p), FileChangeType.Created)); FileUtils.projectToUri(p), FileChangeType.Created));
}); }, true, true);
} }
} }

View file

@ -21,9 +21,42 @@ import com.intellij.openapi.vfs.VirtualFileEvent;
import com.intellij.openapi.vfs.VirtualFileListener; import com.intellij.openapi.vfs.VirtualFileListener;
import com.intellij.openapi.vfs.VirtualFileMoveEvent; import com.intellij.openapi.vfs.VirtualFileMoveEvent;
import com.intellij.openapi.vfs.VirtualFilePropertyEvent; import com.intellij.openapi.vfs.VirtualFilePropertyEvent;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileCopyEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileMoveEvent;
import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent;
import lombok.val;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class VFSListener implements VirtualFileListener { import java.util.List;
public class VFSListener implements BulkFileListener {
@Override
public void before(@NotNull List<? extends @NotNull VFileEvent> events) {
}
@Override
public void after(@NotNull List<? extends @NotNull VFileEvent> events) {
for (val event: events) {
if (event instanceof VFilePropertyChangeEvent propEvent)
propertyChanged(propEvent);
else if (event instanceof VFileContentChangeEvent changeEvent)
contentsChanged(changeEvent);
else if (event instanceof VFileDeleteEvent deleteEvent)
fileDeleted(deleteEvent);
else if (event instanceof VFileMoveEvent moveEvent)
fileMoved(moveEvent);
else if (event instanceof VFileCopyEvent copyEvent)
fileCopied(copyEvent);
else if (event instanceof VFileCreateEvent createEvent)
fileCreated(createEvent);
}
}
/** /**
* Fired when a virtual file is renamed from within IDEA, or its writable status is changed. * Fired when a virtual file is renamed from within IDEA, or its writable status is changed.
@ -31,8 +64,7 @@ public class VFSListener implements VirtualFileListener {
* *
* @param event the event object containing information about the change. * @param event the event object containing information about the change.
*/ */
@Override public void propertyChanged(@NotNull VFilePropertyChangeEvent event) {
public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
if (event.getPropertyName().equals(VirtualFile.PROP_NAME)) { if (event.getPropertyName().equals(VirtualFile.PROP_NAME)) {
LSPFileEventManager.fileRenamed((String) event.getOldValue(), (String) event.getNewValue()); LSPFileEventManager.fileRenamed((String) event.getOldValue(), (String) event.getNewValue());
} }
@ -43,8 +75,7 @@ public class VFSListener implements VirtualFileListener {
* *
* @param event the event object containing information about the change. * @param event the event object containing information about the change.
*/ */
@Override public void contentsChanged(@NotNull VFileContentChangeEvent event) {
public void contentsChanged(@NotNull VirtualFileEvent event) {
LSPFileEventManager.fileChanged(event.getFile()); LSPFileEventManager.fileChanged(event.getFile());
} }
@ -53,8 +84,7 @@ public class VFSListener implements VirtualFileListener {
* *
* @param event the event object containing information about the change. * @param event the event object containing information about the change.
*/ */
@Override public void fileDeleted(@NotNull VFileDeleteEvent event) {
public void fileDeleted(@NotNull VirtualFileEvent event) {
LSPFileEventManager.fileDeleted(event.getFile()); LSPFileEventManager.fileDeleted(event.getFile());
} }
@ -63,8 +93,7 @@ public class VFSListener implements VirtualFileListener {
* *
* @param event the event object containing information about the change. * @param event the event object containing information about the change.
*/ */
@Override public void fileMoved(@NotNull VFileMoveEvent event) {
public void fileMoved(@NotNull VirtualFileMoveEvent event) {
LSPFileEventManager.fileMoved(event); LSPFileEventManager.fileMoved(event);
} }
@ -73,9 +102,8 @@ public class VFSListener implements VirtualFileListener {
* *
* @param event the event object containing information about the change. * @param event the event object containing information about the change.
*/ */
@Override public void fileCopied(@NotNull VFileCopyEvent event) {
public void fileCopied(@NotNull VirtualFileCopyEvent event) { LSPFileEventManager.fileCreated(event.findCreatedFile());
fileCreated(event);
} }
/** /**
@ -83,44 +111,7 @@ public class VFSListener implements VirtualFileListener {
* *
* @param event the event object containing information about the change. * @param event the event object containing information about the change.
*/ */
@Override public void fileCreated(@NotNull VFileCreateEvent event) {
public void fileCreated(@NotNull VirtualFileEvent event) {
LSPFileEventManager.fileCreated(event.getFile()); LSPFileEventManager.fileCreated(event.getFile());
} }
/**
* Fired before the change of a name or writable status of a file is processed.
*
* @param event the event object containing information about the change.
*/
@Override
public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) {
}
/**
* Fired before the change of contents of a file is processed.
*
* @param event the event object containing information about the change.
*/
@Override
public void beforeContentsChange(@NotNull VirtualFileEvent event) {
}
/**
* Fired before the deletion of a file is processed.
*
* @param event the event object containing information about the change.
*/
@Override
public void beforeFileDeletion(@NotNull VirtualFileEvent event) {
}
/**
* Fired before the movement of a file is processed.
*
* @param event the event object containing information about the change.
*/
@Override
public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) {
}
} }

View file

@ -16,7 +16,6 @@
package com.falsepattern.zigbrains.lsp.requests; package com.falsepattern.zigbrains.lsp.requests;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.ui.UIUtil;
import com.vladsch.flexmark.html.HtmlRenderer; import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.parser.Parser;
import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Hover;
@ -66,7 +65,7 @@ public class HoverHandler {
result.add(renderer.render(parser.parse(string))); result.add(renderer.render(parser.parse(string)));
} }
} }
return "<html>" + String.join("\n\n", result) + "</html>"; return String.join("\n", result);
} else { } else {
return ""; return "";
} }
@ -75,9 +74,7 @@ public class HoverHandler {
if (markedContent.isEmpty()) { if (markedContent.isEmpty()) {
return ""; return "";
} }
Parser parser = Parser.builder().build(); return markedContent;
HtmlRenderer renderer = HtmlRenderer.builder().build();
return "<html>" + renderer.render(parser.parse(markedContent)) + "</html>";
} else { } else {
return ""; return "";
} }

View file

@ -17,9 +17,12 @@
package com.falsepattern.zigbrains.zig.lsp; package com.falsepattern.zigbrains.zig.lsp;
import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.RawCommandServerDefinition; import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.RawCommandServerDefinition;
import lombok.val;
import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.PublishDiagnosticsCapabilities; import org.eclipse.lsp4j.PublishDiagnosticsCapabilities;
import java.util.List;
public class ZLSServerDefinition extends RawCommandServerDefinition { public class ZLSServerDefinition extends RawCommandServerDefinition {
public ZLSServerDefinition(String[] command) { public ZLSServerDefinition(String[] command) {
super("zig", command); super("zig", command);
@ -31,5 +34,17 @@ public class ZLSServerDefinition extends RawCommandServerDefinition {
if (textCaps.getPublishDiagnostics() == null) { if (textCaps.getPublishDiagnostics() == null) {
textCaps.setPublishDiagnostics(new PublishDiagnosticsCapabilities()); textCaps.setPublishDiagnostics(new PublishDiagnosticsCapabilities());
} }
val completion = textCaps.getCompletion();
if (completion != null) {
val completionItem = completion.getCompletionItem();
if (completionItem != null) {
completionItem.setDocumentationFormat(List.of("markdown"));
}
}
val hover = textCaps.getHover();
if (hover != null) {
hover.setContentFormat(List.of("markdown"));
}
} }
} }

View file

@ -141,10 +141,10 @@
<!-- region LSP --> <!-- region LSP -->
<!-- required for lsp file sync --> <!-- required for lsp file sync -->
<listener class="com.falsepattern.zigbrains.lsp.listeners.VFSListener"
topic="com.intellij.openapi.vfs.VirtualFileListener"/>
<listener class="com.falsepattern.zigbrains.lsp.listeners.LSPProjectManagerListener" <listener class="com.falsepattern.zigbrains.lsp.listeners.LSPProjectManagerListener"
topic="com.intellij.openapi.project.ProjectManagerListener"/> topic="com.intellij.openapi.project.ProjectManagerListener"/>
<listener class="com.falsepattern.zigbrains.lsp.listeners.VFSListener"
topic="com.intellij.openapi.vfs.newvfs.BulkFileListener"/>
<!-- endregion LSP --> <!-- endregion LSP -->
</applicationListeners> </applicationListeners>