Skip to content

Commit e8e759f

Browse files
committed
Made the shortcut input intercept shortcuts and output them instead of being a simple text input
- Fixed an issue where deleting a snippet didn't remove its shortcut from the IDE - Prevented immediate execution of snippets when the OK button is clicked
1 parent 800d74c commit e8e759f

File tree

7 files changed

+176
-120
lines changed

7 files changed

+176
-120
lines changed

CHANGELOG.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,33 @@
22

33
# typewriter-plugin Changelog
44

5-
## [Unreleased]
5+
## Unreleased
6+
7+
## 0.1.1 - 2023-11-15
8+
9+
### Fixed
10+
11+
- Fixed an issue where deleting a snippet didn't remove its shortcut from the IDE
12+
613
### Changed
14+
15+
- Made the shortcut input intercept shortcuts and output them instead of being a simple text input
16+
- Prevented immediate execution of snippets when the OK button is clicked
17+
18+
## 0.1.0 - 2025-03-15
19+
20+
### Changed
21+
722
- Changed the default shortcut to Ctrl+Shift+T,W: two keys, but does not interfere with native shortcuts
823

9-
## [0.0.6]
24+
## 0.0.6
25+
1026
### Added
27+
1128
- Adds an icon
1229

13-
## [0.0.5]
30+
## 0.0.5
31+
1432
### Added
33+
1534
- Initial scaffold created from [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template)

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
pluginGroup = com.github.asm0dey.typewriterplugin
44
pluginName = typewriter-plugin
55
# SemVer format -> https://semver.org
6-
pluginVersion = 0.1.0
6+
pluginVersion = 0.1.1
77

88
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
99
pluginSinceBuild = 222

gradle/libs.versions.toml

Lines changed: 1 addition & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -15,85 +15,10 @@ annotations = "24.0.1"
1515

1616
# plugins
1717
dokka = "1.8.10"
18-
kotlin = "2.1.10"
19-
## ⬆ = "1.8.20-Beta"
20-
## ⬆ = "1.8.20-RC"
21-
## ⬆ = "1.8.20-RC2"
22-
## ⬆ = "1.8.20"
23-
## ⬆ = "1.8.21"
24-
## ⬆ = "1.8.22"
25-
## ⬆ = "1.9.0-Beta"
26-
## ⬆ = "1.9.0-RC"
27-
## ⬆ = "1.9.0"
28-
## ⬆ = "1.9.10"
29-
## ⬆ = "1.9.20-Beta"
30-
## ⬆ = "1.9.20-Beta2"
31-
## ⬆ = "1.9.20-RC"
32-
## ⬆ = "1.9.20-RC2"
33-
## ⬆ = "1.9.20"
34-
## ⬆ = "1.9.21"
35-
## ⬆ = "1.9.22"
36-
## ⬆ = "1.9.23"
37-
## ⬆ = "1.9.24"
38-
## ⬆ = "1.9.25"
39-
## ⬆ = "2.0.0-Beta1"
40-
## ⬆ = "2.0.0-Beta2"
41-
## ⬆ = "2.0.0-Beta3"
42-
## ⬆ = "2.0.0-Beta4"
43-
## ⬆ = "2.0.0-Beta5"
44-
## ⬆ = "2.0.0-RC1"
45-
## ⬆ = "2.0.0-RC2"
46-
## ⬆ = "2.0.0-RC3"
47-
## ⬆ = "2.0.0"
48-
## ⬆ = "2.0.10-RC"
49-
## ⬆ = "2.0.10-RC2"
50-
## ⬆ = "2.0.10"
51-
## ⬆ = "2.0.20-Beta1"
52-
## ⬆ = "2.0.20-Beta2"
53-
## ⬆ = "2.0.20-RC"
54-
## ⬆ = "2.0.20-RC2"
55-
## ⬆ = "2.0.20"
56-
## ⬆ = "2.0.21-RC"
57-
## ⬆ = "2.0.21"
58-
## ⬆ = "2.1.0-Beta1"
59-
## ⬆ = "2.1.0-Beta2"
60-
## ⬆ = "2.1.0-RC"
61-
## ⬆ = "2.1.0-RC2"
62-
## ⬆ = "2.1.0"
63-
## ⬆ = "2.1.10-RC"
64-
## ⬆ = "2.1.10-RC2"
65-
## ⬆ = "2.1.10"
66-
## ⬆ = "2.1.20-Beta1"
67-
## ⬆ = "2.1.20-Beta2"
68-
## ⬆ = "2.1.20-RC"
69-
## ⬆ = "2.1.20-RC2"
70-
## ⬆ = "2.1.20-RC3"
18+
kotlin = "2.1.20"
7119
changelog = "2.2.1"
72-
## ⬆ = "2.1.0"
73-
## ⬆ = "2.1.1"
74-
## ⬆ = "2.1.2"
75-
## ⬆ = "2.2.0"
76-
## ⬆ = "2.2.1"
7720
gradleIntelliJPlugin = "1.17.4"
7821
kover = "0.9.1"
79-
## ⬆ = "0.7.0-Alpha"
80-
## ⬆ = "0.7.0-Beta"
81-
## ⬆ = "0.7.0"
82-
## ⬆ = "0.7.1"
83-
## ⬆ = "0.7.2"
84-
## ⬆ = "0.7.3"
85-
## ⬆ = "0.7.4"
86-
## ⬆ = "0.7.5"
87-
## ⬆ = "0.7.6"
88-
## ⬆ = "0.8.0-Beta"
89-
## ⬆ = "0.8.0-Beta2"
90-
## ⬆ = "0.8.0"
91-
## ⬆ = "0.8.1"
92-
## ⬆ = "0.8.2"
93-
## ⬆ = "0.8.3"
94-
## ⬆ = "0.9.0-RC"
95-
## ⬆ = "0.9.0"
96-
## ⬆ = "0.9.1"
9722

9823
[libraries]
9924

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.github.asm0dey.typewriterplugin
2+
3+
import com.intellij.ui.components.JBTextField
4+
import java.awt.event.KeyAdapter
5+
import java.awt.event.KeyEvent
6+
import javax.swing.KeyStroke
7+
import java.awt.event.InputEvent
8+
9+
/**
10+
* A custom text field that intercepts keyboard shortcuts and displays them.
11+
* Instead of allowing the user to type the shortcut as text, this component
12+
* captures the actual key presses and converts them to a shortcut representation.
13+
*/
14+
class ShortcutTextField : JBTextField() {
15+
16+
init {
17+
// Make the field non-editable through normal typing
18+
isEditable = false
19+
20+
// Add a key listener to capture keyboard shortcuts
21+
addKeyListener(object : KeyAdapter() {
22+
override fun keyPressed(e: KeyEvent) {
23+
// Prevent default behavior
24+
e.consume()
25+
26+
// Skip modifier-only key presses
27+
if (e.keyCode == KeyEvent.VK_CONTROL ||
28+
e.keyCode == KeyEvent.VK_ALT ||
29+
e.keyCode == KeyEvent.VK_SHIFT ||
30+
e.keyCode == KeyEvent.VK_META) {
31+
return
32+
}
33+
34+
// Create a KeyStroke from the key event
35+
val keyStroke = KeyStroke.getKeyStrokeForEvent(e)
36+
37+
// Convert the KeyStroke to a string representation
38+
val shortcutText = keyStrokeToString(keyStroke)
39+
40+
// Update the text field
41+
text = shortcutText
42+
43+
// Notify document listeners
44+
fireActionPerformed()
45+
}
46+
})
47+
}
48+
49+
/**
50+
* Converts a KeyStroke to a string representation that can be parsed by KeyStroke.getKeyStroke()
51+
*/
52+
private fun keyStrokeToString(keyStroke: KeyStroke): String {
53+
val modifiers = keyStroke.modifiers
54+
val keyCode = keyStroke.keyCode
55+
56+
val modifierText = StringBuilder()
57+
58+
// Add modifiers in the order expected by KeyStroke.getKeyStroke()
59+
if (modifiers and InputEvent.CTRL_DOWN_MASK != 0) {
60+
modifierText.append("ctrl ")
61+
}
62+
if (modifiers and InputEvent.META_DOWN_MASK != 0) {
63+
modifierText.append("meta ")
64+
}
65+
if (modifiers and InputEvent.ALT_DOWN_MASK != 0) {
66+
modifierText.append("alt ")
67+
}
68+
if (modifiers and InputEvent.SHIFT_DOWN_MASK != 0) {
69+
modifierText.append("shift ")
70+
}
71+
72+
// Add the key code
73+
val keyText = KeyEvent.getKeyText(keyCode)
74+
75+
return modifierText.toString() + keyText
76+
}
77+
78+
/**
79+
* Clears the current shortcut
80+
*/
81+
fun clearShortcut() {
82+
text = ""
83+
}
84+
}

src/main/kotlin/com/github/asm0dey/typewriterplugin/SnippetManagerDialog.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.intellij.ui.components.JBScrollPane
1212
import com.intellij.ui.dsl.builder.*
1313
import com.intellij.ui.table.JBTable
1414
import java.awt.Dimension
15+
import java.util.*
1516
import javax.swing.JComponent
1617
import javax.swing.ListSelectionModel
1718
import javax.swing.table.AbstractTableModel
@@ -77,6 +78,26 @@ class SnippetManagerDialog(private val project: Project) : DialogWrapper(project
7778
dialog.openingSequence,
7879
dialog.closingSequence
7980
)
81+
82+
// If the shortcut has changed, unregister the old one
83+
if (snippet.shortcut != updatedSnippet.shortcut && snippet.shortcut.isNotBlank()) {
84+
// Unregister the action for the old shortcut
85+
val oldActionId = "com.github.asm0dey.typewriterplugin.DirectSnippetExecution.${snippet.shortcut}"
86+
ActionManager.getInstance().unregisterAction(oldActionId)
87+
88+
// Remove the old keyboard shortcut
89+
try {
90+
val keymap = KeymapManager.getInstance().activeKeymap
91+
val shortcuts = keymap.getShortcuts(oldActionId)
92+
for (shortcut in shortcuts) {
93+
keymap.removeShortcut(oldActionId, shortcut)
94+
}
95+
} catch (e: Exception) {
96+
// Log error or handle any issues
97+
println("Failed to remove old shortcut for snippet: ${snippet.shortcut}")
98+
}
99+
}
100+
80101
snippetStorage.addSnippet(updatedSnippet)
81102
tableModel.updateSnippet(selectedRow, updatedSnippet)
82103

@@ -89,7 +110,7 @@ class SnippetManagerDialog(private val project: Project) : DialogWrapper(project
89110
try {
90111
// Check for valid modifier keys
91112
val validModifiers = setOf("ctrl", "control", "meta", "alt", "shift", "altGraph", "button1", "button2", "button3")
92-
val parts = updatedSnippet.shortcut.toLowerCase().split(" ").map { it.trim() }
113+
val parts = updatedSnippet.shortcut.lowercase(Locale.US).split(" ").map { it.trim() }
93114

94115
// Check if all modifiers are valid
95116
val modifiers = parts.dropLast(1)

src/main/kotlin/com/github/asm0dey/typewriterplugin/TypeWriterAction.kt

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ class TypeWriterAction : DumbAwareAction() {
1919
val dialog = TypeWriterDialog(project)
2020

2121
if (!dialog.showAndGet()) return
22-
val text = dialog.text
23-
val scheduler = service<TypewriterExecutorService>()
2422

2523
// Save snippet if shortcut is provided
2624
if (dialog.shortcut.isNotBlank()) {
2725
val snippetStorage = service<SnippetStorage>()
26+
27+
// Check if there's an existing snippet with the same shortcut
28+
val existingSnippet = snippetStorage.getSnippet(dialog.shortcut)
29+
2830
val snippet = Snippet(
2931
dialog.shortcut,
3032
dialog.text,
@@ -33,11 +35,29 @@ class TypeWriterAction : DumbAwareAction() {
3335
dialog.openingSequence,
3436
dialog.closingSequence
3537
)
38+
39+
// If there's an existing snippet with the same shortcut, unregister its action
40+
if (existingSnippet != null) {
41+
val oldActionId = "com.github.asm0dey.typewriterplugin.DirectSnippetExecution.${existingSnippet.shortcut}"
42+
ActionManager.getInstance().unregisterAction(oldActionId)
43+
44+
// Remove the old keyboard shortcut
45+
try {
46+
val keymap = KeymapManager.getInstance().activeKeymap
47+
val shortcuts = keymap.getShortcuts(oldActionId)
48+
for (shortcut in shortcuts) {
49+
keymap.removeShortcut(oldActionId, shortcut)
50+
}
51+
} catch (e: Exception) {
52+
// Log error or handle any issues
53+
println("Failed to remove shortcut for snippet: ${existingSnippet.shortcut}")
54+
}
55+
}
56+
3657
snippetStorage.addSnippet(snippet)
3758

3859
// Register the action for this shortcut
39-
val actionId =
40-
"TypeWriter ${dialog.shortcut}"
60+
val actionId = "com.github.asm0dey.typewriterplugin.DirectSnippetExecution.${dialog.shortcut}"
4161
val action = DirectSnippetExecutionAction(snippet)
4262
ActionManager.getInstance().registerAction(actionId, action)
4363

@@ -49,16 +69,6 @@ class TypeWriterAction : DumbAwareAction() {
4969
keymap.addShortcut(actionId, shortcut)
5070
}
5171
}
52-
53-
executeTyping(
54-
e,
55-
text,
56-
dialog.openingSequence,
57-
dialog.closingSequence,
58-
dialog.delay.toLong(),
59-
dialog.jitter,
60-
scheduler
61-
)
6272
}
6373

6474
companion object {

0 commit comments

Comments
 (0)