mirror of
				https://github.com/chylex/IntelliJ-IdeaVim.git
				synced 2025-10-31 20:17:13 +01:00 
			
		
		
		
	Compare commits
	
		
			40 Commits
		
	
	
		
			943ddeb5ab
			...
			customized
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8e162bce95 | |||
| 2cdd62371e | |||
| d461f61438 | |||
| de7fe30a97 | |||
| 6d80c21344 | |||
| a57a80442c | |||
| 866e6dc831 | |||
| f11bb12f19 | |||
| 70ea1bd46f | |||
|   | 2845beaf8a | ||
|   | 1e58ead126 | ||
|   | fafa7572d0 | ||
|   | 4ddeb72bfe | ||
|   | 28ba36dddb | ||
|   | b6c501311c | ||
|   | 01d4ebe254 | ||
|   | 34dd332f0b | ||
|   | 596d0c7115 | ||
|   | 672601b028 | ||
|   | e5045f28ab | ||
|   | 12ba067db3 | ||
|   | 5413606425 | ||
|   | 72ae18557b | ||
|   | 99bd119ed6 | ||
|   | faebf66065 | ||
|   | dc030d6895 | ||
|   | 7f626005a5 | ||
|   | 28d0741e14 | ||
|   | c0e17a6c61 | ||
|   | b5046b089e | ||
|   | 1075112bfa | ||
|   | 37fddacf8e | ||
|   | 2091a59897 | ||
|   | d2a427b38f | ||
|   | c069719c1c | ||
|   | 654a443d4b | ||
|   | a6ec2d5ed7 | ||
|   | 02ac083175 | ||
|   | 561fce5d40 | ||
|   | a88263874a | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| * text=auto eol=lf | ||||
							
								
								
									
										31
									
								
								.github/workflows/checkNewPlugins.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								.github/workflows/checkNewPlugins.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| # This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created | ||||
| # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle | ||||
|  | ||||
| # This workflow syncs changes from the docs folder of IdeaVim to the IdeaVim.wiki repository | ||||
|  | ||||
| name: Check new plugin dependencies | ||||
|  | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|   schedule: | ||||
|     - cron: '0 5 * * *' | ||||
|  | ||||
| jobs: | ||||
|   build: | ||||
|  | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     steps: | ||||
|       - name: Fetch origin repo | ||||
|         uses: actions/checkout@v3 | ||||
|  | ||||
|       - name: Set up JDK 17 | ||||
|         uses: actions/setup-java@v2 | ||||
|         with: | ||||
|           java-version: '17' | ||||
|           distribution: 'adopt' | ||||
|           server-id: github # Value of the distributionManagement/repository/id field of the pom.xml | ||||
|           settings-path: ${{ github.workspace }} # location for the settings.xml file | ||||
|  | ||||
|       - name: Check new plugins | ||||
|         run: ./gradlew scripts:checkNewPluginDependencies | ||||
							
								
								
									
										17
									
								
								.github/workflows/mergePr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.github/workflows/mergePr.yml
									
									
									
									
										vendored
									
									
								
							| @@ -5,7 +5,7 @@ name: Update Changelog On PR | ||||
|  | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|   pull_request: | ||||
|   pull_request_target: | ||||
|     types: [ closed ] | ||||
|  | ||||
| jobs: | ||||
| @@ -30,7 +30,20 @@ jobs: | ||||
|         id: update_authors | ||||
|         run: ./gradlew updateMergedPr -PprId=${{ github.event.number }} | ||||
|         env: | ||||
|           GITHUB_OAUTH: ${{ secrets.MERGE_PR }} | ||||
|           GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }} | ||||
|  | ||||
|       # Reuse from update changelog | ||||
|       - uses: nrwl/last-successful-commit-action@v1 | ||||
|         id: last_successful_commit | ||||
|         with: | ||||
|           branch: 'master' | ||||
|           workflow_id: 'updateChangelog.yml' | ||||
|           github_token: ${{ secrets.GITHUB_TOKEN }} | ||||
|  | ||||
|       - name: Update changelog | ||||
|         run: ./gradlew updateChangelog | ||||
|         env: | ||||
|           SUCCESS_COMMIT: ${{ steps.last_successful_commit.outputs.commit_hash }} | ||||
|  | ||||
|       - name: Commit changes | ||||
|         uses: stefanzweifel/git-auto-commit-action@v4 | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/updateChangelog.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/updateChangelog.yml
									
									
									
									
										vendored
									
									
								
							| @@ -11,6 +11,7 @@ on: | ||||
| jobs: | ||||
|   build: | ||||
|  | ||||
|     if: github.event.pull_request.merged != true | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     steps: | ||||
| @@ -45,3 +46,4 @@ jobs: | ||||
|           commit_user_email: aleksei.plate@jetbrains.com | ||||
|           commit_author: Alex Plate <aleksei.plate@jetbrains.com> | ||||
|           file_pattern: CHANGES.md | ||||
|           skip_fetch: false | ||||
| @@ -25,6 +25,9 @@ usual beta standards. | ||||
|  | ||||
| ## To Be Released | ||||
|  | ||||
| ### Features: | ||||
| * Add IdeaVim tutor. You can access it via the status bar icon. | ||||
|  | ||||
| ### Fixes: | ||||
| * [VIM-2797](https://youtrack.jetbrains.com/issue/VIM-2797) Introduce variable to mute default argtextobj mappings | ||||
| * [VIM-758](https://youtrack.jetbrains.com/issue/VIM-758) Support d mappings | ||||
| @@ -37,6 +40,9 @@ usual beta standards. | ||||
| * [553](https://github.com/JetBrains/ideavim/pull/553) by [Matt Ellis](https://github.com/citizenmatt): Rearrange and rename some code in engine | ||||
| * [560](https://github.com/JetBrains/ideavim/pull/560) by [Runinho](https://github.com/Runinho): Fix(VIM-2577) paste not working at end of notebook cell | ||||
| * [571](https://github.com/JetBrains/ideavim/pull/571) by [Ada](https://github.com/adaext): Remove the redundant quotation mark at the end of "packadd matchit" command | ||||
| * [561](https://github.com/JetBrains/ideavim/pull/561) by [Matt Ellis](https://github.com/citizenmatt): Fix incremental search not scrolling to current match | ||||
| * [559](https://github.com/JetBrains/ideavim/pull/559) by [Runinho](https://github.com/Runinho): Fix(VIM-2760) notebookCommandMode detection | ||||
| * [579](https://github.com/JetBrains/ideavim/pull/579) by [Martin Yzeiri](https://github.com/myzeiri): VIM-2799: Add Matchit support for cshtml files | ||||
|  | ||||
| ## 2.0.0, 2022-11-01 | ||||
|  | ||||
|   | ||||
| @@ -89,7 +89,7 @@ Here are some examples of supported vim features and commands: | ||||
| * Vim web help | ||||
| * `~/.ideavimrc` configuration file | ||||
|  | ||||
| [IdeaVim plugins](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins): | ||||
| [IdeaVim plugins](https://github.com/JetBrains/ideavim/wiki/IdeaVim-Plugins): | ||||
|  | ||||
| * vim-easymotion | ||||
| * NERDTree | ||||
| @@ -104,6 +104,7 @@ Here are some examples of supported vim features and commands: | ||||
| * vim-paragraph-motion | ||||
| * vim-indent-object | ||||
| * match.it   | ||||
| etc | ||||
|  | ||||
| See also: | ||||
|  | ||||
| @@ -202,7 +203,7 @@ Put your settings to `$XDG_CONFIG_HOME/ideavim/ideavimrc` file. | ||||
| IdeaVim Plugins | ||||
| -------------------- | ||||
|  | ||||
| See [doc/emulated-plugins.md](https://github.com/JetBrains/ideavim/wiki/Emulated-plugins) | ||||
| See [doc/emulated-plugins.md](https://github.com/JetBrains/ideavim/wiki/IdeaVim-Plugins) | ||||
|  | ||||
| Executing IDE Actions | ||||
| --------------------- | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| IdeaVim project is licensed under MIT license except the following parts of it: | ||||
|  | ||||
| File [RegExp.kt](src/main/java/com/maddyhome/idea/vim/regexp/RegExp.kt) is licensed under Vim License.   | ||||
| File [Tutor.kt](src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt) is licensed under Vim License. | ||||
|  | ||||
| ``` | ||||
| VIM LICENSE | ||||
|   | ||||
| @@ -61,6 +61,7 @@ plugins { | ||||
|     antlr | ||||
|     java | ||||
|     kotlin("jvm") version "1.7.20" | ||||
|     application | ||||
|  | ||||
|     id("org.jetbrains.intellij") version "1.11.1-SNAPSHOT" | ||||
|     id("org.jetbrains.changelog") version "1.3.1" | ||||
| @@ -808,7 +809,7 @@ fun updateMergedPr(number: Int) { | ||||
|     if (pullRequest.user.login == "dependabot[bot]") return | ||||
|  | ||||
|     val prNumber = pullRequest.number | ||||
|     val userName = pullRequest.user.name | ||||
|     val userName = pullRequest.user.name ?: pullRequest.user.login | ||||
|     val login = pullRequest.user.login | ||||
|     val title = pullRequest.title | ||||
|     val section = | ||||
|   | ||||
| @@ -381,3 +381,18 @@ Original plugin: [matchit.vim](https://github.com/chrisbra/matchit). | ||||
| https://github.com/adelarsq/vim-matchit/blob/master/doc/matchit.txt | ||||
|  | ||||
| </details> | ||||
|  | ||||
| <details> | ||||
| <summary><h2>IdeaVim-Quickscope</h2></summary> | ||||
|  | ||||
| Original plugin: [quick-scope](https://github.com/unblevable/quick-scope). | ||||
|  | ||||
| ### Setup: | ||||
| - Install [IdeaVim-sneak](https://plugins.jetbrains.com/plugin/15348-ideavim-sneak) plugin. | ||||
| - Add the following command to `~/.ideavimrc`: `set quickscope` | ||||
|  | ||||
| ### Instructions | ||||
|  | ||||
| https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope | ||||
|  | ||||
| </details> | ||||
|   | ||||
| @@ -8,15 +8,14 @@ | ||||
|  | ||||
| # suppress inspection "UnusedProperty" for whole file | ||||
|  | ||||
| ideaVersion=LATEST-EAP-SNAPSHOT | ||||
| ideaVersion=2022.3 | ||||
| downloadIdeaSources=true | ||||
| instrumentPluginCode=true | ||||
| version=SNAPSHOT | ||||
| version=chylex-15 | ||||
| javaVersion=17 | ||||
| remoteRobotVersion=0.11.15 | ||||
| antlrVersion=4.10.1 | ||||
|  | ||||
|  | ||||
| # Please don't forget to update kotlin version in buildscript section | ||||
| kotlinVersion=1.7.20 | ||||
| publishToken=token | ||||
|   | ||||
							
								
								
									
										51
									
								
								scripts/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								scripts/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| /* | ||||
|  * Copyright 2003-2022 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. | ||||
|  */ | ||||
|  | ||||
| plugins { | ||||
|     java | ||||
|     kotlin("jvm") | ||||
|     application | ||||
| } | ||||
|  | ||||
| // group 'org.jetbrains.ideavim' | ||||
| // version 'SNAPSHOT' | ||||
|  | ||||
| repositories { | ||||
|     mavenCentral() | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     compileOnly("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20") | ||||
|  | ||||
|     implementation("io.ktor:ktor-client-core:2.1.3") | ||||
|     implementation("io.ktor:ktor-client-cio:2.1.3") | ||||
|     implementation("io.ktor:ktor-client-content-negotiation:2.1.3") | ||||
|     implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.3") | ||||
| } | ||||
|  | ||||
| tasks { | ||||
|     compileKotlin { | ||||
|         kotlinOptions { | ||||
|             freeCompilerArgs = listOf("-Xjvm-default=all-compatibility") | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| tasks.register("generateIdeaVimConfigurations", JavaExec::class) { | ||||
|     group = "verification" | ||||
|     description = "This job tracks if there are any new plugins in marketplace we don't know about" | ||||
|     mainClass.set("scripts.MainKt") | ||||
|     classpath = sourceSets["main"].runtimeClasspath | ||||
| } | ||||
|  | ||||
| tasks.register("checkNewPluginDependencies", JavaExec::class) { | ||||
|     group = "verification" | ||||
|     description = "This job tracks if there are any new plugins in marketplace we don't know about" | ||||
|     mainClass.set("scripts.CheckNewPluginDependenciesKt") | ||||
|     classpath = sourceSets["main"].runtimeClasspath | ||||
| } | ||||
							
								
								
									
										13
									
								
								scripts/src/main/kotlin/scripts/Main.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								scripts/src/main/kotlin/scripts/Main.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| /* | ||||
|  * Copyright 2003-2022 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 scripts | ||||
|  | ||||
| fun main() { | ||||
|   println("Hello") | ||||
| } | ||||
| @@ -0,0 +1,54 @@ | ||||
| /* | ||||
|  * Copyright 2003-2022 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 scripts | ||||
|  | ||||
| import io.ktor.client.* | ||||
| import io.ktor.client.call.* | ||||
| import io.ktor.client.engine.cio.* | ||||
| import io.ktor.client.plugins.contentnegotiation.* | ||||
| import io.ktor.client.request.* | ||||
| import io.ktor.serialization.kotlinx.json.* | ||||
| import kotlinx.coroutines.runBlocking | ||||
|  | ||||
| /** | ||||
|  * Marketplace has a API to get all plugins that depend on our plugin. | ||||
|  * Here we have a list of dependent plugins at some moment and we check if something changed in that. | ||||
|  * If so, we need to update our list of plugins. | ||||
|  * | ||||
|  * This script makes no actions and aimed to notify the devs in case they need to update the list of IdeaVim plugins. | ||||
|  */ | ||||
|  | ||||
| val knownPlugins = listOf( | ||||
|   "IdeaVimExtension", | ||||
|   "github.zgqq.intellij-enhance", | ||||
|   "org.jetbrains.IdeaVim-EasyMotion", | ||||
|   "io.github.mishkun.ideavimsneak", | ||||
|   "eu.theblob42.idea.whichkey", | ||||
|   "com.github.copilot", | ||||
|   "com.github.dankinsoid.multicursor", | ||||
|   "com.joshestein.ideavim-quickscope", | ||||
| ) | ||||
|  | ||||
| fun main() { | ||||
|   val client = HttpClient(CIO) { | ||||
|     install(ContentNegotiation) { | ||||
|       json() | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   runBlocking { | ||||
|     val res = client.get("https://plugins.jetbrains.com/api/plugins/") { | ||||
|       parameter("dependency", "IdeaVIM") | ||||
|       parameter("includeOptional", true) | ||||
|     } | ||||
|     val output = res.body<List<String>>() | ||||
|     println(output) | ||||
|     if (knownPlugins != output) error("Unknown plugins list: ${output}") | ||||
|   } | ||||
| } | ||||
| @@ -10,4 +10,5 @@ pluginManagement { | ||||
|  | ||||
| rootProject.name = 'IdeaVIM' | ||||
| include 'vim-engine' | ||||
| include 'scripts' | ||||
|  | ||||
|   | ||||
| @@ -92,6 +92,16 @@ public class EventFacade { | ||||
|     EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable); | ||||
|   } | ||||
|  | ||||
|   public void addCaretListener(@NotNull Editor editor, | ||||
|                                @NotNull CaretListener listener, | ||||
|                                @NotNull Disposable disposable) { | ||||
|     editor.getCaretModel().addCaretListener(listener, disposable); | ||||
|   } | ||||
|  | ||||
|   public void removeCaretListener(@NotNull Editor editor, @NotNull CaretListener listener) { | ||||
|     editor.getCaretModel().removeCaretListener(listener); | ||||
|   } | ||||
|  | ||||
|   public void addEditorMouseListener(@NotNull Editor editor, | ||||
|                                      @NotNull EditorMouseListener listener, | ||||
|                                      @NotNull Disposable disposable) { | ||||
|   | ||||
| @@ -242,13 +242,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable | ||||
|  | ||||
|   public static @NotNull String getVersion() { | ||||
|     final IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(getPluginId()); | ||||
|     if (!ApplicationManager.getApplication().isInternal()) { | ||||
|     return plugin != null ? plugin.getVersion() : "SNAPSHOT"; | ||||
|   } | ||||
|     else { | ||||
|       return "INTERNAL" + (plugin != null ? " - " + plugin.getVersion() : ""); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public static boolean isEnabled() { | ||||
|     return getInstance().enabled; | ||||
|   | ||||
| @@ -144,6 +144,10 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ { | ||||
|  | ||||
|       if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) return false | ||||
|        | ||||
|       if (keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT) return false | ||||
|       if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN) return false | ||||
|       if (keyCode == KeyEvent.VK_HOME || keyCode == KeyEvent.VK_END) return false | ||||
|  | ||||
|       if (editor.inInsertMode) { | ||||
|         if (keyCode == KeyEvent.VK_TAB) { | ||||
|           // TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view | ||||
|   | ||||
| @@ -228,12 +228,13 @@ private object FileTypePatterns { | ||||
|     } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { | ||||
|       this.cMakePatterns | ||||
|     } else { | ||||
|       return null | ||||
|       this.htmlPatterns | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private val htmlLikeFileTypes = setOf( | ||||
|     "HTML", "XML", "XHTML", "JSP", "JavaScript", "JSX Harmony", "TypeScript", "TypeScript JSX", "Vue.js", "Handlebars/Mustache" | ||||
|     "HTML", "XML", "XHTML", "JSP", "JavaScript", "JSX Harmony", "TypeScript", | ||||
|     "TypeScript JSX", "Vue.js", "Handlebars/Mustache", "Razor" | ||||
|   ) | ||||
|  | ||||
|   private val htmlPatterns = createHtmlPatterns() | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| import com.maddyhome.idea.vim.api.VimChangeGroup | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.getLineEndOffset | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| @@ -30,6 +31,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction | ||||
| import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret | ||||
| import com.maddyhome.idea.vim.helper.editorMode | ||||
| import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore | ||||
| import com.maddyhome.idea.vim.key.OperatorFunction | ||||
| import com.maddyhome.idea.vim.newapi.IjVimCaret | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| @@ -75,22 +77,20 @@ class VimSurroundExtension : VimExtension { | ||||
|     override val isRepeatable = true | ||||
|  | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       setOperatorFunction(Operator()) | ||||
|       setOperatorFunction(Operator(supportsMultipleCursors = false)) // TODO | ||||
|       executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class VSurroundHandler : ExtensionHandler { | ||||
|     override fun execute(editor: VimEditor, context: ExecutionContext) { | ||||
|       val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart | ||||
|       // NB: Operator ignores SelectionType anyway | ||||
|       if (!Operator().apply(editor, context, SelectionType.CHARACTER_WISE)) { | ||||
|       if (!Operator(supportsMultipleCursors = true).apply(editor, context, SelectionType.CHARACTER_WISE)) { | ||||
|         return | ||||
|       } | ||||
|       runWriteAction { | ||||
|         // Leave visual mode | ||||
|         executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) | ||||
|         editor.ij.caretModel.moveToOffset(selectionStart) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -111,6 +111,10 @@ class VimSurroundExtension : VimExtension { | ||||
|  | ||||
|     companion object { | ||||
|       fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||
|         editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) } | ||||
|       } | ||||
|        | ||||
|       fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { | ||||
|         // Save old register values for carets | ||||
|         val surroundings = editor.sortedCarets() | ||||
|           .map { | ||||
| @@ -209,27 +213,45 @@ class VimSurroundExtension : VimExtension { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private class Operator : OperatorFunction { | ||||
|   private class Operator(private val supportsMultipleCursors: Boolean) : OperatorFunction { | ||||
|     override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType): Boolean { | ||||
|       val editor = vimEditor.ij | ||||
|       val c = getChar(editor) | ||||
|       if (c.code == 0) return true | ||||
|  | ||||
|       val pair = getOrInputPair(c, editor) ?: return false | ||||
|       // XXX: Will it work with line-wise or block-wise selections? | ||||
|       val range = getSurroundRange(editor) ?: return false | ||||
|  | ||||
|       runWriteAction { | ||||
|         val change = VimPlugin.getChange() | ||||
|         val leftSurround = pair.first | ||||
|         val primaryCaret = editor.caretModel.primaryCaret | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, leftSurround) | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + leftSurround.length, pair.second) | ||||
|         if (supportsMultipleCursors) { | ||||
|           editor.runWithEveryCaretAndRestore { | ||||
|             applyOnce(editor, change, pair) | ||||
|           } | ||||
|         } | ||||
|         else { | ||||
|           applyOnce(editor, change, pair) | ||||
|           // Jump back to start | ||||
|           executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor) | ||||
|         } | ||||
|       } | ||||
|       return true | ||||
|     } | ||||
|      | ||||
|     private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>) { | ||||
|       // XXX: Will it work with line-wise or block-wise selections? | ||||
|       val range = getSurroundRange(editor) | ||||
|       if (range != null) { | ||||
|         val primaryCaret = editor.caretModel.primaryCaret | ||||
|         change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, pair.first) | ||||
|         change.insertText( | ||||
|           IjVimEditor(editor), | ||||
|           IjVimCaret(primaryCaret), | ||||
|           range.endOffset + pair.first.length, | ||||
|           pair.second | ||||
|         ) | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     private fun getSurroundRange(editor: Editor): TextRange? = when (editor.editorMode) { | ||||
|       VimStateMachine.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor.vim) | ||||
|       VimStateMachine.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) } | ||||
|   | ||||
| @@ -82,7 +82,7 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|     String initText = getRange(((IjVimEditor) editor).getEditor(), cmd); | ||||
|     VimStateMachine.getInstance(editor).pushModes(VimStateMachine.Mode.CMD_LINE, VimStateMachine.SubMode.NONE); | ||||
|     ExEntryPanel panel = ExEntryPanel.getInstance(); | ||||
|     panel.activate(((IjVimEditor) editor).getEditor(), ((IjExecutionContext) context).getContext(), ":", initText, 1); | ||||
|     panel.activate(((IjVimEditor) editor).getEditor(), ((IjExecutionContext) context).getContext(), ":", initText, cmd.getCount()); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
| @@ -113,7 +113,7 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|  | ||||
|       logger.debug("processing command"); | ||||
|  | ||||
|       final String text = panel.getText(); | ||||
|       String text = panel.getText(); | ||||
|  | ||||
|       if (!panel.getLabel().equals(":")) { | ||||
|         // Search is handled via Argument.Type.EX_STRING. Although ProcessExEntryAction is registered as the handler for | ||||
| @@ -124,8 +124,16 @@ public class ProcessGroup extends VimProcessGroupBase { | ||||
|  | ||||
|       if (logger.isDebugEnabled()) logger.debug("swing=" + SwingUtilities.isEventDispatchThread()); | ||||
|  | ||||
|       int repeat = 1; | ||||
|       if (text.contains("raction ")) { | ||||
|         text = text.replace("raction ", "action "); | ||||
|         repeat = panel.getCount(); | ||||
|       } | ||||
|  | ||||
|       for (int i = 0; i < repeat; i++) { | ||||
|         VimInjectorKt.getInjector().getVimscriptExecutor().execute(text, editor, context, skipHistory(editor), true, CommandLineVimLContext.INSTANCE); | ||||
|       } | ||||
|     } | ||||
|     catch (ExException e) { | ||||
|       VimPlugin.showMessage(e.getMessage()); | ||||
|       VimPlugin.indicateError(); | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys | ||||
| import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.RangeMarker | ||||
| import com.intellij.openapi.ide.CopyPasteManager | ||||
| import com.intellij.util.PlatformUtils | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimCaret | ||||
| @@ -146,6 +147,9 @@ class PutGroup : VimPutBase() { | ||||
|     startOffset: Int, | ||||
|     endOffset: Int, | ||||
|   ): Int { | ||||
|     // Temp fix for VIM-2808. Should be removed after rider will fix it's issues | ||||
|     if (PlatformUtils.isRider()) return endOffset | ||||
|  | ||||
|     val startLine = editor.offsetToBufferPosition(startOffset).line | ||||
|     val endLine = editor.offsetToBufferPosition(endOffset - 1).line | ||||
|     val startLineOffset = (editor as IjVimEditor).editor.document.getLineStartOffset(startLine) | ||||
|   | ||||
| @@ -8,13 +8,17 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.group.visual | ||||
|  | ||||
| import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.maddyhome.idea.vim.api.getLineEndForOffset | ||||
| import com.maddyhome.idea.vim.api.getLineStartForOffset | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.helper.inBlockSubMode | ||||
| import com.maddyhome.idea.vim.helper.isEndAllowed | ||||
| import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset | ||||
| import com.maddyhome.idea.vim.helper.vimSelectionStart | ||||
| import com.maddyhome.idea.vim.newapi.IjVimEditor | ||||
| import com.maddyhome.idea.vim.newapi.vim | ||||
|  | ||||
| fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode: VimStateMachine.Mode) { | ||||
|   if (predictedMode != VimStateMachine.Mode.VISUAL) { | ||||
| @@ -40,3 +44,10 @@ fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode: VimState | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @Deprecated("Use same method on VimCaret") | ||||
| fun Caret.vimSetSelection(start: Int, end: Int = start, moveCaretToSelectionEnd: Boolean = false) { | ||||
|   vimSelectionStart = start | ||||
|   setVisualSelection(start, end, this.vim) | ||||
|   if (moveCaretToSelectionEnd && !editor.inBlockSubMode) moveToInlayAwareOffset(end) | ||||
| } | ||||
|   | ||||
| @@ -46,6 +46,12 @@ public class EditorHelper { | ||||
|     return editor.getScrollingModel().getVisibleAreaOnScrollingFinished(); | ||||
|   } | ||||
|  | ||||
|   //("Use extension function with the same name on VimEditor") | ||||
|   @Deprecated | ||||
|   public static boolean isLineEmpty(final @NotNull Editor editor, final int line, final boolean allowBlanks) { | ||||
|     return EngineEditorHelperKt.isLineEmpty(new IjVimEditor(editor), line, allowBlanks); | ||||
|   } | ||||
|  | ||||
|   public static boolean scrollVertically(@NotNull Editor editor, int verticalOffset) { | ||||
|     final ScrollingModel scrollingModel = editor.getScrollingModel(); | ||||
|     final Rectangle area = scrollingModel.getVisibleAreaOnScrollingFinished(); | ||||
|   | ||||
| @@ -12,6 +12,7 @@ package com.maddyhome.idea.vim.helper | ||||
|  | ||||
| import com.intellij.codeWithMe.ClientId | ||||
| import com.intellij.openapi.editor.Caret | ||||
| import com.intellij.openapi.editor.CaretState | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.ex.util.EditorUtil | ||||
| import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx | ||||
| @@ -96,3 +97,41 @@ val Caret.vimLine: Int | ||||
|  */ | ||||
| val Editor.vimLine: Int | ||||
|   get() = this.caretModel.currentCaret.vimLine | ||||
|  | ||||
| inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) { | ||||
|   val caretModel = this.caretModel | ||||
|   val carets = if (this.inBlockSubMode) null else caretModel.allCarets | ||||
|   if (carets == null || carets.size == 1) { | ||||
|     action() | ||||
|   } | ||||
|   else { | ||||
|     var initialDocumentSize = this.document.textLength | ||||
|     var documentSizeDifference = 0 | ||||
|  | ||||
|     val caretOffsets = carets.map { it.selectionStart to it.selectionEnd } | ||||
|     val restoredCarets = mutableListOf<CaretState>() | ||||
|  | ||||
|     caretModel.removeSecondaryCarets() | ||||
|      | ||||
|     for ((selectionStart, selectionEnd) in caretOffsets) { | ||||
|       if (selectionStart == selectionEnd) { | ||||
|         caretModel.primaryCaret.moveToOffset(selectionStart + documentSizeDifference) | ||||
|       } | ||||
|       else { | ||||
|         caretModel.primaryCaret.setSelection( | ||||
|           selectionStart + documentSizeDifference, | ||||
|           selectionEnd + documentSizeDifference | ||||
|         ) | ||||
|       } | ||||
|        | ||||
|       action() | ||||
|       restoredCarets.add(caretModel.caretsAndSelections.single()) | ||||
|  | ||||
|       val documentLength = this.document.textLength | ||||
|       documentSizeDifference += documentLength - initialDocumentSize | ||||
|       initialDocumentSize = documentLength | ||||
|     } | ||||
|  | ||||
|     caretModel.caretsAndSelections = restoredCarets | ||||
|   }  | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
|  | ||||
| package com.maddyhome.idea.vim.helper | ||||
|  | ||||
| import com.intellij.openapi.actionSystem.ex.ActionUtil | ||||
| import com.intellij.openapi.editor.Editor | ||||
| import com.intellij.openapi.editor.actionSystem.TypedAction | ||||
| import com.intellij.openapi.editor.actionSystem.TypedActionHandler | ||||
| @@ -49,7 +50,15 @@ class HandlerInjector { | ||||
|     fun notebookCommandMode(editor: Editor?): Boolean { | ||||
|       return if (editor != null) { | ||||
|         val inEditor = EditorHelper.getVirtualFile(editor)?.extension == "ipynb" | ||||
|         TypedAction.getInstance().rawHandler::class.java.simpleName.equals("JupyterCommandModeTypingBlocker") && inEditor | ||||
|         return if (TypedAction.getInstance().rawHandler::class.java.simpleName.equals("JupyterCommandModeTypingBlocker")) { | ||||
|           inEditor | ||||
|         } else { | ||||
|           // only true in command mode. | ||||
|           // Set by `org.jetbrains.plugins.notebooks.ui.editor.actions.command.mode.NotebookEditorModeListenerAdapter` | ||||
|           // appears to be null in non Notebook editors | ||||
|           val allow_plain_letter_shortcuts = editor.contentComponent.getClientProperty(ActionUtil.ALLOW_PlAIN_LETTER_SHORTCUTS) | ||||
|           inEditor && (allow_plain_letter_shortcuts != null && allow_plain_letter_shortcuts as Boolean) | ||||
|         } | ||||
|       } else { | ||||
|         TypedAction.getInstance().rawHandler::class.java.simpleName.equals("JupyterCommandModeTypingBlocker") | ||||
|       } | ||||
|   | ||||
| @@ -15,9 +15,11 @@ import com.intellij.openapi.command.impl.UndoManagerImpl | ||||
| import com.intellij.openapi.command.undo.UndoManager | ||||
| import com.intellij.openapi.components.Service | ||||
| import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.command.VimStateMachine | ||||
| import com.maddyhome.idea.vim.common.ChangesListener | ||||
| import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| @@ -42,6 +44,7 @@ class UndoRedoHelper : UndoRedoBase() { | ||||
|     if (undoManager.isUndoAvailable(fileEditor)) { | ||||
|       if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { | ||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) } | ||||
|         restoreVisualMode(editor) | ||||
|       } else { | ||||
|         performUntilFileChanges(editor, { undoManager.isUndoAvailable(fileEditor) }, { undoManager.undo(fileEditor) }) | ||||
|  | ||||
| @@ -71,6 +74,7 @@ class UndoRedoHelper : UndoRedoBase() { | ||||
|     if (undoManager.isRedoAvailable(fileEditor)) { | ||||
|       if (injector.optionService.isSet(OptionScope.GLOBAL, IjVimOptionService.oldUndo)) { | ||||
|         SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) } | ||||
|         restoreVisualMode(editor) | ||||
|       } else { | ||||
|         performUntilFileChanges(editor, { undoManager.isRedoAvailable(fileEditor) }, { undoManager.redo(fileEditor) }) | ||||
|         CommandProcessor.getInstance().runUndoTransparentAction { | ||||
| @@ -105,4 +109,21 @@ class UndoRedoHelper : UndoRedoBase() { | ||||
|   private fun ifFilePathChanged(editor: VimEditor, oldPath: String?): Boolean { | ||||
|     return editor.getPath() != oldPath | ||||
|   } | ||||
|  | ||||
|   private fun restoreVisualMode(editor: VimEditor) { | ||||
|     if (!editor.mode.inVisualMode && editor.getSelectionModel().hasSelection()) { | ||||
|       val detectedMode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor) | ||||
|        | ||||
|       // Visual block selection is restored into multiple carets, so multi-carets that form a block are always | ||||
|       // identified as visual block mode, leading to false positives. | ||||
|       // Since I use visual block mode much less often than multi-carets, this is a judgment call to never restore | ||||
|       // visual block mode. | ||||
|       val wantedMode = if (detectedMode == VimStateMachine.SubMode.VISUAL_BLOCK) | ||||
|         VimStateMachine.SubMode.VISUAL_CHARACTER | ||||
|       else | ||||
|         detectedMode | ||||
|        | ||||
|       VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,29 +0,0 @@ | ||||
| /* | ||||
|  * Copyright 2003-2022 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.helper | ||||
|  | ||||
| import com.intellij.ide.plugins.StandalonePluginUpdateChecker | ||||
| import com.intellij.openapi.components.Service | ||||
| import com.intellij.openapi.components.service | ||||
| import com.maddyhome.idea.vim.VimPlugin | ||||
| import com.maddyhome.idea.vim.group.NotificationService | ||||
| import com.maddyhome.idea.vim.icons.VimIcons | ||||
|  | ||||
| @Service(Service.Level.APP) | ||||
| class VimStandalonePluginUpdateChecker : StandalonePluginUpdateChecker( | ||||
|   VimPlugin.getPluginId(), | ||||
|   updateTimestampProperty = PROPERTY_NAME, | ||||
|   NotificationService.IDEAVIM_STICKY_GROUP, | ||||
|   VimIcons.IDEAVIM, | ||||
| ) { | ||||
|   companion object { | ||||
|     private const val PROPERTY_NAME = "ideavim.statistics.timestamp" | ||||
|     val instance: VimStandalonePluginUpdateChecker = service() | ||||
|   } | ||||
| } | ||||
| @@ -63,7 +63,9 @@ private var Caret._vimSelectionStart: Int? by userDataCaretToEditor() | ||||
|  | ||||
| // Keep a track of the column that we intended to navigate to but were unable to. This might be because of inlays, | ||||
| // virtual indent or moving from the end of a long line to the end of a short line. Keep a track of the position when | ||||
| // the value is set, if it's not the same during get, we've been moved by IJ and so no longer valid | ||||
| // the value is set, if it's not the same during get, we've been moved by IJ and so no longer valid. We also invalidate | ||||
| // the cached value through a caret listener handler, to prevent issues with the caret being moved and returned before | ||||
| // the cache is checked/invalidated | ||||
| var Caret.vimLastColumn: Int | ||||
|   get() { | ||||
|     if (visualPosition != _vimLastColumnPos) { | ||||
|   | ||||
| @@ -52,7 +52,6 @@ import com.maddyhome.idea.vim.group.visual.VimVisualTimer | ||||
| import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd | ||||
| import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently | ||||
| import com.maddyhome.idea.vim.helper.GuicursorChangeListener | ||||
| import com.maddyhome.idea.vim.helper.VimStandalonePluginUpdateChecker | ||||
| import com.maddyhome.idea.vim.helper.exitSelectMode | ||||
| import com.maddyhome.idea.vim.helper.exitVisualMode | ||||
| import com.maddyhome.idea.vim.helper.forceBarCursor | ||||
| @@ -62,6 +61,7 @@ import com.maddyhome.idea.vim.helper.isEndAllowed | ||||
| import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere | ||||
| import com.maddyhome.idea.vim.helper.localEditors | ||||
| import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset | ||||
| import com.maddyhome.idea.vim.helper.resetVimLastColumn | ||||
| import com.maddyhome.idea.vim.helper.subMode | ||||
| import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes | ||||
| import com.maddyhome.idea.vim.helper.vimDisabled | ||||
| @@ -157,6 +157,7 @@ object VimListenerManager { | ||||
|       eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler, disposable) | ||||
|       eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler, disposable) | ||||
|       eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener, disposable) | ||||
|       eventFacade.addCaretListener(editor, EditorCaretHandler, disposable) | ||||
|  | ||||
|       VimPlugin.getEditor().editorCreated(editor) | ||||
|  | ||||
| @@ -175,6 +176,7 @@ object VimListenerManager { | ||||
|       eventFacade.removeEditorMouseMotionListener(editor, EditorMouseHandler) | ||||
|       eventFacade.removeEditorSelectionListener(editor, EditorSelectionHandler) | ||||
|       eventFacade.removeComponentMouseListener(editor.contentComponent, ComponentMouseListener) | ||||
|       eventFacade.removeCaretListener(editor, EditorCaretHandler) | ||||
|  | ||||
|       VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased) | ||||
|  | ||||
| @@ -206,7 +208,6 @@ object VimListenerManager { | ||||
|   private object VimEditorFactoryListener : EditorFactoryListener { | ||||
|     override fun editorCreated(event: EditorFactoryEvent) { | ||||
|       add(event.editor) | ||||
|       VimStandalonePluginUpdateChecker.instance.pluginUsed() | ||||
|     } | ||||
|  | ||||
|     override fun editorReleased(event: EditorFactoryEvent) { | ||||
| @@ -494,6 +495,12 @@ object VimListenerManager { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private object EditorCaretHandler : CaretListener { | ||||
|     override fun caretPositionChanged(event: CaretEvent) { | ||||
|       event.caret?.resetVimLastColumn() | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   enum class SelectionSource { | ||||
|     MOUSE, | ||||
|     OTHER | ||||
|   | ||||
| @@ -11,12 +11,17 @@ package com.maddyhome.idea.vim.ui | ||||
| import com.intellij.icons.AllIcons | ||||
| import com.intellij.ide.BrowserUtil | ||||
| import com.intellij.ide.DataManager | ||||
| import com.intellij.ide.projectView.ProjectView | ||||
| import com.intellij.ide.scratch.ScratchRootType | ||||
| import com.intellij.ide.util.PsiNavigationSupport | ||||
| import com.intellij.openapi.actionSystem.ActionManager | ||||
| import com.intellij.openapi.actionSystem.ActionPlaces | ||||
| import com.intellij.openapi.actionSystem.ActionUpdateThread | ||||
| import com.intellij.openapi.actionSystem.AnActionEvent | ||||
| import com.intellij.openapi.actionSystem.DataContext | ||||
| import com.intellij.openapi.actionSystem.DefaultActionGroup | ||||
| import com.intellij.openapi.application.impl.LaterInvocator | ||||
| import com.intellij.openapi.fileTypes.PlainTextLanguage | ||||
| import com.intellij.openapi.options.ShowSettingsUtil | ||||
| import com.intellij.openapi.project.DumbAwareAction | ||||
| import com.intellij.openapi.project.Project | ||||
| @@ -30,6 +35,7 @@ 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.widget.StatusBarWidgetsManager | ||||
| import com.intellij.psi.PsiManager | ||||
| import com.intellij.ui.awt.RelativePoint | ||||
| import com.intellij.util.Consumer | ||||
| import com.intellij.util.ui.LafIconLookup | ||||
| @@ -193,6 +199,10 @@ private object VimActionsPopup { | ||||
|       ) | ||||
|     ) | ||||
|  | ||||
|     actionGroup.addSeparator("Learn") | ||||
|  | ||||
|     actionGroup.add(TutorAction()) | ||||
|  | ||||
|     actionGroup.addSeparator(MessageHelper.message("action.contacts.help.text")) | ||||
|     actionGroup.add( | ||||
|       HelpLink( | ||||
| @@ -220,6 +230,22 @@ private object VimActionsPopup { | ||||
|   } | ||||
| } | ||||
|  | ||||
| private class TutorAction() : DumbAwareAction("Tutor") { | ||||
|   override fun actionPerformed(e: AnActionEvent) { | ||||
|     val project = e.project ?: return | ||||
|     val file = ScratchRootType.getInstance() | ||||
|       .createScratchFile(project, "Tutor.txt", PlainTextLanguage.INSTANCE, tutor) ?: return | ||||
|  | ||||
|     PsiNavigationSupport.getInstance() | ||||
|       .createNavigatable(project, file, 0) | ||||
|       .navigate(!LaterInvocator.isInModalContextForProject(project)) | ||||
|  | ||||
|     PsiManager.getInstance(project).findFile(file)?.let { | ||||
|       ProjectView.getInstance(project).selectPsiElement(it, false) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| private class HelpLink( | ||||
|   @NlsActions.ActionText name: String, | ||||
|   val link: String, | ||||
|   | ||||
							
								
								
									
										927
									
								
								src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										927
									
								
								src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,927 @@ | ||||
| /* | ||||
|  * Copyright 2003-2022 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 | ||||
|  | ||||
| internal val tutor = """ | ||||
|   =============================================================================== | ||||
|   =                       Welcome to the IdeaVim Tutor                          = | ||||
|   =============================================================================== | ||||
|  | ||||
|        Based on the original VIM Tutor: | ||||
|        https://github.com/vim/vim/blob/master/runtime/tutor/tutor | ||||
|  | ||||
|        Vim is a very powerful editor that has many commands, too many to | ||||
|        explain in a tutor such as this.  This tutor is designed to describe | ||||
|        enough of the commands that you will be able to easily use Vim as | ||||
|        an all-purpose editor. | ||||
|         | ||||
|        IdeaVim is a Vim engine for JetBrains IDEs, such as IntelliJ IDEA, PyCharm, | ||||
|        and more. It allows you to use Vim-style editing inside of a full-fledged IDE. | ||||
|  | ||||
|        This tutorial doesn't cover a few things that are explained in Vim tutor, | ||||
|        but aren’t relevant to IdeaVim (for example, how to exit Vim). If you’re | ||||
|        still interested in them, see the last section. | ||||
|  | ||||
|        The approximate time required to complete the tutor is 30 minutes, | ||||
|        depending upon how much time is spent with experimentation. | ||||
|  | ||||
|        It is important to remember that this tutor is set up to teach by | ||||
|        use.  That means that you need to execute the commands to learn them | ||||
|        properly.  If you only read the text, you will forget the commands! | ||||
|  | ||||
|        Now, make sure that your Caps-Lock key is NOT depressed and press | ||||
|        the   j   key enough times to move the cursor so that lesson 1.1 | ||||
|        completely fills the screen. | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                    Lesson 1.1:  MOVING THE CURSOR | ||||
|  | ||||
|  | ||||
|      ** To move the cursor, press the h,j,k,l keys as indicated. ** | ||||
|              ^ | ||||
|              k        Hint:  The h key is at the left and moves left. | ||||
|         < h     l >          The l key is at the right and moves right. | ||||
|              j               The j key looks like a down arrow. | ||||
|              v | ||||
|     1. Move the cursor around the screen until you are comfortable. | ||||
|  | ||||
|     2. Hold down the down key (j) until it repeats. | ||||
|        Now you know how to move to the next lesson. | ||||
|  | ||||
|     3. Using the down key, move to lesson 1.2. | ||||
|  | ||||
|   NOTE: If you are ever unsure about something you typed, press <ESC> to place | ||||
|         you in Normal mode.  Then retype the command you wanted. | ||||
|  | ||||
|   NOTE: The cursor keys should also work.  But using hjkl you will be able to | ||||
|         move around much faster, once you get used to it.  Really! | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                  Lesson 1.2: TEXT EDITING - DELETION | ||||
|  | ||||
|  | ||||
|        ** Press  x  to delete the character under the cursor. ** | ||||
|  | ||||
|     1. Move the cursor to the line below marked --->. | ||||
|  | ||||
|     2. To fix the errors, move the cursor until it is on top of the | ||||
|        character to be deleted. | ||||
|  | ||||
|     3. Press the  x  key to delete the unwanted character. | ||||
|  | ||||
|     4. Repeat steps 2 through 4 until the sentence is correct. | ||||
|  | ||||
|   ---> The ccow jumpedd ovverr thhe mooon. | ||||
|  | ||||
|     5. Now that the line is correct, go on to lesson 1.4. | ||||
|  | ||||
|   NOTE: As you go through this tutor, do not try to memorize, learn by usage. | ||||
|  | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                Lesson 1.3: TEXT EDITING - INSERTION | ||||
|  | ||||
|  | ||||
|                  ** Press  i  to insert text. ** | ||||
|  | ||||
|     1. Move the cursor to the first line below marked --->. | ||||
|  | ||||
|     2. To make the first line the same as the second, move the cursor on top | ||||
|        of the character BEFORE which the text is to be inserted. | ||||
|  | ||||
|     3. Press  i  and type in the necessary additions. | ||||
|  | ||||
|     4. As each error is fixed press <ESC> to return to Normal mode. | ||||
|        Repeat steps 2 through 4 to correct the sentence. | ||||
|  | ||||
|   ---> There is text misng this . | ||||
|   ---> There is some text missing from this line. | ||||
|  | ||||
|     5. When you are comfortable inserting text move to lesson 1.5. | ||||
|  | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                 Lesson 1.4: TEXT EDITING - APPENDING | ||||
|  | ||||
|  | ||||
|                   ** Press  A  to append text. ** | ||||
|  | ||||
|     1. Move the cursor to the first line below marked --->. | ||||
|        It does not matter on what character the cursor is in that line. | ||||
|  | ||||
|     2. Press  A  and type in the necessary additions. | ||||
|  | ||||
|     3. As the text has been appended press <ESC> to return to Normal mode. | ||||
|  | ||||
|     4. Move the cursor to the second line marked ---> and repeat | ||||
|        steps 2 and 3 to correct this sentence. | ||||
|  | ||||
|   ---> There is some text missing from th | ||||
|        There is some text missing from this line. | ||||
|   ---> There is also some text miss | ||||
|        There is also some text missing here. | ||||
|  | ||||
|     5. When you are comfortable appending text move to lesson 1.6. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
|                          Lesson 1 SUMMARY | ||||
|  | ||||
|  | ||||
|     1. The cursor is moved using either the arrow keys or the hjkl keys. | ||||
|            h (left)  j (down)       k (up)      l (right) | ||||
|  | ||||
|     2. To delete the character at the cursor type:  x | ||||
|  | ||||
|     3. To insert or append text type: | ||||
|           i   type inserted text   <ESC>         insert before the cursor | ||||
|           A   type appended text   <ESC>         append after the line | ||||
|  | ||||
|   NOTE: Pressing <ESC> will place you in Normal mode or will cancel | ||||
|         an unwanted and partially completed command. | ||||
|  | ||||
|   Now continue with lesson 2. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                     Lesson 2.1: DELETION COMMANDS | ||||
|  | ||||
|  | ||||
|                   ** Type  dw  to delete a word. ** | ||||
|  | ||||
|     1. Press  <ESC>  to make sure you are in Normal mode. | ||||
|  | ||||
|     2. Move the cursor to the line below marked --->. | ||||
|  | ||||
|     3. Move the cursor to the beginning of a word that needs to be deleted. | ||||
|  | ||||
|     4. Type   dw   to make the word disappear. | ||||
|  | ||||
|     NOTE: The letter  d  will appear in the status bar as you type it. | ||||
|           Vim is waiting for you to type  w .  If you see another character | ||||
|           than  d  you typed something wrong; press  <ESC>  and start over. | ||||
|  | ||||
|   ---> There are a some words fun that don't belong paper in this sentence. | ||||
|  | ||||
|     5. Repeat steps 3 and 4 until the sentence is correct and go to lesson 2.2. | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                  Lesson 2.2: MORE DELETION COMMANDS | ||||
|  | ||||
|  | ||||
|        ** Type  d${'$'}  to delete to the end of the line. ** | ||||
|  | ||||
|     1. Press  <ESC>  to make sure you are in Normal mode. | ||||
|  | ||||
|     2. Move the cursor to the line below marked --->. | ||||
|  | ||||
|     3. Move the cursor to the end of the correct line (AFTER the first . ). | ||||
|  | ||||
|     4. Type    d${'$'}    to delete to the end of the line. | ||||
|  | ||||
|   ---> Somebody typed the end of this line twice. end of this line twice. | ||||
|  | ||||
|  | ||||
|     5. Move on to lesson 2.3 to understand what is happening. | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                   Lesson 2.3: ON OPERATORS AND MOTIONS | ||||
|  | ||||
|  | ||||
|     Many commands that change text are made from an operator and a motion. | ||||
|     The format for a delete command with the  d  delete operator is as follows: | ||||
|  | ||||
|          d   motion | ||||
|  | ||||
|     Where: | ||||
|       d      - is the delete operator. | ||||
|       motion - is what the operator will operate on (listed below). | ||||
|  | ||||
|     A short list of motions: | ||||
|       w - until the start of the next word, EXCLUDING its first character. | ||||
|       e - to the end of the current word, INCLUDING the last character. | ||||
|       ${'$'} - to the end of the line, INCLUDING the last character. | ||||
|  | ||||
|     Thus typing  de  will delete from the cursor to the end of the word. | ||||
|  | ||||
|   NOTE:  Pressing just the motion while in Normal mode without an operator will | ||||
|          move the cursor as specified. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                   Lesson 2.4: USING A COUNT FOR A MOTION | ||||
|  | ||||
|  | ||||
|      ** Typing a number before a motion repeats it that many times. ** | ||||
|  | ||||
|     1. Move the cursor to the start of the line below marked --->. | ||||
|  | ||||
|     2. Type  2w  to move the cursor two words forward. | ||||
|  | ||||
|     3. Type  3e  to move the cursor to the end of the third word forward. | ||||
|  | ||||
|     4. Type  0  (zero) to move to the start of the line. | ||||
|  | ||||
|     5. Repeat steps 2 and 3 with different numbers. | ||||
|  | ||||
|   ---> This is just a line with words you can move around in. | ||||
|  | ||||
|     6. Move on to lesson 2.5. | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                  Lesson 2.5: USING A COUNT TO DELETE MORE | ||||
|  | ||||
|  | ||||
|      ** Typing a number with an operator repeats it that many times. ** | ||||
|  | ||||
|     In the combination of the delete operator and a motion mentioned above you | ||||
|     insert a count before the motion to delete more: | ||||
|            d   number   motion | ||||
|  | ||||
|     1. Move the cursor to the first UPPER CASE word in the line marked --->. | ||||
|  | ||||
|     2. Type  d2w  to delete the two UPPER CASE words. | ||||
|  | ||||
|     3. Repeat steps 1 and 2 with a different count to delete the consecutive | ||||
|        UPPER CASE words with one command. | ||||
|  | ||||
|   --->  this ABC DE line FGHI JK LMN OP of words is Q RS TUV cleaned up. | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                      Lesson 2.6: OPERATING ON LINES | ||||
|  | ||||
|  | ||||
|          ** Type  dd   to delete a whole line. ** | ||||
|  | ||||
|     Due to the frequency of whole line deletion, the designers of Vi decided | ||||
|     it would be easier to simply type two d's to delete a line. | ||||
|  | ||||
|     1. Move the cursor to the second line in the phrase below. | ||||
|     2. Type  dd  to delete the line. | ||||
|     3. Now move to the fourth line. | ||||
|     4. Type   2dd   to delete two lines. | ||||
|  | ||||
|   --->  1)  Roses are red, | ||||
|   --->  2)  Mud is fun, | ||||
|   --->  3)  Violets are blue, | ||||
|   --->  4)  I have a car, | ||||
|   --->  5)  Clocks tell time, | ||||
|   --->  6)  Sugar is sweet | ||||
|   --->  7)  And so are you. | ||||
|  | ||||
|   Doubling to operate on a line also works for operators mentioned below. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                    Lesson 2.7: THE UNDO COMMAND | ||||
|  | ||||
|  | ||||
|      ** Press  u  to undo the last commands. ** | ||||
|  | ||||
|     1. Move the cursor to the line below marked ---> and place it on the | ||||
|        first error. | ||||
|     2. Type  x  to delete the first unwanted character. | ||||
|     3. Now type  u  to undo the last command executed. | ||||
|     4. This time fix all the errors on the line using the  x  command. | ||||
|     6. Now type  u  a few times to undo the preceding commands. | ||||
|     7. Now type CTRL-R (keeping CTRL key pressed while hitting R) a few times | ||||
|        to redo the commands. | ||||
|  | ||||
|   ---> Fiix the errors oon thhis line and reeplace them witth undo. | ||||
|  | ||||
|     8. These are very useful commands.  Now move on to the lesson 2 Summary. | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                         Lesson 2 SUMMARY | ||||
|  | ||||
|     1. To delete from the cursor up to the next word type:        dw | ||||
|     2. To delete from the cursor up to the end of the word type:  de | ||||
|     3. To delete from the cursor to the end of a line type:       d${'$'} | ||||
|     4. To delete a whole line type:                               dd | ||||
|  | ||||
|     5. To repeat a motion prepend it with a number:   2w | ||||
|     6. The format for a change command is: | ||||
|                  operator   [number]   motion | ||||
|        where: | ||||
|          operator - is what to do, such as  d  for delete | ||||
|          [number] - is an optional count to repeat the motion | ||||
|          motion   - moves over the text to operate on, such as  w (word), | ||||
|                     e (end of word),  ${'$'} (end of the line), etc. | ||||
|  | ||||
|     7. To move to the start of the line use a zero:  0 | ||||
|  | ||||
|     8. To undo previous actions, type:           u  (lowercase u) | ||||
|        To undo the undo's, type:                 CTRL-R | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                           Lesson 3.1: THE PUT COMMAND | ||||
|  | ||||
|  | ||||
|          ** Type  p  to put previously deleted text after the cursor. ** | ||||
|  | ||||
|     1. Move the cursor to the first line below marked --->. | ||||
|  | ||||
|     2. Type  dd  to delete the line and store it in a Vim register. | ||||
|  | ||||
|     3. Move the cursor to the c) line, ABOVE where the deleted line should go. | ||||
|  | ||||
|     4. Type   p   to put the line below the cursor. | ||||
|  | ||||
|     5. Repeat steps 2 through 4 to put all the lines in correct order. | ||||
|  | ||||
|   ---> d) Can you learn too? | ||||
|   ---> b) Violets are blue, | ||||
|   ---> c) Intelligence is learned, | ||||
|   ---> a) Roses are red, | ||||
|  | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                          Lesson 3.2: THE REPLACE COMMAND | ||||
|  | ||||
|  | ||||
|          ** Type  rx  to replace the character at the cursor with  x . ** | ||||
|  | ||||
|     1. Move the cursor to the first line below marked --->. | ||||
|  | ||||
|     2. Move the cursor so that it is on top of the first error. | ||||
|  | ||||
|     3. Type   r  and then the character which should be there. | ||||
|  | ||||
|     4. Repeat steps 2 and 3 until the first line is equal to the second one. | ||||
|  | ||||
|   --->  Whan this lime was tuoed in, someone presswd some wrojg keys! | ||||
|   --->  When this line was typed in, someone pressed some wrong keys! | ||||
|  | ||||
|     5. Now move on to lesson 3.3. | ||||
|  | ||||
|   NOTE: Remember that you should be learning by doing, not memorization. | ||||
|  | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                     Lesson 3.3: THE CHANGE OPERATOR | ||||
|  | ||||
|  | ||||
|           ** To change until the end of a word, type  ce . ** | ||||
|  | ||||
|     1. Move the cursor to the first line below marked --->. | ||||
|  | ||||
|     2. Place the cursor on the  u  in  lubw. | ||||
|  | ||||
|     3. Type  ce  and the correct word (in this case, type  ine ). | ||||
|  | ||||
|     4. Press <ESC> and move to the next character that needs to be changed. | ||||
|  | ||||
|     5. Repeat steps 3 and 4 until the first sentence is the same as the second. | ||||
|  | ||||
|   ---> This lubw has a few wptfd that mrrf changing usf the change operator. | ||||
|   ---> This line has a few words that need changing using the change operator. | ||||
|  | ||||
|   Notice that  ce  deletes the word and places you in Insert mode. | ||||
|                cc  does the same for the whole line. | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                         Lesson 3.4: MORE CHANGES USING c | ||||
|  | ||||
|  | ||||
|        ** The change operator is used with the same motions as delete. ** | ||||
|  | ||||
|     1. The change operator works in the same way as delete.  The format is: | ||||
|  | ||||
|            c    [number]   motion | ||||
|  | ||||
|     2. The motions are the same, such as   w (word) and  ${'$'} (end of line). | ||||
|  | ||||
|     3. Move the cursor to the first line below marked --->. | ||||
|  | ||||
|     4. Move the cursor to the first error. | ||||
|  | ||||
|     5. Type  c${'$'}  and type the rest of the line like the second and press <ESC>. | ||||
|  | ||||
|   ---> The end of this line needs some help to make it like the second. | ||||
|   ---> The end of this line needs to be corrected using the  c${'$'}  command. | ||||
|  | ||||
|   NOTE:  You can use the Backspace key to correct mistakes while typing. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                            Lesson 3 SUMMARY | ||||
|  | ||||
|  | ||||
|     1. To put back text that has just been deleted, type   p .  This puts the | ||||
|        deleted text AFTER the cursor (if a line was deleted it will go on the | ||||
|        line below the cursor). | ||||
|  | ||||
|     2. To replace the character under the cursor, type   r   and then the | ||||
|        character you want to have there. | ||||
|  | ||||
|     3. The change operator allows you to change from the cursor to where the | ||||
|        motion takes you.  eg. Type  ce  to change from the cursor to the end of | ||||
|        the word,  c${'$'}  to change to the end of a line. | ||||
|  | ||||
|     4. The format for change is: | ||||
|  | ||||
|            c   [number]   motion | ||||
|  | ||||
|   Now go on to the next lesson. | ||||
|  | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                Lesson 4.1: CURSOR LOCATION AND FILE STATUS | ||||
|  | ||||
|               ** Type  G  to move to a line in the file. ** | ||||
|  | ||||
|     NOTE: Read this entire lesson before executing any of the steps!! | ||||
|  | ||||
|     1. Remember the line number for Step 3. | ||||
|  | ||||
|     2. Press  G  to move you to the bottom of the file. | ||||
|        Type  gg  to move you to the start of the file. | ||||
|  | ||||
|     3. Type the number of the line you were on and then  G .  This will | ||||
|        return you to the line you were on before pressing  G. | ||||
|  | ||||
|     4. If you feel confident to do this, execute steps 1 through 3. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                          Lesson 4.2: THE SEARCH COMMAND | ||||
|  | ||||
|  | ||||
|        ** Type  /  followed by a phrase to search for the phrase. ** | ||||
|  | ||||
|     1. In Normal mode type the  /  character.  Notice that it and the cursor | ||||
|        appear at the bottom of the screen as with the  :  command. | ||||
|  | ||||
|     2. Now type 'errroor' <ENTER>.  This is the word you want to search for. | ||||
|  | ||||
|     3. To search for the same phrase again, simply type  n . | ||||
|        To search for the same phrase in the opposite direction, type  N . | ||||
|  | ||||
|     4. To search for a phrase in the backward direction, use  ?  instead of  / . | ||||
|  | ||||
|   --->  "errroor" is not the way to spell error;  errroor is an error. | ||||
|   NOTE: When the search reaches the end of the file it will continue at the | ||||
|         start, unless the 'wrapscan' option has been reset. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                  Lesson 4.3: MATCHING PARENTHESES SEARCH | ||||
|  | ||||
|  | ||||
|               ** Type  %  to find a matching ),], or } . ** | ||||
|  | ||||
|     1. Place the cursor on any (, [, or { in the line below marked --->. | ||||
|  | ||||
|     2. Now type the  %  character. | ||||
|  | ||||
|     3. The cursor will move to the matching parenthesis or bracket. | ||||
|  | ||||
|     4. Type  %  to move the cursor to the other matching bracket. | ||||
|  | ||||
|     5. Move the cursor to another (,),[,],{ or } and see what  %  does. | ||||
|  | ||||
|   ---> This ( is a test line with ('s, ['s ] and {'s } in it. )) | ||||
|  | ||||
|  | ||||
|   NOTE: This is very useful in debugging a program with unmatched parentheses! | ||||
|  | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                     Lesson 4.4: THE SUBSTITUTE COMMAND | ||||
|  | ||||
|  | ||||
|           ** Type  :s/old/new/g  to substitute 'new' for 'old'. ** | ||||
|  | ||||
|     1. Move the cursor to the line below marked --->. | ||||
|  | ||||
|     2. Type  :s/thee/the <ENTER>  .  Note that this command only changes the | ||||
|        first occurrence of "thee" in the line. | ||||
|  | ||||
|     3. Now type  :s/thee/the/g .  Adding the  g  flag means to substitute | ||||
|        globally in the line, change all occurrences of "thee" in the line. | ||||
|  | ||||
|   ---> thee best time to see thee flowers is in thee spring. | ||||
|  | ||||
|     4. To change every occurrence of a character string between two lines, | ||||
|        type   :#,#s/old/new/g    where #,# are the line numbers of the range | ||||
|                                  of lines where the substitution is to be done. | ||||
|        Type   :%s/old/new/g      to change every occurrence in the whole file. | ||||
|        Type   :%s/old/new/gc     to find every occurrence in the whole file, | ||||
|                     with a prompt whether to substitute or not. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                             Lesson 4 SUMMARY | ||||
|  | ||||
|  | ||||
|     1. G  moves to the end of the file. | ||||
|        number  G  moves to that line number. | ||||
|               gg  moves to the first line. | ||||
|  | ||||
|     2. Typing  /  followed by a phrase searches FORWARD for the phrase. | ||||
|        Typing  ?  followed by a phrase searches BACKWARD for the phrase. | ||||
|        After a search type  n  to find the next occurrence in the same direction | ||||
|        or  N  to search in the opposite direction. | ||||
|  | ||||
|     3. Typing  %  while the cursor is on a (,),[,],{, or } goes to its match. | ||||
|  | ||||
|     4. To substitute new for the first old in a line type    :s/old/new | ||||
|        To substitute new for all 'old's on a line type       :s/old/new/g | ||||
|        To substitute phrases between two line #'s type       :#,#s/old/new/g | ||||
|        To substitute all occurrences in the file type        :%s/old/new/g | ||||
|        To ask for confirmation each time add 'c'             :%s/old/new/gc | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                          Lesson 5.1: THE OPEN COMMAND | ||||
|  | ||||
|  | ||||
|    ** Type  o  to open a line below the cursor and place you in Insert mode. ** | ||||
|  | ||||
|     1. Move the cursor to the first line below marked --->. | ||||
|  | ||||
|     2. Type the lowercase letter  o  to open up a line BELOW the cursor and place | ||||
|        you in Insert mode. | ||||
|  | ||||
|     3. Now type some text and press <ESC> to exit Insert mode. | ||||
|  | ||||
|   ---> After typing  o  the cursor is placed on the open line in Insert mode. | ||||
|  | ||||
|     4. To open up a line ABOVE the cursor, simply type a capital  O , rather | ||||
|        than a lowercase  o.  Try this on the line below. | ||||
|  | ||||
|   ---> Open up a line above this by typing O while the cursor is on this line. | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                    Lesson 5.2: THE APPEND COMMAND | ||||
|  | ||||
|  | ||||
|          ** Type  a  to insert text AFTER the cursor. ** | ||||
|  | ||||
|     1. Move the cursor to the start of the first line below marked --->. | ||||
|  | ||||
|     2. Press  e  until the cursor is on the end of  li . | ||||
|  | ||||
|     3. Type an  a  (lowercase) to append text AFTER the cursor. | ||||
|  | ||||
|     4. Complete the word like the line below it.  Press <ESC> to exit Insert | ||||
|        mode. | ||||
|  | ||||
|     5. Use  e  to move to the next incomplete word and repeat steps 3 and 4. | ||||
|  | ||||
|   ---> This li will allow you to pract appendi text to a line. | ||||
|   ---> This line will allow you to practice appending text to a line. | ||||
|  | ||||
|   NOTE:  a, i and A all go to the same Insert mode, the only difference is where | ||||
|          the characters are inserted. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                       Lesson 5.3: ANOTHER WAY TO REPLACE | ||||
|  | ||||
|  | ||||
|         ** Type a capital  R  to replace more than one character. ** | ||||
|  | ||||
|     1. Move the cursor to the first line below marked --->.  Move the cursor to | ||||
|        the beginning of the first  xxx . | ||||
|  | ||||
|     2. Now press  R  and type the number below it in the second line, so that it | ||||
|        replaces the xxx . | ||||
|  | ||||
|     3. Press <ESC> to leave Replace mode.  Notice that the rest of the line | ||||
|        remains unmodified. | ||||
|  | ||||
|     4. Repeat the steps to replace the remaining xxx. | ||||
|  | ||||
|   ---> Adding 123 to xxx gives you xxx. | ||||
|   ---> Adding 123 to 456 gives you 579. | ||||
|  | ||||
|   NOTE:  Replace mode is like Insert mode, but every typed character deletes an | ||||
|          existing character. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                  Lesson 5.4: COPY AND PASTE TEXT | ||||
|  | ||||
|  | ||||
|       ** Use the  y  operator to copy text and  p  to paste it ** | ||||
|  | ||||
|     1. Move to the line below marked ---> and place the cursor after "a)". | ||||
|  | ||||
|     2. Start Visual mode with  v  and move the cursor to just before "first". | ||||
|  | ||||
|     3. Type  y  to yank (copy) the highlighted text. | ||||
|  | ||||
|     4. Move the cursor to the end of the next line:  j${'$'} | ||||
|  | ||||
|     5. Type  p  to put (paste) the text.  Then type:  a second <ESC> . | ||||
|  | ||||
|     6. Use Visual mode to select " item.", yank it with  y , move to the end of | ||||
|        the next line with  j${'$'}  and put the text there with  p . | ||||
|  | ||||
|   --->  a) this is the first item. | ||||
|         b) | ||||
|  | ||||
|     NOTE: You can also use  y  as an operator:  yw  yanks one word, | ||||
|           yy  yanks the whole line, then  p  puts that line. | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                      Lesson 5.5: SET OPTION | ||||
|  | ||||
|  | ||||
|       ** Set an option so a search or substitute ignores case ** | ||||
|  | ||||
|     1. Search for 'ignore' by entering:  /ignore <ENTER> | ||||
|        Repeat several times by pressing  n . | ||||
|  | ||||
|     2. Set the 'ic' (Ignore case) option by entering:   :set ic | ||||
|  | ||||
|     3. Now search for 'ignore' again by pressing  n | ||||
|        Notice that Ignore and IGNORE are now also found. | ||||
|  | ||||
|     4. Set the 'hlsearch' and 'incsearch' options:  :set hls is | ||||
|  | ||||
|     5. Now type the search command again and see what happens:  /ignore <ENTER> | ||||
|  | ||||
|     6. To disable ignoring case enter:  :set noic | ||||
|  | ||||
|   NOTE:  To remove the highlighting of matches enter:   :nohlsearch | ||||
|   NOTE:  If you want to ignore case for just one search command, use  \c | ||||
|          in the phrase:  /ignore\c <ENTER> | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                       Lesson 5 SUMMARY | ||||
|  | ||||
|     1. Type  o  to open a line BELOW the cursor and start Insert mode. | ||||
|        Type  O  to open a line ABOVE the cursor. | ||||
|  | ||||
|     2. Type  a  to insert text AFTER the cursor. | ||||
|        Type  A  to insert text after the end of the line. | ||||
|  | ||||
|     3. The  e  command moves to the end of a word. | ||||
|  | ||||
|     4. The  y  operator yanks (copies) text,  p  puts (pastes) it. | ||||
|  | ||||
|     5. Typing a capital  R  enters Replace mode until  <ESC>  is pressed. | ||||
|  | ||||
|     6. Typing ":set xxx" sets the option "xxx".  Some options are: | ||||
|       'ic'  'ignorecase'  ignore upper/lower case when searching | ||||
|       'is'  'incsearch'   show partial matches for a search phrase | ||||
|       'hls' 'hlsearch'    highlight all matching phrases | ||||
|        You can either use the long or the short option name. | ||||
|  | ||||
|     7. Prepend "no" to switch an option off:   :set noic | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
|             Lesson 6.1: CREATE A STARTUP SCRIPT | ||||
|  | ||||
|  | ||||
|               ** Enable IdeaVim features ** | ||||
|                | ||||
|     IdeaVim and Vim have many more features than Vi, but most of them are disabled | ||||
|     by default. To start using more features, first create an "ideavimrc" file. | ||||
|     For Vim it’s a "vimrc" file. | ||||
|  | ||||
|     To do so, click the IdeaVim status bar icon | ||||
|     (the green V letter) and click "Create ~/.ideavimrc". This will create a | ||||
|     ".ideavimrc" file in your home directory. | ||||
|  | ||||
|     Add the following lines to the file: | ||||
|      | ||||
|           Plug 'machakann/vim-highlightedyank' | ||||
|           set incsearch | ||||
|  | ||||
|     Click on the reload icon in the upper-right corner of the editor. | ||||
|  | ||||
|     With these commands, you've enable the "highlightedyank" plugin and incremental | ||||
|     search. The "highlightedyank" plugin highlights the text that was yanked. | ||||
|     The incremental search feature shows search matches as you type the query. | ||||
|  | ||||
|     You can find more plugins by clicking “Status bar icon | Plugins…”. | ||||
|  | ||||
|     You can add all of your preferred settings to this "ideavimrc" file. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|                          Lesson 6 SUMMARY | ||||
|  | ||||
|  | ||||
|     1. Create an ideavimrc startup script to keep your preferred settings. | ||||
|  | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
|     This concludes the IdeaVim Tutor.  It was intended to give a brief overview of | ||||
|     the IdeaVim plugin, just enough to allow you to use the editor fairly easily. | ||||
|     It is far from complete as IdeaVim and Vim have many many more commands. | ||||
|  | ||||
|     For further reading and studying, this book is recommended: | ||||
|        Vim - Vi Improved - by Steve Oualline | ||||
|        Publisher: New Riders | ||||
|     The first book completely dedicated to Vim.  Especially useful for beginners. | ||||
|     There are many examples and pictures. | ||||
|     See https://iccf-holland.org/click5.html | ||||
|  | ||||
|     This book is older and more about Vi than Vim, but also recommended: | ||||
|        Learning the Vi Editor - by Linda Lamb | ||||
|         Publisher: O'Reilly & Associates Inc. | ||||
|     It is a good book to get to know almost anything you want to do with Vi. | ||||
|     The sixth edition also includes information on Vim. | ||||
|  | ||||
|     This tutorial was written by Michael C. Pierce and Robert K. Ware, | ||||
|     Colorado School of Mines using ideas supplied by Charles Smith, | ||||
|     Colorado State University.  E-mail: bware@mines.colorado.edu. | ||||
|  | ||||
|     To learn more about IdeaVim, visit the official GitHub repository: | ||||
|     https://github.com/JetBrains/ideavim | ||||
|  | ||||
|     Modified for Vim by Bram Moolenaar. | ||||
|  | ||||
|     Modified for IdeaVim by Alex Plate. | ||||
|  | ||||
|   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
|     We removed the Vim Tutor sections that are related to the Vim editor, | ||||
|     but not really applicable to the IdeaVim plugin. | ||||
|     We recommend running these steps in an actual Vim editor. | ||||
|  | ||||
|             Lesson 1.2: EXITING VIM | ||||
|        | ||||
|          !! NOTE: Before executing any of the steps below, read this entire lesson!! | ||||
|  | ||||
|     1. Press the <ESC> key (to make sure you are in Normal mode). | ||||
|  | ||||
|     2. Type:  :q! <ENTER>. | ||||
|        This exits the editor, DISCARDING any changes you have made. | ||||
|  | ||||
|     3. Get back here by executing the command that got you into this tutor. That | ||||
|        might be:  vimtutor <ENTER> | ||||
|  | ||||
|     4. If you have these steps memorized and are confident, execute steps | ||||
|        1 through 3 to exit and re-enter the editor. | ||||
|  | ||||
|   NOTE:  :q! <ENTER>  discards any changes you made.  In a few lessons you | ||||
|          will learn how to save the changes to a file. | ||||
|  | ||||
|  | ||||
|            Lesson 1.6: EDITING A FILE | ||||
|  | ||||
|           ** Use  :wq  to save a file and exit. ** | ||||
|  | ||||
|     !! NOTE: Before executing any of the steps below, read this entire lesson!! | ||||
|  | ||||
|     1.  If you have access to another terminal, do the following there. | ||||
|         Otherwise, exit this tutor as you did in lesson 1.2:  :q! | ||||
|  | ||||
|     2. At the shell prompt type this command:  vim file.txt <ENTER> | ||||
|        'vim' is the command to start the Vim editor, 'file.txt' is the name of | ||||
|        the file you wish to edit.  Use the name of a file that you can change. | ||||
|  | ||||
|     3. Insert and delete text as you learned in the previous lessons. | ||||
|  | ||||
|     4. Save the file with changes and exit Vim with:  :wq <ENTER> | ||||
|  | ||||
|     5. If you have quit vimtutor in step 1 restart the vimtutor and move down to | ||||
|        the following summary. | ||||
|  | ||||
|     6. After reading the above steps and understanding them: do it. | ||||
|  | ||||
|       Lesson 5.1: HOW TO EXECUTE AN EXTERNAL COMMAND | ||||
|  | ||||
|  | ||||
|      ** Type  :!  followed by an external command to execute that command. ** | ||||
|  | ||||
|     1. Type the familiar command  :  to set the cursor at the bottom of the | ||||
|        screen.  This allows you to enter a command-line command. | ||||
|  | ||||
|     2. Now type the  !  (exclamation point) character.  This allows you to | ||||
|        execute any external shell command. | ||||
|  | ||||
|     3. As an example type   ls   following the ! and then hit <ENTER>.  This | ||||
|        will show you a listing of your directory, just as if you were at the | ||||
|        shell prompt.  Or use  :!dir  if ls doesn't work. | ||||
|  | ||||
|   NOTE:  It is possible to execute any external command this way, also with | ||||
|          arguments. | ||||
|  | ||||
|   NOTE:  All  :  commands must be finished by hitting <ENTER> | ||||
|          From here on we will not always mention it. | ||||
|  | ||||
|             Lesson 5.2: MORE ON WRITING FILES | ||||
|  | ||||
|  | ||||
|        ** To save the changes made to the text, type  :w FILENAME  ** | ||||
|  | ||||
|     1. Type  :!dir  or  :!ls  to get a listing of your directory. | ||||
|        You already know you must hit <ENTER> after this. | ||||
|  | ||||
|     2. Choose a filename that does not exist yet, such as TEST. | ||||
|  | ||||
|     3. Now type:   :w TEST   (where TEST is the filename you chose.) | ||||
|  | ||||
|     4. This saves the whole file (the Vim Tutor) under the name TEST. | ||||
|        To verify this, type    :!dir  or  :!ls   again to see your directory. | ||||
|  | ||||
|   NOTE: If you were to exit Vim and start it again with  vim TEST , the file | ||||
|         would be an exact copy of the tutor when you saved it. | ||||
|  | ||||
|     5. Now remove the file by typing (Windows):   :!del TEST | ||||
|           or (Unix):  :!rm TEST | ||||
|  | ||||
|           Lesson 5.3: SELECTING TEXT TO WRITE | ||||
|  | ||||
|  | ||||
|     ** To save part of the file, type  v  motion  :w FILENAME ** | ||||
|  | ||||
|     1. Move the cursor to this line. | ||||
|  | ||||
|     2. Press  v  and move the cursor to the fifth item below.  Notice that the | ||||
|        text is highlighted. | ||||
|  | ||||
|     3. Press the  :  character.  At the bottom of the screen  :'<,'> will appear. | ||||
|  | ||||
|     4. Type  w TEST  , where TEST is a filename that does not exist yet.  Verify | ||||
|        that you see  :'<,'>w TEST  before you press <ENTER>. | ||||
|  | ||||
|     5. Vim will write the selected lines to the file TEST.  Use  :!dir  or  :!ls | ||||
|        to see it.  Do not remove it yet!  We will use it in the next lesson. | ||||
|  | ||||
|   NOTE:  Pressing  v  starts Visual selection.  You can move the cursor around | ||||
|          to make the selection bigger or smaller.  Then you can use an operator | ||||
|          to do something with the text.  For example,  d  deletes the text. | ||||
|  | ||||
|          Lesson 5.4: RETRIEVING AND MERGING FILES | ||||
|  | ||||
|  | ||||
|          ** To insert the contents of a file, type  :r FILENAME  ** | ||||
|  | ||||
|     1. Place the cursor just above this line. | ||||
|  | ||||
|   NOTE:  After executing Step 2 you will see text from lesson 5.3.  Then move | ||||
|          DOWN to see this lesson again. | ||||
|  | ||||
|     2. Now retrieve your TEST file using the command   :r TEST   where TEST is | ||||
|        the name of the file you used. | ||||
|        The file you retrieve is placed below the cursor line. | ||||
|  | ||||
|     3. To verify that a file was retrieved, cursor back and notice that there | ||||
|        are now two copies of lesson 5.3, the original and the file version. | ||||
|  | ||||
|   NOTE:  You can also read the output of an external command.  For example, | ||||
|          :r !ls  reads the output of the ls command and puts it below the | ||||
|          cursor. | ||||
|  | ||||
|              Lesson 7.1: GETTING HELP | ||||
|  | ||||
|  | ||||
|             ** Use the on-line help system ** | ||||
|  | ||||
|     Vim has a comprehensive on-line help system.  To get started, try one of | ||||
|     these three: | ||||
|     - press the <HELP> key (if you have one) | ||||
|     - press the <F1> key (if you have one) | ||||
|     - type   :help <ENTER> | ||||
|  | ||||
|     Read the text in the help window to find out how the help works. | ||||
|     Type  CTRL-W CTRL-W   to jump from one window to another. | ||||
|     Type    :q <ENTER>    to close the help window. | ||||
|  | ||||
|     You can find help on just about any subject, by giving an argument to the | ||||
|     ":help" command.  Try these (don't forget pressing <ENTER>): | ||||
|  | ||||
|     :help w | ||||
|     :help c_CTRL-D | ||||
|     :help insert-index | ||||
|     :help user-manual | ||||
|  | ||||
|              Lesson 7.3: COMPLETION | ||||
|  | ||||
|  | ||||
|           ** Command line completion with CTRL-D and <TAB> ** | ||||
|  | ||||
|     1. Make sure Vim is not in compatible mode:  :set nocp | ||||
|  | ||||
|     2. Look what files exist in the directory:  :!ls   or  :!dir | ||||
|  | ||||
|     3. Type the start of a command:  :e | ||||
|  | ||||
|     4. Press  CTRL-D  and Vim will show a list of commands that start with "e". | ||||
|  | ||||
|     5. Type  d<TAB>  and Vim will complete the command name to ":edit". | ||||
|  | ||||
|     6. Now add a space and the start of an existing file name:  :edit FIL | ||||
|  | ||||
|     7. Press <TAB>.  Vim will complete the name (if it is unique). | ||||
|  | ||||
|   NOTE:  Completion works for many commands.  Just try pressing CTRL-D and | ||||
|          <TAB>.  It is especially useful for  :help . | ||||
| """.trimIndent() | ||||
| @@ -293,7 +293,7 @@ public class ExEntryPanel extends JPanel { | ||||
|         VimPlugin.getEditor().closeEditorSearchSession(editor); | ||||
|         final int matchOffset = SearchHighlightsHelper.updateIncsearchHighlights(editor, pattern, forwards, caretOffset, searchRange); | ||||
|         if (matchOffset != -1) { | ||||
|           editor.getCaretModel().getPrimaryCaret().moveToOffset(matchOffset); | ||||
|           new IjVimCaret(editor.getCaretModel().getPrimaryCaret()).moveToOffset(matchOffset); | ||||
|         } | ||||
|         else { | ||||
|           resetCaretOffset(editor); | ||||
|   | ||||
| @@ -0,0 +1,48 @@ | ||||
| /* | ||||
|  * IdeaVim - Vim emulator for IDEs based on the IntelliJ platform | ||||
|  * Copyright (C) 2003-2021 The IdeaVim authors | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| package com.maddyhome.idea.vim.vimscript.model.functions.handlers | ||||
|  | ||||
| import com.intellij.refactoring.rename.inplace.InplaceRefactoring | ||||
| import com.maddyhome.idea.vim.api.ExecutionContext | ||||
| import com.maddyhome.idea.vim.api.VimEditor | ||||
| import com.maddyhome.idea.vim.newapi.ij | ||||
| import com.maddyhome.idea.vim.vimscript.model.VimLContext | ||||
| import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType | ||||
| import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt | ||||
| import com.maddyhome.idea.vim.vimscript.model.expressions.Expression | ||||
| import com.maddyhome.idea.vim.vimscript.model.functions.FunctionHandler | ||||
|  | ||||
| object RenamingFunctionHandler : FunctionHandler() { | ||||
|  | ||||
|   override val name = "renaming" | ||||
|   override val minimumNumberOfArguments = 0 | ||||
|   override val maximumNumberOfArguments = 0 | ||||
|    | ||||
|   override fun doFunction( | ||||
|     argumentValues: List<Expression>, | ||||
|     editor: VimEditor, | ||||
|     context: ExecutionContext, | ||||
|     vimContext: VimLContext, | ||||
|   ): VimDataType { | ||||
|     return if (InplaceRefactoring.getActiveInplaceRenamer(editor.ij) == null) | ||||
|       VimInt.ZERO | ||||
|     else | ||||
|       VimInt.ONE | ||||
|   } | ||||
| } | ||||
| @@ -226,12 +226,12 @@ | ||||
|  | ||||
|     <!-- Change --> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerMotionAction" mappingModes="N" keys="gu"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction" mappingModes="X" keys="u"/> | ||||
| <!--    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseLowerVisualAction" mappingModes="X" keys="u"/>--> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleCharacterAction" mappingModes="N" keys="~"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleMotionAction" mappingModes="N" keys="g~"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseToggleVisualAction" mappingModes="X" keys="~"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperMotionAction" mappingModes="N" keys="gU"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction" mappingModes="X" keys="U"/> | ||||
| <!--    <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCaseUpperVisualAction" mappingModes="X" keys="U"/>--> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharacterAction" mappingModes="N" keys="r"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeCharactersAction" mappingModes="N" keys="s"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.change.ChangeEndOfLineAction" mappingModes="N" keys="C"/> | ||||
| @@ -329,8 +329,8 @@ | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.RepeatChangeAction" mappingModes="N" keys="."/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.ExEntryAction" mappingModes="NXO" keys=":"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.ResetModeAction" mappingModes="ALL" keys="«C-\»«C-N»"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.RedoAction" mappingModes="N" keys="«C-R»"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.UndoAction" mappingModes="N"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.RedoAction" mappingModes="NX" keys="U,«C-R»"/> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.UndoAction" mappingModes="NX"/> | ||||
|  | ||||
|     <!-- Keys --> | ||||
|     <vimAction implementation="com.maddyhome.idea.vim.action.change.OperatorAction" mappingModes="N" keys="g@"/> | ||||
|   | ||||
| @@ -22,5 +22,6 @@ | ||||
|     <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.TolowerFunctionHandler" name="tolower"/> | ||||
|     <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.ToupperFunctionHandler" name="toupper"/> | ||||
|     <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.JoinFunctionHandler" name="join"/> | ||||
|     <vimLibraryFunction implementation="com.maddyhome.idea.vim.vimscript.model.functions.handlers.RenamingFunctionHandler" name="renaming"/> | ||||
|   </extensions> | ||||
| </idea-plugin> | ||||
| @@ -1,39 +1,35 @@ | ||||
| <!-- | ||||
|   ~ Copyright 2003-2022 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. | ||||
|   --> | ||||
|  | ||||
| <idea-plugin url="https://plugins.jetbrains.com/plugin/164" xmlns:xi="http://www.w3.org/2001/XInclude"> | ||||
| <idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude"> | ||||
|   <name>IdeaVim</name> | ||||
|   <id>IdeaVIM</id> | ||||
|   <change-notes><![CDATA[ | ||||
|     <h3>Changes:</h3> | ||||
|     <ul> | ||||
|       <li>IdeaVim changes license from GPL-2.0 or later to MIT. <a href="https://youtrack.jetbrains.com/issue/VIM-2782">VIM-2782</a> | ||||
|       </li> | ||||
|     </ul> | ||||
|     <h3>Fixes:</h3> | ||||
|     <ul> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-1758">VIM-1758</a> Commentary plugin in rider</li> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-1903">VIM-1903</a> Autoindent now works in rider</li> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-2744">VIM-2744</a> Fix undo from ex line</li> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-2749">VIM-2749</a> Fix :tabn and :tabN commands</li> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-2718">VIM-2718</a> Fixed case where the primary caret was | ||||
|         changed | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-2797">VIM-2797</a> Introduce variable to mute default argtextobj | ||||
|         mappings | ||||
|       </li> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-2766">VIM-2766</a> Move NERDTree update to background thread | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-758">VIM-758</a> Support d mappings</li> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-2577">VIM-2577</a> Fix paste at the end of notebook cell</li> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-2813">VIM-2813</a> Migrate update checker to | ||||
|         VimStandalonePluginUpdateChecker | ||||
|       </li> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-2768">VIM-2768</a> Refactor listeners</li> | ||||
|       <li><a href="https://youtrack.jetbrains.com/issue/VIM-2776">VIM-2776</a> Use filename index for file search</li> | ||||
|     </ul> | ||||
|  | ||||
|     <h3>Merged PRs:</h3> | ||||
|     <ul> | ||||
|       <li><a href="https://github.com/JetBrains/ideavim/pull/550">550</a> by <a href="https://github.com/citizenmatt">Matt | ||||
|         Ellis</a>: Fix(VIM-2778) Remove override of editor scroll setting | ||||
|       <li><a href="https://github.com/JetBrains/ideavim/pull/558">558</a> by <a href="https://github.com/citizenmatt">Matt | ||||
|         Ellis</a>: Fix incorrect normalising for trailing inlay | ||||
|       </li> | ||||
|       <li><a href="https://github.com/JetBrains/ideavim/pull/554">554</a> by <a href="https://github.com/citizenmatt">Matt | ||||
|         Ellis</a>: Refactor "last column" calculations | ||||
|       </li> | ||||
|       <li><a href="https://github.com/JetBrains/ideavim/pull/553">553</a> by <a href="https://github.com/citizenmatt">Matt | ||||
|         Ellis</a>: Rearrange and rename some code in engine | ||||
|       </li> | ||||
|       <li><a href="https://github.com/JetBrains/ideavim/pull/560">560</a> by <a | ||||
|           href="https://github.com/Runinho">Runinho</a>: Fix(VIM-2577) paste not working at end of notebook cell | ||||
|       </li> | ||||
|       <li><a href="https://github.com/JetBrains/ideavim/pull/571">571</a> by <a href="https://github.com/adaext">Ada</a>: | ||||
|         Remove the redundant quotation mark at the end of "packadd matchit" command | ||||
|       </li> | ||||
|     </ul> | ||||
|     ]]> | ||||
| @@ -50,7 +46,7 @@ | ||||
|         <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> | ||||
|       </ul> | ||||
|     ]]></description> | ||||
|   <version>SNAPSHOT</version> | ||||
|   <version>chylex</version> | ||||
|   <vendor>JetBrains</vendor> | ||||
|  | ||||
|   <!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version --> | ||||
|   | ||||
| @@ -80,6 +80,28 @@ class MotionLastColumnActionTest : VimTestCase() { | ||||
|     doTest(keys, before, after, VimStateMachine.Mode.VISUAL, VimStateMachine.SubMode.VISUAL_BLOCK) | ||||
|   } | ||||
|  | ||||
|   fun `test dollar motion resets intended location after motion`() { | ||||
|     doTest( | ||||
|       "\$hlj", | ||||
|       """ | ||||
|           A Discovery | ||||
|  | ||||
|           I ${c}found it in a legendary land | ||||
|           all rocks and lavender and tufted grass,[ additional symbols] | ||||
|           where it was settled on some sodden sand | ||||
|           hard by the torrent of a mountain pass. | ||||
|       """.trimIndent(), | ||||
|       """ | ||||
|           A Discovery | ||||
|  | ||||
|           I found it in a legendary land | ||||
|           all rocks and lavender and tu${c}fted grass,[ additional symbols] | ||||
|           where it was settled on some sodden sand | ||||
|           hard by the torrent of a mountain pass. | ||||
|       """.trimIndent() | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   @VimBehaviorDiffers( | ||||
|     originalVimAfter = """ | ||||
|             A Discovery | ||||
|   | ||||
| @@ -213,7 +213,7 @@ class KeyHandler { | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * See the description for [com.maddyhome.idea.vim.action.DuplicableOperatorAction] | ||||
|    * See the description for [com.maddyhome.idea.vim.command.DuplicableOperatorAction] | ||||
|    */ | ||||
|   private fun mapOpCommand( | ||||
|     key: KeyStroke, | ||||
|   | ||||
| @@ -176,7 +176,8 @@ abstract class VimChangeGroupBase : VimChangeGroup { | ||||
|       val startOffsets = updatedRange.startOffsets | ||||
|       val endOffsets = updatedRange.endOffsets | ||||
|       for (i in updatedRange.size() - 1 downTo 0) { | ||||
|         editor.deleteString(TextRange(startOffsets[i], endOffsets[i])) | ||||
|         val (newRange, _) = editor.search(startOffsets[i].offset to endOffsets[i].offset, editor, LineDeleteShift.NL_ON_END) ?: continue | ||||
|         editor.deleteString(TextRange(newRange.first.point, newRange.second.point)) | ||||
|       } | ||||
|       if (type != null) { | ||||
|         val start = updatedRange.startOffset | ||||
|   | ||||
| @@ -0,0 +1,22 @@ | ||||
| package com.maddyhome.idea.vim.option | ||||
|  | ||||
| import com.maddyhome.idea.vim.api.injector | ||||
| import com.maddyhome.idea.vim.options.OptionConstants | ||||
| import com.maddyhome.idea.vim.options.OptionScope | ||||
|  | ||||
|  | ||||
| @Deprecated("Please use StrictMode from com.maddyhome.idea.vim.options.helpers", replaceWith = ReplaceWith("com.maddyhome.idea.vim.options.helpers.StrictMode")) | ||||
| object StrictMode { | ||||
|   @JvmName("assertTrue") | ||||
|   fun assert(condition: Boolean, message: String) { | ||||
|     if (!condition) { | ||||
|       fail(message) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   fun fail(message: String) { | ||||
|     if (injector.optionService.isSet(OptionScope.GLOBAL, OptionConstants.ideastrictmodeName)) { | ||||
|       error(message) | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user