feat: Improved docs, more reliable file sync
This commit is contained in:
parent
8a0c862446
commit
23b72086bc
8 changed files with 129 additions and 129 deletions
|
@ -16,10 +16,9 @@
|
|||
package com.falsepattern.zigbrains.common.util;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
||||
import com.intellij.openapi.project.NoAccessDuringPsiEvents;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import lombok.val;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -50,16 +49,7 @@ public class ApplicationUtil {
|
|||
}
|
||||
|
||||
static public <T> T computableReadAction(Computable<T> computable) {
|
||||
if (ApplicationManager.getApplication().isDispatchThread() ||
|
||||
ApplicationManagerEx.getApplicationEx().holdsReadLock()) {
|
||||
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;
|
||||
}
|
||||
return ApplicationManager.getApplication().runReadAction(computable);
|
||||
}
|
||||
|
||||
static public void writeAction(Runnable runnable) {
|
||||
|
@ -70,15 +60,15 @@ public class ApplicationUtil {
|
|||
return ApplicationManager.getApplication().runWriteAction(computable);
|
||||
}
|
||||
|
||||
static public void invokeAfterPsiEvents(Runnable runnable) {
|
||||
static public void invokeAfterPsiEvents(Runnable runnable, boolean readLock, boolean writeLock) {
|
||||
Runnable wrapper = () -> {
|
||||
if (NoAccessDuringPsiEvents.isInsideEventProcessing()) {
|
||||
invokeAfterPsiEvents(runnable);
|
||||
invokeAfterPsiEvents(runnable, readLock, writeLock);
|
||||
} else {
|
||||
runnable.run();
|
||||
}
|
||||
};
|
||||
|
||||
ApplicationManager.getApplication().invokeLater(wrapper, (Condition<Void>) value -> false);
|
||||
val app = ApplicationManager.getApplication();
|
||||
app.invokeLater(writeLock ? wrapper : () -> app.executeOnPooledThread(readLock ? () -> app.runReadAction(wrapper) : wrapper));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.falsepattern.zigbrains.lsp.requests.Timeouts;
|
|||
import com.falsepattern.zigbrains.common.util.ApplicationUtil;
|
||||
import com.falsepattern.zigbrains.lsp.utils.DocumentUtils;
|
||||
import com.falsepattern.zigbrains.lsp.utils.FileUtils;
|
||||
import com.intellij.markdown.utils.doc.DocMarkdownToHtmlConverter;
|
||||
import com.intellij.model.Pointer;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
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.SmartPointerManager;
|
||||
import com.intellij.psi.SmartPsiFileRange;
|
||||
import lombok.val;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.lsp4j.HoverParams;
|
||||
import org.eclipse.lsp4j.jsonrpc.JsonRpcException;
|
||||
|
@ -102,13 +104,15 @@ public class LSPDocumentationTargetProvider implements DocumentationTargetProvid
|
|||
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)) {
|
||||
LOG.warn(String.format("Hover string returned is empty for file %s and pos (%d;%d)",
|
||||
identifier.getUri(), serverPos.getLine(), serverPos.getCharacter()));
|
||||
return null;
|
||||
}
|
||||
return DocumentationResult.documentation(string.lines().collect(Collectors.joining("<br>\n")));
|
||||
return DocumentationResult.documentation(string);
|
||||
} catch (TimeoutException e) {
|
||||
LOG.warn(e);
|
||||
wrapper.notifyFailure(Timeouts.HOVER);
|
||||
|
|
|
@ -78,48 +78,49 @@ public class DocumentEventManager {
|
|||
DidChangeTextDocumentParams changesParams = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(),
|
||||
Collections.singletonList(new TextDocumentContentChangeEvent()));
|
||||
changesParams.getTextDocument().setUri(identifier.getUri());
|
||||
|
||||
|
||||
changesParams.getTextDocument().setVersion(++version);
|
||||
|
||||
if (syncKind == TextDocumentSyncKind.Incremental) {
|
||||
TextDocumentContentChangeEvent changeEvent = changesParams.getContentChanges().get(0);
|
||||
CharSequence newText = event.getNewFragment();
|
||||
int offset = event.getOffset();
|
||||
int newTextLength = event.getNewLength();
|
||||
|
||||
EditorEventManager editorEventManager = EditorEventManagerBase.forUri(FileUtils.documentToUri(document));
|
||||
if (editorEventManager == null) {
|
||||
LOG.warn("no editor associated with document");
|
||||
return;
|
||||
}
|
||||
Editor editor = editorEventManager.editor;
|
||||
Position lspPosition = DocumentUtils.offsetToLSPPos(editor, offset);
|
||||
if (lspPosition == null) {
|
||||
return;
|
||||
}
|
||||
int startLine = lspPosition.getLine();
|
||||
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 (oldText.length() > 0) {
|
||||
endLine = startLine + StringUtil.countNewLines(oldText);
|
||||
String content = oldText.toString();
|
||||
String[] oldLines = content.split("\n");
|
||||
int oldTextLength = oldLines.length == 0 ? 0 : oldLines[oldLines.length - 1].length();
|
||||
endColumn = content.endsWith("\n") ? 0 : oldLines.length == 1 ? startColumn + oldTextLength : oldTextLength;
|
||||
} else { //if insert or no text change, the end position is the same
|
||||
endLine = startLine;
|
||||
endColumn = startColumn;
|
||||
}
|
||||
Range range = new Range(new Position(startLine, startColumn), new Position(endLine, endColumn));
|
||||
changeEvent.setRange(range);
|
||||
changeEvent.setText(newText.toString());
|
||||
} else if (syncKind == TextDocumentSyncKind.Full) {
|
||||
// TODO this incremental update logic is kinda broken, investigate later...
|
||||
// if (syncKind == TextDocumentSyncKind.Incremental) {
|
||||
// TextDocumentContentChangeEvent changeEvent = changesParams.getContentChanges().get(0);
|
||||
// CharSequence newText = event.getNewFragment();
|
||||
// int offset = event.getOffset();
|
||||
// int newTextLength = event.getNewLength();
|
||||
//
|
||||
// EditorEventManager editorEventManager = EditorEventManagerBase.forUri(FileUtils.documentToUri(document));
|
||||
// if (editorEventManager == null) {
|
||||
// LOG.warn("no editor associated with document");
|
||||
// return;
|
||||
// }
|
||||
// Editor editor = editorEventManager.editor;
|
||||
// Position lspPosition = DocumentUtils.offsetToLSPPos(editor, offset);
|
||||
// if (lspPosition == null) {
|
||||
// return;
|
||||
// }
|
||||
// int startLine = lspPosition.getLine();
|
||||
// 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 (oldText.length() > 0) {
|
||||
// endLine = startLine + StringUtil.countNewLines(oldText);
|
||||
// String content = oldText.toString();
|
||||
// String[] oldLines = content.split("\n");
|
||||
// int oldTextLength = oldLines.length == 0 ? 0 : oldLines[oldLines.length - 1].length();
|
||||
// endColumn = content.endsWith("\n") ? 0 : oldLines.length == 1 ? startColumn + oldTextLength : oldTextLength;
|
||||
// } else { //if insert or no text change, the end position is the same
|
||||
// endLine = startLine;
|
||||
// endColumn = startColumn;
|
||||
// }
|
||||
// Range range = new Range(new Position(startLine, startColumn), new Position(endLine, endColumn));
|
||||
// changeEvent.setRange(range);
|
||||
// changeEvent.setText(newText.toString());
|
||||
// } else if (syncKind == TextDocumentSyncKind.Full) {
|
||||
if (syncKind != TextDocumentSyncKind.None) {
|
||||
changesParams.getContentChanges().get(0).setText(document.getText());
|
||||
}
|
||||
// }
|
||||
ApplicationUtil.pool(() -> wrapper.getRequestManager().didChange(changesParams));
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
|
|||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
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.FileChangeType;
|
||||
import org.eclipse.lsp4j.FileEvent;
|
||||
|
@ -80,9 +80,9 @@ class LSPFileEventManager {
|
|||
|
||||
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||
EditorEventManagerBase.documentSaved(uri);
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
||||
FileUtils.projectToUri(p), FileChangeType.Changed));
|
||||
});
|
||||
FileUtils.findProjectsFor(file)
|
||||
.forEach(p -> changedConfiguration(uri, FileUtils.projectToUri(p), FileChangeType.Changed));
|
||||
}, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,7 +90,7 @@ class LSPFileEventManager {
|
|||
*
|
||||
* @param event The file move event
|
||||
*/
|
||||
static void fileMoved(VirtualFileMoveEvent event) {
|
||||
static void fileMoved(VFileMoveEvent event) {
|
||||
try {
|
||||
VirtualFile file = event.getFile();
|
||||
if (!FileUtils.isFileSupported(file)) {
|
||||
|
@ -102,7 +102,7 @@ class LSPFileEventManager {
|
|||
if (newFileUri == null || oldParentUri == null) {
|
||||
return;
|
||||
}
|
||||
String oldFileUri = String.format("%s/%s", oldParentUri, event.getFileName());
|
||||
String oldFileUri = String.format("%s/%s", oldParentUri, event.getFile().getName());
|
||||
closeAndReopenAffectedFile(file, oldFileUri);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("LSP file move event failed due to :", e);
|
||||
|
@ -125,7 +125,7 @@ class LSPFileEventManager {
|
|||
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
||||
FileUtils.projectToUri(p), FileChangeType.Deleted));
|
||||
});
|
||||
}, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,18 +143,20 @@ class LSPFileEventManager {
|
|||
.flatMap(p -> searchFiles(newFileName, p).stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (VirtualFile file : files) {
|
||||
if (!FileUtils.isFileSupported(file)) {
|
||||
continue;
|
||||
ApplicationUtil.invokeLater(() -> {
|
||||
for (VirtualFile file : files) {
|
||||
if (!FileUtils.isFileSupported(file)) {
|
||||
continue;
|
||||
}
|
||||
String newFileUri = FileUtil.URIFromVirtualFile(file);
|
||||
String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
|
||||
closeAndReopenAffectedFile(file, oldFileUri);
|
||||
}
|
||||
String newFileUri = FileUtil.URIFromVirtualFile(file);
|
||||
String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
|
||||
closeAndReopenAffectedFile(file, oldFileUri);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
LOG.warn("LSP file rename event failed due to : ", e);
|
||||
}
|
||||
});
|
||||
}, true, false);
|
||||
}
|
||||
|
||||
private static void closeAndReopenAffectedFile(VirtualFile file, String oldFileUri) {
|
||||
|
@ -195,7 +197,7 @@ class LSPFileEventManager {
|
|||
ApplicationUtil.invokeAfterPsiEvents(() -> {
|
||||
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
|
||||
FileUtils.projectToUri(p), FileChangeType.Created));
|
||||
});
|
||||
}, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,42 @@ import com.intellij.openapi.vfs.VirtualFileEvent;
|
|||
import com.intellij.openapi.vfs.VirtualFileListener;
|
||||
import com.intellij.openapi.vfs.VirtualFileMoveEvent;
|
||||
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;
|
||||
|
||||
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.
|
||||
|
@ -31,8 +64,7 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
|
||||
public void propertyChanged(@NotNull VFilePropertyChangeEvent event) {
|
||||
if (event.getPropertyName().equals(VirtualFile.PROP_NAME)) {
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
public void contentsChanged(@NotNull VirtualFileEvent event) {
|
||||
public void contentsChanged(@NotNull VFileContentChangeEvent event) {
|
||||
LSPFileEventManager.fileChanged(event.getFile());
|
||||
}
|
||||
|
||||
|
@ -53,8 +84,7 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void fileDeleted(@NotNull VirtualFileEvent event) {
|
||||
public void fileDeleted(@NotNull VFileDeleteEvent event) {
|
||||
LSPFileEventManager.fileDeleted(event.getFile());
|
||||
}
|
||||
|
||||
|
@ -63,8 +93,7 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void fileMoved(@NotNull VirtualFileMoveEvent event) {
|
||||
public void fileMoved(@NotNull VFileMoveEvent event) {
|
||||
LSPFileEventManager.fileMoved(event);
|
||||
}
|
||||
|
||||
|
@ -73,9 +102,8 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void fileCopied(@NotNull VirtualFileCopyEvent event) {
|
||||
fileCreated(event);
|
||||
public void fileCopied(@NotNull VFileCopyEvent event) {
|
||||
LSPFileEventManager.fileCreated(event.findCreatedFile());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,44 +111,7 @@ public class VFSListener implements VirtualFileListener {
|
|||
*
|
||||
* @param event the event object containing information about the change.
|
||||
*/
|
||||
@Override
|
||||
public void fileCreated(@NotNull VirtualFileEvent event) {
|
||||
public void fileCreated(@NotNull VFileCreateEvent event) {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package com.falsepattern.zigbrains.lsp.requests;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
|
@ -66,7 +65,7 @@ public class HoverHandler {
|
|||
result.add(renderer.render(parser.parse(string)));
|
||||
}
|
||||
}
|
||||
return "<html>" + String.join("\n\n", result) + "</html>";
|
||||
return String.join("\n", result);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
@ -75,9 +74,7 @@ public class HoverHandler {
|
|||
if (markedContent.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
Parser parser = Parser.builder().build();
|
||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
||||
return "<html>" + renderer.render(parser.parse(markedContent)) + "</html>";
|
||||
return markedContent;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
package com.falsepattern.zigbrains.zig.lsp;
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.client.languageserver.serverdefinition.RawCommandServerDefinition;
|
||||
import lombok.val;
|
||||
import org.eclipse.lsp4j.InitializeParams;
|
||||
import org.eclipse.lsp4j.PublishDiagnosticsCapabilities;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ZLSServerDefinition extends RawCommandServerDefinition {
|
||||
public ZLSServerDefinition(String[] command) {
|
||||
super("zig", command);
|
||||
|
@ -31,5 +34,17 @@ public class ZLSServerDefinition extends RawCommandServerDefinition {
|
|||
if (textCaps.getPublishDiagnostics() == null) {
|
||||
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"));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,10 +141,10 @@
|
|||
<!-- region LSP -->
|
||||
|
||||
<!-- 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"
|
||||
topic="com.intellij.openapi.project.ProjectManagerListener"/>
|
||||
<listener class="com.falsepattern.zigbrains.lsp.listeners.VFSListener"
|
||||
topic="com.intellij.openapi.vfs.newvfs.BulkFileListener"/>
|
||||
|
||||
<!-- endregion LSP -->
|
||||
</applicationListeners>
|
||||
|
|
Loading…
Add table
Reference in a new issue