From 93507b2ddc3d11d091a7e2472570d57ed21f7bad Mon Sep 17 00:00:00 2001 From: Zhiming Ma Date: Sun, 24 Nov 2024 23:56:50 +0800 Subject: [PATCH] refactor(intellij): update lsp method to use `tabby/status` and `tabby/config` instead of `tabby/agent/*`. (#3450) --- .../intellijtabby/actions/CheckIssueDetail.kt | 108 ++------------ .../tabbyml/intellijtabby/chat/ChatBrowser.kt | 80 ++++++---- .../intellijtabby/events/CombinedState.kt | 68 +++------ .../intellijtabby/lsp/LanguageClient.kt | 66 +++++---- .../lsp/protocol/ProtocolData.kt | 140 +++++++----------- .../lsp/protocol/client/LanguageClient.kt | 25 +--- .../lsp/protocol/server/AgentFeature.kt | 24 --- .../lsp/protocol/server/ConfigFeature.kt | 12 ++ .../lsp/protocol/server/EditorsFeature.kt | 11 ++ .../lsp/protocol/server/LanguageServer.kt | 12 +- .../lsp/protocol/server/StatusFeature.kt | 20 +++ .../lsp/protocol/server/WorkspaceFeature.kt | 6 + .../notifications/Notifications.kt | 42 +++++- .../intellijtabby/settings/SettingsPanel.kt | 76 +++++----- .../intellijtabby/settings/SettingsService.kt | 7 - .../intellijtabby/settings/SettingsState.kt | 1 - .../widgets/StatusBarWidgetFactory.kt | 70 ++++----- 17 files changed, 348 insertions(+), 420 deletions(-) delete mode 100644 clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/AgentFeature.kt create mode 100644 clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/ConfigFeature.kt create mode 100644 clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/EditorsFeature.kt create mode 100644 clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/StatusFeature.kt diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/actions/CheckIssueDetail.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/actions/CheckIssueDetail.kt index d073274b4f22..08cc3525aa36 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/actions/CheckIssueDetail.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/actions/CheckIssueDetail.kt @@ -1,109 +1,35 @@ package com.tabbyml.intellijtabby.actions import com.intellij.icons.AllIcons -import com.intellij.openapi.actionSystem.* -import com.intellij.openapi.application.invokeLater -import com.intellij.openapi.components.service +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.components.serviceOrNull -import com.intellij.openapi.ui.Messages -import com.intellij.openapi.ui.popup.JBPopupFactory import com.tabbyml.intellijtabby.events.CombinedState import com.tabbyml.intellijtabby.lsp.ConnectionService -import com.tabbyml.intellijtabby.lsp.protocol.IssueDetailParams -import com.tabbyml.intellijtabby.lsp.protocol.IssueName -import com.tabbyml.intellijtabby.settings.SettingsService +import com.tabbyml.intellijtabby.lsp.protocol.StatusInfo import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.future.await import kotlinx.coroutines.launch +import org.eclipse.lsp4j.ExecuteCommandParams class CheckIssueDetail : AnAction() { + private val scope = CoroutineScope(Dispatchers.IO) + override fun actionPerformed(e: AnActionEvent) { val project = e.getRequiredData(CommonDataKeys.PROJECT) val combinedState = project.serviceOrNull() ?: return - val issueName = combinedState.state.agentIssue ?: return - - val settings = service() val connectionService = project.serviceOrNull() ?: return - val scope = CoroutineScope(Dispatchers.IO) scope.launch { val server = connectionService.getServerAsync() ?: return@launch - val detail = server.agentFeature.getIssueDetail( - IssueDetailParams( - name = issueName, helpMessageFormat = IssueDetailParams.HelpMessageFormat.HTML - ) - ).await() ?: return@launch - if (detail.name == IssueName.CONNECTION_FAILED) { - invokeLater { - val selected = Messages.showDialog( - "${detail.helpMessage}", - "Cannot Connect to Tabby Server", - arrayOf("OK", "Online Help"), - 0, - Messages.getErrorIcon(), - ) - when (selected) { - 0 -> { - // OK - } - - 1 -> { - // Online Help - showOnlineHelp(e) - } - } - } - } else { - val title = when (detail.name) { - IssueName.SLOW_COMPLETION_RESPONSE_TIME -> "Completion Requests Appear to Take Too Much Time" - IssueName.HIGH_COMPLETION_TIMEOUT_RATE -> "Most Completion Requests Timed Out" - else -> return@launch - } - invokeLater { - val selected = Messages.showDialog( - "${detail.helpMessage}", - title, - arrayOf("OK", "Online Help", "Don't Show Again"), - 0, - Messages.getWarningIcon(), - ) - when (selected) { - 0 -> { - // OK - } - - 1 -> { - // Online Help - showOnlineHelp(e) - } + val command = combinedState.state.agentStatus?.command ?: return@launch - 2 -> { - // Don't Show Again - settings.notificationsMuted += listOf("completionResponseTimeIssues") - settings.notifyChanges(project) - } - } - } - } - } - } - - private fun showOnlineHelp(e: AnActionEvent) { - e.project?.let { - invokeLater { - val actionManager = ActionManager.getInstance() - val actionGroup = actionManager.getAction("Tabby.OpenOnlineHelp") as ActionGroup - val popup = JBPopupFactory.getInstance().createActionGroupPopup( - "Online Help", - actionGroup, - e.dataContext, - false, - null, - 10, - ) - popup.showCenteredInCurrentWindow(it) - } + server.workspaceFeature.executeCommand(ExecuteCommandParams( + command.command, + command.arguments, + )) } } @@ -112,12 +38,8 @@ class CheckIssueDetail : AnAction() { val project = e.getData(CommonDataKeys.PROJECT) ?: return val combinedState = project.serviceOrNull() ?: return - val muted = mutableListOf() - if (combinedState.state.settings.notificationsMuted.contains("completionResponseTimeIssues")) { - muted += listOf(IssueName.SLOW_COMPLETION_RESPONSE_TIME, IssueName.HIGH_COMPLETION_TIMEOUT_RATE) - } - e.presentation.isVisible = combinedState.state.agentIssue != null && combinedState.state.agentIssue !in muted - e.presentation.icon = if (combinedState.state.agentIssue == IssueName.CONNECTION_FAILED) { + e.presentation.isVisible = combinedState.state.agentStatus?.command != null + e.presentation.icon = if (combinedState.state.agentStatus?.status == StatusInfo.Status.DISCONNECTED) { AllIcons.General.Error } else { AllIcons.General.Warning diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/chat/ChatBrowser.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/chat/ChatBrowser.kt index 665f46ed413a..b1b98e3c87e9 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/chat/ChatBrowser.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/chat/ChatBrowser.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.application.invokeLater import com.intellij.openapi.application.runReadAction import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.components.service +import com.intellij.openapi.components.serviceOrNull import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.colors.EditorColors import com.intellij.openapi.editor.colors.EditorColorsListener @@ -22,11 +23,16 @@ import com.intellij.ui.jcef.JBCefBrowserBase import com.intellij.ui.jcef.JBCefJSQuery import com.tabbyml.intellijtabby.events.CombinedState import com.tabbyml.intellijtabby.git.GitProvider -import com.tabbyml.intellijtabby.lsp.protocol.ServerInfo -import com.tabbyml.intellijtabby.lsp.protocol.Status +import com.tabbyml.intellijtabby.lsp.ConnectionService +import com.tabbyml.intellijtabby.lsp.protocol.Config +import com.tabbyml.intellijtabby.lsp.protocol.StatusInfo +import com.tabbyml.intellijtabby.lsp.protocol.StatusRequestParams import io.github.z4kn4fein.semver.Version import io.github.z4kn4fein.semver.constraints.Constraint import io.github.z4kn4fein.semver.constraints.satisfiedBy +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.cef.browser.CefBrowser import org.cef.handler.CefLoadHandlerAdapter import java.awt.Color @@ -53,10 +59,13 @@ class ChatBrowser(private val project: Project) : JBCefBrowser( private val gitProvider = project.service() private val messageBusConnection = project.messageBus.connect() + private val scope = CoroutineScope(Dispatchers.IO) + private suspend fun getServer() = project.serviceOrNull()?.getServerAsync() + private val reloadHandler = JBCefJSQuery.create(this as JBCefBrowserBase) private val chatPanelRequestHandler = JBCefJSQuery.create(this as JBCefBrowserBase) - private var currentConfig: ServerInfo.ServerInfoConfig? = null + private var currentConfig: Config.ServerConfig? = null private var isChatPanelLoaded = false private val pendingScripts: MutableList = mutableListOf() @@ -249,42 +258,49 @@ class ChatBrowser(private val project: Project) : JBCefBrowser( private fun reloadContent(force: Boolean = false) { if (force) { - // FIXME(@icycodes): force reload requires await reconnection then get server health - reloadContentInternal(true) + scope.launch { + val server = getServer() ?: return@launch + server.statusFeature.getStatus(StatusRequestParams(recheckConnection = true)).thenAccept { + reloadContentInternal(it, true) + } + } } else { - reloadContentInternal(false) + reloadContentInternal(combinedState.state.agentStatus) } } - private fun reloadContentInternal(force: Boolean = false) { - val status = combinedState.state.agentStatus - when (status) { - Status.NOT_INITIALIZED, Status.FINALIZED -> { - showContent("Initializing...") - } + private fun reloadContentInternal(statusInfo: StatusInfo?, force: Boolean = false) { + if (statusInfo == null) { + showContent("Initializing...") + } else { + when (statusInfo.status) { + StatusInfo.Status.CONNECTING -> { + showContent("Connecting to Tabby server...") + } - Status.DISCONNECTED -> { - showContent("Cannot connect to Tabby server, please check your settings.") - } + StatusInfo.Status.UNAUTHORIZED -> { + showContent("Authorization required, please set your token in settings.") + } - Status.UNAUTHORIZED -> { - showContent("Authorization required, please set your token in settings.") - } + StatusInfo.Status.DISCONNECTED -> { + showContent("Cannot connect to Tabby server, please check your settings.") + } - else -> { - val health = combinedState.state.agentServerInfo?.health - val error = checkServerHealth(health) - if (error != null) { - showContent(error) - } else { - val config = combinedState.state.agentServerInfo?.config - if (config == null) { - showContent("Initializing...") - } else if (force || currentConfig != config) { - showContent("Loading chat panel...") - isChatPanelLoaded = false - currentConfig = config - jsLoadChatPanel() + else -> { + val health = statusInfo.serverHealth + val error = checkServerHealth(health) + if (error != null) { + showContent(error) + } else { + val config = combinedState.state.agentConfig?.server + if (config == null) { + showContent("Initializing...") + } else if (force || currentConfig != config) { + showContent("Loading chat panel...") + isChatPanelLoaded = false + currentConfig = config + jsLoadChatPanel() + } } } } diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/events/CombinedState.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/events/CombinedState.kt index 56b794aa9292..2344daff3b3c 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/events/CombinedState.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/events/CombinedState.kt @@ -5,12 +5,11 @@ import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.util.messages.Topic -import com.tabbyml.intellijtabby.completion.InlineCompletionService import com.tabbyml.intellijtabby.lsp.ConnectionService import com.tabbyml.intellijtabby.lsp.LanguageClient -import com.tabbyml.intellijtabby.lsp.protocol.IssueList -import com.tabbyml.intellijtabby.lsp.protocol.ServerInfo -import com.tabbyml.intellijtabby.lsp.protocol.Status +import com.tabbyml.intellijtabby.lsp.protocol.Config +import com.tabbyml.intellijtabby.lsp.protocol.StatusInfo +import com.tabbyml.intellijtabby.notifications.hideAuthRequiredNotification import com.tabbyml.intellijtabby.notifications.notifyAuthRequired import com.tabbyml.intellijtabby.safeSyncPublisher import com.tabbyml.intellijtabby.settings.SettingsService @@ -22,47 +21,31 @@ class CombinedState(private val project: Project) : Disposable { data class State( val settings: SettingsService.Settings, val connectionState: ConnectionService.State, - val agentStatus: String, - val agentIssue: String?, - val agentServerInfo: ServerInfo?, - val isInlineCompletionLoading: Boolean, + val agentStatus: StatusInfo?, + val agentConfig: Config?, ) { fun withSettings(settings: SettingsService.Settings): State { - return State(settings, connectionState, agentStatus, agentIssue, agentServerInfo, isInlineCompletionLoading) + return State(settings, connectionState, agentStatus, agentConfig) } fun withConnectionState(connectionState: ConnectionService.State): State { - return State(settings, connectionState, agentStatus, agentIssue, agentServerInfo, isInlineCompletionLoading) + return State(settings, connectionState, agentStatus, agentConfig) } - fun withAgentStatus(agentStatus: String): State { - return State(settings, connectionState, agentStatus, agentIssue, agentServerInfo, isInlineCompletionLoading) + fun withStatus(status: StatusInfo): State { + return State(settings, connectionState, status, agentConfig) } - fun withAgentIssue(currentIssue: String?): State { - return State(settings, connectionState, agentStatus, currentIssue, agentServerInfo, isInlineCompletionLoading) - } - - fun withoutAgentIssue(): State { - return withAgentIssue(null) - } - - fun withAgentServerInfo(serverInfo: ServerInfo?): State { - return State(settings, connectionState, agentStatus, agentIssue, serverInfo, isInlineCompletionLoading) - } - - fun withInlineCompletionLoading(isInlineCompletionLoading: Boolean = true): State { - return State(settings, connectionState, agentStatus, agentIssue, agentServerInfo, isInlineCompletionLoading) + fun withConfig(config: Config): State { + return State(settings, connectionState, agentStatus, config) } } var state = State( service().settings(), ConnectionService.State.INITIALIZING, - Status.NOT_INITIALIZED, null, null, - false, ) private set @@ -79,32 +62,21 @@ class CombinedState(private val project: Project) : Disposable { project.safeSyncPublisher(Listener.TOPIC)?.stateChanged(this@CombinedState.state) } }) - messageBusConnection.subscribe(LanguageClient.AgentListener.TOPIC, object : LanguageClient.AgentListener { - override fun agentStatusChanged(status: String) { - state = state.withAgentStatus(status) + messageBusConnection.subscribe(LanguageClient.StatusListener.TOPIC, object : LanguageClient.StatusListener { + override fun statusChanged(status: StatusInfo) { + state = state.withStatus(status) project.safeSyncPublisher(Listener.TOPIC)?.stateChanged(state) - if (status == Status.UNAUTHORIZED) { + if (status.status == StatusInfo.Status.UNAUTHORIZED) { notifyAuthRequired() + } else { + hideAuthRequiredNotification() } } - - override fun agentIssueUpdated(issueList: IssueList) { - state = issueList.issues.firstOrNull()?.let { - state.withAgentIssue(it) - } ?: state.withoutAgentIssue() - project.safeSyncPublisher(Listener.TOPIC)?.stateChanged(state) - } - - override fun agentServerInfoUpdated(serverInfo: ServerInfo) { - state = state.withAgentServerInfo(serverInfo) - project.safeSyncPublisher(Listener.TOPIC)?.stateChanged(state) - } }) - - messageBusConnection.subscribe(InlineCompletionService.Listener.TOPIC, object : InlineCompletionService.Listener { - override fun loadingStateChanged(loading: Boolean) { - state = state.withInlineCompletionLoading(loading) + messageBusConnection.subscribe(LanguageClient.ConfigListener.TOPIC, object : LanguageClient.ConfigListener { + override fun configChanged(config: Config) { + state = state.withConfig(config) project.safeSyncPublisher(Listener.TOPIC)?.stateChanged(state) } }) diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/LanguageClient.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/LanguageClient.kt index 8380e1db11b7..2a135cc863fe 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/LanguageClient.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/LanguageClient.kt @@ -3,12 +3,14 @@ package com.tabbyml.intellijtabby.lsp import com.intellij.ide.plugins.PluginManagerCore import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationInfo +import com.intellij.openapi.application.invokeLater import com.intellij.openapi.components.serviceOrNull import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Document import com.intellij.openapi.extensions.PluginId import com.intellij.openapi.project.Project import com.intellij.openapi.project.guessProjectDir +import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.TextRange import com.intellij.psi.codeStyle.CodeStyleSettingsManager import com.intellij.util.messages.Topic @@ -22,22 +24,15 @@ import com.tabbyml.intellijtabby.lsp.protocol.* import com.tabbyml.intellijtabby.lsp.protocol.ClientCapabilities import com.tabbyml.intellijtabby.lsp.protocol.ClientInfo import com.tabbyml.intellijtabby.lsp.protocol.InitializeParams -import com.tabbyml.intellijtabby.lsp.protocol.InitializeResult -import com.tabbyml.intellijtabby.lsp.protocol.ServerInfo import com.tabbyml.intellijtabby.lsp.protocol.TextDocumentClientCapabilities import com.tabbyml.intellijtabby.lsp.protocol.server.LanguageServer import com.tabbyml.intellijtabby.safeSyncPublisher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.future.await -import kotlinx.coroutines.launch import org.eclipse.lsp4j.* import java.util.concurrent.CompletableFuture class LanguageClient(private val project: Project) : com.tabbyml.intellijtabby.lsp.protocol.client.LanguageClient(), Disposable { private val logger = Logger.getInstance(LanguageClient::class.java) - private val scope = CoroutineScope(Dispatchers.IO) private val gitProvider = project.serviceOrNull() private val languageSupportService = project.serviceOrNull() private val configurationSync = ConfigurationSync(project) @@ -70,7 +65,8 @@ class LanguageClient(private val project: Project) : com.tabbyml.intellijtabby.l didChangeConfiguration = DidChangeConfigurationCapabilities() }, tabby = TabbyClientCapabilities( - agent = true, + configDidChangeListener = true, + statusDidChangeListener = true, gitProvider = gitProvider?.isSupported(), workspaceFileSystem = true, languageSupport = languageSupportService != null, @@ -85,23 +81,14 @@ class LanguageClient(private val project: Project) : com.tabbyml.intellijtabby.l override fun processInitializeResult(server: LanguageServer, result: InitializeResult?) { configurationSync.startSync(server) textDocumentSync.startSync(server) - scope.launch { - project.safeSyncPublisher(AgentListener.TOPIC)?.agentStatusChanged(server.agentFeature.status().await()) - project.safeSyncPublisher(AgentListener.TOPIC)?.agentIssueUpdated(server.agentFeature.issues().await()) - project.safeSyncPublisher(AgentListener.TOPIC)?.agentServerInfoUpdated(server.agentFeature.serverInfo().await()) - } - } - - override fun didChangeStatus(params: DidChangeStatusParams) { - project.safeSyncPublisher(AgentListener.TOPIC)?.agentStatusChanged(params.status) } - override fun didUpdateIssues(params: DidUpdateIssueParams) { - project.safeSyncPublisher(AgentListener.TOPIC)?.agentIssueUpdated(params) + override fun configDidChange(params: Config) { + project.safeSyncPublisher(ConfigListener.TOPIC)?.configChanged(params) } - override fun didUpdateServerInfo(params: DidUpdateServerInfoParams) { - project.safeSyncPublisher(AgentListener.TOPIC)?.agentServerInfoUpdated(params.serverInfo) + override fun statusDidChange(params: StatusInfo) { + project.safeSyncPublisher(StatusListener.TOPIC)?.statusChanged(params) } override fun editorOptions(params: EditorOptionsParams): CompletableFuture { @@ -229,6 +216,26 @@ class LanguageClient(private val project: Project) : com.tabbyml.intellijtabby.l } } + override fun showMessageRequest(params: ShowMessageRequestParams): CompletableFuture { + return CompletableFuture().apply { + invokeLater { + val actions = params.actions.map { it.title }.toTypedArray() + val selected = Messages.showDialog( + params.message, + "Tabby", + actions, + 0, + when (params.type) { + MessageType.Error -> Messages.getErrorIcon() + MessageType.Warning -> Messages.getWarningIcon() + else -> Messages.getInformationIcon() + }, + ) + complete(actions.getOrNull(selected)?.let { MessageActionItem(it) }) + } + } + } + override fun logMessage(params: MessageParams) { when (params.type) { MessageType.Error -> logger.warn(params.message) @@ -306,14 +313,21 @@ class LanguageClient(private val project: Project) : com.tabbyml.intellijtabby.l ) } - interface AgentListener { - fun agentStatusChanged(status: String) {} - fun agentIssueUpdated(issueList: IssueList) {} - fun agentServerInfoUpdated(serverInfo: ServerInfo) {} + interface ConfigListener { + fun configChanged(config: Config) {} + + companion object { + @Topic.ProjectLevel + val TOPIC = Topic(ConfigListener::class.java, Topic.BroadcastDirection.NONE) + } + } + + interface StatusListener { + fun statusChanged(status: StatusInfo) {} companion object { @Topic.ProjectLevel - val TOPIC = Topic(AgentListener::class.java, Topic.BroadcastDirection.NONE) + val TOPIC = Topic(StatusListener::class.java, Topic.BroadcastDirection.NONE) } } } \ No newline at end of file diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/ProtocolData.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/ProtocolData.kt index 6f274ceefc22..5e2a449d5107 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/ProtocolData.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/ProtocolData.kt @@ -4,7 +4,6 @@ import com.tabbyml.intellijtabby.lsp.protocol.ClientProvidedConfig.InlineComplet import com.tabbyml.intellijtabby.lsp.protocol.ClientProvidedConfig.Keybindings import com.tabbyml.intellijtabby.lsp.protocol.EventParams.EventType import com.tabbyml.intellijtabby.lsp.protocol.EventParams.SelectKind -import com.tabbyml.intellijtabby.lsp.protocol.IssueDetailParams.HelpMessageFormat import com.tabbyml.intellijtabby.lsp.protocol.ReadFileParams.Format import org.eclipse.lsp4j.* @@ -51,7 +50,8 @@ data class TextDocumentClientCapabilities( typealias InlineCompletionCapabilities = DynamicRegistrationCapabilities data class TabbyClientCapabilities( - val agent: Boolean? = null, + val configDidChangeListener: Boolean? = null, + val statusDidChangeListener: Boolean? = null, val workspaceFileSystem: Boolean? = null, val dataStore: Boolean? = null, val languageSupport: Boolean? = null, @@ -59,31 +59,9 @@ data class TabbyClientCapabilities( val editorOptions: Boolean? = null, ) -data class InitializeResult( - val capabilities: ServerCapabilities, - val serverInfo: ServerInfo? = null, -) { - data class ServerInfo( - val name: String, - val version: String? = null, - ) -} - -data class ServerCapabilities( - val workspace: WorkspaceServerCapabilities? = null, - val textDocumentSync: TextDocumentSyncOptions? = null, - val notebookDocumentSync: NotebookDocumentSyncOptions? = null, - val completionProvider: CompletionOptions? = null, - val inlineCompletionProvider: Boolean? = null, - val tabby: TabbyServerCapabilities? = null, -) - -data class TabbyServerCapabilities( - val chat: Boolean? = null -) - data class ClientProvidedConfig( val server: ServerConfig? = null, + val proxy: ProxyConfig? = null, val inlineCompletion: InlineCompletionConfig? = null, /** * [Keybindings] @@ -96,6 +74,11 @@ data class ClientProvidedConfig( val token: String? = null, ) + data class ProxyConfig( + val url: String? = null, + val authorization: String? = null, + ) + data class InlineCompletionConfig( /** * [TriggerMode] @@ -199,6 +182,11 @@ data class CompletionEventId( val choiceIndex: Int, ) +data class DidChangeActiveEditorParams( + val activeEditor: Location, + val visibleEditors: List? = null, +) + data class EventParams( /** * [EventType] @@ -227,80 +215,68 @@ data class EventParams( } } -data class DidUpdateServerInfoParams( - val serverInfo: ServerInfo -) - -data class ServerInfo( - val config: ServerInfoConfig, - val health: Map?, +data class Config( + val server: ServerConfig, ) { - data class ServerInfoConfig( + data class ServerConfig( val endpoint: String, - val token: String?, - val requestHeaders: Map?, + val token: String, + val requestHeaders: Map, ) } -data class DidChangeStatusParams( - /** - * [Status] - */ - val status: String, +data class StatusRequestParams( + val recheckConnection: Boolean? = null, ) -sealed class Status { - companion object { - const val NOT_INITIALIZED = "notInitialized" - const val READY = "ready" - const val DISCONNECTED = "disconnected" - const val UNAUTHORIZED = "unauthorized" - const val FINALIZED = "finalized" - } -} - -typealias DidUpdateIssueParams = IssueList - -data class IssueList( +data class StatusInfo( /** - * List of [IssueName] + * [Status] */ - val issues: List -) - -sealed class IssueName { - companion object { - const val SLOW_COMPLETION_RESPONSE_TIME = "slowCompletionResponseTime" - const val HIGH_COMPLETION_TIMEOUT_RATE = "highCompletionTimeoutRate" - const val CONNECTION_FAILED = "connectionFailed" + val status: String, + val tooltip: String? = null, + val serverHealth: Map? = null, + val command: Command? = null, + val helpMessage: String? = null, +) { + sealed class Status { + companion object { + const val CONNECTING = "connecting" + const val UNAUTHORIZED = "unauthorized" + const val DISCONNECTED = "disconnected" + const val READY = "ready" + const val READY_FOR_AUTO_TRIGGER = "readyForAutoTrigger" + const val READY_FOR_MANUAL_TRIGGER = "readyForManualTrigger" + const val FETCHING = "fetching" + const val COMPLETION_RESPONSE_SLOW = "completionResponseSlow" + } } } -data class IssueDetailParams( +data class StatusIgnoredIssuesEditParams( /** - * [IssueName] + * [Operation] */ - val name: String, + val operation: String, /** - * [HelpMessageFormat] + * [StatusIssuesName] */ - val helpMessageFormat: String? = null, + val issues: List? = null, ) { - sealed class HelpMessageFormat { + sealed class Operation { companion object { - const val MARKDOWN = "markdown" - const val HTML = "html" + const val ADD = "add" + const val REMOVE = "remove" + const val REMOVE_ALL = "removeAll" } } -} -data class IssueDetailResult( - /** - * [IssueName] - */ - val name: String, - val helpMessage: String? = null, -) + sealed class StatusIssuesName { + companion object { + const val COMPLETION_RESPONSE_SLOW = "completionResponseSlow" + } + } +} data class ReadFileParams( val uri: String, @@ -321,14 +297,6 @@ data class ReadFileResult( val text: String? = null ) -data class DataStoreGetParams( - val key: String -) - -data class DataStoreSetParams( - val key: String, val value: Any? = null -) - data class SemanticTokensRangeResult( val legend: SemanticTokensLegend, val tokens: SemanticTokens, diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/client/LanguageClient.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/client/LanguageClient.kt index ebe9e3143514..ded948b72735 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/client/LanguageClient.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/client/LanguageClient.kt @@ -2,7 +2,6 @@ package com.tabbyml.intellijtabby.lsp.protocol.client import com.tabbyml.intellijtabby.lsp.protocol.* import com.tabbyml.intellijtabby.lsp.protocol.InitializeParams -import com.tabbyml.intellijtabby.lsp.protocol.InitializeResult import com.tabbyml.intellijtabby.lsp.protocol.server.LanguageServer import org.eclipse.lsp4j.* import org.eclipse.lsp4j.jsonrpc.services.JsonNotification @@ -14,30 +13,18 @@ abstract class LanguageClient { open fun processInitializeResult(server: LanguageServer, result: InitializeResult?) {} - @JsonNotification("tabby/agent/didUpdateServerInfo") - open fun didUpdateServerInfo(params: DidUpdateServerInfoParams) { - } - - @JsonNotification("tabby/agent/didChangeStatus") - open fun didChangeStatus(params: DidChangeStatusParams) { - } - - @JsonNotification("tabby/agent/didUpdateIssues") - open fun didUpdateIssues(params: DidUpdateIssueParams) { - } - - @JsonRequest("tabby/workspaceFileSystem/readFile") - open fun readFile(params: ReadFileParams): CompletableFuture { + @JsonNotification("tabby/config/didChange") + open fun configDidChange(params: Config) { throw UnsupportedOperationException() } - @JsonRequest("tabby/dataStore/get") - open fun dataStoreGet(params: DataStoreGetParams): CompletableFuture { + @JsonNotification("tabby/status/didChange") + open fun statusDidChange(params: StatusInfo) { throw UnsupportedOperationException() } - @JsonRequest("tabby/dataStore/set") - open fun dataStoreSet(params: DataStoreSetParams): CompletableFuture { + @JsonRequest("tabby/workspaceFileSystem/readFile") + open fun readFile(params: ReadFileParams): CompletableFuture { throw UnsupportedOperationException() } diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/AgentFeature.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/AgentFeature.kt deleted file mode 100644 index b88a370d7395..000000000000 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/AgentFeature.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tabbyml.intellijtabby.lsp.protocol.server - -import com.tabbyml.intellijtabby.lsp.protocol.IssueDetailParams -import com.tabbyml.intellijtabby.lsp.protocol.IssueDetailResult -import com.tabbyml.intellijtabby.lsp.protocol.IssueList -import com.tabbyml.intellijtabby.lsp.protocol.ServerInfo -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest -import org.eclipse.lsp4j.jsonrpc.services.JsonSegment -import java.util.concurrent.CompletableFuture - -@JsonSegment("tabby/agent") -interface AgentFeature { - @JsonRequest - fun serverInfo(): CompletableFuture - - @JsonRequest - fun status(): CompletableFuture - - @JsonRequest - fun issues(): CompletableFuture - - @JsonRequest(value = "issue/detail") - fun getIssueDetail(params: IssueDetailParams): CompletableFuture -} diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/ConfigFeature.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/ConfigFeature.kt new file mode 100644 index 000000000000..23d9c1cd5c98 --- /dev/null +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/ConfigFeature.kt @@ -0,0 +1,12 @@ +package com.tabbyml.intellijtabby.lsp.protocol.server + +import com.tabbyml.intellijtabby.lsp.protocol.Config +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment +import java.util.concurrent.CompletableFuture + +@JsonSegment("tabby") +interface ConfigFeature { + @JsonRequest("config") + fun getConfig(): CompletableFuture +} diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/EditorsFeature.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/EditorsFeature.kt new file mode 100644 index 000000000000..383e0bc20f88 --- /dev/null +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/EditorsFeature.kt @@ -0,0 +1,11 @@ +package com.tabbyml.intellijtabby.lsp.protocol.server + +import com.tabbyml.intellijtabby.lsp.protocol.DidChangeActiveEditorParams +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment + +@JsonSegment("tabby/editors") +interface EditorsFeature { + @JsonNotification + fun didChangeActiveEditor(params: DidChangeActiveEditorParams) +} diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/LanguageServer.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/LanguageServer.kt index b3e75062bb12..bc41f72eca97 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/LanguageServer.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/LanguageServer.kt @@ -1,7 +1,7 @@ package com.tabbyml.intellijtabby.lsp.protocol.server import com.tabbyml.intellijtabby.lsp.protocol.InitializeParams -import com.tabbyml.intellijtabby.lsp.protocol.InitializeResult +import org.eclipse.lsp4j.InitializeResult import org.eclipse.lsp4j.InitializedParams import org.eclipse.lsp4j.SetTraceParams import org.eclipse.lsp4j.jsonrpc.services.JsonDelegate @@ -32,10 +32,16 @@ interface LanguageServer { val workspaceFeature: WorkspaceFeature @get:JsonDelegate - val agentFeature: AgentFeature + val telemetryFeature: TelemetryFeature @get:JsonDelegate - val telemetryFeature: TelemetryFeature + val configFeature: ConfigFeature + + @get:JsonDelegate + val statusFeature: StatusFeature + + @get:JsonDelegate + val editorsFeature: EditorsFeature @JsonNotification("$/setTrace") fun setTrace(params: SetTraceParams) diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/StatusFeature.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/StatusFeature.kt new file mode 100644 index 000000000000..9ce9aab4511e --- /dev/null +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/StatusFeature.kt @@ -0,0 +1,20 @@ +package com.tabbyml.intellijtabby.lsp.protocol.server + +import com.tabbyml.intellijtabby.lsp.protocol.StatusIgnoredIssuesEditParams +import com.tabbyml.intellijtabby.lsp.protocol.StatusInfo +import com.tabbyml.intellijtabby.lsp.protocol.StatusRequestParams +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment +import java.util.concurrent.CompletableFuture + +@JsonSegment("tabby") +interface StatusFeature { + @JsonRequest("status") + fun getStatus(params: StatusRequestParams): CompletableFuture + + @JsonRequest("status/showHelpMessage") + fun showHelpMessage(params: Any): CompletableFuture + + @JsonRequest("status/ignoredIssues/edit") + fun editIgnoredIssues(params: StatusIgnoredIssuesEditParams): CompletableFuture +} diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/WorkspaceFeature.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/WorkspaceFeature.kt index 47e38452cbca..059b9d9a2079 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/WorkspaceFeature.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/lsp/protocol/server/WorkspaceFeature.kt @@ -2,8 +2,11 @@ package com.tabbyml.intellijtabby.lsp.protocol.server import com.tabbyml.intellijtabby.lsp.protocol.DidChangeConfigurationParams import org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams +import org.eclipse.lsp4j.ExecuteCommandParams import org.eclipse.lsp4j.jsonrpc.services.JsonNotification +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest import org.eclipse.lsp4j.jsonrpc.services.JsonSegment +import java.util.concurrent.CompletableFuture @JsonSegment("workspace") interface WorkspaceFeature { @@ -12,4 +15,7 @@ interface WorkspaceFeature { @JsonNotification fun didChangeWorkspaceFolders(params: DidChangeWorkspaceFoldersParams) + + @JsonRequest + fun executeCommand(params: ExecuteCommandParams): CompletableFuture } diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/notifications/Notifications.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/notifications/Notifications.kt index 1d5a4a33db25..85cd688bb301 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/notifications/Notifications.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/notifications/Notifications.kt @@ -4,14 +4,21 @@ import com.intellij.ide.BrowserUtil import com.intellij.notification.Notification import com.intellij.notification.NotificationType import com.intellij.notification.Notifications +import com.intellij.openapi.actionSystem.ActionGroup +import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.application.invokeLater import com.intellij.openapi.options.ShowSettingsUtil +import com.intellij.openapi.ui.popup.JBPopupFactory import com.tabbyml.intellijtabby.lsp.ConnectionService import com.tabbyml.intellijtabby.settings.Configurable +var initializationFailedNotification: Notification? = null + fun notifyInitializationFailed(exception: ConnectionService.InitializationException) { + initializationFailedNotification?.expire() + val notification = Notification( "com.tabbyml.intellijtabby.notifications.warning", "Tabby initialization failed", @@ -24,12 +31,16 @@ fun notifyInitializationFailed(exception: ConnectionService.InitializationExcept BrowserUtil.browse("https://tabby.tabbyml.com/docs/extensions/troubleshooting/#tabby-initialization-failed") } }) + initializationFailedNotification = notification invokeLater { Notifications.Bus.notify(notification) } } +var authRequiredNotification: Notification? = null + fun notifyAuthRequired() { + authRequiredNotification?.expire() val notification = Notification( "com.tabbyml.intellijtabby.notifications.warning", "Tabby server requires authentication, please set your personal token.", @@ -41,7 +52,36 @@ fun notifyAuthRequired() { ShowSettingsUtil.getInstance().showSettingsDialog(e.project, Configurable::class.java) } }) + notification.addAction(object : AnAction("Open Online Help") { + override fun actionPerformed(e: AnActionEvent) { + notification.expire() + BrowserUtil.browse("https://tabby.tabbyml.com/docs/quick-start/register-account/") + } + }) + authRequiredNotification = notification invokeLater { Notifications.Bus.notify(notification) } -} \ No newline at end of file +} + +fun hideAuthRequiredNotification() { + authRequiredNotification?.expire() +} + +fun showOnlineHelp(e: AnActionEvent) { + e.project?.let { + invokeLater { + val actionManager = ActionManager.getInstance() + val actionGroup = actionManager.getAction("Tabby.OpenOnlineHelp") as ActionGroup + val popup = JBPopupFactory.getInstance().createActionGroupPopup( + "Online Help", + actionGroup, + e.dataContext, + false, + null, + 10, + ) + popup.showCenteredInCurrentWindow(it) + } + } +} diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsPanel.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsPanel.kt index 6885b2021ba0..608c61ac0d0c 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsPanel.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsPanel.kt @@ -16,9 +16,7 @@ import com.intellij.util.ui.FormBuilder import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil import com.tabbyml.intellijtabby.lsp.ConnectionService -import com.tabbyml.intellijtabby.lsp.protocol.IssueDetailParams -import com.tabbyml.intellijtabby.lsp.protocol.IssueName -import com.tabbyml.intellijtabby.lsp.protocol.Status +import com.tabbyml.intellijtabby.lsp.protocol.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -30,6 +28,7 @@ import javax.swing.JPanel class SettingsPanel(private val project: Project) { private val settings = service() + private val scope = CoroutineScope(Dispatchers.IO) private suspend fun getServer() = project.serviceOrNull()?.getServerAsync() private val serverEndpointTextField = JBTextField() @@ -41,7 +40,6 @@ class SettingsPanel(private val project: Project) { val task = object : Task.Modal( project, parentComponent, "Check Connection", true ) { - private val scope = CoroutineScope(Dispatchers.IO) lateinit var job: Job override fun run(indicator: ProgressIndicator) { job = scope.launch { @@ -52,17 +50,13 @@ class SettingsPanel(private val project: Project) { settings.notifyChanges(project) val server = getServer() ?: return@launch - val status = server.agentFeature.status().await() - when (status) { - Status.READY -> { - invokeLater(ModalityState.stateForComponent(parentComponent)) { - Messages.showInfoMessage( - parentComponent, "Successfully connected to the Tabby server.", "Check Connection Completed" - ) - } + val statusInfo = server.statusFeature.getStatus(StatusRequestParams(recheckConnection = true)).await() + when (statusInfo.status) { + StatusInfo.Status.CONNECTING -> { + // Do nothing } - Status.UNAUTHORIZED -> { + StatusInfo.Status.UNAUTHORIZED -> { invokeLater(ModalityState.stateForComponent(parentComponent)) { Messages.showErrorDialog( parentComponent, @@ -72,22 +66,24 @@ class SettingsPanel(private val project: Project) { } } + StatusInfo.Status.DISCONNECTED -> { + invokeLater(ModalityState.stateForComponent(parentComponent)) { + val errorMessage = statusInfo.helpMessage ?: "Unknown error." + val messages = "Failed to connect to the Tabby server:
${errorMessage}" + Messages.showErrorDialog(parentComponent, messages, "Check Connection Failed") + } + } + else -> { - val detail = server.agentFeature.getIssueDetail( - IssueDetailParams( - name = IssueName.CONNECTION_FAILED, helpMessageFormat = IssueDetailParams.HelpMessageFormat.HTML + invokeLater(ModalityState.stateForComponent(parentComponent)) { + Messages.showInfoMessage( + parentComponent, "Successfully connected to the Tabby server.", "Check Connection Completed" ) - ).await() - if (detail?.name == IssueName.CONNECTION_FAILED) { - invokeLater(ModalityState.stateForComponent(parentComponent)) { - val errorMessage = detail.helpMessage ?: "Unknown error." - val messages = "Failed to connect to the Tabby server:
${errorMessage}" - Messages.showErrorDialog(parentComponent, messages, "Check Connection Failed") - } } } } } + while (job.isActive) { indicator.checkCanceled() Thread.sleep(100) @@ -98,6 +94,7 @@ class SettingsPanel(private val project: Project) { job.cancel() } } + ProgressManager.getInstance().run(task) } } @@ -181,10 +178,14 @@ class SettingsPanel(private val project: Project) { private val resetMutedNotificationsButton = JButton("Reset \"Don't Show Again\" Notifications").apply { addActionListener { - settings.notificationsMuted = mutableListOf() - settings.notifyChanges(project) - invokeLater(ModalityState.stateForComponent(this@SettingsPanel.mainPanel)) { - Messages.showInfoMessage("Reset \"Don't Show Again\" notifications successfully.", "Reset Notifications") + scope.launch { + val server = getServer() ?: return@launch + server.statusFeature.editIgnoredIssues(StatusIgnoredIssuesEditParams(operation = StatusIgnoredIssuesEditParams.Operation.REMOVE_ALL)) + .thenAccept { + invokeLater(ModalityState.stateForComponent(this@SettingsPanel.mainPanel)) { + Messages.showInfoMessage("Reset \"Don't Show Again\" notifications successfully.", "Reset Notifications") + } + } } } } @@ -192,16 +193,19 @@ class SettingsPanel(private val project: Project) { FormBuilder.createFormBuilder().addComponent(resetMutedNotificationsButton).panel val mainPanel: JPanel = - FormBuilder.createFormBuilder().addLabeledComponent("Server", serverEndpointPanel, 5, false).addSeparator(5) - .addLabeledComponent("Inline completion trigger", completionTriggerModePanel, 5, false).addSeparator(5) - .addLabeledComponent("Keymap", keymapStylePanel, 5, false).addSeparator(5) + FormBuilder.createFormBuilder().addLabeledComponent("Server", serverEndpointPanel, 5, false) + .addSeparator(5) + .addLabeledComponent("Inline completion trigger", completionTriggerModePanel, 5, false) + .addSeparator(5) + .addLabeledComponent("Keymap", keymapStylePanel, 5, false) + .addSeparator(5) .addLabeledComponent("Node binary
(Requires restart IDE)", nodeBinaryPanel, 5, false) - .addSeparator(5).addLabeledComponent("Anonymous usage tracking", isAnonymousUsageTrackingPanel, 5, false).apply { - if (settings.notificationsMuted.isNotEmpty()) { - addSeparator(5) - addLabeledComponent("Notifications", resetMutedNotificationsPanel, 5, false) - } - }.addComponentFillVertically(JPanel(), 0).panel + .addSeparator(5) + .addLabeledComponent("Anonymous usage tracking", isAnonymousUsageTrackingPanel, 5, false) + .addSeparator(5) + .addLabeledComponent("Notifications", resetMutedNotificationsPanel, 5, false) + .addComponentFillVertically(JPanel(), 0) + .panel var serverEndpoint: String get() = serverEndpointTextField.text diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsService.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsService.kt index 7a219c0dcbe0..320f7fbacacc 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsService.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsService.kt @@ -38,11 +38,6 @@ class SettingsService : SimplePersistentStateComponent(SettingsSt set(value) { state.isAnonymousUsageTrackingDisabled = value } - var notificationsMuted - get() = state.notificationsMuted - set(value) { - state.notificationsMuted = value - } fun notifyChanges(project: Project) { project.safeSyncPublisher(Listener.TOPIC)?.settingsChanged(settings()) @@ -54,7 +49,6 @@ class SettingsService : SimplePersistentStateComponent(SettingsSt val serverToken: String, val nodeBinary: String, val isAnonymousUsageTrackingDisabled: Boolean, - val notificationsMuted: List, ) fun settings(): Settings { @@ -64,7 +58,6 @@ class SettingsService : SimplePersistentStateComponent(SettingsSt serverToken, nodeBinary, isAnonymousUsageTrackingDisabled, - notificationsMuted, ) } diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsState.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsState.kt index 77d2557137d9..9daac6821597 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsState.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/settings/SettingsState.kt @@ -13,5 +13,4 @@ class SettingsState : BaseState() { var serverToken by string() var nodeBinary by string() var isAnonymousUsageTrackingDisabled by property(false) - var notificationsMuted by list() } \ No newline at end of file diff --git a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/widgets/StatusBarWidgetFactory.kt b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/widgets/StatusBarWidgetFactory.kt index b056093b473a..f4fba94b1b96 100644 --- a/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/widgets/StatusBarWidgetFactory.kt +++ b/clients/intellij/src/main/kotlin/com/tabbyml/intellijtabby/widgets/StatusBarWidgetFactory.kt @@ -16,9 +16,7 @@ import com.intellij.openapi.wm.impl.status.widget.StatusBarEditorBasedWidgetFact import com.intellij.ui.AnimatedIcon import com.tabbyml.intellijtabby.events.CombinedState import com.tabbyml.intellijtabby.lsp.ConnectionService -import com.tabbyml.intellijtabby.lsp.protocol.IssueName -import com.tabbyml.intellijtabby.lsp.protocol.Status -import com.tabbyml.intellijtabby.settings.SettingsState +import com.tabbyml.intellijtabby.lsp.protocol.StatusInfo import javax.swing.Icon class StatusBarWidgetFactory : StatusBarEditorBasedWidgetFactory() { @@ -89,53 +87,37 @@ class StatusBarWidgetFactory : StatusBarEditorBasedWidgetFactory() { } ConnectionService.State.READY -> { - when (combinedState.agentStatus) { - Status.NOT_INITIALIZED, Status.FINALIZED -> { - icon = AnimatedIcon.Default() - tooltip = "Tabby: Initializing" - } + val statusInfo = combinedState.agentStatus + if (statusInfo == null) { + icon = AnimatedIcon.Default() + tooltip = "Tabby: Updating status" + } else { + icon = when (statusInfo.status) { + StatusInfo.Status.CONNECTING, StatusInfo.Status.FETCHING -> { + AnimatedIcon.Default() + } - Status.DISCONNECTED -> { - icon = AllIcons.General.Error - tooltip = "Tabby: Cannot connect to Server, please check settings" - } + StatusInfo.Status.UNAUTHORIZED, StatusInfo.Status.COMPLETION_RESPONSE_SLOW -> { + AllIcons.General.Warning + } - Status.UNAUTHORIZED -> { - icon = AllIcons.General.Warning - tooltip = "Tabby: Authorization required, please set your personal token in settings" - } + StatusInfo.Status.DISCONNECTED -> { + AllIcons.General.Error + } + + StatusInfo.Status.READY, StatusInfo.Status.READY_FOR_AUTO_TRIGGER -> { + AllIcons.Actions.Checked + } - Status.READY -> { - val muted = mutableListOf() - if (combinedState.settings.notificationsMuted.contains("completionResponseTimeIssues")) { - muted += listOf(IssueName.SLOW_COMPLETION_RESPONSE_TIME, IssueName.HIGH_COMPLETION_TIMEOUT_RATE) + StatusInfo.Status.READY_FOR_MANUAL_TRIGGER -> { + AllIcons.General.ChevronRight } - val agentIssue = combinedState.agentIssue - if (agentIssue != null && agentIssue !in muted) { - icon = AllIcons.General.Warning - tooltip = when (agentIssue) { - IssueName.SLOW_COMPLETION_RESPONSE_TIME -> "Tabby: Completion requests appear to take too much time" - IssueName.HIGH_COMPLETION_TIMEOUT_RATE -> "Tabby: Most completion requests timed out" - IssueName.CONNECTION_FAILED -> "Tabby: Cannot connect to Server, please check settings" - else -> "Tabby: Please check issues" - } - } else if (combinedState.isInlineCompletionLoading) { - icon = AnimatedIcon.Default() - tooltip = "Tabby: Generating code completions" - } else { - when (combinedState.settings.completionTriggerMode) { - SettingsState.TriggerMode.AUTOMATIC -> { - icon = AllIcons.Actions.Checked - tooltip = "Tabby: Automatic code completion is enabled" - } - - SettingsState.TriggerMode.MANUAL -> { - icon = AllIcons.General.ChevronRight - tooltip = "Tabby: Standing by, please manually trigger code completion." - } - } + + else -> { + AnimatedIcon.Default() } } + tooltip = statusInfo.tooltip ?: "Tabby: ${statusInfo.status}" } } }