Skip to content

Commit

Permalink
feat: Path patching API
Browse files Browse the repository at this point in the history
Fixes #702

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Dec 17, 2024
1 parent 926caf2 commit 3f6c56a
Show file tree
Hide file tree
Showing 64 changed files with 688 additions and 347 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
Expand All @@ -46,12 +45,12 @@ public class DocumentContentSynchronizer implements DocumentListener {
@NotNull final CompletableFuture<Void> didOpenFuture;

public DocumentContentSynchronizer(@NotNull LanguageServerWrapper languageServerWrapper,
@NotNull URI fileUri,
@NotNull String fileUri,
@NotNull VirtualFile file,
@NotNull Document document,
@Nullable TextDocumentSyncKind syncKind) {
this.languageServerWrapper = languageServerWrapper;
this.fileUri = fileUri.toASCIIString();
this.fileUri = fileUri;
this.syncKind = syncKind != null ? syncKind : TextDocumentSyncKind.Full;

this.document = document;
Expand Down
37 changes: 17 additions & 20 deletions src/main/java/com/redhat/devtools/lsp4ij/LSPFileListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -55,14 +54,11 @@ public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile f
return;
}
// Manage textDocument/didClose
URI uri = LSPIJUtils.toUri(file);
if (uri != null) {
try {
// Disconnect the given file from the current language servers
languageServerWrapper.disconnect(uri, !languageServerWrapper.isDisposed());
} catch (Exception e) {
LOGGER.warn("Error while disconnecting the file '" + uri + "' from all language servers", e);
}
try {
// Disconnect the given file from the current language servers
languageServerWrapper.disconnect(file, !languageServerWrapper.isDisposed());
} catch (Exception e) {
LOGGER.warn("Error while disconnecting the file '" + file.getUrl() + "' from language server '" + languageServerWrapper.getServerDefinition().getDisplayName() + "'.", e);
}
}

Expand Down Expand Up @@ -96,7 +92,7 @@ private void moveFile(URI oldFileUri, VirtualFile newFile) {
if (isMatchFilePatterns(oldFileUri, WatchKind.Delete)) {
changes.add(fe(oldFileUri, FileChangeType.Deleted));
}
URI newFileUri = LSPIJUtils.toUri(newFile);
URI newFileUri = languageServerWrapper.toUri(newFile);
if (isMatchFilePatterns(newFileUri, WatchKind.Create)) {
changes.add(fe(newFileUri, FileChangeType.Created));
}
Expand All @@ -109,7 +105,7 @@ private void moveFile(URI oldFileUri, VirtualFile newFile) {
@Override
public void contentsChanged(@NotNull VirtualFileEvent event) {
VirtualFile file = event.getFile();
URI uri = LSPIJUtils.toUri(file);
URI uri = languageServerWrapper.toUri(file);
if (uri != null) {
LSPVirtualFileData documentListener = languageServerWrapper.connectedDocuments.get(uri);
if (documentListener != null) {
Expand All @@ -126,7 +122,7 @@ public void contentsChanged(@NotNull VirtualFileEvent event) {
@Override
public void fileCreated(@NotNull VirtualFileEvent event) {
VirtualFile file = event.getFile();
URI uri = LSPIJUtils.toUri(file);
URI uri = languageServerWrapper.toUri(file);
if (isMatchFilePatterns(uri, WatchKind.Create)) {
// 2. Send a workspace/didChangeWatchedFiles with 'Created' file change type.
didChangeWatchedFiles(fe(uri, FileChangeType.Created));
Expand All @@ -136,7 +132,7 @@ public void fileCreated(@NotNull VirtualFileEvent event) {
@Override
public void fileDeleted(@NotNull VirtualFileEvent event) {
VirtualFile file = event.getFile();
URI uri = LSPIJUtils.toUri(file);
URI uri = languageServerWrapper.toUri(file);
if (isMatchFilePatterns(uri, WatchKind.Delete)) {
// Send a workspace/didChangeWatchedFiles with 'Deleted' file change type.
didChangeWatchedFiles(fe(uri, FileChangeType.Deleted));
Expand Down Expand Up @@ -166,20 +162,21 @@ private FileEvent fe(URI uri, FileChangeType type) {
* to a different directory.
*
* @param virtualParentFile directory of the file after renaming
* @param oldFileName file name before renaming
* @param virtualNewFile virtual file after renaming
* @param oldFileName file name before renaming
* @param virtualNewFile virtual file after renaming
* @return URI of the file before renaming
*/
private @NotNull URI didRename(VirtualFile virtualParentFile, String oldFileName, VirtualFile virtualNewFile) {
File parentFile = VfsUtilCore.virtualToIoFile(virtualParentFile);
URI oldUri = LSPIJUtils.toUri(new File(parentFile, oldFileName));
private @NotNull URI didRename(@NotNull VirtualFile virtualParentFile,
@NotNull String oldFileName,
@NotNull VirtualFile virtualNewFile) {
URI parentFileUri = languageServerWrapper.toUri(virtualParentFile);
URI oldUri = parentFileUri.resolve(oldFileName);
boolean docIsConnected = languageServerWrapper.isConnectedTo(oldUri);
if (docIsConnected) {
// 1. Send a textDocument/didClose for the old file name
languageServerWrapper.disconnect(oldUri, false);
// 2. Send a textDocument/didOpen for the new file name
File newFile = VfsUtilCore.virtualToIoFile(virtualNewFile);
URI newUri = LSPIJUtils.toUri(newFile);
URI newUri = languageServerWrapper.toUri(virtualNewFile);
if (!languageServerWrapper.isConnectedTo(newUri)) {
languageServerWrapper.connect(virtualNewFile, null);
}
Expand Down
124 changes: 111 additions & 13 deletions src/main/java/com/redhat/devtools/lsp4ij/LSPIJUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.redhat.devtools.lsp4ij.client.features.FileUriSupport;
import com.redhat.devtools.lsp4ij.internal.SimpleLanguageUtils;
import com.redhat.devtools.lsp4ij.internal.StringUtils;
import com.redhat.devtools.lsp4ij.usages.LocationData;
import org.apache.commons.io.FileUtils;
import org.eclipse.lsp4j.*;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
Expand All @@ -56,8 +59,11 @@
* Utilities class for LSP.
*/
public class LSPIJUtils {

private static final Logger LOGGER = LoggerFactory.getLogger(LSPIJUtils.class);

private static final Key<URI> DEFAULT_LSP_FILE_URI_KEY = Key.create("lsp.file.uri");

private static final String JAR_PROTOCOL = "jar";

private static final String JRT_PROTOCOL = "jrt";
Expand Down Expand Up @@ -88,21 +94,41 @@ public class LSPIJUtils {

/**
* Open the LSP location in an editor.
* <p>
* Not used but declared to support backward compatibility.
* </p>
*
* @param location the LSP location.
* @param project the project.
* @return true if the file was opened and false otherwise.
*/
public static boolean openInEditor(@Nullable Location location,
@NotNull Project project) {
return openInEditor(location, null, project);
}

/**
* Open the LSP location in an editor.
*
* @param location the LSP location.
* @param fileUriSupport the file Uri support.
* @param project the project.
* @return true if the file was opened and false otherwise.
*/
public static boolean openInEditor(@Nullable Location location,
@Nullable FileUriSupport fileUriSupport,
@NotNull Project project) {
if (location == null) {
return false;
}
return openInEditor(location.getUri(), location.getRange() != null ? location.getRange().getStart() : null, project);
return openInEditor(location.getUri(), location.getRange() != null ? location.getRange().getStart() : null, fileUriSupport, project);
}

/**
* Open the given fileUri with the given position in an editor.
* <p>
* Not used but declared to support backward compatibility.
* </p>
*
* @param fileUri the file Uri.
* @param position the position.
Expand All @@ -112,12 +138,31 @@ public static boolean openInEditor(@Nullable Location location,
public static boolean openInEditor(@NotNull String fileUri,
@Nullable Position position,
@NotNull Project project) {
return openInEditor(fileUri, position, true, project);
return openInEditor(fileUri, position, null, project);
}

/**
* Open the given fileUri with the given position in an editor.
*
* @param fileUri the file Uri.
* @param position the position.
* @param fileUriSupport the file Uri support.
* @param project the project.
* @return true if the file was opened and false otherwise.
*/
public static boolean openInEditor(@NotNull String fileUri,
@Nullable Position position,
@Nullable FileUriSupport fileUriSupport,
@NotNull Project project) {
return openInEditor(fileUri, position, true, fileUriSupport, project);
}

/**
* Open the given fileUri with the given position in an editor.
* <p>
* Not used but declared to support backward compatibility.
* </p>
*
* @param fileUri the file Uri.
* @param position the position.
* @param focusEditor true if editor will take the focus and false otherwise.
Expand All @@ -128,7 +173,55 @@ public static boolean openInEditor(@NotNull String fileUri,
@Nullable Position position,
boolean focusEditor,
@NotNull Project project) {
return openInEditor(fileUri, position, focusEditor, false, project);
return openInEditor(fileUri, position, focusEditor, null, project);
}

/**
* Open the given fileUri with the given position in an editor.
*
* @param fileUri the file Uri.
* @param position the position.
* @param focusEditor true if editor will take the focus and false otherwise.
* @param fileUriSupport the file Uri support.
* @param project the project.
* @return true if the file was opened and false otherwise.
*/
public static boolean openInEditor(@NotNull String fileUri,
@Nullable Position position,
boolean focusEditor,
@Nullable FileUriSupport fileUriSupport,
@NotNull Project project) {
return openInEditor(fileUri, position, focusEditor, false, fileUriSupport, project);
}

/**
* Open the given fileUrl in an editor.
* <p>
* Not used but declared to support backward compatibility.
* </p>
*
* <p>
* the following syntax is supported for fileUrl:
* <ul>
* <li>file:///C:/Users/username/foo.txt</li>
* <li>C:/Users/username/foo.txt</li>
* <li>file:///C:/Users/username/foo.txt#L1:5</li>
* </ul>
* </p>
*
* @param fileUri the file Uri to open.
* @param position the position.
* @param focusEditor true if editor will take the focus and false otherwise.
* @param createFileIfNeeded true if file must be created if doesn't exist and false otherwise.
* @param project the project.
* @return true if file Url can be opened and false otherwise.
*/
public static boolean openInEditor(@NotNull String fileUri,
@Nullable Position position,
boolean focusEditor,
boolean createFileIfNeeded,
@NotNull Project project) {
return openInEditor(fileUri, position, focusEditor, createFileIfNeeded, null, project);
}

/**
Expand All @@ -147,13 +240,15 @@ public static boolean openInEditor(@NotNull String fileUri,
* @param position the position.
* @param focusEditor true if editor will take the focus and false otherwise.
* @param createFileIfNeeded true if file must be created if doesn't exist and false otherwise.
* @param fileUriSupport the file Uri support.
* @param project the project.
* @return true if file Url can be opened and false otherwise.
*/
public static boolean openInEditor(@NotNull String fileUri,
@Nullable Position position,
boolean focusEditor,
boolean createFileIfNeeded,
@Nullable FileUriSupport fileUriSupport,
@NotNull Project project) {
if (position == null) {
// Try to get position information from the fileUri
Expand All @@ -173,7 +268,7 @@ public static boolean openInEditor(@NotNull String fileUri,
fileUri = fileUri.substring(0, hashIndex);
}
}
VirtualFile file = findResourceFor(fileUri);
VirtualFile file = FileUriSupport.findFileByUri(fileUri, fileUriSupport);
if (file == null && createFileIfNeeded) {
// The file doesn't exist,
// open a dialog to confirm the creation of the file.
Expand Down Expand Up @@ -348,7 +443,13 @@ private static Language doGetFileLanguage(@NotNull VirtualFile file, @NotNull Pr
}

public static @NotNull URI toUri(@NotNull VirtualFile file) {
return toUri(VfsUtilCore.virtualToIoFile(file));
URI fileUri = file.getUserData(DEFAULT_LSP_FILE_URI_KEY);
if (fileUri == null) {
// Cache the file Uri to avoid recomputing again
fileUri = toUri(VfsUtilCore.virtualToIoFile(file));
file.putUserData(DEFAULT_LSP_FILE_URI_KEY, fileUri);
}
return fileUri;
}

public static @Nullable String toUriAsString(@NotNull PsiFile psFile) {
Expand Down Expand Up @@ -969,11 +1070,7 @@ private static void applyRenameFile(RenameFile renameFile) {
}

public static TextDocumentIdentifier toTextDocumentIdentifier(VirtualFile file) {
return toTextDocumentIdentifier(toUri(file));
}

public static TextDocumentIdentifier toTextDocumentIdentifier(final URI uri) {
return new TextDocumentIdentifier(uri.toASCIIString());
return new TextDocumentIdentifier(toUriAsString(file));
}

/**
Expand Down Expand Up @@ -1222,21 +1319,22 @@ public static String getProjectUri(Project project) {
* @param locations the locations/links
* @return the most specific location information that was found based on the provided locations/links
*/
public static @NotNull List<Location> getLocations(@Nullable Either<List<? extends Location>, List<? extends LocationLink>> locations) {
public static @NotNull List<LocationData> getLocations(@Nullable Either<List<? extends Location>, List<? extends LocationLink>> locations,
@Nullable LanguageServerItem languageServer) {
if (locations == null) {
// textDocument/definition may return null
return Collections.emptyList();
}
if (locations.isLeft()) {
return locations.getLeft()
.stream()
.map(l -> new Location(l.getUri(), l.getRange()))
.map(l -> new LocationData(new Location(l.getUri(), l.getRange()), languageServer))
.toList();

}
return locations.getRight()
.stream()
.map(l -> new Location(l.getTargetUri(), l.getTargetSelectionRange() != null ? l.getTargetSelectionRange() : l.getTargetRange()))
.map(l -> new LocationData(new Location(l.getTargetUri(), l.getTargetSelectionRange() != null ? l.getTargetSelectionRange() : l.getTargetRange()), languageServer))
.toList();
}
}
Loading

0 comments on commit 3f6c56a

Please sign in to comment.