Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cody settings editor #1972

Merged
merged 5 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ kotlin.stdlib.default.dependency=false
nodeBinaries.commit=8755ae4c05fd476cd23f2972049111ba436c86d4
nodeBinaries.version=v20.12.2
cody.autocomplete.enableFormatting=true
cody.commit=6443a0b6eccb4aa1442f9c5a2b09f2755a3d89da
cody.commit=066d9c6ff48beb96a834f17021affc4e62094415
2 changes: 2 additions & 0 deletions src/main/java/com/sourcegraph/cody/CodyToolWindowFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.sourcegraph.cody.config.actions.OpenCodySettingsEditorAction;
import com.sourcegraph.config.ConfigUtil;
import com.sourcegraph.config.OpenPluginSettingsAction;
import java.util.ArrayList;
Expand All @@ -34,6 +35,7 @@ public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindo
toolWindow.getContentManager().addContent(content);
DefaultActionGroup customCodySettings = new DefaultActionGroup();
customCodySettings.add(new OpenPluginSettingsAction("Cody Settings..."));
customCodySettings.add(new OpenCodySettingsEditorAction());
customCodySettings.addSeparator();
toolWindow.setAdditionalGearActions(customCodySettings);
List<AnAction> titleActions = new ArrayList<>();
Expand Down
20 changes: 19 additions & 1 deletion src/main/kotlin/com/sourcegraph/cody/agent/CodyAgentServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,22 @@ import com.sourcegraph.cody.agent.protocol.RemoteRepoHasResponse
import com.sourcegraph.cody.agent.protocol.RemoteRepoListParams
import com.sourcegraph.cody.agent.protocol.RemoteRepoListResponse
import com.sourcegraph.cody.agent.protocol.TelemetryEvent
import com.sourcegraph.cody.agent.protocol_generated.*
import com.sourcegraph.cody.agent.protocol_generated.ClientInfo
import com.sourcegraph.cody.agent.protocol_generated.CodeActions_ProvideParams
import com.sourcegraph.cody.agent.protocol_generated.CodeActions_ProvideResult
import com.sourcegraph.cody.agent.protocol_generated.CodeActions_TriggerParams
import com.sourcegraph.cody.agent.protocol_generated.Diagnostics_PublishParams
import com.sourcegraph.cody.agent.protocol_generated.EditTask
import com.sourcegraph.cody.agent.protocol_generated.EditTask_AcceptParams
import com.sourcegraph.cody.agent.protocol_generated.EditTask_CancelParams
import com.sourcegraph.cody.agent.protocol_generated.EditTask_GetTaskDetailsParams
import com.sourcegraph.cody.agent.protocol_generated.EditTask_RetryParams
import com.sourcegraph.cody.agent.protocol_generated.EditTask_UndoParams
import com.sourcegraph.cody.agent.protocol_generated.ExtensionConfiguration
import com.sourcegraph.cody.agent.protocol_generated.Graphql_GetRepoIdsParams
import com.sourcegraph.cody.agent.protocol_generated.Graphql_GetRepoIdsResult
import com.sourcegraph.cody.agent.protocol_generated.Null
import com.sourcegraph.cody.agent.protocol_generated.ServerInfo
import com.sourcegraph.cody.chat.ConnectionId
import java.util.concurrent.CompletableFuture
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification
Expand Down Expand Up @@ -65,6 +80,9 @@ interface _SubsetGeneratedCodyAgentServer {
params: Graphql_GetRepoIdsParams
): CompletableFuture<Graphql_GetRepoIdsResult>

@JsonRequest("extensionConfiguration/getSettingsSchema")
fun extensionConfiguration_getSettingsSchema(params: Null?): CompletableFuture<String>

// // =============
// // Notifications
// // =============
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface CodyAgentClient {
@JsonRequest("textDocument/edit")
fun textDocument_edit(params: TextDocumentEditParams): CompletableFuture<Boolean>
@JsonRequest("textDocument/openUntitledDocument")
fun textDocument_openUntitledDocument(params: UntitledTextDocument): CompletableFuture<Boolean>
fun textDocument_openUntitledDocument(params: UntitledTextDocument): CompletableFuture<ProtocolTextDocument?>
@JsonRequest("textDocument/show")
fun textDocument_show(params: TextDocument_ShowParams): CompletableFuture<Boolean>
@JsonRequest("workspace/edit")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,14 @@ interface CodyAgentServer {
fun testing_progressCancelation(params: Testing_ProgressCancelationParams): CompletableFuture<Testing_ProgressCancelationResult>
@JsonRequest("testing/reset")
fun testing_reset(params: Null?): CompletableFuture<Null?>
@JsonRequest("testing/autocomplete/completionEvent")
fun testing_autocomplete_completionEvent(params: CompletionItemParams): CompletableFuture<CompletionBookkeepingEvent?>
@JsonRequest("extensionConfiguration/change")
fun extensionConfiguration_change(params: ExtensionConfiguration): CompletableFuture<AuthStatus?>
@JsonRequest("extensionConfiguration/status")
fun extensionConfiguration_status(params: Null?): CompletableFuture<AuthStatus?>
@JsonRequest("extensionConfiguration/getSettingsSchema")
fun extensionConfiguration_getSettingsSchema(params: Null?): CompletableFuture<String>
@JsonRequest("textDocument/change")
fun textDocument_change(params: ProtocolTextDocument): CompletableFuture<TextDocument_ChangeResult>
@JsonRequest("attribution/search")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ data class CompletionBookkeepingEvent(
val acceptedAt: Long? = null,
val items: List<CompletionItemInfo>,
val loggedPartialAcceptedLength: Long,
val read: Boolean,
)

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ data class ConfigParams(
val agentIDE: CodyIDE? = null, // Oneof: VSCode, JetBrains, Neovim, Emacs, Web, VisualStudio
val agentExtensionVersion: String? = null,
val serverEndpoint: String,
val experimentalUnitTest: Boolean,
val webviewType: WebviewType? = null, // Oneof: sidebar, editor
val uiKindIsWeb: Boolean,
)

Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ object Constants {
const val `context_remote-repos` = "context/remote-repos"
const val setConfigFeatures = "setConfigFeatures"
const val allMentionProvidersMetadata = "allMentionProvidersMetadata"
const val updateEditorState = "updateEditorState"
const val queryPrompts_response = "queryPrompts/response"
const val embeddings = "embeddings"
const val indeterminate = "indeterminate"
const val `no-match` = "no-match"
Expand Down Expand Up @@ -94,6 +94,7 @@ object Constants {
const val assistant = "assistant"
const val system = "system"
const val isChatErrorGuard = "isChatErrorGuard"
const val sidebar = "sidebar"
const val Idle = "Idle"
const val Working = "Working"
const val Inserting = "Inserting"
Expand All @@ -114,6 +115,8 @@ object Constants {
const val IDEEXTENSION = "IDEEXTENSION"
const val ignore = "ignore"
const val use = "use"
const val `tree-sitter` = "tree-sitter"
const val indentation = "indentation"
const val info = "info"
const val suggestion = "suggestion"
const val initialized = "initialized"
Expand Down Expand Up @@ -161,9 +164,7 @@ object Constants {
const val `attribution-search` = "attribution-search"
const val troubleshoot_reloadAuth = "troubleshoot/reloadAuth"
const val getAllMentionProvidersMetadata = "getAllMentionProvidersMetadata"
const val `experimental-unit-test-prompt` = "experimental-unit-test-prompt"
const val `tree-sitter` = "tree-sitter"
const val indentation = "indentation"
const val queryPrompts = "queryPrompts"
const val Automatic = "Automatic"
const val Invoke = "Invoke"
const val none = "none"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Generated file - DO NOT EDIT MANUALLY
* They are copied from the cody agent project using the copyProtocol gradle task.
* This is only a temporary solution before we fully migrate to generated protocol messages.
*/
@file:Suppress("FunctionName", "ClassName", "unused", "EnumEntryName", "UnusedImport")
package com.sourcegraph.cody.agent.protocol_generated;

data class DefinitionParams(
val text: String,
)

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data class ExtensionConfiguration(
val autocompleteAdvancedModel: String? = null,
val debug: Boolean? = null,
val verboseDebug: Boolean? = null,
val telemetryClientName: String? = null,
val codebase: String? = null,
val eventProperties: EventProperties? = null,
val customConfiguration: Map<String, Any>? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ sealed class ExtensionMessage {
"context/remote-repos" -> context.deserialize<`context_remote-reposExtensionMessage`>(element, `context_remote-reposExtensionMessage`::class.java)
"setConfigFeatures" -> context.deserialize<SetConfigFeaturesExtensionMessage>(element, SetConfigFeaturesExtensionMessage::class.java)
"allMentionProvidersMetadata" -> context.deserialize<AllMentionProvidersMetadataExtensionMessage>(element, AllMentionProvidersMetadataExtensionMessage::class.java)
"updateEditorState" -> context.deserialize<UpdateEditorStateExtensionMessage>(element, UpdateEditorStateExtensionMessage::class.java)
"queryPrompts/response" -> context.deserialize<QueryPrompts_responseExtensionMessage>(element, QueryPrompts_responseExtensionMessage::class.java)
else -> throw Exception("Unknown discriminator ${element}")
}
}
Expand Down Expand Up @@ -202,6 +202,7 @@ data class `context_remote-reposExtensionMessage`(
data class SetConfigFeaturesExtensionMessage(
val type: TypeEnum, // Oneof: setConfigFeatures
val configFeatures: ConfigFeaturesParams,
val exportedFeatureFlags: Map<String, Boolean>,
) : ExtensionMessage() {

enum class TypeEnum {
Expand All @@ -219,13 +220,14 @@ data class AllMentionProvidersMetadataExtensionMessage(
}
}

data class UpdateEditorStateExtensionMessage(
val type: TypeEnum, // Oneof: updateEditorState
val editorState: Any? = null,
data class QueryPrompts_responseExtensionMessage(
val type: TypeEnum, // Oneof: queryPrompts/response
val result: List<Prompt>? = null,
val error: String? = null,
) : ExtensionMessage() {

enum class TypeEnum {
@SerializedName("updateEditorState") UpdateEditorState,
@SerializedName("queryPrompts/response") QueryPrompts_response,
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Generated file - DO NOT EDIT MANUALLY
* They are copied from the cody agent project using the copyProtocol gradle task.
* This is only a temporary solution before we fully migrate to generated protocol messages.
*/
@file:Suppress("FunctionName", "ClassName", "unused", "EnumEntryName", "UnusedImport")
package com.sourcegraph.cody.agent.protocol_generated;

data class OwnerParams(
val namespaceName: String,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Generated file - DO NOT EDIT MANUALLY
* They are copied from the cody agent project using the copyProtocol gradle task.
* This is only a temporary solution before we fully migrate to generated protocol messages.
*/
@file:Suppress("FunctionName", "ClassName", "unused", "EnumEntryName", "UnusedImport")
package com.sourcegraph.cody.agent.protocol_generated;

data class Prompt(
val id: String,
val name: String,
val nameWithOwner: String,
val owner: OwnerParams,
val description: String? = null,
val draft: Boolean,
val definition: DefinitionParams,
val url: String,
)

Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ sealed class WebviewMessage {
"attribution-search" -> context.deserialize<`attribution-searchWebviewMessage`>(element, `attribution-searchWebviewMessage`::class.java)
"troubleshoot/reloadAuth" -> context.deserialize<Troubleshoot_reloadAuthWebviewMessage>(element, Troubleshoot_reloadAuthWebviewMessage::class.java)
"getAllMentionProvidersMetadata" -> context.deserialize<GetAllMentionProvidersMetadataWebviewMessage>(element, GetAllMentionProvidersMetadataWebviewMessage::class.java)
"experimental-unit-test-prompt" -> context.deserialize<`experimental-unit-test-promptWebviewMessage`>(element, `experimental-unit-test-promptWebviewMessage`::class.java)
"queryPrompts" -> context.deserialize<QueryPromptsWebviewMessage>(element, QueryPromptsWebviewMessage::class.java)
else -> throw Exception("Unknown discriminator ${element}")
}
}
Expand Down Expand Up @@ -430,12 +430,13 @@ data class GetAllMentionProvidersMetadataWebviewMessage(
}
}

data class `experimental-unit-test-promptWebviewMessage`(
val command: CommandEnum, // Oneof: experimental-unit-test-prompt
data class QueryPromptsWebviewMessage(
val command: CommandEnum, // Oneof: queryPrompts
val query: String,
) : WebviewMessage() {

enum class CommandEnum {
@SerializedName("experimental-unit-test-prompt") `Experimental-unit-test-prompt`,
@SerializedName("queryPrompts") QueryPrompts,
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Generated file - DO NOT EDIT MANUALLY
* They are copied from the cody agent project using the copyProtocol gradle task.
* This is only a temporary solution before we fully migrate to generated protocol messages.
*/
@file:Suppress("FunctionName", "ClassName", "unused", "EnumEntryName", "UnusedImport")
package com.sourcegraph.cody.agent.protocol_generated;

typealias WebviewType = String // One of: sidebar, editor

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.sourcegraph.cody.config

import com.intellij.openapi.editor.Document
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileDocumentManagerListener
import com.intellij.openapi.project.Project
import com.sourcegraph.cody.agent.CodyAgentService
import com.sourcegraph.config.ConfigUtil

class CodySettingsChangeListener(private val project: Project) : FileDocumentManagerListener {
override fun beforeDocumentSaving(document: Document) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any risk that the restart will happen to often while editing the file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think not, this happen only on Ctrl+S, or if you switch IJ window.

val currentFile = FileDocumentManager.getInstance().getFile(document)
if (currentFile?.toNioPath() == ConfigUtil.getSettingsFile(project)) {
CodyAgentService.getInstance(project).restartAgent(project)
// TODO: we should instead call `extensionConfiguration_didChange` there:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it an issue for Cody repo?

// `it.server.extensionConfiguration_didChange(ConfigUtil.getAgentConfiguration(project,
// document.text))` but it seams that some of the settings changes (like enabling/disabling
// autocomplete) requires agent restart to take effect.

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.sourcegraph.cody.config.actions

import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.util.io.write
import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration
import com.jetbrains.jsonSchema.UserDefinedJsonSchemaConfiguration
import com.jetbrains.jsonSchema.ide.JsonSchemaService
import com.jetbrains.jsonSchema.impl.JsonSchemaVersion
import com.sourcegraph.cody.agent.CodyAgentService
import com.sourcegraph.common.ui.DumbAwareEDTAction
import com.sourcegraph.config.ConfigUtil
import com.sourcegraph.utils.CodyEditorUtil
import kotlin.io.path.name

class OpenCodySettingsEditorAction : DumbAwareEDTAction("Open Cody Settings Editor") {
override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return

val settingsVf =
CodyEditorUtil.createFileOrScratchFromUntitled(
project, ConfigUtil.getSettingsFile(project).toString(), content = "{\n \n}")
?: run {
logger.warn("Could not create settings file")
return
}

CodyEditorUtil.showDocument(project, settingsVf)

reloadSchemaAsync(project)
}

private fun reloadSchemaAsync(project: Project) {
CodyAgentService.withAgentRestartIfNeeded(project) { agent ->
val settingsSchema = agent.server.extensionConfiguration_getSettingsSchema(null).get()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this actions not only opens the file but also fetches the config from the agent and updates the content of the file, is that right? that makes the agent the source of truth at the moment we use this action - I wonder, shouldn't it be the client holding the truth?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, extensionConfiguration_getSettingsSchema always takes null, why is that? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder, shouldn't it be the client holding the truth?
I do not think so, and I'm not sure how that would be supposed to work?
After all it's VSC code which knows which settings are used, etc.
Keeping it in client would be artificial, and would also need to be duplicated for every client.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. My main concern was about persisting the settings b/w the IDE runs. Now I can see that ConfigUtil::getAgentConfiguration handles the initialization properly on the startup - all good 👍


val schemaFile = ConfigUtil.getConfigDir(project).resolve("cody_settings.schema.json")
schemaFile.write(settingsSchema)
LocalFileSystem.getInstance().refreshAndFindFileByNioFile(schemaFile)

val configName = "Cody Settings"
val schemaConfig =
UserDefinedJsonSchemaConfiguration(
configName,
JsonSchemaVersion.SCHEMA_7,
schemaFile.toString(),
/* applicationDefined = */ false,
listOf(
UserDefinedJsonSchemaConfiguration.Item(
"*/${ConfigUtil.getSettingsFile(project).name}",
/* isPattern = */ true,
/* isDirectory = */ false)))

val schemaMapping = JsonSchemaMappingsProjectConfiguration.getInstance(project)

schemaMapping.stateMap
.filter { it.value.name == configName }
.forEach { schemaMapping.removeConfiguration(it.value) }
schemaMapping.addConfiguration(schemaConfig)
JsonSchemaService.Impl.get(project).reset()
}
}

companion object {
private val logger = Logger.getInstance(OpenCodySettingsEditorAction::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.sourcegraph.cody.initialization

import com.intellij.AppTopics
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import com.sourcegraph.cody.agent.CodyAgentService
import com.sourcegraph.cody.config.CodySettingsChangeListener
import com.sourcegraph.cody.config.migration.SettingsMigration
import com.sourcegraph.cody.config.ui.CheckUpdatesTask
import com.sourcegraph.cody.listeners.CodyCaretListener
Expand Down Expand Up @@ -53,6 +55,9 @@ class PostStartupActivity : StartupActivity.DumbAware {
multicaster.addCaretListener(CodyCaretListener(project), disposable)
multicaster.addSelectionListener(CodySelectionListener(project), disposable)
multicaster.addDocumentListener(CodyDocumentListener(project), disposable)
project.messageBus
.connect(disposable)
.subscribe(AppTopics.FILE_DOCUMENT_SYNC, CodySettingsChangeListener(project))

TelemetryV2.sendTelemetryEvent(project, "cody.extension", "started")
}
Expand Down
Loading
Loading