feat(intellij): update tabby-agent to 0.3.1. (#490)
parent
4ebad71805
commit
52c4ef38d3
|
|
@ -24,7 +24,7 @@ jobs:
|
||||||
distribution: zulu
|
distribution: zulu
|
||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Test Build
|
- name: Test Build
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2.4.2
|
||||||
with:
|
with:
|
||||||
arguments: buildPlugin
|
arguments: buildPlugin
|
||||||
build-root-directory: clients/intellij
|
build-root-directory: clients/intellij
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.tabbyml"
|
group = "com.tabbyml"
|
||||||
version = "0.5.0"
|
version = "0.6.0-dev"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -8,7 +8,6 @@ import com.intellij.openapi.application.invokeLater
|
||||||
import com.intellij.openapi.components.service
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.ui.Messages
|
import com.intellij.openapi.ui.Messages
|
||||||
import com.tabbyml.intellijtabby.agent.Agent
|
|
||||||
import com.tabbyml.intellijtabby.agent.AgentService
|
import com.tabbyml.intellijtabby.agent.AgentService
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
@ -89,7 +88,7 @@ class CheckIssueDetail : AnAction() {
|
||||||
|
|
||||||
override fun update(e: AnActionEvent) {
|
override fun update(e: AnActionEvent) {
|
||||||
val agentService = service<AgentService>()
|
val agentService = service<AgentService>()
|
||||||
e.presentation.isVisible = agentService.status.value == Agent.Status.ISSUES_EXIST
|
e.presentation.isVisible = agentService.currentIssue.value != null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getActionUpdateThread(): ActionUpdateThread {
|
override fun getActionUpdateThread(): ActionUpdateThread {
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package com.tabbyml.intellijtabby.actions
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
|
||||||
import com.intellij.openapi.actionSystem.AnAction
|
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
|
||||||
import com.intellij.openapi.components.service
|
|
||||||
import com.tabbyml.intellijtabby.settings.ApplicationSettingsState
|
|
||||||
|
|
||||||
class ToggleAutoCompletionEnabled : AnAction() {
|
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
|
||||||
val settings = service<ApplicationSettingsState>()
|
|
||||||
settings.isAutoCompletionEnabled = !settings.isAutoCompletionEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun update(e: AnActionEvent) {
|
|
||||||
val settings = service<ApplicationSettingsState>()
|
|
||||||
if (settings.isAutoCompletionEnabled) {
|
|
||||||
e.presentation.text = "Disable Auto Completion"
|
|
||||||
e.presentation.description = "Tabby does not show completion suggestions automatically, you can still request them on demand."
|
|
||||||
} else {
|
|
||||||
e.presentation.text = "Enable Auto Completion"
|
|
||||||
e.presentation.description = "Tabby shows inline completion suggestions automatically."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getActionUpdateThread(): ActionUpdateThread {
|
|
||||||
return ActionUpdateThread.BGT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.tabbyml.intellijtabby.actions
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
|
import com.tabbyml.intellijtabby.settings.ApplicationSettingsState
|
||||||
|
|
||||||
|
class ToggleInlineCompletionTriggerMode : AnAction() {
|
||||||
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
|
val settings = service<ApplicationSettingsState>()
|
||||||
|
settings.completionTriggerMode = when (settings.completionTriggerMode) {
|
||||||
|
ApplicationSettingsState.TriggerMode.AUTOMATIC -> ApplicationSettingsState.TriggerMode.MANUAL
|
||||||
|
ApplicationSettingsState.TriggerMode.MANUAL -> ApplicationSettingsState.TriggerMode.AUTOMATIC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(e: AnActionEvent) {
|
||||||
|
val settings = service<ApplicationSettingsState>()
|
||||||
|
if (settings.completionTriggerMode == ApplicationSettingsState.TriggerMode.AUTOMATIC) {
|
||||||
|
e.presentation.text = "Switch to Manual Mode"
|
||||||
|
e.presentation.description = "Manual trigger inline completion suggestions by pressing `Alt + \\`."
|
||||||
|
} else {
|
||||||
|
e.presentation.text = "Switch to Automatic Mode"
|
||||||
|
e.presentation.description = "Show inline completion suggestions automatically."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getActionUpdateThread(): ActionUpdateThread {
|
||||||
|
return ActionUpdateThread.BGT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,15 +5,15 @@ import com.intellij.openapi.actionSystem.AnAction
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
import com.intellij.openapi.components.service
|
import com.intellij.openapi.components.service
|
||||||
import com.tabbyml.intellijtabby.editor.CompletionScheduler
|
import com.tabbyml.intellijtabby.editor.CompletionProvider
|
||||||
|
|
||||||
|
|
||||||
class TriggerCompletion : AnAction() {
|
class TriggerCompletion : AnAction() {
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
val completionScheduler = service<CompletionScheduler>()
|
val completionScheduler = service<CompletionProvider>()
|
||||||
val editor = e.getRequiredData(CommonDataKeys.EDITOR)
|
val editor = e.getRequiredData(CommonDataKeys.EDITOR)
|
||||||
val offset = editor.caretModel.primaryCaret.offset
|
val offset = editor.caretModel.primaryCaret.offset
|
||||||
completionScheduler.schedule(editor, offset, manually = true)
|
completionScheduler.provideCompletion(editor, offset, manually = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(e: AnActionEvent) {
|
override fun update(e: AnActionEvent) {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ class Agent : ProcessAdapter() {
|
||||||
READY,
|
READY,
|
||||||
DISCONNECTED,
|
DISCONNECTED,
|
||||||
UNAUTHORIZED,
|
UNAUTHORIZED,
|
||||||
ISSUES_EXIST,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val statusFlow = MutableStateFlow(Status.NOT_INITIALIZED)
|
private val statusFlow = MutableStateFlow(Status.NOT_INITIALIZED)
|
||||||
|
|
@ -140,17 +139,30 @@ class Agent : ProcessAdapter() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun initialize(config: Config, client: String): Boolean {
|
data class ClientProperties(
|
||||||
|
val user: Map<String, Any>,
|
||||||
|
val session: Map<String, Any>,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun initialize(config: Config, clientProperties: ClientProperties): Boolean {
|
||||||
return request(
|
return request(
|
||||||
"initialize", listOf(
|
"initialize", listOf(
|
||||||
mapOf(
|
mapOf(
|
||||||
"config" to config,
|
"config" to config,
|
||||||
"client" to client,
|
"clientProperties" to clientProperties,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun finalize(): Boolean {
|
||||||
|
return request("finalize", listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateClientProperties(type: String, key: String, value: Any): Boolean {
|
||||||
|
return request("updateClientProperties", listOf(type, key, value))
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun updateConfig(key: String, config: Any): Boolean {
|
suspend fun updateConfig(key: String, config: Any): Boolean {
|
||||||
return request("updateConfig", listOf(key, config))
|
return request("updateConfig", listOf(key, config))
|
||||||
}
|
}
|
||||||
|
|
@ -159,14 +171,44 @@ class Agent : ProcessAdapter() {
|
||||||
return request("clearConfig", listOf(key))
|
return request("clearConfig", listOf(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getIssues(): List<Map<String, Any>> {
|
suspend fun getConfig(): Config {
|
||||||
|
return request("getConfig", listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getStatus(): Status {
|
||||||
|
return request("getStatus", listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getIssues(): List<String> {
|
||||||
return request("getIssues", listOf())
|
return request("getIssues", listOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class GetIssueDetailOptions(
|
||||||
|
val index: Int? = null,
|
||||||
|
val name: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun getIssueDetail(options: GetIssueDetailOptions): Map<String, Any>? {
|
||||||
|
return request("getIssueDetail", listOf(options))
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getServerHealthState(): Map<String, Any>? {
|
suspend fun getServerHealthState(): Map<String, Any>? {
|
||||||
return request("getServerHealthState", listOf())
|
return request("getServerHealthState", listOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class AuthUrlResponse(
|
||||||
|
val authUrl: String,
|
||||||
|
val code: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun requestAuthUrl(): AuthUrlResponse? {
|
||||||
|
return request("requestAuthUrl", listOf(ABORT_SIGNAL_ENABLED))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun waitForAuthToken(code: String) {
|
||||||
|
return request("waitForAuthToken", listOf(code, ABORT_SIGNAL_ENABLED))
|
||||||
|
}
|
||||||
|
|
||||||
data class CompletionRequest(
|
data class CompletionRequest(
|
||||||
val filepath: String,
|
val filepath: String,
|
||||||
val language: String,
|
val language: String,
|
||||||
|
|
@ -185,14 +227,6 @@ class Agent : ProcessAdapter() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun requestAuthUrl(): AuthUrlResponse? {
|
|
||||||
return request("requestAuthUrl", listOf(ABORT_SIGNAL_ENABLED))
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun waitForAuthToken(code: String) {
|
|
||||||
return request("waitForAuthToken", listOf(code, ABORT_SIGNAL_ENABLED))
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun provideCompletions(request: CompletionRequest): CompletionResponse? {
|
suspend fun provideCompletions(request: CompletionRequest): CompletionResponse? {
|
||||||
return request("provideCompletions", listOf(request, ABORT_SIGNAL_ENABLED))
|
return request("provideCompletions", listOf(request, ABORT_SIGNAL_ENABLED))
|
||||||
}
|
}
|
||||||
|
|
@ -201,6 +235,7 @@ class Agent : ProcessAdapter() {
|
||||||
val type: EventType,
|
val type: EventType,
|
||||||
@SerializedName("completion_id") val completionId: String,
|
@SerializedName("completion_id") val completionId: String,
|
||||||
@SerializedName("choice_index") val choiceIndex: Int,
|
@SerializedName("choice_index") val choiceIndex: Int,
|
||||||
|
@SerializedName("select_kind") val selectKind: SelectKind? = null,
|
||||||
) {
|
) {
|
||||||
enum class EventType {
|
enum class EventType {
|
||||||
@SerializedName("view")
|
@SerializedName("view")
|
||||||
|
|
@ -209,16 +244,17 @@ class Agent : ProcessAdapter() {
|
||||||
@SerializedName("select")
|
@SerializedName("select")
|
||||||
SELECT,
|
SELECT,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class SelectKind {
|
||||||
|
@SerializedName("line")
|
||||||
|
LINE,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun postEvent(event: LogEventRequest) {
|
suspend fun postEvent(event: LogEventRequest) {
|
||||||
request<Any>("postEvent", listOf(event, ABORT_SIGNAL_ENABLED))
|
request<Any>("postEvent", listOf(event, ABORT_SIGNAL_ENABLED))
|
||||||
}
|
}
|
||||||
|
|
||||||
data class AuthUrlResponse(
|
|
||||||
val authUrl: String,
|
|
||||||
val code: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun close() {
|
fun close() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -304,12 +340,8 @@ class Agent : ProcessAdapter() {
|
||||||
"ready" -> Status.READY
|
"ready" -> Status.READY
|
||||||
"disconnected" -> Status.DISCONNECTED
|
"disconnected" -> Status.DISCONNECTED
|
||||||
"unauthorized" -> Status.UNAUTHORIZED
|
"unauthorized" -> Status.UNAUTHORIZED
|
||||||
"issuesExist" -> Status.ISSUES_EXIST
|
|
||||||
else -> Status.NOT_INITIALIZED
|
else -> Status.NOT_INITIALIZED
|
||||||
}
|
}
|
||||||
if (statusFlow.value !== Status.ISSUES_EXIST) {
|
|
||||||
currentIssueFlow.value = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"configUpdated" -> {
|
"configUpdated" -> {
|
||||||
|
|
@ -321,9 +353,9 @@ class Agent : ProcessAdapter() {
|
||||||
authRequiredEventFlow.tryEmit(Unit)
|
authRequiredEventFlow.tryEmit(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
"newIssue" -> {
|
"issuesUpdated" -> {
|
||||||
logger.info("Agent notification $event")
|
logger.info("Agent notification $event")
|
||||||
currentIssueFlow.value = (event["issue"] as Map<*, *>)["name"] as String?
|
currentIssueFlow.value = (event["issues"] as List<*>).firstOrNull() as String?
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
|
|
|
||||||
|
|
@ -26,22 +26,27 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class AgentService : Disposable {
|
class AgentService : Disposable {
|
||||||
private val logger = Logger.getInstance(AgentService::class.java)
|
private val logger = Logger.getInstance(AgentService::class.java)
|
||||||
private var agent: Agent = Agent()
|
private var agent: Agent = Agent()
|
||||||
val scope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
val scope: CoroutineScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
private var initFailedNotification: Notification? = null
|
private var initFailedNotification: Notification? = null
|
||||||
var authNotification: Notification? = null
|
var authNotification: Notification? = null
|
||||||
private set
|
private set
|
||||||
var issueNotification: Notification? = null
|
var issueNotification: Notification? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
private var completionResponseWarningShown = false
|
||||||
|
|
||||||
enum class Status {
|
enum class Status {
|
||||||
INITIALIZING,
|
INITIALIZING,
|
||||||
INITIALIZATION_FAILED,
|
INITIALIZATION_FAILED,
|
||||||
}
|
}
|
||||||
|
|
||||||
private var initResultFlow: MutableStateFlow<Boolean?> = MutableStateFlow(null)
|
private var initResultFlow: MutableStateFlow<Boolean?> = MutableStateFlow(null)
|
||||||
val status = initResultFlow.combine(agent.status) { initResult, agentStatus ->
|
val status = initResultFlow.combine(agent.status) { initResult, agentStatus ->
|
||||||
if (initResult == null) {
|
if (initResult == null) {
|
||||||
|
|
@ -59,14 +64,12 @@ class AgentService : Disposable {
|
||||||
val settings = service<ApplicationSettingsState>()
|
val settings = service<ApplicationSettingsState>()
|
||||||
val anonymousUsageLogger = service<AnonymousUsageLogger>()
|
val anonymousUsageLogger = service<AnonymousUsageLogger>()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val appInfo = ApplicationInfo.getInstance().fullApplicationName
|
|
||||||
val pluginId = "com.tabbyml.intellij-tabby"
|
|
||||||
val pluginVersion = PluginManagerCore.getPlugin(PluginId.getId(pluginId))?.version
|
|
||||||
val client = "$appInfo $pluginId $pluginVersion"
|
|
||||||
|
|
||||||
|
val config = createAgentConfig(settings.data)
|
||||||
|
val clientProperties = createClientProperties(settings.data)
|
||||||
try {
|
try {
|
||||||
agent.open()
|
agent.open()
|
||||||
agent.initialize(createAgentConfig(settings.data), client)
|
agent.initialize(config, clientProperties)
|
||||||
initResultFlow.value = true
|
initResultFlow.value = true
|
||||||
logger.info("Agent init done.")
|
logger.info("Agent init done.")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
@ -74,7 +77,7 @@ class AgentService : Disposable {
|
||||||
logger.warn("Agent init failed: $e")
|
logger.warn("Agent init failed: $e")
|
||||||
anonymousUsageLogger.event(
|
anonymousUsageLogger.event(
|
||||||
"IntelliJInitFailed", mapOf(
|
"IntelliJInitFailed", mapOf(
|
||||||
"client" to client, "error" to e.stackTraceToString()
|
"client" to clientProperties.session["client"] as String, "error" to e.stackTraceToString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val notification = Notification(
|
val notification = Notification(
|
||||||
|
|
@ -99,6 +102,7 @@ class AgentService : Disposable {
|
||||||
} else {
|
} else {
|
||||||
clearConfig("server.endpoint")
|
clearConfig("server.endpoint")
|
||||||
}
|
}
|
||||||
|
updateClientProperties("user", "intellij.triggerMode", it.completionTriggerMode)
|
||||||
updateConfig("anonymousUsageTracking.disable", it.isAnonymousUsageTrackingDisabled)
|
updateConfig("anonymousUsageTracking.disable", it.isAnonymousUsageTrackingDisabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -127,6 +131,10 @@ class AgentService : Disposable {
|
||||||
"highCompletionTimeoutRate" -> "Most completion requests timed out"
|
"highCompletionTimeoutRate" -> "Most completion requests timed out"
|
||||||
else -> return@collect
|
else -> return@collect
|
||||||
}
|
}
|
||||||
|
if (completionResponseWarningShown) {
|
||||||
|
return@collect
|
||||||
|
}
|
||||||
|
completionResponseWarningShown = true
|
||||||
val notification = Notification(
|
val notification = Notification(
|
||||||
"com.tabbyml.intellijtabby.notification.warning",
|
"com.tabbyml.intellijtabby.notification.warning",
|
||||||
content,
|
content,
|
||||||
|
|
@ -161,10 +169,36 @@ class AgentService : Disposable {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createClientProperties(state: ApplicationSettingsState.State): Agent.ClientProperties {
|
||||||
|
val appInfo = ApplicationInfo.getInstance()
|
||||||
|
val appVersion = appInfo.fullVersion
|
||||||
|
val appName = appInfo.fullApplicationName.replace(appVersion, "").trim()
|
||||||
|
val pluginId = "com.tabbyml.intellij-tabby"
|
||||||
|
val pluginVersion = PluginManagerCore.getPlugin(PluginId.getId(pluginId))?.version
|
||||||
|
val client = "$appName $pluginId $pluginVersion"
|
||||||
|
return Agent.ClientProperties(
|
||||||
|
user = mapOf(
|
||||||
|
"intellij" to mapOf(
|
||||||
|
"triggerMode" to state.completionTriggerMode,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
session = mapOf(
|
||||||
|
"client" to client,
|
||||||
|
"ide" to mapOf("name" to appName, "version" to appVersion),
|
||||||
|
"tabby_plugin" to mapOf("name" to pluginId, "version" to pluginVersion),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun waitForInitialized() {
|
private suspend fun waitForInitialized() {
|
||||||
agent.status.first { it != Agent.Status.NOT_INITIALIZED }
|
agent.status.first { it != Agent.Status.NOT_INITIALIZED }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun updateClientProperties(type: String, key: String, config: Any) {
|
||||||
|
waitForInitialized()
|
||||||
|
agent.updateClientProperties(type, key, config)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun updateConfig(key: String, config: Any) {
|
private suspend fun updateConfig(key: String, config: Any) {
|
||||||
waitForInitialized()
|
waitForInitialized()
|
||||||
agent.updateConfig(key, config)
|
agent.updateConfig(key, config)
|
||||||
|
|
@ -235,7 +269,7 @@ class AgentService : Disposable {
|
||||||
|
|
||||||
suspend fun getCurrentIssueDetail(): Map<String, Any>? {
|
suspend fun getCurrentIssueDetail(): Map<String, Any>? {
|
||||||
waitForInitialized()
|
waitForInitialized()
|
||||||
return agent.getIssues().firstOrNull { it["name"] == currentIssue.value }
|
return agent.getIssueDetail(Agent.GetIssueDetailOptions(name = currentIssue.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getServerHealthState(): Map<String, Any>? {
|
suspend fun getServerHealthState(): Map<String, Any>? {
|
||||||
|
|
@ -244,6 +278,11 @@ class AgentService : Disposable {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
|
runBlocking {
|
||||||
|
runCatching {
|
||||||
|
agent.finalize()
|
||||||
|
}
|
||||||
|
}
|
||||||
agent.close()
|
agent.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,44 +5,41 @@ import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.tabbyml.intellijtabby.agent.AgentService
|
import com.tabbyml.intellijtabby.agent.AgentService
|
||||||
import com.tabbyml.intellijtabby.settings.ApplicationSettingsState
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class CompletionScheduler {
|
class CompletionProvider {
|
||||||
private val logger = Logger.getInstance(CompletionScheduler::class.java)
|
private val logger = Logger.getInstance(CompletionProvider::class.java)
|
||||||
|
|
||||||
data class CompletionContext(val editor: Editor, val offset: Int, val job: Job)
|
data class CompletionContext(val editor: Editor, val offset: Int, val job: Job)
|
||||||
|
|
||||||
var scheduled: CompletionContext? = null
|
private val ongoingCompletionFlow: MutableStateFlow<CompletionContext?> = MutableStateFlow(null)
|
||||||
private set
|
val ongoingCompletion = ongoingCompletionFlow.asStateFlow()
|
||||||
|
|
||||||
fun schedule(editor: Editor, offset: Int, manually: Boolean = false) {
|
fun provideCompletion(editor: Editor, offset: Int, manually: Boolean = false) {
|
||||||
val agentService = service<AgentService>()
|
val agentService = service<AgentService>()
|
||||||
val inlineCompletionService = service<InlineCompletionService>()
|
val inlineCompletionService = service<InlineCompletionService>()
|
||||||
val settings = service<ApplicationSettingsState>()
|
|
||||||
clear()
|
clear()
|
||||||
val job = agentService.scope.launch {
|
val job = agentService.scope.launch {
|
||||||
if (!manually && !settings.isAutoCompletionEnabled) {
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Trigger completion at $offset")
|
logger.info("Trigger completion at $offset")
|
||||||
agentService.provideCompletion(editor, offset, manually)?.let {
|
agentService.provideCompletion(editor, offset, manually)?.let {
|
||||||
logger.info("Show completion at $offset: $it")
|
logger.info("Show completion at $offset: $it")
|
||||||
inlineCompletionService.show(editor, offset, it)
|
inlineCompletionService.show(editor, offset, it)
|
||||||
|
ongoingCompletionFlow.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scheduled = CompletionContext(editor, offset, job)
|
ongoingCompletionFlow.value = CompletionContext(editor, offset, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
val inlineCompletionService = service<InlineCompletionService>()
|
val inlineCompletionService = service<InlineCompletionService>()
|
||||||
inlineCompletionService.dismiss()
|
inlineCompletionService.dismiss()
|
||||||
scheduled?.let {
|
ongoingCompletionFlow.value?.let {
|
||||||
it.job.cancel()
|
it.job.cancel()
|
||||||
scheduled = null
|
ongoingCompletionFlow.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||||
import com.intellij.util.messages.MessageBusConnection
|
import com.intellij.util.messages.MessageBusConnection
|
||||||
|
import com.tabbyml.intellijtabby.settings.ApplicationSettingsState
|
||||||
|
|
||||||
class EditorListener : EditorFactoryListener {
|
class EditorListener : EditorFactoryListener {
|
||||||
private val logger = Logger.getInstance(EditorListener::class.java)
|
private val logger = Logger.getInstance(EditorListener::class.java)
|
||||||
|
|
@ -16,14 +17,17 @@ class EditorListener : EditorFactoryListener {
|
||||||
override fun editorCreated(event: EditorFactoryEvent) {
|
override fun editorCreated(event: EditorFactoryEvent) {
|
||||||
val editor = event.editor
|
val editor = event.editor
|
||||||
val editorManager = editor.project?.let { FileEditorManager.getInstance(it) } ?: return
|
val editorManager = editor.project?.let { FileEditorManager.getInstance(it) } ?: return
|
||||||
val completionScheduler = service<CompletionScheduler>()
|
val settings = service<ApplicationSettingsState>()
|
||||||
|
val completionProvider = service<CompletionProvider>()
|
||||||
|
|
||||||
editor.caretModel.addCaretListener(object : CaretListener {
|
editor.caretModel.addCaretListener(object : CaretListener {
|
||||||
override fun caretPositionChanged(event: CaretEvent) {
|
override fun caretPositionChanged(event: CaretEvent) {
|
||||||
if (editorManager.selectedTextEditor == editor) {
|
if (editorManager.selectedTextEditor == editor) {
|
||||||
completionScheduler.scheduled?.let {
|
completionProvider.ongoingCompletion.value.let {
|
||||||
if (it.editor != editor || it.offset != editor.caretModel.primaryCaret.offset) {
|
if (it != null && it.editor == editor && it.offset == editor.caretModel.primaryCaret.offset) {
|
||||||
completionScheduler.clear()
|
// keep ongoing completion
|
||||||
|
} else {
|
||||||
|
completionProvider.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,8 +37,10 @@ class EditorListener : EditorFactoryListener {
|
||||||
editor.document.addDocumentListener(object : DocumentListener {
|
editor.document.addDocumentListener(object : DocumentListener {
|
||||||
override fun documentChanged(event: DocumentEvent) {
|
override fun documentChanged(event: DocumentEvent) {
|
||||||
if (editorManager.selectedTextEditor == editor) {
|
if (editorManager.selectedTextEditor == editor) {
|
||||||
val offset = event.offset + event.newFragment.length
|
if (settings.completionTriggerMode == ApplicationSettingsState.TriggerMode.AUTOMATIC) {
|
||||||
completionScheduler.schedule(editor, offset)
|
val offset = event.offset + event.newFragment.length
|
||||||
|
completionProvider.provideCompletion(editor, offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -42,10 +48,10 @@ class EditorListener : EditorFactoryListener {
|
||||||
editor.project?.messageBus?.connect()?.let {
|
editor.project?.messageBus?.connect()?.let {
|
||||||
it.subscribe(
|
it.subscribe(
|
||||||
FileEditorManagerListener.FILE_EDITOR_MANAGER,
|
FileEditorManagerListener.FILE_EDITOR_MANAGER,
|
||||||
object: FileEditorManagerListener {
|
object : FileEditorManagerListener {
|
||||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||||
logger.info("FileEditorManagerListener selectionChanged.")
|
logger.info("FileEditorManagerListener selectionChanged.")
|
||||||
completionScheduler.clear()
|
completionProvider.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -54,9 +60,6 @@ class EditorListener : EditorFactoryListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun editorReleased(event: EditorFactoryEvent) {
|
override fun editorReleased(event: EditorFactoryEvent) {
|
||||||
messagesConnection[event.editor]?.let {
|
messagesConnection[event.editor]?.disconnect()
|
||||||
it.disconnect()
|
|
||||||
it.dispose()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -18,21 +18,21 @@ class ApplicationConfigurable : Configurable {
|
||||||
|
|
||||||
override fun isModified(): Boolean {
|
override fun isModified(): Boolean {
|
||||||
val settings = service<ApplicationSettingsState>()
|
val settings = service<ApplicationSettingsState>()
|
||||||
return settingsPanel.isAutoCompletionEnabled != settings.isAutoCompletionEnabled
|
return settingsPanel.completionTriggerMode != settings.completionTriggerMode
|
||||||
|| settingsPanel.serverEndpoint != settings.serverEndpoint
|
|| settingsPanel.serverEndpoint != settings.serverEndpoint
|
||||||
|| settingsPanel.isAnonymousUsageTrackingDisabled != settings.isAnonymousUsageTrackingDisabled
|
|| settingsPanel.isAnonymousUsageTrackingDisabled != settings.isAnonymousUsageTrackingDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun apply() {
|
override fun apply() {
|
||||||
val settings = service<ApplicationSettingsState>()
|
val settings = service<ApplicationSettingsState>()
|
||||||
settings.isAutoCompletionEnabled = settingsPanel.isAutoCompletionEnabled
|
settings.completionTriggerMode = settingsPanel.completionTriggerMode
|
||||||
settings.serverEndpoint = settingsPanel.serverEndpoint
|
settings.serverEndpoint = settingsPanel.serverEndpoint
|
||||||
settings.isAnonymousUsageTrackingDisabled = settingsPanel.isAnonymousUsageTrackingDisabled
|
settings.isAnonymousUsageTrackingDisabled = settingsPanel.isAnonymousUsageTrackingDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reset() {
|
override fun reset() {
|
||||||
val settings = service<ApplicationSettingsState>()
|
val settings = service<ApplicationSettingsState>()
|
||||||
settingsPanel.isAutoCompletionEnabled = settings.isAutoCompletionEnabled
|
settingsPanel.completionTriggerMode = settings.completionTriggerMode
|
||||||
settingsPanel.serverEndpoint = settings.serverEndpoint
|
settingsPanel.serverEndpoint = settings.serverEndpoint
|
||||||
settingsPanel.isAnonymousUsageTrackingDisabled = settings.isAnonymousUsageTrackingDisabled
|
settingsPanel.isAnonymousUsageTrackingDisabled = settings.isAnonymousUsageTrackingDisabled
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,62 @@
|
||||||
package com.tabbyml.intellijtabby.settings
|
package com.tabbyml.intellijtabby.settings
|
||||||
|
|
||||||
import com.intellij.ui.components.JBCheckBox
|
import com.intellij.ui.components.JBCheckBox
|
||||||
|
import com.intellij.ui.components.JBRadioButton
|
||||||
import com.intellij.ui.components.JBTextField
|
import com.intellij.ui.components.JBTextField
|
||||||
import com.intellij.util.ui.FormBuilder
|
import com.intellij.util.ui.FormBuilder
|
||||||
|
import javax.swing.ButtonGroup
|
||||||
import javax.swing.JPanel
|
import javax.swing.JPanel
|
||||||
|
|
||||||
class ApplicationSettingsPanel {
|
class ApplicationSettingsPanel {
|
||||||
private val isAutoCompletionEnabledCheckBox = JBCheckBox("Enable auto completion")
|
|
||||||
private val serverEndpointTextField = JBTextField()
|
private val serverEndpointTextField = JBTextField()
|
||||||
private val isAnonymousUsageTrackingDisabledCheckBox = JBCheckBox("Disable anonymous usage tracking")
|
private val serverEndpointPanel = FormBuilder.createFormBuilder()
|
||||||
|
.addComponent(serverEndpointTextField)
|
||||||
|
.addTooltip(
|
||||||
|
"""
|
||||||
|
<html>
|
||||||
|
A http or https URL of Tabby server endpoint.<br/>
|
||||||
|
If leave empty, server endpoint config in <i>~/.tabby-client/agent/config.toml</i> will be used<br/>
|
||||||
|
Default to <i>http://localhost:8080</i>.
|
||||||
|
</html>
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
.panel
|
||||||
|
|
||||||
|
private val completionTriggerModeAutomaticRadioButton = JBRadioButton("Automatic")
|
||||||
|
private val completionTriggerModeManualRadioButton = JBRadioButton("Manual")
|
||||||
|
private val completionTriggerModeRadioGroup = ButtonGroup().apply {
|
||||||
|
add(completionTriggerModeAutomaticRadioButton)
|
||||||
|
add(completionTriggerModeManualRadioButton)
|
||||||
|
}
|
||||||
|
private val completionTriggerModePanel: JPanel = FormBuilder.createFormBuilder()
|
||||||
|
.addComponent(completionTriggerModeAutomaticRadioButton)
|
||||||
|
.addTooltip("Trigger automatically when you stop typing")
|
||||||
|
.addComponent(completionTriggerModeManualRadioButton)
|
||||||
|
.addTooltip("Trigger manually by pressing `Alt + \\`")
|
||||||
|
.panel
|
||||||
|
|
||||||
|
private val isAnonymousUsageTrackingDisabledCheckBox = JBCheckBox("Disable")
|
||||||
|
|
||||||
val mainPanel: JPanel = FormBuilder.createFormBuilder()
|
val mainPanel: JPanel = FormBuilder.createFormBuilder()
|
||||||
.addLabeledComponent("Server endpoint", serverEndpointTextField, 1, false)
|
.addLabeledComponent("Server endpoint", serverEndpointPanel, 5, false)
|
||||||
.addTooltip("A http or https URL of Tabby server endpoint.")
|
.addSeparator(5)
|
||||||
.addTooltip("If leave empty, server endpoint config in `~/.tabby-client/agent/config.toml` will be used")
|
.addLabeledComponent("Inline completion trigger", completionTriggerModePanel, 5, false)
|
||||||
.addTooltip("Default to 'http://localhost:8080'.")
|
.addSeparator(5)
|
||||||
.addSeparator()
|
.addLabeledComponent("Anonymous usage tracking", isAnonymousUsageTrackingDisabledCheckBox, 5, false)
|
||||||
.addComponent(isAutoCompletionEnabledCheckBox, 1)
|
|
||||||
.addComponent(isAnonymousUsageTrackingDisabledCheckBox, 1)
|
|
||||||
.addComponentFillVertically(JPanel(), 0)
|
.addComponentFillVertically(JPanel(), 0)
|
||||||
.panel
|
.panel
|
||||||
|
|
||||||
var isAutoCompletionEnabled: Boolean
|
var completionTriggerMode: ApplicationSettingsState.TriggerMode
|
||||||
get() = isAutoCompletionEnabledCheckBox.isSelected
|
get() = if (completionTriggerModeAutomaticRadioButton.isSelected) {
|
||||||
|
ApplicationSettingsState.TriggerMode.AUTOMATIC
|
||||||
|
} else {
|
||||||
|
ApplicationSettingsState.TriggerMode.MANUAL
|
||||||
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
isAutoCompletionEnabledCheckBox.isSelected = value
|
when (value) {
|
||||||
|
ApplicationSettingsState.TriggerMode.AUTOMATIC -> completionTriggerModeAutomaticRadioButton.isSelected = true
|
||||||
|
ApplicationSettingsState.TriggerMode.MANUAL -> completionTriggerModeManualRadioButton.isSelected = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverEndpoint: String
|
var serverEndpoint: String
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tabbyml.intellijtabby.settings
|
package com.tabbyml.intellijtabby.settings
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.intellij.openapi.components.PersistentStateComponent
|
import com.intellij.openapi.components.PersistentStateComponent
|
||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
import com.intellij.openapi.components.State
|
import com.intellij.openapi.components.State
|
||||||
|
|
@ -14,7 +15,14 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||||
storages = [Storage("intellij-tabby.xml")]
|
storages = [Storage("intellij-tabby.xml")]
|
||||||
)
|
)
|
||||||
class ApplicationSettingsState : PersistentStateComponent<ApplicationSettingsState> {
|
class ApplicationSettingsState : PersistentStateComponent<ApplicationSettingsState> {
|
||||||
var isAutoCompletionEnabled: Boolean = true
|
enum class TriggerMode {
|
||||||
|
@SerializedName("manual")
|
||||||
|
MANUAL,
|
||||||
|
@SerializedName("automatic")
|
||||||
|
AUTOMATIC,
|
||||||
|
}
|
||||||
|
|
||||||
|
var completionTriggerMode: TriggerMode = TriggerMode.AUTOMATIC
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
stateFlow.value = this.data
|
stateFlow.value = this.data
|
||||||
|
|
@ -31,14 +39,14 @@ class ApplicationSettingsState : PersistentStateComponent<ApplicationSettingsSta
|
||||||
}
|
}
|
||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val isAutoCompletionEnabled: Boolean,
|
val completionTriggerMode: TriggerMode,
|
||||||
val serverEndpoint: String,
|
val serverEndpoint: String,
|
||||||
val isAnonymousUsageTrackingDisabled: Boolean,
|
val isAnonymousUsageTrackingDisabled: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
val data: State
|
val data: State
|
||||||
get() = State(
|
get() = State(
|
||||||
isAutoCompletionEnabled = isAutoCompletionEnabled,
|
completionTriggerMode = completionTriggerMode,
|
||||||
serverEndpoint = serverEndpoint,
|
serverEndpoint = serverEndpoint,
|
||||||
isAnonymousUsageTrackingDisabled = isAnonymousUsageTrackingDisabled,
|
isAnonymousUsageTrackingDisabled = isAnonymousUsageTrackingDisabled,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,17 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.openapi.wm.StatusBarWidget
|
import com.intellij.openapi.wm.StatusBarWidget
|
||||||
import com.intellij.openapi.wm.impl.status.EditorBasedStatusBarPopup
|
import com.intellij.openapi.wm.impl.status.EditorBasedStatusBarPopup
|
||||||
import com.intellij.openapi.wm.impl.status.widget.StatusBarEditorBasedWidgetFactory
|
import com.intellij.openapi.wm.impl.status.widget.StatusBarEditorBasedWidgetFactory
|
||||||
|
import com.intellij.ui.AnimatedIcon
|
||||||
import com.tabbyml.intellijtabby.agent.Agent
|
import com.tabbyml.intellijtabby.agent.Agent
|
||||||
import com.tabbyml.intellijtabby.agent.AgentService
|
import com.tabbyml.intellijtabby.agent.AgentService
|
||||||
|
import com.tabbyml.intellijtabby.editor.CompletionProvider
|
||||||
import com.tabbyml.intellijtabby.settings.ApplicationSettingsState
|
import com.tabbyml.intellijtabby.settings.ApplicationSettingsState
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.swing.Icon
|
||||||
|
|
||||||
class StatusBarWidgetFactory : StatusBarEditorBasedWidgetFactory() {
|
class StatusBarWidgetFactory : StatusBarEditorBasedWidgetFactory() {
|
||||||
override fun getId(): String {
|
override fun getId(): String {
|
||||||
|
|
@ -30,20 +33,28 @@ class StatusBarWidgetFactory : StatusBarEditorBasedWidgetFactory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createWidget(project: Project): StatusBarWidget {
|
override fun createWidget(project: Project): StatusBarWidget {
|
||||||
|
data class CombinedState(
|
||||||
|
val settings: ApplicationSettingsState.State,
|
||||||
|
val agentStatus: Enum<*>,
|
||||||
|
val currentIssue: String?,
|
||||||
|
val ongoingCompletion: CompletionProvider.CompletionContext?,
|
||||||
|
)
|
||||||
|
|
||||||
return object : EditorBasedStatusBarPopup(project, false) {
|
return object : EditorBasedStatusBarPopup(project, false) {
|
||||||
val updateStatusScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
|
val updateStatusScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
|
||||||
val text = "Tabby"
|
val text = "Tabby"
|
||||||
var icon = AllIcons.Actions.Refresh
|
var icon: Icon = AnimatedIcon.Default()
|
||||||
var tooltip = "Tabby: Initializing"
|
var tooltip = "Tabby: Initializing"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val settings = service<ApplicationSettingsState>()
|
val settings = service<ApplicationSettingsState>()
|
||||||
val agentService = service<AgentService>()
|
val agentService = service<AgentService>()
|
||||||
|
val completionProvider = service<CompletionProvider>()
|
||||||
updateStatusScope.launch {
|
updateStatusScope.launch {
|
||||||
combine(settings.state, agentService.status, agentService.currentIssue) { settings, agentStatus, currentIssue ->
|
combine(settings.state, agentService.status, agentService.currentIssue, completionProvider.ongoingCompletion) { settings, agentStatus, currentIssue, ongoingCompletion ->
|
||||||
Triple(settings, agentStatus, currentIssue)
|
CombinedState(settings, agentStatus, currentIssue, ongoingCompletion)
|
||||||
}.collect {
|
}.collect {
|
||||||
updateStatus(it.first, it.second, it.third)
|
updateStatus(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +85,7 @@ class StatusBarWidgetFactory : StatusBarEditorBasedWidgetFactory() {
|
||||||
return arrayOf(
|
return arrayOf(
|
||||||
actionManager.getAction("Tabby.OpenAuthPage"),
|
actionManager.getAction("Tabby.OpenAuthPage"),
|
||||||
actionManager.getAction("Tabby.CheckIssueDetail"),
|
actionManager.getAction("Tabby.CheckIssueDetail"),
|
||||||
actionManager.getAction("Tabby.ToggleAutoCompletionEnabled"),
|
actionManager.getAction("Tabby.ToggleInlineCompletionTriggerMode"),
|
||||||
actionManager.getAction("Tabby.OpenSettings"),
|
actionManager.getAction("Tabby.OpenSettings"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -86,41 +97,51 @@ class StatusBarWidgetFactory : StatusBarEditorBasedWidgetFactory() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateStatus(settingsState: ApplicationSettingsState.State, agentStatus: Enum<*>, currentIssue: String?) {
|
private fun updateStatus(state: CombinedState) {
|
||||||
if (!settingsState.isAutoCompletionEnabled) {
|
when(state.agentStatus) {
|
||||||
icon = AllIcons.Windows.CloseSmall
|
AgentService.Status.INITIALIZING, Agent.Status.NOT_INITIALIZED -> {
|
||||||
tooltip = "Tabby: Auto completion is disabled"
|
icon = AnimatedIcon.Default()
|
||||||
} else {
|
tooltip = "Tabby: Initializing"
|
||||||
when(agentStatus) {
|
}
|
||||||
AgentService.Status.INITIALIZING, Agent.Status.NOT_INITIALIZED -> {
|
AgentService.Status.INITIALIZATION_FAILED -> {
|
||||||
icon = AllIcons.Actions.Refresh
|
icon = AllIcons.General.Error
|
||||||
tooltip = "Tabby: Initializing"
|
tooltip = "Tabby: Initialization failed"
|
||||||
}
|
}
|
||||||
AgentService.Status.INITIALIZATION_FAILED -> {
|
Agent.Status.READY -> {
|
||||||
icon = AllIcons.General.Error
|
if (state.currentIssue != null) {
|
||||||
tooltip = "Tabby: Initialization failed"
|
|
||||||
}
|
|
||||||
Agent.Status.READY -> {
|
|
||||||
icon = AllIcons.Actions.Checked
|
|
||||||
tooltip = "Tabby: Ready"
|
|
||||||
}
|
|
||||||
Agent.Status.DISCONNECTED -> {
|
|
||||||
icon = AllIcons.General.Error
|
|
||||||
tooltip = "Tabby: Cannot connect to Server"
|
|
||||||
}
|
|
||||||
Agent.Status.UNAUTHORIZED -> {
|
|
||||||
icon = AllIcons.General.Warning
|
icon = AllIcons.General.Warning
|
||||||
tooltip = "Tabby: Requires authorization"
|
tooltip = when(state.currentIssue) {
|
||||||
}
|
|
||||||
Agent.Status.ISSUES_EXIST -> {
|
|
||||||
icon = AllIcons.General.Warning
|
|
||||||
tooltip = when(currentIssue) {
|
|
||||||
"slowCompletionResponseTime" -> "Tabby: Completion requests appear to take too much time"
|
"slowCompletionResponseTime" -> "Tabby: Completion requests appear to take too much time"
|
||||||
"highCompletionTimeoutRate" -> "Tabby: Most completion requests timed out"
|
"highCompletionTimeoutRate" -> "Tabby: Most completion requests timed out"
|
||||||
else -> "Tabby: Issues exist"
|
else -> "Tabby: Issues exist"
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
when (state.settings.completionTriggerMode) {
|
||||||
|
ApplicationSettingsState.TriggerMode.AUTOMATIC -> {
|
||||||
|
icon = AllIcons.Actions.Checked
|
||||||
|
tooltip = "Tabby: Automatic code completion is enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationSettingsState.TriggerMode.MANUAL -> {
|
||||||
|
if (state.ongoingCompletion == null) {
|
||||||
|
icon = AllIcons.General.ChevronRight
|
||||||
|
tooltip = "Tabby: Standing by, press `Alt + \\` to trigger code completion."
|
||||||
|
} else {
|
||||||
|
icon = AnimatedIcon.Default()
|
||||||
|
tooltip = "Tabby: Generating code completions"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Agent.Status.DISCONNECTED -> {
|
||||||
|
icon = AllIcons.General.Error
|
||||||
|
tooltip = "Tabby: Cannot connect to Server, please check settings"
|
||||||
|
}
|
||||||
|
Agent.Status.UNAUTHORIZED -> {
|
||||||
|
icon = AllIcons.General.Warning
|
||||||
|
tooltip = "Tabby: Authorization required, click to continue"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
invokeLater {
|
invokeLater {
|
||||||
update { myStatusBar?.updateWidget(ID()) }
|
update { myStatusBar?.updateWidget(ID()) }
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,7 @@
|
||||||
<h2 id="demo">Demo</h2>
|
<h2 id="demo">Demo</h2>
|
||||||
<p>Try our online demo <a href="https://tabby.tabbyml.com/playground/">here</a>.</p>
|
<p>Try our online demo <a href="https://tabby.tabbyml.com/playground/">here</a>.</p>
|
||||||
<h2 id="requirements">Requirements</h2>
|
<h2 id="requirements">Requirements</h2>
|
||||||
Tabby plugin requires <a href="https://nodejs.org/">Node.js</a> 16.0+ installed and added into <code>PATH</code> enviroment variable. </p>
|
Tabby plugin requires <a href="https://nodejs.org/">Node.js</a> v18+ installed and added into <code>PATH</code> environment variable. </p>
|
||||||
<h2 id="get-started">Get Started</h2>
|
|
||||||
<ol>
|
|
||||||
<li>
|
|
||||||
Set up the Tabby server: you can get a Tabby Cloud hosted server <a href="https://app.tabbyml.com">here</a>, or build your self-hosted Tabby server following <a href="https://tabby.tabbyml.com/docs/installation/">this guide</a>.<br/>
|
|
||||||
<b>Note:</b> Tabby Cloud is currently in beta. Join our <a href="https://join.slack.com/t/tabbycommunity/shared_invite/zt-1xeiddizp-bciR2RtFTaJ37RBxr8VxpA">Slack community</a> and ask in Tabby Cloud channel to get a beta invite.
|
|
||||||
</li>
|
|
||||||
<li>Open the settings page <code>Settings > Editor > Tabby</code>, or click the <code>Tabby</code> status bar item and <code>Open Settings...</code>. Fill in the server endpoint URL to connect the plugin to your Tabby server. The status bar item will show a checked icon if the connection is successful.</li>
|
|
||||||
<li>Once setup is complete, Tabby will provide inline suggestions automatically, and you can accept suggestions by just pressing the <code>Tab</code> key.</li>
|
|
||||||
<li>You can find more actions and hotkey in the IDE tools menu <code>Code > Tabby</code>.</li>
|
|
||||||
</ol>
|
|
||||||
]]></description>
|
]]></description>
|
||||||
|
|
||||||
<!-- Product and plugin compatibility requirements.
|
<!-- Product and plugin compatibility requirements.
|
||||||
|
|
@ -93,8 +83,8 @@
|
||||||
text="Check Issue Detail..."
|
text="Check Issue Detail..."
|
||||||
description="Show detail information for current issue.">
|
description="Show detail information for current issue.">
|
||||||
</action>
|
</action>
|
||||||
<action id="Tabby.ToggleAutoCompletionEnabled"
|
<action id="Tabby.ToggleInlineCompletionTriggerMode"
|
||||||
class="com.tabbyml.intellijtabby.actions.ToggleAutoCompletionEnabled">
|
class="com.tabbyml.intellijtabby.actions.ToggleInlineCompletionTriggerMode">
|
||||||
</action>
|
</action>
|
||||||
<action id="Tabby.OpenSettings"
|
<action id="Tabby.OpenSettings"
|
||||||
class="com.tabbyml.intellijtabby.actions.OpenSettings"
|
class="com.tabbyml.intellijtabby.actions.OpenSettings"
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 107 KiB |
Loading…
Reference in New Issue