1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-05-30 13:34:08 +02:00
IntelliJ-IdeaVim/src/main/java/com/maddyhome/idea/vim/ui/ShowCmd.kt

151 lines
5.1 KiB
Kotlin

/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.ui
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManagerListener
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.wm.StatusBar
import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.intellij.openapi.wm.WindowManager
import com.intellij.openapi.wm.impl.status.EditorBasedWidget
import com.intellij.util.Consumer
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.EditorListener
import com.maddyhome.idea.vim.helper.EngineStringHelper
import com.maddyhome.idea.vim.helper.VimNlsSafe
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
import org.jetbrains.annotations.NonNls
import java.awt.Component
import java.awt.event.MouseEvent
internal object ShowCmd {
// https://github.com/vim/vim/blob/b376ace1aeaa7614debc725487d75c8f756dd773/src/vim.h#L1721
private const val SHOWCMD_COLS = 10
@NonNls
internal const val ID = "IdeaVimShowCmd"
@NlsSafe
internal const val displayName = "IdeaVim showcmd"
fun update() {
val windowManager = WindowManager.getInstance()
ProjectManager.getInstance().openProjects.forEach {
val statusBar = windowManager.getStatusBar(it)
statusBar.updateWidget(ID)
}
}
fun getWidgetText(editor: Editor?): String {
// Vim only shows the last 10 characters. See normal.c:add_to_showcmd
// https://github.com/vim/vim/blob/b376ace1aeaa7614debc725487d75c8f756dd773/src/normal.c#L1885-L1890
return getFullText(editor).takeLast(SHOWCMD_COLS)
}
fun getFullText(editor: Editor?): String {
if (!injector.globalOptions().showcmd || editor == null || editor.isDisposed) return ""
val keyState = KeyHandler.getInstance().keyHandlerState
return EngineStringHelper.toPrintableCharacters(keyState.commandBuilder.keys + keyState.mappingState.keys)
}
}
internal object ShowCmdOptionChangeListener : GlobalOptionChangeListener {
override fun onGlobalOptionChanged() {
ShowCmd.update()
}
}
internal class ShowCmdStatusBarWidgetFactory : StatusBarWidgetFactory/*, LightEditCompatible*/ {
override fun getId() = ShowCmd.ID
override fun getDisplayName(): String = ShowCmd.displayName
override fun disposeWidget(widget: StatusBarWidget) {
// Nothing
}
override fun isAvailable(project: Project): Boolean {
VimPlugin.getInstance()
return injector.globalOptions().showcmd
}
override fun createWidget(project: Project): StatusBarWidget = Widget(project)
override fun canBeEnabledOn(statusBar: StatusBar): Boolean = true
// Should be configured via `set showcmd`
override fun isConfigurable(): Boolean = false
}
// `:help 'showcmd'`
// Widget shows:
// * Partial command, as it's being typed
// * When selecting characters within a line, the number of characters
// * Tabs are shown as one char
// * If the number of bytes is different, this is also shown: "2-6"
// * When selecting more than one line, the number of lines
// * When selecting a block, the size in screen characters: {lines}x{columns}
//
// We only need to show partial commands, since the standard PositionPanel shows the other information already, with
// the exception of "{lines}x{columns}" (it shows "x carets" instead)
internal class Widget(project: Project) :
EditorBasedWidget(project),
StatusBarWidget.Multiframe,
StatusBarWidget.TextPresentation,
FileEditorManagerListener {
override fun ID() = ShowCmd.ID
override fun getPresentation(): StatusBarWidget.WidgetPresentation = this
override fun getClickConsumer(): Consumer<MouseEvent>? = null
@VimNlsSafe
override fun getTooltipText(): String {
var count = ShowCmd.getFullText(getEditor())
if (count.isNotBlank()) count = ": $count"
return "${ShowCmd.displayName}$count"
}
override fun getText(): String = ShowCmd.getWidgetText(getEditor())
override fun getAlignment() = Component.CENTER_ALIGNMENT
// Multiframe#copy to show the widget on popped out editors
override fun copy(): StatusBarWidget = Widget(project)
}
internal class ShowCmdWidgetUpdater : EditorListener {
override fun focusGained(editor: VimEditor) {
editor.ij.project?.let { selectionChanged(it) }
}
override fun focusLost(editor: VimEditor) {
editor.ij.project?.let { selectionChanged(it) }
}
private fun selectionChanged(project: Project) {
val windowManager = WindowManager.getInstance()
val statusBar = windowManager.getStatusBar(project)
statusBar.updateWidget(ShowCmd.ID)
}
}