Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fixes #200

Merged
merged 9 commits into from
Jan 9, 2025
Merged

Fixes #200

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
workflow: node.js.yml
workflow_search: true
repo: smallcloudai/refact-chat-js
branch: main
branch: alpha
name: refact-chat-js-latest
path: ./src/main/resources/webview/dist

Expand Down
2 changes: 1 addition & 1 deletion refact_lsp
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.10.5
main
38 changes: 0 additions & 38 deletions src/main/kotlin/com/smallcloud/refactai/Initializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,44 +40,6 @@ class Initializer : ProjectActivity, Disposable {
PluginInstaller.addStateListener(UninstallListener())
UpdateChecker.instance

ApplicationManager.getApplication()
.messageBus
.connect(PluginState.instance)
.subscribe(KeymapManagerListener.TOPIC, object : KeymapManagerListener {
override fun shortcutsChanged(
keymap: Keymap,
actionIds: MutableCollection<String?>,
fromSettings: Boolean
) {
if (Thread.currentThread().stackTrace.count { it.className.startsWith("com.smallcloud.refactai.Initializer") } > 1) {
return
}
for (id in actionIds) {
if (!listOf(IdeActions.ACTION_INSERT_INLINE_COMPLETION, ACTION_ID_).contains(id)) {
continue
}
val shortcuts = keymap.getShortcuts(id)
if (id == IdeActions.ACTION_INSERT_INLINE_COMPLETION) {
keymap.removeAllActionShortcuts(ACTION_ID_)
for (shortcut in shortcuts) {
keymap.addShortcut(
ACTION_ID_,
shortcut
)
}
} else if (id == ACTION_ID_) {
keymap.removeAllActionShortcuts(IdeActions.ACTION_INSERT_INLINE_COMPLETION)
for (shortcut in shortcuts) {
keymap.addShortcut(
IdeActions.ACTION_INSERT_INLINE_COMPLETION,
shortcut
)
}
}
}
}
})

ApplicationManager.getApplication().getService(CloudMessageService::class.java)
if (!isJcefCanStart()) {
emitInfo(RefactAIBundle.message("notifications.chatCanNotStartWarning"), false)
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/com/smallcloud/refactai/Resources.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ object Resources {
val defaultCodeCompletionUrlSuffix = URI("v1/code-completion")
val cloudUserMessage: URI = defaultCloudUrl.resolve("/v1/user-message")
val defaultReportUrlSuffix: URI = URI("v1/telemetry-network")
val defaultChatReportUrlSuffix: URI = URI("v1/telemetry-chat")
val defaultSnippetAcceptedUrlSuffix: URI = URI("v1/snippet-accepted")
val version: String = getVersion()
const val client: String = "jetbrains"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package com.smallcloud.refactai.code_lens

import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.wm.ToolWindowManager
import com.smallcloud.refactai.Resources
import com.smallcloud.refactai.panes.RefactAIToolboxPaneFactory
import com.smallcloud.refactai.statistic.UsageStatistic
import com.smallcloud.refactai.statistic.UsageStats
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.io.path.relativeTo

Expand Down Expand Up @@ -50,6 +53,7 @@ class CodeLensAction(
chat?.activate {
RefactAIToolboxPaneFactory.chat?.requestFocus()
RefactAIToolboxPaneFactory.chat?.executeCodeLensCommand(formatMessage(), sendImmediately, openNewTab)
editor.project?.service<UsageStats>()?.addChatStatistic(true, UsageStatistic("openChatByCodelens"), "")
}

// If content is empty, then it's "Open Chat" instruction, selecting range of code in active tab
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Document
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.ui.components.JBLabel
import com.intellij.util.application
import com.intellij.util.ui.JBFont
Expand Down Expand Up @@ -50,6 +51,9 @@ import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import com.smallcloud.refactai.io.InferenceGlobalContext.Companion.instance as InferenceGlobalContext

val EditorRefactLastSnippetTelemetryIdKey = Key.create<Int>("refact.snippetTelemetryId")
val EditorRefactLastCompletionIsMultilineKey = Key.create<Boolean>("refact.lastCompletion.isMultiline")

private class Default : InlineCompletionSuggestionUpdateManager.Adapter {
override fun onDocumentChange(
event: InlineCompletionEvent.DocumentChange,
Expand Down Expand Up @@ -144,17 +148,16 @@ class RefactAICompletionProvider : DebouncedInlineCompletionProvider() {

override fun restartOn(event: InlineCompletionEvent): Boolean = false

private fun getActiveFile(document: Document, project: Project?): String? {
val projectPath = project?.basePath ?: return null
private fun getActiveFile(document: Document): String? {
val file = FileDocumentManager.getInstance().getFile(document) ?: return null
return Path(file.path).toUri().toString().replace(Path(projectPath).toUri().toString(), "")
return Path(file.path).toString()
}

private class Context(val request: SMCRequest, val editorState: EditorTextState, val force: Boolean = false)


private fun makeContext(request: InlineCompletionRequest): Context? {
val fileName = getActiveFile(request.document, request.editor.project) ?: return null
val fileName = getActiveFile(request.document) ?: return null
if (PrivacyService.instance.getPrivacy(FileDocumentManager.getInstance().getFile(request.document))
== Privacy.DISABLED && !InferenceGlobalContext.isSelfHosted
) return null
Expand Down Expand Up @@ -253,10 +256,11 @@ class RefactAICompletionProvider : DebouncedInlineCompletionProvider() {
send(it)
delay(2)
}
EditorRefactLastSnippetTelemetryIdKey[request.editor] = completion.snippetTelemetryId
EditorRefactLastCompletionIsMultilineKey[request.editor] = completion.multiline
}
}
awaitClose()

})

private fun getSingleLineElements(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,21 @@ class RefactAIContinuousEvent(val editor: Editor, val offset: Int) : InlineCompl
@RequiresBlockingContext
private fun getPsiFile(editor: Editor, project: Project): PsiFile? {
return runReadAction {
val file =
PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return@runReadAction null
// * [PsiUtilBase] takes into account injected [PsiFile] (like in Jupyter Notebooks)
// * However, it loads a file into the memory, which is expensive
// * Some tests forbid loading a file when tearing down
// * On tearing down, Lookup Cancellation happens, which causes the event
// * Existence of [treeElement] guarantees that it's in the memory
if (file.isLoadedInMemory()) {
PsiUtilBase.getPsiFileInEditor(editor, project)
} else {
file
try {
val file =
PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return@runReadAction null
// * [PsiUtilBase] takes into account injected [PsiFile] (like in Jupyter Notebooks)
// * However, it loads a file into the memory, which is expensive
// * Some tests forbid loading a file when tearing down
// * On tearing down, Lookup Cancellation happens, which causes the event
// * Existence of [treeElement] guarantees that it's in the memory
if (file.isLoadedInMemory()) {
PsiUtilBase.getPsiFileInEditor(editor, project)
} else {
file
}
} catch (e: Exception) {
return@runReadAction null
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,6 @@ class InferenceGlobalContext : Disposable {
.astFileLimitChanged(newValue)
}

var astLightMode: Boolean
get() = AppSettingsState.astLightMode
set(newValue) {
if (newValue == astLightMode) return
messageBus
.syncPublisher(InferenceGlobalContextChangedNotifier.TOPIC)
.astLightModeChanged(newValue)
}


var vecdbIsEnabled: Boolean
get() = AppSettingsState.vecdbIsEnabled
set(newValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ interface InferenceGlobalContextChangedNotifier {
fun deploymentModeChanged(newMode: DeploymentMode) {}
fun astFlagChanged(newValue: Boolean) {}
fun astFileLimitChanged(newValue: Int) {}
fun astLightModeChanged(newValue: Boolean) {}
fun vecdbFlagChanged(newValue: Boolean) {}
fun vecdbFileLimitChanged(newValue: Int) {}
fun xDebugLSPPortChanged(newPort: Int?) {}
Expand Down
21 changes: 16 additions & 5 deletions src/main/kotlin/com/smallcloud/refactai/listeners/AcceptAction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,39 @@ import com.intellij.codeInsight.hint.HintManagerImpl.ActionToIgnore
import com.intellij.codeInsight.inline.completion.InlineCompletion
import com.intellij.codeInsight.inline.completion.session.InlineCompletionContext
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.EditorAction
import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler
import com.smallcloud.refactai.Resources
import com.smallcloud.refactai.codecompletion.EditorRefactLastCompletionIsMultilineKey
import com.smallcloud.refactai.codecompletion.EditorRefactLastSnippetTelemetryIdKey
import com.smallcloud.refactai.codecompletion.InlineCompletionGrayTextElementCustom
import com.smallcloud.refactai.modes.ModeProvider
import com.smallcloud.refactai.statistic.UsageStats

const val ACTION_ID_ = "TabPressedAction"

class TabPressedAction : EditorAction(InlineCompletionHandler()), ActionToIgnore {
class TabPressedAction : EditorAction(InsertInlineCompletionHandler()), ActionToIgnore {
val ACTION_ID = ACTION_ID_

init {
this.templatePresentation.icon = Resources.Icons.LOGO_RED_16x16
}

class InlineCompletionHandler : EditorWriteActionHandler() {
class InsertInlineCompletionHandler : EditorWriteActionHandler() {
override fun executeWriteAction(editor: Editor, caret: Caret?, dataContext: DataContext) {
Logger.getInstance("RefactTabPressedAction").debug("executeWriteAction")
val provider = ModeProvider.getOrCreateModeProvider(editor)
if (provider.isInCompletionMode()) {
InlineCompletion.getHandlerOrNull(editor)?.insert()
EditorRefactLastSnippetTelemetryIdKey[editor]?.also {
editor.project?.service<UsageStats>()?.snippetAccepted(it)
EditorRefactLastSnippetTelemetryIdKey[editor] = null
EditorRefactLastCompletionIsMultilineKey[editor] = null
}
} else {
provider.onTabPressed(editor, caret, dataContext)
}
Expand All @@ -42,10 +51,12 @@ class TabPressedAction : EditorAction(InlineCompletionHandler()), ActionToIgnore
val provider = ModeProvider.getOrCreateModeProvider(editor)
if (provider.isInCompletionMode()) {
val ctx = InlineCompletionContext.getOrNull(editor) ?: return false
if (ctx.state.elements.size != 1) return false
if (ctx.state.elements.isEmpty()) return false
val elem = ctx.state.elements.first()
if (elem !is InlineCompletionGrayTextElementCustom.Presentable) return false
return elem.delta == caret.logicalPosition.column
val isMultiline = EditorRefactLastCompletionIsMultilineKey[editor]
if (isMultiline && elem is InlineCompletionGrayTextElementCustom.Presentable)
return elem.delta == caret.logicalPosition.column
return true
} else {
return ModeProvider.getOrCreateModeProvider(editor).modeInActiveState()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.smallcloud.refactai.listeners

import com.intellij.codeInsight.inline.completion.session.InlineCompletionContext
import com.intellij.openapi.actionSystem.ActionPromoter
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.CommonDataKeys
Expand All @@ -10,9 +11,12 @@ class AcceptActionsPromoter : ActionPromoter {
private fun getEditor(dataContext: DataContext): Editor? {
return CommonDataKeys.EDITOR.getData(dataContext)
}
override fun promote(actions: MutableList<out AnAction>, context: DataContext): MutableList<AnAction> {
if (getEditor(context) == null)
return actions.toMutableList()
return actions.filterIsInstance<TabPressedAction>().toMutableList()
override fun promote(actions: MutableList<out AnAction>, context: DataContext): List<AnAction> {
val editor = getEditor(context) ?: return emptyList()
if (InlineCompletionContext.getOrNull(editor) == null) {
return emptyList()
}
actions.filterIsInstance<TabPressedAction>().takeIf { it.isNotEmpty() }?.let { return it }
return emptyList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class GenerateGitCommitMessageAction : AnAction(
val lspService =
event.project?.service<com.smallcloud.refactai.lsp.LSPProcessHolder>() ?: return@invokeLater

val isEnabled = lspService.isWorking && (commitWorkflowUi.getIncludedChanges().isNotEmpty() && commitWorkflowUi.getIncludedUnversionedFiles().isNotEmpty())
val isEnabled = lspService.isWorking && (commitWorkflowUi.getIncludedChanges().isNotEmpty() || commitWorkflowUi.getIncludedUnversionedFiles().isNotEmpty())

event.presentation.isEnabled = isEnabled
event.presentation.text = if (lspService.isWorking) {
Expand All @@ -54,7 +54,6 @@ class GenerateGitCommitMessageAction : AnAction(
return
}


val gitDiff = getDiff(event, project) ?: return
val commitWorkflowUi = event.getData(VcsDataKeys.COMMIT_WORKFLOW_UI)
if (commitWorkflowUi != null) {
Expand Down Expand Up @@ -87,7 +86,6 @@ class GenerateGitCommitMessageAction : AnAction(

try {
val includedChanges = commitWorkflowUi.getIncludedChanges()
commitWorkflowUi.getIncludedUnversionedFiles()
val filePatches = IdeaTextPatchBuilder.buildPatch(
project, includedChanges, projectFileVcsRoot.toNioPath(), false, true
)
Expand Down
5 changes: 0 additions & 5 deletions src/main/kotlin/com/smallcloud/refactai/lsp/LSPConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ data class LSPConfig(
var deployment: DeploymentMode = DeploymentMode.CLOUD,
var ast: Boolean = true,
var astFileLimit: Int? = null,
var astLightMode: Boolean = false,
var vecdb: Boolean = true,
var vecdbFileLimit: Int? = null,
var insecureSSL: Boolean = false,
Expand Down Expand Up @@ -44,9 +43,6 @@ data class LSPConfig(
params.add("--ast-max-files")
params.add("$astFileLimit")
}
if (ast && astLightMode) {
params.add("--ast-light-mode")
}
if (vecdb) {
params.add("--vecdb")
}
Expand All @@ -72,7 +68,6 @@ data class LSPConfig(
if (useTelemetry != other.useTelemetry) return false
if (deployment != other.deployment) return false
if (ast != other.ast) return false
if (astLightMode != other.astLightMode) return false
if (vecdb != other.vecdb) return false
if (astFileLimit != other.astFileLimit) return false
if (vecdbFileLimit != other.vecdbFileLimit) return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,6 @@ class LSPProcessHolder(val project: Project) : Disposable {
}
}

override fun astLightModeChanged(newValue: Boolean) {
AppExecutorUtil.getAppScheduledExecutorService().submit {
settingsChanged()
}
}

override fun vecdbFlagChanged(newValue: Boolean) {
AppExecutorUtil.getAppScheduledExecutorService().submit {
settingsChanged()
Expand Down Expand Up @@ -192,7 +186,6 @@ class LSPProcessHolder(val project: Project) : Disposable {
deployment = InferenceGlobalContext.deploymentMode,
ast = InferenceGlobalContext.astIsEnabled,
astFileLimit = InferenceGlobalContext.astFileLimit,
astLightMode = InferenceGlobalContext.astLightMode,
vecdb = InferenceGlobalContext.vecdbIsEnabled,
vecdbFileLimit = InferenceGlobalContext.vecdbFileLimit,
insecureSSL = InferenceGlobalContext.insecureSSL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package com.smallcloud.refactai.panes.sharedchat

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.components.service
import com.intellij.openapi.wm.ToolWindowManager
import com.smallcloud.refactai.Resources
import com.smallcloud.refactai.panes.RefactAIToolboxPaneFactory
import com.smallcloud.refactai.statistic.UsageStatistic
import com.smallcloud.refactai.statistic.UsageStats
import com.smallcloud.refactai.utils.getLastUsedProject

class ChatPaneInvokeAction: AnAction(Resources.Icons.LOGO_RED_16x16) {
Expand All @@ -14,8 +17,9 @@ class ChatPaneInvokeAction: AnAction(Resources.Icons.LOGO_RED_16x16) {

fun actionPerformed() {
val chat = ToolWindowManager.getInstance(getLastUsedProject()).getToolWindow("Refact")
chat?.activate{
chat?.activate {
RefactAIToolboxPaneFactory.focusChat()
getLastUsedProject().service<UsageStats>().addChatStatistic(true, UsageStatistic("openChatByShortcut"), "")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ class SharedChatPane(val project: Project) : JPanel(), Disposable {
}

val webView by lazy {
System.setProperty("ide.browser.jcef.log.level", "info")
// System.setProperty("ide.browser.jcef.log.level", "info")
browser.webView
}

Expand Down
Loading
Loading