From 16d2c404f25d1f353181775cdf5a34917d86768e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20B=C5=82ach?= Date: Fri, 22 Dec 2023 12:29:59 +0100 Subject: [PATCH] Use RFC 3986-compliant URIs for file paths (#226) Fixes #218 The Java URIs uses a different standard (RFC 2396) for encoding than the library vscode-uri used by Cody agent (RFC 3986). Due to the fact that I was unable to find a reasonable library, this patch merely imitates the encoding and normalization of such addresses. Additional change: Remove unused CodyAgentDocuments. ## Test Plan 1. Verify basic features on Windows 2. Verify basic features on Linux --- .../cody/CodyAgentFocusListener.java | 2 +- .../cody/CodyFileEditorListener.java | 4 +- .../cody/agent/CodyAgentClient.java | 1 - .../cody/agent/CodyAgentDocuments.java | 49 ------------------- .../com/sourcegraph/cody/agent/CodyAgent.kt | 1 - .../cody/agent/protocol/TextDocument.kt | 32 ++++++------ .../agent/protocol/util/Rfc3986UriEncoder.kt | 15 ++++++ .../autocomplete/CodyEditorFactoryListener.kt | 2 +- .../protocol/util/Rfc3986UriEncoderTest.kt | 21 ++++++++ 9 files changed, 57 insertions(+), 70 deletions(-) delete mode 100644 src/main/java/com/sourcegraph/cody/agent/CodyAgentDocuments.java create mode 100644 src/main/kotlin/com/sourcegraph/cody/agent/protocol/util/Rfc3986UriEncoder.kt create mode 100644 src/test/kotlin/com/sourcegraph/cody/agent/protocol/util/Rfc3986UriEncoderTest.kt diff --git a/src/main/java/com/sourcegraph/cody/CodyAgentFocusListener.java b/src/main/java/com/sourcegraph/cody/CodyAgentFocusListener.java index f4430d85bd..47821e81a3 100644 --- a/src/main/java/com/sourcegraph/cody/CodyAgentFocusListener.java +++ b/src/main/java/com/sourcegraph/cody/CodyAgentFocusListener.java @@ -30,7 +30,7 @@ public void focusGained(@NotNull Editor editor) { if (client.server == null) { return; } - client.server.textDocumentDidFocus(new TextDocument(file.getPath())); + client.server.textDocumentDidFocus(TextDocument.fromPath(file.getPath())); if (client.codebase == null) { return; diff --git a/src/main/java/com/sourcegraph/cody/CodyFileEditorListener.java b/src/main/java/com/sourcegraph/cody/CodyFileEditorListener.java index b82249e385..942cafa292 100644 --- a/src/main/java/com/sourcegraph/cody/CodyFileEditorListener.java +++ b/src/main/java/com/sourcegraph/cody/CodyFileEditorListener.java @@ -43,7 +43,7 @@ public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile f } server.textDocumentDidOpen( - new TextDocument(file.getPath(), document.getText())); + TextDocument.fromPath(file.getPath(), document.getText())); CodyAgentClient client = CodyAgent.getClient(source.getProject()); if (client.codebase == null) { @@ -63,6 +63,6 @@ public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile f if (server == null) { return; } - server.textDocumentDidClose(new TextDocument(file.getPath())); + server.textDocumentDidClose(TextDocument.fromPath(file.getPath())); } } diff --git a/src/main/java/com/sourcegraph/cody/agent/CodyAgentClient.java b/src/main/java/com/sourcegraph/cody/agent/CodyAgentClient.java index b87231554b..756bd114aa 100644 --- a/src/main/java/com/sourcegraph/cody/agent/CodyAgentClient.java +++ b/src/main/java/com/sourcegraph/cody/agent/CodyAgentClient.java @@ -18,7 +18,6 @@ public class CodyAgentClient { private static final Logger logger = Logger.getInstance(CodyAgentClient.class); @Nullable public CodyAgentServer server; - @Nullable public CodyAgentDocuments documents; @Nullable public CodyAgentCodebase codebase; // Callback that is invoked when the agent sends a "chat/updateMessageInProgress" notification. @Nullable public Consumer onChatUpdateMessageInProgress; diff --git a/src/main/java/com/sourcegraph/cody/agent/CodyAgentDocuments.java b/src/main/java/com/sourcegraph/cody/agent/CodyAgentDocuments.java deleted file mode 100644 index d87668cef9..0000000000 --- a/src/main/java/com/sourcegraph/cody/agent/CodyAgentDocuments.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.sourcegraph.cody.agent; - -import com.sourcegraph.cody.agent.protocol.TextDocument; -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -// Work-in-progress implementation of a helper class to optimize the notification traffic for -// textDocument/* methods. For example, we don't need to include the content of the document -// when we move the cursor around, or we don't need to send repeated didFocus events for the -// same file path. Currently, we send duplicate didFocus events when the user focuses on -// another application than IntelliJ, and then focuses back on the original document. -public class CodyAgentDocuments { - private final CodyAgentServer underlying; - private URI focusedPath = null; - private Map documents = new HashMap<>(); - - public CodyAgentDocuments(CodyAgentServer underlying) { - this.underlying = underlying; - } - - private void handleDocument(TextDocument document) { - TextDocument old = this.documents.get(document.getUri()); - if (old == null) { - this.documents.put(document.getUri(), document); - return; - } - if (document.getContent() == null) { - document.setContent(old.getContent()); - } - if (document.getSelection() == null) { - document.setSelection(old.getSelection()); - } - this.documents.put(document.getUri(), document); - } - - public void didOpen(TextDocument document) { - this.documents.put(document.getUri(), document); - underlying.textDocumentDidOpen(document); - } - - public void didFocus(TextDocument document) { - this.documents.put(document.getUri(), document); - } - - public void didChange(TextDocument document) {} - - public void didClose(TextDocument document) {} -} diff --git a/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgent.kt b/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgent.kt index 2e216e77a4..daf66c3c32 100644 --- a/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgent.kt +++ b/src/main/kotlin/com/sourcegraph/cody/agent/CodyAgent.kt @@ -162,7 +162,6 @@ class CodyAgent(private val project: Project) : Disposable { .create() val server = launcher.remoteProxy client.server = server - client.documents = CodyAgentDocuments(server) client.codebase = CodyAgentCodebase(server, project) listeningToJsonRpc = launcher.startListening() } diff --git a/src/main/kotlin/com/sourcegraph/cody/agent/protocol/TextDocument.kt b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/TextDocument.kt index c2e0f1a195..a3e972e8f0 100644 --- a/src/main/kotlin/com/sourcegraph/cody/agent/protocol/TextDocument.kt +++ b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/TextDocument.kt @@ -1,21 +1,23 @@ package com.sourcegraph.cody.agent.protocol -import java.net.URI +import com.sourcegraph.cody.agent.protocol.util.Rfc3986UriEncoder import java.nio.file.Paths -data class TextDocument -// JvmOverloads needed until CodyAgentFocusListener -// and CodyFileEditorListener are converted to Kotlin. -@JvmOverloads -constructor( - var uri: URI, - var content: String? = null, - var selection: Range? = null, +class TextDocument +private constructor( + var uri: String, + var content: String?, + var selection: Range?, ) { - @JvmOverloads - constructor( - filePath: String, - content: String? = null, - selection: Range? = null, - ) : this(Paths.get(filePath).toUri(), content, selection) + + companion object { + + @JvmStatic + @JvmOverloads + fun fromPath(path: String, content: String? = null, selection: Range? = null): TextDocument { + val uri = Paths.get(path).toUri().toString() + val rfc3986Uri = Rfc3986UriEncoder.encode(uri) + return TextDocument(rfc3986Uri, content, selection) + } + } } diff --git a/src/main/kotlin/com/sourcegraph/cody/agent/protocol/util/Rfc3986UriEncoder.kt b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/util/Rfc3986UriEncoder.kt new file mode 100644 index 0000000000..772b66e443 --- /dev/null +++ b/src/main/kotlin/com/sourcegraph/cody/agent/protocol/util/Rfc3986UriEncoder.kt @@ -0,0 +1,15 @@ +package com.sourcegraph.cody.agent.protocol.util + +object Rfc3986UriEncoder { + + // todo solve this with library + fun encode(uri: String): String { + val isWindowsPath = uri.matches("^file:///[A-Za-z]:.*".toRegex()) + if (isWindowsPath) { + val found = "file:///([A-Za-z]):".toRegex().find(uri)!! + val partition = found.groups[1]!!.value + return uri.replace("file:///$partition:", "file:///${partition.lowercase()}%3A") + } + return uri + } +} diff --git a/src/main/kotlin/com/sourcegraph/cody/autocomplete/CodyEditorFactoryListener.kt b/src/main/kotlin/com/sourcegraph/cody/autocomplete/CodyEditorFactoryListener.kt index fad17637ad..6c5fd24a28 100644 --- a/src/main/kotlin/com/sourcegraph/cody/autocomplete/CodyEditorFactoryListener.kt +++ b/src/main/kotlin/com/sourcegraph/cody/autocomplete/CodyEditorFactoryListener.kt @@ -166,7 +166,7 @@ class CodyEditorFactoryListener : EditorFactoryListener { return } val file = FileDocumentManager.getInstance().getFile(editor.document) ?: return - val document = TextDocument(file.path, editor.document.text, getSelection(editor)) + val document = TextDocument.fromPath(file.path, editor.document.text, getSelection(editor)) client.server!!.textDocumentDidChange(document) if (client.codebase == null || skipCodebaseOnFileOpened) { return diff --git a/src/test/kotlin/com/sourcegraph/cody/agent/protocol/util/Rfc3986UriEncoderTest.kt b/src/test/kotlin/com/sourcegraph/cody/agent/protocol/util/Rfc3986UriEncoderTest.kt new file mode 100644 index 0000000000..55dc4baa78 --- /dev/null +++ b/src/test/kotlin/com/sourcegraph/cody/agent/protocol/util/Rfc3986UriEncoderTest.kt @@ -0,0 +1,21 @@ +package com.sourcegraph.cody.agent.protocol.util + +import junit.framework.TestCase + +class Rfc3986UriEncoderTest : TestCase() { + + fun `test encode Windows path`() { + val fixedUri = Rfc3986UriEncoder.encode("file:///C:/Users/user/Test.java") + assertEquals("file:///c%3A/Users/user/Test.java", fixedUri) + } + + fun `test encode Windows path with lowercase partition`() { + val fixedUri = Rfc3986UriEncoder.encode("file:///c:/Users/user/Test.java") + assertEquals("file:///c%3A/Users/user/Test.java", fixedUri) + } + + fun `test encode Linux path`() { + val uri = Rfc3986UriEncoder.encode("file:///home/user/Test.java") + assertEquals("file:///home/user/Test.java", uri) + } +}