diff --git a/src/main/java/ee/carlrobert/codegpt/settings/configuration/ConfigurationComponent.java b/src/main/java/ee/carlrobert/codegpt/settings/configuration/ConfigurationComponent.java index b86c7c34..f11cfe16 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/configuration/ConfigurationComponent.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/configuration/ConfigurationComponent.java @@ -29,6 +29,7 @@ public class ConfigurationComponent { private final JBTextField temperatureField; private final CodeCompletionConfigurationForm codeCompletionForm; private final ChatCompletionConfigurationForm chatCompletionForm; + private final ScreenshotConfigurationForm screenshotForm; public ConfigurationComponent( Disposable parentDisposable, @@ -74,6 +75,8 @@ public class ConfigurationComponent { codeCompletionForm = new CodeCompletionConfigurationForm(); chatCompletionForm = new ChatCompletionConfigurationForm(); + screenshotForm = new ScreenshotConfigurationForm(); + screenshotForm.loadState(configuration.getScreenshotWatchPaths()); mainPanel = FormBuilder.createFormBuilder() .addComponent(checkForPluginUpdatesCheckBox) @@ -84,6 +87,9 @@ public class ConfigurationComponent { .addComponent(new TitledSeparator( CodeGPTBundle.get("configurationConfigurable.section.assistant.title"))) .addComponent(createAssistantConfigurationForm()) + .addComponent(new TitledSeparator( + CodeGPTBundle.get("configurationConfigurable.section.screenshots.title"))) + .addComponent(screenshotForm.createPanel()) .addComponent(new TitledSeparator( CodeGPTBundle.get("configurationConfigurable.section.codeCompletion.title"))) .addComponent(codeCompletionForm.createPanel()) @@ -108,6 +114,11 @@ public class ConfigurationComponent { state.setAutoFormattingEnabled(autoFormattingCheckBox.isSelected()); state.setCodeCompletionSettings(codeCompletionForm.getFormState()); state.setChatCompletionSettings(chatCompletionForm.getFormState()); + + var screenshotPaths = screenshotForm.getState(); + state.getScreenshotWatchPaths().clear(); + state.getScreenshotWatchPaths().addAll(screenshotPaths); + return state; } @@ -121,6 +132,7 @@ public class ConfigurationComponent { autoFormattingCheckBox.setSelected(configuration.getAutoFormattingEnabled()); codeCompletionForm.resetForm(configuration.getCodeCompletionSettings()); chatCompletionForm.resetForm(configuration.getChatCompletionSettings()); + screenshotForm.loadState(configuration.getScreenshotWatchPaths()); } // Formatted keys are not referenced in the messages bundle file diff --git a/src/main/kotlin/ee/carlrobert/codegpt/CodeGPTProjectActivity.kt b/src/main/kotlin/ee/carlrobert/codegpt/CodeGPTProjectActivity.kt index e5799d04..dfba79f3 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/CodeGPTProjectActivity.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/CodeGPTProjectActivity.kt @@ -8,33 +8,49 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.startup.ProjectActivity import ee.carlrobert.codegpt.actions.editor.EditorActionsUtil import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings +import ee.carlrobert.codegpt.settings.configuration.ScreenshotPathDetector import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTService import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.AttachImageNotifier import ee.carlrobert.codegpt.ui.OverlayUtil +import com.intellij.openapi.diagnostic.thisLogger import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.absolutePathString class CodeGPTProjectActivity : ProjectActivity { - private val watchExtensions = setOf("jpg", "jpeg", "png") + private val logger = thisLogger() override suspend fun execute(project: Project) { EditorActionsUtil.refreshActions() - project.service().syncUserDetailsAsync() if (!ApplicationManager.getApplication().isUnitTestMode && service().state.checkForNewScreenshots ) { - val desktopPath = Paths.get(System.getProperty("user.home"), "Desktop") - project.service().watch(desktopPath) { - if (watchExtensions.contains(getFileExtension(it))) { - showImageAttachmentNotification( - project, - desktopPath.resolve(it).absolutePathString() - ) + val configurationState = service().state + val watchPaths = configurationState.screenshotWatchPaths.ifEmpty { + ScreenshotPathDetector.getDefaultPaths() + } + val watchExtensions = ScreenshotPathDetector.getDefaultFileExtensions().toSet() + logger.debug("Screenshot watch configuration - paths: $watchPaths, extensions: $watchExtensions") + val validPaths = watchPaths.filter { ScreenshotPathDetector.isValidWatchPath(it) } + logger.debug("Valid watch paths after filtering: $validPaths") + if (validPaths.isNotEmpty()) { + logger.info("Starting screenshot file watching for ${validPaths.size} paths") + project.service().watchMultiplePaths(validPaths) { fileName, watchPath -> + val fileExtension = getFileExtension(fileName) + logger.trace("File detected: fileName=$fileName, extension='$fileExtension', watchPath=$watchPath") + if (watchExtensions.contains(fileExtension)) { + val fullPath = Paths.get(watchPath).resolve(fileName).absolutePathString() + logger.info("New screenshot file created: $fullPath (extension='$fileExtension')") + showImageAttachmentNotification(project, fullPath) + } else { + logger.trace("File extension '$fileExtension' not in watch list: $watchExtensions") + } } + } else { + logger.warn("No valid screenshot watch paths found - screenshot detection disabled") } } } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/FileWatcher.kt b/src/main/kotlin/ee/carlrobert/codegpt/FileWatcher.kt index 6f8f90e8..0a44d786 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/FileWatcher.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/FileWatcher.kt @@ -2,42 +2,84 @@ package ee.carlrobert.codegpt import com.intellij.openapi.Disposable import com.intellij.openapi.components.Service +import com.intellij.openapi.diagnostic.thisLogger import java.nio.file.FileSystems import java.nio.file.Path +import java.nio.file.Paths import java.nio.file.StandardWatchEventKinds.ENTRY_CREATE +import java.nio.file.WatchService import kotlin.concurrent.thread import kotlin.io.path.exists - @Service(Service.Level.PROJECT) class FileWatcher : Disposable { - private var fileMonitor: Thread? = null + private val watchServices = mutableListOf() + private val fileMonitors = mutableListOf() + private val logger = thisLogger() - fun watch(pathToWatch: Path, onFileCreated: (Path) -> Unit) { - fileMonitor = pathToWatch.takeIf { it.exists() }?.let { + fun watchMultiplePaths(pathsToWatch: List, onFileCreated: (Path, String) -> Unit) { + dispose() + + pathsToWatch.forEach { pathString -> try { - FileSystems.getDefault().newWatchService().also { - pathToWatch.register(it, ENTRY_CREATE) // watch for new files + val path = Paths.get(pathString) + if (path.exists()) { + val watchService = FileSystems.getDefault().newWatchService() + path.register(watchService, ENTRY_CREATE) + watchServices.add(watchService) + logger.debug("Successfully registered watch service for path: $pathString (absolute: ${path.toAbsolutePath()})") + + val monitor = thread { + try { + logger.debug("File watch monitor thread started for path: $pathString") + generateSequence { watchService.take() }.forEach { key -> + logger.trace("Watch event received for path: $pathString") + key.pollEvents().forEach { event -> + val fileName = event.context() as Path + val fullPath = path.resolve(fileName) + logger.debug("File event detected: ${event.kind()} - fileName=$fileName, fullPath=$fullPath") + onFileCreated(fileName, pathString) + } + val resetResult = key.reset() + if (!resetResult) { + logger.warn("Watch key reset failed for path: $pathString - watch may have become invalid") + } + } + } catch (e: InterruptedException) { + logger.debug("File watch monitor thread interrupted for path: $pathString") + Thread.currentThread().interrupt() + } catch (e: Exception) { + logger.warn("Error in file watcher for path: $pathString", e) + } finally { + logger.debug("File watch monitor thread stopped for path: $pathString") + } + } + fileMonitors.add(monitor) + + logger.info("Started watching path: $pathString") + } else { + logger.warn("Path does not exist or is not accessible: $pathString") } } catch (e: Exception) { - null // WatchService or registration failed - } - }?.let { watchService -> - thread { - try { - generateSequence { watchService.take() }.forEach { key -> - key.pollEvents().forEach { onFileCreated(it.context() as Path) } - key.reset() - } - } catch (e: InterruptedException) { - Thread.currentThread().interrupt() - } + logger.warn("Failed to set up watcher for path: $pathString", e) } } } override fun dispose() { - fileMonitor?.interrupt() + logger.debug("Disposing FileWatcher - stopping ${fileMonitors.size} monitor threads and ${watchServices.size} watch services") + fileMonitors.forEach { it.interrupt() } + fileMonitors.clear() + + watchServices.forEach { watchService -> + try { + watchService.close() + } catch (e: Exception) { + logger.warn("Error closing watch service", e) + } + } + watchServices.clear() + logger.debug("FileWatcher disposal completed") } } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ConfigurationSettings.kt b/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ConfigurationSettings.kt index c2ed34ab..64f17cc1 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ConfigurationSettings.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ConfigurationSettings.kt @@ -27,6 +27,7 @@ class ConfigurationSettingsState : BaseState() { var temperature by property(0.1f) { max(0f, min(1f, it)) } var checkForPluginUpdates by property(true) var checkForNewScreenshots by property(true) + var screenshotWatchPaths by list() var ignoreGitCommitTokenLimit by property(false) var methodNameGenerationEnabled by property(true) var captureCompileErrors by property(true) @@ -34,9 +35,14 @@ class ConfigurationSettingsState : BaseState() { var tableData by map() var chatCompletionSettings by property(ChatCompletionSettingsState()) var codeCompletionSettings by property(CodeCompletionSettingsState()) + var myAwesomeFeatureEnabled by property(false) init { tableData.putAll(EditorActionsUtil.DEFAULT_ACTIONS) + + if (screenshotWatchPaths.isEmpty()) { + screenshotWatchPaths.addAll(ScreenshotPathDetector.getDefaultPaths()) + } } } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/PathInputDialog.kt b/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/PathInputDialog.kt new file mode 100644 index 00000000..57230447 --- /dev/null +++ b/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/PathInputDialog.kt @@ -0,0 +1,34 @@ +package ee.carlrobert.codegpt.settings.configuration + +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.openapi.ui.TextFieldWithBrowseButton +import com.intellij.util.ui.FormBuilder +import com.intellij.util.ui.JBUI +import ee.carlrobert.codegpt.CodeGPTBundle +import java.awt.Dimension +import javax.swing.JComponent + +class PathInputDialog( + title: String, + private val textField: TextFieldWithBrowseButton +) : DialogWrapper(true) { + + init { + this.title = title + init() + } + + override fun createCenterPanel(): JComponent { + val panel = FormBuilder.createFormBuilder() + .addLabeledComponent( + CodeGPTBundle.get("configurationConfigurable.screenshotPaths.dialog.path.label"), + textField, + true + ) + .panel + panel.preferredSize = Dimension(JBUI.scale(500), panel.preferredSize.height) + return panel + } + + override fun getPreferredFocusedComponent(): JComponent = textField +} \ No newline at end of file diff --git a/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ScreenshotConfigurationForm.kt b/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ScreenshotConfigurationForm.kt new file mode 100644 index 00000000..9963fe2f --- /dev/null +++ b/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ScreenshotConfigurationForm.kt @@ -0,0 +1,135 @@ +package ee.carlrobert.codegpt.settings.configuration + +import com.intellij.openapi.fileChooser.FileChooserDescriptor +import com.intellij.openapi.ui.TextBrowseFolderListener +import com.intellij.openapi.ui.TextFieldWithBrowseButton +import com.intellij.ui.ToolbarDecorator +import com.intellij.ui.components.JBList +import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.panel +import ee.carlrobert.codegpt.CodeGPTBundle +import java.awt.BorderLayout +import javax.swing.DefaultListModel +import javax.swing.JPanel +import javax.swing.ListSelectionModel + +class ScreenshotConfigurationForm { + + private val pathListModel = DefaultListModel() + private val pathList = JBList(pathListModel) + + init { + pathList.selectionMode = ListSelectionModel.SINGLE_SELECTION + pathList.visibleRowCount = 3 + } + + fun createPanel(): JPanel { + val pathPanel = createPathConfigurationPanel() + + return panel { + row { + label(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.label")) + } + row { + cell(pathPanel) + .align(AlignX.FILL) + .resizableColumn() + .comment(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.comment")) + } + } + } + + private fun createPathConfigurationPanel(): JPanel { + val panel = JPanel(BorderLayout()) + + val decorator = ToolbarDecorator.createDecorator(pathList) + .setAddAction { addPath() } + .setRemoveAction { removePath() } + .setEditAction { editPath() } + .setAddActionName(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.add")) + .setRemoveActionName(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.remove")) + .setEditActionName(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.edit")) + + panel.add(decorator.createPanel(), BorderLayout.CENTER) + return panel + } + + + private fun addPath() { + val textField = TextFieldWithBrowseButton() + val fileChooserDescriptor = FileChooserDescriptor(false, true, false, false, false, false) + fileChooserDescriptor.title = + CodeGPTBundle.get("configurationConfigurable.screenshotPaths.chooser.title") + fileChooserDescriptor.description = + CodeGPTBundle.get("configurationConfigurable.screenshotPaths.chooser.description") + + textField.addBrowseFolderListener( + TextBrowseFolderListener(fileChooserDescriptor) + ) + + val dialog = PathInputDialog( + CodeGPTBundle.get("configurationConfigurable.screenshotPaths.add.title"), + textField + ) + + if (dialog.showAndGet()) { + val path = textField.text + if (path.isNotBlank() && !pathListModel.contains(path)) { + pathListModel.addElement(path) + } + } + } + + private fun removePath() { + val selectedIndex = pathList.selectedIndex + if (selectedIndex >= 0) { + pathListModel.removeElementAt(selectedIndex) + } + } + + private fun editPath() { + val selectedIndex = pathList.selectedIndex + if (selectedIndex >= 0) { + val currentPath = pathListModel.getElementAt(selectedIndex) + val textField = TextFieldWithBrowseButton() + textField.text = currentPath + + val fileChooserDescriptor = + FileChooserDescriptor(false, true, false, false, false, false) + fileChooserDescriptor.title = + CodeGPTBundle.get("configurationConfigurable.screenshotPaths.chooser.title") + fileChooserDescriptor.description = + CodeGPTBundle.get("configurationConfigurable.screenshotPaths.chooser.description") + + textField.addBrowseFolderListener( + TextBrowseFolderListener(fileChooserDescriptor) + ) + + val dialog = PathInputDialog( + CodeGPTBundle.get("configurationConfigurable.screenshotPaths.edit.title"), + textField + ) + + if (dialog.showAndGet()) { + val newPath = textField.text + if (newPath.isNotBlank()) { + pathListModel.setElementAt(newPath, selectedIndex) + } + } + } + } + + + fun loadState(paths: List) { + pathListModel.clear() + paths.forEach { pathListModel.addElement(it) } + } + + fun getState(): List { + val paths = mutableListOf() + for (i in 0 until pathListModel.size()) { + paths.add(pathListModel.getElementAt(i)) + } + return paths + } +} \ No newline at end of file diff --git a/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ScreenshotPathDetector.kt b/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ScreenshotPathDetector.kt new file mode 100644 index 00000000..6c36f3e3 --- /dev/null +++ b/src/main/kotlin/ee/carlrobert/codegpt/settings/configuration/ScreenshotPathDetector.kt @@ -0,0 +1,89 @@ +package ee.carlrobert.codegpt.settings.configuration + +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.util.SystemInfo +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.exists + +object ScreenshotPathDetector { + + private val logger = thisLogger() + + fun getDefaultPaths(): List { + return when { + SystemInfo.isWindows -> getWindowsDefaultPaths() + SystemInfo.isMac -> getMacDefaultPaths() + SystemInfo.isLinux -> getLinuxDefaultPaths() + else -> listOf(getDesktopPath()) + } + } + + fun isValidWatchPath(pathString: String): Boolean { + return try { + val path = Paths.get(pathString) + path.exists() && path.toFile().canRead() + } catch (e: Exception) { + logger.warn("Error validating watch path: $pathString", e) + false + } + } + + fun getDefaultFileExtensions(): List { + return listOf("jpg", "jpeg", "png", "gif", "bmp", "webp") + } + + private fun getWindowsDefaultPaths(): List { + val paths = mutableListOf() + paths.add(getDesktopPath()) + + val screenshotsPath = Paths.get(System.getProperty("user.home"), "Pictures", "Screenshots") + if (screenshotsPath.exists()) { + paths.add(screenshotsPath.toString()) + } + + return paths + } + + private fun getMacDefaultPaths(): List { + val paths = mutableListOf() + paths.add(getDesktopPath()) + + val picturesPath = Paths.get(System.getProperty("user.home"), "Pictures") + if (picturesPath.exists()) { + paths.add(picturesPath.toString()) + } + + return paths + } + + private fun getLinuxDefaultPaths(): List { + val paths = mutableListOf() + paths.add(getDesktopPath()) + + val picturesPath = getXdgPicturesPath() + if (picturesPath.exists()) { + paths.add(picturesPath.toString()) + } + + val screenshotsPath = picturesPath.resolve("Screenshots") + if (screenshotsPath.exists()) { + paths.add(screenshotsPath.toString()) + } + + return paths + } + + private fun getDesktopPath(): String { + return Paths.get(System.getProperty("user.home"), "Desktop").toString() + } + + private fun getXdgPicturesPath(): Path { + val xdgPictures = System.getenv("XDG_PICTURES_DIR") + return if (xdgPictures != null) { + Paths.get(xdgPictures) + } else { + Paths.get(System.getProperty("user.home"), "Pictures") + } + } +} \ No newline at end of file diff --git a/src/main/resources/messages/codegpt.properties b/src/main/resources/messages/codegpt.properties index a2cb8549..aa6967cf 100644 --- a/src/main/resources/messages/codegpt.properties +++ b/src/main/resources/messages/codegpt.properties @@ -120,6 +120,19 @@ configurationConfigurable.table.action.revertToDefaults.text=Revert to Defaults configurationConfigurable.table.action.addKeymap.text=Add Shortcut configurationConfigurable.checkForPluginUpdates.label=Check for plugin updates automatically configurationConfigurable.checkForNewScreenshots.label=Check for new screenshots automatically + +# Screenshot Configuration +configurationConfigurable.section.screenshots.title=Screenshot Detection +configurationConfigurable.screenshotPaths.label=Watch Paths: +configurationConfigurable.screenshotPaths.add=Add Path +configurationConfigurable.screenshotPaths.remove=Remove Path +configurationConfigurable.screenshotPaths.edit=Edit Path +configurationConfigurable.screenshotPaths.add.title=Add Screenshot Watch Path +configurationConfigurable.screenshotPaths.edit.title=Edit Screenshot Watch Path +configurationConfigurable.screenshotPaths.chooser.title=Select Directory to Watch for Screenshots +configurationConfigurable.screenshotPaths.chooser.description=Choose a directory where screenshot files will be detected +configurationConfigurable.screenshotPaths.dialog.path.label=Path: +configurationConfigurable.screenshotPaths.comment=Add directories where screenshots are typically saved. The plugin monitors these directories for new image files. configurationConfigurable.openNewTabCheckBox.label=Open a new chat on each action configurationConfigurable.enableMethodNameGeneration.label=Enable method name lookup suggestions configurationConfigurable.autoFormatting.label=Enable automatic code formatting @@ -371,7 +384,7 @@ settings.models.chat.label=Chat: settings.models.code.label=Autocomplete: settings.models.autoApply.label=Auto apply: settings.models.commitMessages.label=Commit messages: -settings.models.editCode.label=Edit code: +settings.models.inlineEdit.label=Inline edit: settings.models.nextEdit.label=Next edits: settings.models.nameLookups.label=Name lookups: settings.models.selectModel=Select a model diff --git a/src/main/resources/messages/codegpt_zh.properties b/src/main/resources/messages/codegpt_zh.properties index 289bcf1e..7ab63a4d 100644 --- a/src/main/resources/messages/codegpt_zh.properties +++ b/src/main/resources/messages/codegpt_zh.properties @@ -120,6 +120,19 @@ configurationConfigurable.table.action.revertToDefaults.text=\u6062\u590D\u9ED8\ configurationConfigurable.table.action.addKeymap.text=\u6DFB\u52A0\u5FEB\u6377\u952E configurationConfigurable.checkForPluginUpdates.label=\u81EA\u52A8\u68C0\u67E5\u63D2\u4EF6\u66F4\u65B0 configurationConfigurable.checkForNewScreenshots.label=\u81EA\u52A8\u68C0\u67E5\u65B0\u622A\u56FE + +# Screenshot Configuration +configurationConfigurable.section.screenshots.title=\u622A\u56FE\u68C0\u6D4B +configurationConfigurable.screenshotPaths.label=\u76D1\u89C6\u8DEF\u5F84: +configurationConfigurable.screenshotPaths.add=\u6DFB\u52A0\u8DEF\u5F84 +configurationConfigurable.screenshotPaths.remove=\u5220\u9664\u8DEF\u5F84 +configurationConfigurable.screenshotPaths.edit=\u7F16\u8F91\u8DEF\u5F84 +configurationConfigurable.screenshotPaths.add.title=\u6DFB\u52A0\u622A\u56FE\u76D1\u89C6\u8DEF\u5F84 +configurationConfigurable.screenshotPaths.edit.title=\u7F16\u8F91\u622A\u56FE\u76D1\u89C6\u8DEF\u5F84 +configurationConfigurable.screenshotPaths.chooser.title=\u9009\u62E9\u76D1\u89C6\u622A\u56FE\u7684\u76EE\u5F55 +configurationConfigurable.screenshotPaths.chooser.description=\u9009\u62E9\u4E00\u4E2A\u7528\u4E8E\u68C0\u6D4B\u622A\u56FE\u6587\u4EF6\u7684\u76EE\u5F55 +configurationConfigurable.screenshotPaths.dialog.path.label=\u8DEF\u5F84: +configurationConfigurable.screenshotPaths.comment=\u6DFB\u52A0\u901A\u5E38\u4FDD\u5B58\u622A\u56FE\u7684\u76EE\u5F55\u3002\u63D2\u4EF6\u4F1A\u76D1\u89C6\u8FD9\u4E9B\u76EE\u5F55\u4E2D\u7684\u65B0\u56FE\u50CF\u6587\u4EF6\u3002 configurationConfigurable.openNewTabCheckBox.label=\u6BCF\u4E2A\u64CD\u4F5C\u6253\u5F00\u65B0\u804A\u5929\u6807\u7B7E configurationConfigurable.enableMethodNameGeneration.label=\u542F\u7528\u65B9\u6CD5\u540D\u79F0\u67E5\u627E\u5EFA\u8BAE configurationConfigurable.autoFormatting.label=\u542F\u7528\u81EA\u52A8\u4EE3\u7801\u683C\u5F0F\u5316 @@ -370,7 +383,7 @@ settings.models.chat.label=\u804A\u5929: settings.models.code.label=\u81EA\u52A8\u8865\u5168: settings.models.autoApply.label=\u81EA\u52A8\u5E94\u7528: settings.models.commitMessages.label=\u63D0\u4EA4\u6D88\u606F: -settings.models.editCode.label=\u7F16\u8F91\u4EE3\u7801: +settings.models.inlineEdit.label=\u7F16\u8F91\u4EE3\u7801: settings.models.nextEdit.label=\u4E0B\u4E00\u6B65\u7F16\u8F91: settings.models.nameLookups.label=\u540D\u79F0\u67E5\u627E: settings.models.selectModel=\u9009\u62E9\u6A21\u578B