Showing preview only (231K chars total). Download the full file or copy to clipboard to get everything.
Repository: nosami/XSVim
Branch: 8.1
Commit: cec402b3509d
Files: 47
Total size: 218.4 KB
Directory structure:
gitextract_1b8pllvk/
├── .gitattributes
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── XSVim/
│ ├── Addin.fs
│ ├── Classes.fs
│ ├── ExMode.fs
│ ├── KeyBindingSchemeVim.xml
│ ├── Mapping.fs
│ ├── PadTreeViews.fs
│ ├── Properties/
│ │ ├── AddinInfo.fs
│ │ ├── AssemblyInfo.fs
│ │ └── Manifest.addin.xml
│ ├── Reflection.fs
│ ├── SettingsPanel.fs
│ ├── SubstituteCommand.fs
│ ├── TreeViewPads.fs
│ ├── Types.fs
│ ├── WindowManagement.fs
│ ├── XSVim.fs
│ ├── XSVim.fsproj
│ └── packages.config
├── XSVim.Tests/
│ ├── ChangeTests.fs
│ ├── DeleteTests.fs
│ ├── ExModeTests.fs
│ ├── IndentationTests.fs
│ ├── InsertionTests.fs
│ ├── KeyParsing.fs
│ ├── KeyboardMap.fs
│ ├── MacrosTests.fs
│ ├── MarkerTests.fs
│ ├── MiscTests.fs
│ ├── Movement.fs
│ ├── Properties/
│ │ └── AssemblyInfo.fs
│ ├── TestHelpers.fs
│ ├── TextObjectSelectionTests.fs
│ ├── VisualTests.fs
│ ├── XSVim.Tests.fsproj
│ ├── YankAndPut.fs
│ └── packages.config
├── XSVim.sln
├── addin-project.xml
├── build.sh
├── copy-assemblies.sh
├── run-from-source.sh
└── test.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.sh text eol=lf
*.fs text eol=lf
Makefile.orig text eol=lf
configure.sh text eol=lf
================================================
FILE: .gitignore
================================================
# F#
[Bb]in/
[Oo]bj/
.fake/
repository/*
*.suo
*.pidb
*.userprefs
*.GhostDoc.xml
*.user
*.dll
*.pdb
*.cache
*.swp
*.swo
*.swn
*.pyc
*.orig
=======
*~
pack/*
.DS_Store
/packages
XSVim/any
.paket/paket.exe
================================================
FILE: .travis.yml
================================================
language: csharp
os: osx
mono: latest
solution: XSVim.sln
install:
- nuget restore XSVim.sln
- wget https://download.visualstudio.microsoft.com/download/pr/29ca6bca-9226-4904-93f8-3678cb24c2ca/596a772e2d2a7d28cbfd570933817a70/visualstudioformac-8.4.8.2.dmg
script:
- msbuild /p:Configuration=Release
#- mono64 "/Volumes/Visual Studio/Visual Studio.app/Contents/Resources/lib/monodevelop/bin/vstool.exe"
# run-md-tests XSVim.Tests/bin/Release/XSVim.Tests.dll -labels
- mono64 "/Volumes/Visual Studio/Visual Studio.app/Contents/Resources/lib/monodevelop/bin/vstool.exe"
setup pack XSVim/bin/Release/XSVim.dll
deploy:
provider: releases
api_key:
secure: GMgK73gqQxvaMXXtgZTVypt2nTmmY/uNOFc3f6pHqe31nvLtDgdwRgaqGC4uFHK/YspWujMXkmdo0OplyWZarZcGTSPJYcJ9/k9bEY4uXDVbLAGve2n7qIyhngprd1Mk9g+3zPEoi+xu7Ugc1y0GEFLy9Z3CXgr9A1AcEM49LsqpV2rqtjP8AjstmpVUgLXv5+6w5MlcJyoi0PulVE3B4N0I9EJoD8zfaTCNyq0YaydekR173bu5iuZsVuYkyqLsrKbWDJb67MiDG5pwW5vvcPIziPXf5TMXHRbnWZHdXmx5jlbV8k0DoaeChWh1gpMtr6gg1IuBCsFV65ackHqeZyyn6iKl/SwIUGBYkTadY4Hob5ns5LC/vRB2Rb32U7MRyP8eN8PpmHpVZL28DQq3PlqvehG3vjYe8AOZrRWY7e/2iu0lrpWxbYwf9X5CjTRhTPOUvMV0u7SgLMYdcf39iZoMwQuJTG3gv79ScZBlimisybEDl88T++XsfTI7LZOE4w91RnzjvhoHAUu2+R8Tpcj2wm0df/eOcFzIZqCTGgT+WefBB4yAo2Art3Ckk8MH/uLIEzRjsVeEYUFTN5xZ6N7YRhQB6JgyEEaP8NT7wZFX6xms+AwgITQEiTf29veyPKPFL0htvAaIlu7XFse7f4JZBtXNGfOja+T0b7nvlEI=
file_glob: true
file: XSVim*.mpack
skip_cleanup: true
on:
tags: true
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2016 Jason Imison
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# XSVim [](https://gitter.im/XSVim/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://travis-ci.org/nosami/XSVim)
# This addin is obsolete
This extension only works for the older editor. For VSMac 8.4, you should install VsVim instead  as VsVim works on the new editor.
Some file types (e.g. F#) are still currently using the old editor in VSMac. This addin will still work for those file types while the transition is made to the new editor.
# Installation
Interact with Visual Studio for Mac as follows:
```
Visual Studio -> Extensions -> Gallery -> IDE Extensions -> "VIM" -> Install
```
Then close the current document that you are working on and open a new document to activate the plugin.
## 8.1 New Editor
Unfortunately, this addin does not work with the new editor that was made default in 8.1. To use this addin, make sure that the old editor is in use.

Alternatively, I have been working on making VsVim work for the new editor. If you want to try this out, please follow the instructions [here](https://github.com/VsVim/VsVim/pull/2733#issuecomment-538998555)
# What works?
Most Vim commands should work. If you see something that doesn't work, please file an issue. There's a good chance that I just don't know about it.
# What doesn't work
- Vim split windows. XSVim uses VS for Mac's side by side mode to emulate this, but it's only possible to have 2 vertical split windows. `<C-w>s` and `<C-w>v` both switch to side by side mode.
- Visual block mode works for most tasks, but there are some differences in the way that VS handles virtual spacing at the end of lines.
- Selecting text with the mouse or using cmd+arrow keys doesn't switch to Visual mode
- No leader key support or configurable key bindings.
# Why don't the control keys work?
Some Vim keybindings (such as Ctrl-F, Ctrl-D etc) conflict with VS's own built in keybindings. However, there is a keybinding scheme included that you may apply if you want (Visual Studio + Vim)

# Extras
- `gd` - Goto declaration
- `gu` - Find usages
- `gb` - Go to base symbol
- `gh` - Show tooltip at current caret location (`G`o `H`over)
- `hjkl` support on the Solution Explorer pad and Test Explorer pad. Pressing `<esc>` on these will switch focus back to the last editor window. `jk` support on the Search Results pad.
- Goto Pad shortcuts start with `gp`
- `gps` - Go to solution explorer
- `gpc` - Go to class pad
- `gpe` - Go to error list pad
- `gpt` - Go to Task List pad
- `gpp` - Go to Property pad
- `gpo` - Go to document outline pad
- `gpb` - Go to breakpoint pad
- `gpl` - Go to locals pad
- `gpw` - Go to watch pad
- `gpi` - Go to immediate pad
- `gpn` - Go to F# Interactive pad
- `gpf` - When there is only one search results pad, go to it
- `gpf1` - When there is more than one search results pad, go to the 1st
- `gpf2` - When there is more than one search results pad, go to the 2nd....etc.
- `gpdt` - Go to debugger threads pad
- `gpds` - Go to debugger stack trace pad
- `gput` - Go to unit test pad
- `gpur` - Go to unit test results pad
- Insert mode escape binding. See example screenshot to see how to configure `jj` to escape when in insert mode.

# Looking for the latest release?
Check the [release page](https://github.com/nosami/XSVim/releases) as there is usually a more recent version of the addin here than on the Visual Studio for Mac feed. Grab the .mpack file and install it via Visual Studio -> Extensions -> Install from file
# Support & Contributions
Jump in our [Gitter channel](https://gitter.im/XSVim/Lobby) and introduce yourself.
# With thanks to
- @shirshov
- @mdizzy
- @tdfacer
================================================
FILE: XSVim/Addin.fs
================================================
namespace XSVim
open System
open System.Collections.Generic
open MonoDevelop.Components.Commands
open MonoDevelop.Core
open MonoDevelop.Ide
open MonoDevelop.Ide.Editor
open MonoDevelop.Ide.Editor.Extension
open MonoDevelop.Ide.FindInFiles
open Reflection
module Subscriptions =
let textChanged (editor:TextEditor) (changes: Text.TextChangeEventArgs) =
for change in changes.TextChanges do
if change.RemovalLength > 0 then
let state = Vim.editorStates.[editor.FileName]
let actions =
[ yield! state.lastAction
for _i in 1..change.RemovalLength do
yield (typeChar VimKey.Backspace) ]
let newState = { state with lastAction = actions }
Vim.editorStates.[editor.FileName] <- newState
if change.Offset + change.InsertionLength = editor.CaretOffset then
let state = Vim.editorStates.[editor.FileName]
let typedChars =
[ for c in change.InsertedText.Text do
yield typeChar (Key c) ]
let vimState =
{ state with lastAction = state.lastAction @ typedChars }
Vim.editorStates.[editor.FileName] <- vimState
type XSVim() as this =
inherit TextEditorExtension()
let mutable disposables : IDisposable list = []
let mutable processingKey = false
let mutable config = Config.Default
static let searchPads = HashSet<string>()
let initConfig() =
let keyboardMapping = match SettingsPanel.KeyboardLayout() with
| "Colemak" -> Colemak
| "Dvorak" -> Dvorak
| _ -> Qwerty
let mapping = SettingsPanel.InsertModeEscapeMapping()
if mapping.Length = 2 then
config <- { insertModeEscapeKey =
{
insertModeEscapeKey1 = string mapping.[0]
insertModeEscapeKey2 = string mapping.[1]
insertModeEscapeTimeout = SettingsPanel.InsertModeEscapeMappingTimeout()
} |> Some
keyboardLayout = keyboardMapping }
else
config <- { Config.Default with keyboardLayout = keyboardMapping }
let initializeSearchResultsPads() =
IdeApp.Workbench
|> Option.ofObj
|> Option.iter(fun workbench ->
workbench.Pads
|> Seq.iter(fun pad ->
try
// fetching pad.Content can throw when there is an exception
// when initializing the pad
tryUnbox<SearchResultPad> pad.Content
|> Option.iter(fun pad ->
let padId = pad.Window.Id
if not (searchPads.Contains padId) then
searchPads.Add padId |> ignore
let tree = pad.Control?nativeWidget?treeviewSearchResults
padTreeViews.initialize tree)
with
| _ -> ()))
let ctrl c =
KeyDescriptor.FromGtk(Enum.Parse(typeof<Gdk.Key>, c) :?> Gdk.Key, char c, Gdk.ModifierType.ControlMask)
|> this.KeyPress
let mutable fileName = FilePath.Empty
member x.State
with get() = Vim.editorStates.[fileName]
and set(value) = Vim.editorStates.[fileName] <- value
override x.IsValidInContext documentContext =
documentContext.Name <> "__FSI__.fs" && documentContext.Name <> "__FSI__.fsx"
override x.Initialize() =
treeViewPads.initialize()
x.Editor.FocusLost.Add(fun _ -> initializeSearchResultsPads())
fileName <- x.Editor.FileName.FullPath
LoggingService.LogDebug("XSVim initializing - " + string fileName)
initConfig()
let editor = x.Editor
let state =
match Vim.getCaretMode editor with
| Insert -> { VimState.Default with mode = InsertMode }
| Block -> VimState.Default
let initialState = Vim.switchToNormalMode editor state
if not (Vim.editorStates.ContainsKey fileName) then
Vim.editorStates.Add(fileName, initialState)
editor.GrabFocus()
let caretChanged =
editor.CaretPositionChanged.Subscribe
(fun _e ->
if not processingKey then // only interested in mouse clicks
let line = editor.GetLine editor.CaretLine
if line.Length > 0 && editor.CaretColumn >= line.LengthIncludingDelimiter then
editor.CaretOffset <- editor.CaretOffset - 1)
let documentClosed =
IdeApp.Workbench |> Option.ofObj
|> Option.map(fun workbench ->
workbench.DocumentClosed.Subscribe
(fun e -> let documentName = e.Document.FileName
if Vim.editorStates.ContainsKey documentName then
Vim.editorStates.Remove documentName |> ignore))
let textChanged = editor.TextChanged.Subscribe(fun changes -> Subscriptions.textChanged editor changes)
let propertyChanged =
PropertyService.PropertyChanged.Subscribe (fun _ -> initConfig())
let focusLost =
editor.FocusLost.Subscribe
(fun _ ->
match x.State.mode with
| ExMode _ ->
x.State <- Vim.switchToNormalMode x.Editor x.State
IdeApp.Workbench.StatusBar.ShowReady()
| _ -> ())
disposables <- [ yield caretChanged
if documentClosed.IsSome then
yield documentClosed.Value
yield propertyChanged
yield textChanged
yield focusLost ]
override x.KeyPress descriptor =
match descriptor.ModifierKeys with
| ModifierKeys.Control
| ModifierKeys.Command when descriptor.KeyChar = 'z' ->
// cmd-z uses the vim undo group
x.State.undoGroup |> Option.iter(fun d -> d.Dispose())
EditActions.Undo x.Editor
x.Editor.ClearSelection()
false
| ModifierKeys.Command when descriptor.KeyChar <> 'z' && descriptor.KeyChar <> 'r' -> false
| _ ->
let oldState = x.State
processingKey <- true
let newState, handledKeyPress = Vim.handleKeyPress x.State descriptor x.Editor config
processingKey <- false
match newState.statusMessage, newState.macro with
| Some m, None -> IdeApp.Workbench.StatusBar.ShowMessage m
| Some m, Some _ -> IdeApp.Workbench.StatusBar.ShowMessage (m + "recording")
| None, Some _ -> IdeApp.Workbench.StatusBar.ShowMessage "recording"
| _ -> IdeApp.Workbench.StatusBar.ShowReady()
x.State <- newState
match oldState.mode, newState.mode, config.insertModeEscapeKey with
| InsertMode, InsertMode, _ when descriptor.ModifierKeys = ModifierKeys.Control && descriptor.KeyChar = 'n' ->
false // Hack: Ctrl-N seems to be hardwired inside VS somehow to Emacs' line down
| InsertMode, InsertMode, None ->
base.KeyPress descriptor
| InsertMode, InsertMode, Some escapeCombo when descriptor.KeyChar.ToString() <> escapeCombo.insertModeEscapeKey1 ->
base.KeyPress descriptor
| VisualMode, _, _ -> false
| _ -> not handledKeyPress
[<CommandUpdateHandler ("MonoDevelop.Ide.Commands.EditCommands.Undo")>]
// We handle cmd-z ourselves to use the vim undo stack
member x.CanUndo(ci:CommandInfo) = ci.Enabled <- false
[<CommandUpdateHandler ("MonoDevelop.Ide.Commands.EditCommands.Rename")>]
member x.Rename(ci:CommandInfo) =
ci.Enabled <- true
// dirty hack - use the command update handler to switch to insert mode
// before the inline rename kicks in
if x.State.mode <> InsertMode then
x.State <- Vim.switchToInsertMode x.Editor x.State false
// Command handlers for all keys that possibly conflict with
// out of the box key binding schemes.
[<CommandHandler ("XSVim.HalfPageDown")>]
member x.HalfPageDown() = ctrl "d"
[<CommandHandler ("XSVim.PageDown")>]
member x.PageDown() = ctrl "f"
[<CommandHandler ("XSVim.PageUp")>]
member x.PageUp() = ctrl "b"
[<CommandHandler ("XSVim.FindFile")>]
member x.FindFile() = ctrl "p"
[<CommandHandler ("XSVim.DynamicAbbrev")>]
member x.DynamicAbbrev() = ctrl "n"
[<CommandHandler ("XSVim.NavigateBackwards")>]
member x.NavigateBackwards() = ctrl "o"
[<CommandHandler ("XSVim.NavigateForwards")>]
member x.NavigateForwards() = ctrl "i"
[<CommandHandler ("XSVim.IncrementNumber")>]
member x.IncrementNumber() = ctrl "x"
[<CommandHandler ("XSVim.DecrementNumber")>]
member x.DecrementNumber() = ctrl "a"
[<CommandHandler ("XSVim.Escape")>]
member x.Escape() = ctrl "c"
override x.Dispose() =
Vim.editorStates.Remove(fileName) |> ignore;
base.Dispose()
disposables |> List.iter(fun d -> d.Dispose())
================================================
FILE: XSVim/Classes.fs
================================================
namespace XSVim
open MonoDevelop.Core
open MonoDevelop.Core.Text
open MonoDevelop.Ide
open MonoDevelop.Ide.Editor
open MonoDevelop.Ide.Editor.Extension
type Marker(editor:TextEditor, name:string) =
let segment = TextSegment(editor.CaretOffset, 1)
// Create an invisible "find usages" marker. This is the only way to construct a TextSegmentMarker
// without reflection hacks. VS checks that the marker is a TextSegmentMarker
// https://github.com/mono/monodevelop/blob/61459958511d7ad4ea8debf4a59a77b1e98793fc/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs#L2744
let marker = TextMarkerFactory.CreateUsageMarker(editor, Usage(segment, FindInFiles.ReferenceUsageType.Unknown))
do
marker.IsVisible <- false
editor.AddMarker marker
member x.Name = name
member x.FileName = string editor.FileName.FullPath
member x.Offset = marker.Offset
member x.TextSegmentMarker = marker
member x.Remove() =
try
editor.RemoveMarker marker |> ignore
with
| ex -> LoggingService.LogError (string ex)
================================================
FILE: XSVim/ExMode.fs
================================================
namespace XSVim
open System
open System.Text.RegularExpressions
open Reflection
open MonoDevelop.Ide
open MonoDevelop.Ide.Commands
open MonoDevelop.Ide.Editor.Extension
module exMode =
let getFirstCharAndRest (s:string) = s.[0], s.[1..]
let processCommand command =
let firstChar, rest = getFirstCharAndRest command
match firstChar, rest with
| '/', _ -> [ runOnce (IncrementalSearch rest) Nothing ]
| '?', _ -> [ runOnce (IncrementalSearchBackwards rest) Nothing ]
| _ -> wait
let save() = IdeApp.Workbench.ActiveDocument.Save() |> Async.AwaitTask
let (|DeleteBetweenMarks|_|) input =
let matches = Regex.Matches(input, "'([a-z]),'([a-z])d", RegexOptions.Compiled)
if matches.Count = 1 then
let m = matches.[0]
Some (m.Groups.[1].Value, m.Groups.[2].Value)
else
None
let (|DeleteLines|_|) input =
let matches = Regex.Matches(input, "([\d]+),([\d]+)d", RegexOptions.Compiled)
if matches.Count = 1 then
let m = matches.[0]
Some (int m.Groups.[1].Value, int m.Groups.[2].Value)
else
None
let (|Substitute|_|) input =
let matches = Regex.Matches(input, "(.*)s/(.*)/(.*)", RegexOptions.Compiled)
if matches.Count = 1 then
let m = matches.[0]
match m.Groups.[1].Value with
| "%" -> Some { find = m.Groups.[2].Value; replace = m.Groups.[3].Value; scope = Document }
| "'<,'>" -> Some { find = m.Groups.[2].Value; replace = m.Groups.[3].Value; scope = Selection }
| _ -> None
else
None
let processKey (state:VimState) (key:KeyDescriptor) =
let setMessage message = { state with statusMessage = message }
let normalMode = { state with statusMessage = None; mode = NormalMode }
match key.SpecialKey with
| SpecialKey.BackSpace ->
let message =
match state.statusMessage with
| Some msg ->
let len = msg.Length
msg.[0..len-2]
| None -> ""
if message.Length > 0 then
setMessage (Some message), processCommand message
else
normalMode, resetKeys
| SpecialKey.Return ->
match state.statusMessage with
| Some message ->
let firstChar, rest = getFirstCharAndRest message
let getSearchAction() = match state.searchAction with | Some action -> action | _ -> Move
let restIsNumeric, number = Int32.TryParse rest
// really bad parser. TODO: try and use https://github.com/jaredpar/VsVim/blob/447d980da9aa6c761238e39df9d2b64424643de1/Src/VimCore/Interpreter_Parser.fs
match firstChar with
| '/' ->
{ state with statusMessage = None; mode = NormalMode; lastSearch = Some (Jump (ToSearch rest)) }
, [ runOnce (getSearchAction()) (Jump (ToSearch rest))]
| '?' ->
{ state with statusMessage = None; mode = NormalMode; lastSearch = Some (Jump (ToSearchBackwards rest)) }
, [ runOnce (getSearchAction()) (Jump (ToSearchBackwards rest))]
| ':' when restIsNumeric ->
{ state with statusMessage = None; mode = NormalMode; }
, [ runOnce Move (Jump (StartOfLineNumber number)) ]
| ':' ->
match rest with
| "q" ->
Window.closeTab()
normalMode, resetKeys
| "q!" ->
Window.forceCloseTab()
normalMode, resetKeys
| "w"
| "w!" ->
async {
do! save()
} |> Async.StartImmediate
normalMode, resetKeys
| "wa"
| "wa!" ->
async {
dispatchCommand FileCommands.SaveAll
} |> Async.StartImmediate
normalMode, resetKeys
| "qa" ->
async {
dispatchCommand FileCommands.CloseAllFiles
} |> Async.StartImmediate
normalMode, resetKeys
| "qa!" ->
IdeApp.Workbench.Documents
|> Seq.iter(fun doc ->
async {
do! doc.Close true |> Async.AwaitTask |> Async.Ignore
} |> Async.StartImmediate)
normalMode, resetKeys
| "wq" ->
async {
do! save()
dispatchCommand FileCommands.CloseFile
} |> Async.StartImmediate
normalMode, resetKeys
| "wq!" ->
async {
do! save()
Window.forceCloseTab()
} |> Async.StartImmediate
normalMode, resetKeys
| "vs"
| "vsp"
| "vsplit"
| "sp"
| "split" ->
let notebooks = Window.getNotebooks()
if notebooks.Length < 2 then
dispatchCommand "MonoDevelop.Ide.Commands.ViewCommands.SideBySideMode"
normalMode, resetKeys
| DeleteBetweenMarks (startMarker, endMarker) ->
let actions =
[ runOnce Move (Jump (ToMark (startMarker, MarkerJumpType.StartOfLine)))
runOnce DeleteWholeLines (Jump (ToMark (endMarker, MarkerJumpType.StartOfLine))) ]
normalMode, actions
| DeleteLines (startLine, endLine) ->
let actions =
[ runOnce Move (Jump (StartOfLineNumber startLine))
runOnce DeleteWholeLines (Jump (StartOfLineNumber endLine)) ]
normalMode, actions
| Substitute substition ->
match Substitute.substitute substition with
| true -> normalMode, resetKeys
| false -> state, resetKeys
| _ ->
{ state with statusMessage = sprintf "Could not parse :%s" rest |> Some; mode = NormalMode; }
, resetKeys
| _ -> normalMode, resetKeys
| _ -> normalMode, resetKeys
| _ ->
let message =
match state.statusMessage with
| Some msg -> sprintf "%s%c" msg key.KeyChar
| None -> string key.KeyChar
setMessage (Some message), processCommand message
================================================
FILE: XSVim/KeyBindingSchemeVim.xml
================================================
<?xml version="1.0" encoding="UTF-8" ?>
<scheme version="1.0">
<!-- Vim keybinding scheme containing all the keys that
conflict with keybindings in the Visual Studio keybinding scheme-->
<binding command="MonoDevelop.Ide.Commands.TextEditorCommands.DeleteRightChar" shortcut="" />
<binding command="XSVim.HalfPageDown" shortcut="Control+D" />
<binding command="MonoDevelop.Ide.Commands.TextEditorCommands.LineUp" shortcut="" />
<binding command="XSVim.FindFile" shortcut="Control+P" />
<binding command="MonoDevelop.Ide.Commands.TextEditorCommands.CharRight" shortcut="" />
<binding command="XSVim.PageDown" shortcut="Control+F" />
<binding command="MonoDevelop.Ide.Commands.TextEditorCommands.CharLeft" shortcut="" />
<binding command="XSVim.PageUp" shortcut="Control+B" />
<binding command="MonoDevelop.Ide.Commands.TextEditorCommands.LineDown" shortcut="" />
<binding command="XSVim.DynamicAbbrev" shortcut="Control+N" />
<binding command="MonoDevelop.Ide.Commands.TextEditorCommands.InsertNewLinePreserveCaretPosition" shortcut="" />
<binding command="XSVim.NavigateBackwards" shortcut="Control+O" />
<binding command="MonoDevelop.Ide.CodeFormatting.CodeFormattingCommands.FormatBuffer" shortcut="" />
<binding command="XSVim.NavigateForwards" shortcut="Control+I" />
<binding command="MonoDevelop.Ide.Commands.TextEditorCommands.LineStart" shortcut = "Meta+Left"/> <!-- removed ctrl-a -->
<binding command="XSVim.DecrementNumber" shortcut="Control+A" />
<binding command="XSVim.IncrementNumber" shortcut="Control+X" />
<binding command="XSVim.Escape" shortcut="Escape Control+C Control+[" />
</scheme>
================================================
FILE: XSVim/Mapping.fs
================================================
namespace XSVim
[<AutoOpen>]
module mapping =
let colemakToQwerty = function
| "f" -> "e"
| "p" -> "r"
| "g" -> "t"
| "j" -> "y"
| "l" -> "u"
| "u" -> "i"
| "y" -> "o"
| ";" -> "p"
| "r" -> "s"
| "s" -> "d"
| "t" -> "f"
| "d" -> "g"
| "n" -> "j"
| "e" -> "k"
| "i" -> "l"
| "o" -> ";"
| "k" -> "n"
| "F" -> "E"
| "P" -> "R"
| "G" -> "T"
| "J" -> "Y"
| "L" -> "U"
| "U" -> "I"
| "Y" -> "O"
| ":" -> "P"
| "R" -> "S"
| "S" -> "D"
| "T" -> "F"
| "D" -> "G"
| "N" -> "J"
| "E" -> "K"
| "I" -> "L"
| "O" -> ":"
| "K" -> "N"
| c -> c
let dvorakToQwerty = function
| "[" -> "-"
| "]" -> "="
| "'" -> "q"
| "," -> "w"
| "." -> "e"
| "p" -> "r"
| "y" -> "t"
| "f" -> "y"
| "g" -> "u"
| "c" -> "i"
| "r" -> "o"
| "l" -> "p"
| "/" -> "["
| "=" -> "]"
| "o" -> "s"
| "e" -> "d"
| "u" -> "f"
| "i" -> "g"
| "d" -> "h"
| "h" -> "j"
| "t" -> "k"
| "n" -> "l"
| "s" -> ";"
| "-" -> "'"
| ";" -> "z"
| "q" -> "x"
| "j" -> "c"
| "k" -> "v"
| "x" -> "b"
| "b" -> "n"
| "w" -> ","
| "v" -> "."
| "z" -> "/"
| "{" -> "_"
| "}" -> "+"
| "\"" -> "Q"
| "<" -> "W"
| ">" -> "E"
| "P" -> "R"
| "Y" -> "T"
| "F" -> "Y"
| "G" -> "U"
| "C" -> "I"
| "R" -> "O"
| "L" -> "P"
| "?" -> "{"
| "+" -> "}"
| "O" -> "S"
| "E" -> "D"
| "U" -> "F"
| "I" -> "G"
| "D" -> "H"
| "H" -> "J"
| "T" -> "K"
| "N" -> "L"
| "S" -> ":"
| "_" -> "\""
| ":" -> "Z"
| "Q" -> "X"
| "J" -> "C"
| "K" -> "V"
| "X" -> "B"
| "B" -> "N"
| "M" -> "M"
| "W" -> "<"
| "V" -> ">"
| "Z" -> "?"
| c -> c
let remap layout key =
match layout with
| Colemak -> colemakToQwerty key
| Dvorak -> dvorakToQwerty key
| _ -> key
================================================
FILE: XSVim/PadTreeViews.fs
================================================
namespace XSVim
open Gtk
open MonoDevelop.Ide.Gui.Components
module padTreeViews =
let select (tree:TreeView) path =
let column = tree.Columns.[0]
tree.Selection.SelectPath path
tree.SetCursor(path, column, false)
let getSelectedPath (tree:TreeView) =
tree.Selection.GetSelectedRows().[0]
let moveDown tree =
let path = getSelectedPath tree
path.Next()
select tree path
let moveUp tree =
let path = getSelectedPath tree
path.Prev() |> ignore
select tree path
let initialize (tree:PadTreeView) =
let processKey (key:KeyPressEventArgs) =
match key.Event.Key with
| Gdk.Key.Escape ->
dispatchCommand "MonoDevelop.Ide.Commands.ViewCommands.FocusCurrentDocument"
key.RetVal <- false
| Gdk.Key.j ->
moveDown tree
key.RetVal <- true
| Gdk.Key.k ->
moveUp tree
key.RetVal <- true
| _ -> ()
tree.KeyPressEvent.Add processKey
================================================
FILE: XSVim/Properties/AddinInfo.fs
================================================
namespace XSVim
open Mono.Addins
open MonoDevelop
[<assembly:Addin (
"XSVim",
Namespace = "XSVim",
Version = version
)>]
[<assembly:AddinName ("Vim")>]
[<assembly:AddinCategory ("IDE extensions")>]
[<assembly:AddinDescription ("Vim emulation layer for Xamarin Studio / Visual Studio for Mac.")>]
[<assembly:AddinUrl ("https://github.com/nosami/XSVim")>]
[<assembly:AddinAuthor ("jason")>]
[<assembly:AddinDependency ("::MonoDevelop.Core", "8.1")>]
[<assembly:AddinDependency ("::MonoDevelop.Ide", "8.1")>]
[<assembly:AddinDependency ("::MonoDevelop.SourceEditor2", "8.1")>]
()
================================================
FILE: XSVim/Properties/AssemblyInfo.fs
================================================
namespace XSVim
open System.Reflection
open System.Runtime.CompilerServices
[<AutoOpen>]
module AddinVersion =
[<Literal>]
let version = "0.65.12.81"
[<assembly: AssemblyTitle("XSVim")>]
[<assembly: AssemblyVersion(version)>]
//[<assembly: AssemblyDelaySign(false)>]
//[<assembly: AssemblyKeyFile("")>]
()
================================================
FILE: XSVim/Properties/Manifest.addin.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionModel>
<Runtime>
</Runtime>
<Extension path="/MonoDevelop/Ide/TextEditorExtensions">
<Class class="XSVim.XSVim" />
</Extension>
<Extension path = "/MonoDevelop/Ide/Commands">
<Category _name = "Vim" id = "Vim" >
<!-- Override undo to use the vim undo stack -->
<Command id = "MonoDevelop.Ide.Commands.EditCommands.Undo"
_label = "_Undo"
icon = "gtk-undo"
_description = "Undo (vim)"
shortcut = "Control|Z"
macShortcut = "Meta|Z" />
<!-- Commands that may conflict with built in keybinding schemes -->
<Command id="XSVim.HalfPageDown" _label="Half page down" _description="Move half a page down" shortcut="Control|D" />
<Command id="XSVim.FindFile" _label="Find file" shortcut="Control|P" />
<Command id="XSVim.PageDown" _label="Page down" shortcut="Control|F" />
<Command id="XSVim.PageUp" _label="Page up" shortcut="Control|B" />
<Command id="XSVim.DynamicAbbrev" _label="Complete from file" shortcut="Control|N" />
<Command id="XSVim.NavigateBackwards" _label="Navigate Backwards" shortcut="Control|O" />
<Command id="XSVim.NavigateForwards" _label="Navigate Forwards" shortcut="Control|I" />
<Command id="XSVim.NavigateForwards" _label="Navigate Forwards" shortcut="Control|I" />
<Command id="XSVim.IncrementNumber" _label="Increment Number" shortcut="Control|X" />
<Command id="XSVim.DecrementNumber" _label="Decrement Number" shortcut="Control|A" />
<Command id="XSVim.Escape" _label="Return to normal mode" shortcut="Escape Control|C Control|[" />
</Category>
</Extension>
<Extension path="/MonoDevelop/Ide/GlobalOptionsDialog/Other">
<Section id="VimSettings" _label="Vim Settings" class = "XSVim.SettingsPanel" icon="md-prefs-source" />
</Extension>
<Extension path = "/MonoDevelop/Ide/KeyBindingSchemes">
<Scheme id="XSVim" _name = "Visual Studio + Vim" resource="KeyBindingSchemeVim.xml" forMac="true" />
</Extension>
</ExtensionModel>
================================================
FILE: XSVim/Reflection.fs
================================================
namespace XSVim
open System
open System.Reflection
open Microsoft.FSharp.Reflection
module Reflection =
// Various flags that specify what members can be called
// NOTE: Remove 'BindingFlags.NonPublic' if you want a version
// that can call only public methods of classes
let staticFlags = BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Static
let instanceFlags = BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Instance
let private ctorFlags = instanceFlags
let inline asMethodBase(a:#MethodBase) = a :> MethodBase
// The operator takes just instance and a name. Depending on how it is used
// it either calls method (when 'R is function) or accesses a property
let (?) (o:obj) name : 'R =
// The return type is a function, which means that we want to invoke a method
if FSharpType.IsFunction(typeof<'R>) then
// Get arguments (from a tuple) and their types
let argType, _resType = FSharpType.GetFunctionElements(typeof<'R>)
// Construct an F# function as the result (and cast it to the
// expected function type specified by 'R)
FSharpValue.MakeFunction(typeof<'R>, fun args ->
// We treat elements of a tuple passed as argument as a list of arguments
// When the 'o' object is 'System.Type', we call static methods
let methods, instance, args =
let args =
// If argument is unit, we treat it as no arguments,
// if it is not a tuple, we create singleton array,
// otherwise we get all elements of the tuple
if argType = typeof<unit> then [| |]
elif not(FSharpType.IsTuple(argType)) then [| args |]
else FSharpValue.GetTupleFields(args)
// Static member call (on value of type System.Type)?
if (typeof<System.Type>).IsAssignableFrom(o.GetType()) then
let methods = (unbox<Type> o).GetMethods(staticFlags) |> Array.map asMethodBase
let ctors = (unbox<Type> o).GetConstructors(ctorFlags) |> Array.map asMethodBase
Array.concat [ methods; ctors ], null, args
else
o.GetType().GetMethods(instanceFlags) |> Array.map asMethodBase, o, args
// A simple overload resolution based on the name and the number of parameters only
// TODO: This doesn't correctly handle multiple overloads with same parameter count
let methods =
[ for m in methods do
if m.Name = name && m.GetParameters().Length = args.Length then yield m ]
// If we find suitable method or constructor to call, do it!
match methods with
| [] -> failwithf "No method '%s' with %d arguments found" name args.Length
| _::_::_ -> failwithf "Multiple methods '%s' with %d arguments found" name args.Length
| [:? ConstructorInfo as c] -> c.Invoke(args)
| [ m ] -> m.Invoke(instance, args) ) |> unbox<'R>
else
// The result type is not an F# function, so we're getting a property
// When the 'o' object is 'System.Type', we access static properties
let typ, flags, instance =
if (typeof<System.Type>).IsAssignableFrom(o.GetType())
then unbox o, staticFlags, null
else o.GetType(), instanceFlags, o
// Find a property that we can call and get the value
let prop = typ.GetProperty(name, flags)
if prop = null && instance = null then
// The syntax can be also used to access nested types of a type
let nested = typ.Assembly.GetType(typ.FullName + "+" + name)
// Return nested type if we found one
if nested = null then
failwithf "Property nested type '%s' not found in '%s'." name typ.Name
elif not ((typeof<'R>).IsAssignableFrom(typeof<System.Type>)) then
let rname = (typeof<'R>.Name)
failwithf "Cannot return nested type '%s' as a type '%s'." nested.Name rname
else nested |> box |> unbox<'R>
else
if prop = null then
// if we didn't find a nested type then search for a field
let field = typ.GetField(name, flags)
if not (isNull field) then
field.GetValue(o) |> unbox<'R>
else
failwithf "Property '%s' found, but doesn't have 'get' method." name
else
// Call property and return result if we found some
let meth = prop.GetGetMethod(true)
try meth.Invoke(instance, [| |]) |> unbox<'R>
with _ -> failwithf "Failed to get value of '%s' property (of type '%s')" name typ.Name
let (?<-) (this:obj) (property:string) (value:'Value) =
this.GetType().GetProperty(property).SetValue(this, value, null)
================================================
FILE: XSVim/SettingsPanel.fs
================================================
namespace XSVim
open Gtk
open MonoDevelop.Components
open MonoDevelop.Core
open MonoDevelop.Ide.Gui.Dialogs
type SettingsWidget() as this =
inherit Gtk.Box()
let labelMapping =
new Label("Insert mode escape binding",
TooltipText = "2 character combination to escape from insert mode (jj / hh / jk etc)")
let escapeMappingEntry = new Entry(2)
let hbox = new HBox(false, 6)
let vbox = new VBox(true, 6)
let labelMappingTimeout =
new Label("Insert mode mapping timeout (ms)",
TooltipText = "Timeout (in milliseconds) before key is registered as an insert mode key press")
let escapeMappingEntryTimeout = new Entry("1000")
let checkDisableAutoCompleteNormalMode =
new CheckButton("Disable intellisense in Normal mode. Use only if you are having issues with the intellisense drop down.",
TooltipText = "Enabling this switches the setting Intellisense on keystroke globally when in Normal mode.")
let labelKeyboardLayout =
new Label("Keyboard Layout",
TooltipText = "Select an keyboard layout for when not in insert mode")
let dropDownKeyboardLayout =
new ComboBox([| "Qwerty"; "Colemak"; "Dvorak" |]);
do
hbox.PackStart labelMapping
hbox.PackStart escapeMappingEntry
let hboxTimeout = new HBox(false, 6)
hboxTimeout.PackStart labelMappingTimeout
hboxTimeout.PackStart escapeMappingEntryTimeout
let hboxKeyboardLayout = new HBox(false, 6)
hboxKeyboardLayout.PackStart labelKeyboardLayout
hboxKeyboardLayout.PackStart dropDownKeyboardLayout
vbox.PackStart hbox
vbox.PackStart hboxTimeout
vbox.PackStart hboxKeyboardLayout
vbox.Add hbox
vbox.Add checkDisableAutoCompleteNormalMode
this.Add vbox
this.ShowAll()
member this.EscapeMappingEntry = escapeMappingEntry
member this.EscapeMappingEntryTimeout = escapeMappingEntryTimeout
member this.DisableAutoCompleteNormalMode = checkDisableAutoCompleteNormalMode
member this.KeyboardLayout = dropDownKeyboardLayout
type SettingsPanel() =
inherit OptionsPanel()
let widget = new SettingsWidget()
static let escapeMappingKey = "VimEscapeMapping"
static let escapeMappingKeyTimeout = "VimEscapeMappingTimeout"
static let disableAutoComplete = "VimDisableAutoComplete"
static let keyboardLayout = "VimAlternateMapping"
static member InsertModeEscapeMapping() =
PropertyService.Get(escapeMappingKey, "")
static member InsertModeEscapeMappingTimeout() =
PropertyService.Get(escapeMappingKeyTimeout, 1000)
static member AutoCompleteInNormalModeIsDisabled() =
PropertyService.Get(disableAutoComplete, false)
static member KeyboardLayout() =
PropertyService.Get(keyboardLayout, "Qwerty")
override x.Dispose() = widget.Dispose()
override x.CreatePanelWidget() =
widget.EscapeMappingEntry.Text <- SettingsPanel.InsertModeEscapeMapping()
widget.EscapeMappingEntryTimeout.Text <- SettingsPanel.InsertModeEscapeMappingTimeout() |> string
widget.DisableAutoCompleteNormalMode.Active <- SettingsPanel.AutoCompleteInNormalModeIsDisabled()
widget.KeyboardLayout.Active <- match SettingsPanel.KeyboardLayout() with
| "Colemak" -> 1
| "Dvorak" -> 2
| _ -> 0
widget.Show()
Control.op_Implicit widget
override x.ApplyChanges() =
match widget.EscapeMappingEntry.Text.Length with
| 2 | 0 ->
PropertyService.Set(escapeMappingKey, widget.EscapeMappingEntry.Text)
| _ ->
let md = new MessageDialog (null, DialogFlags.Modal ||| DialogFlags.DestroyWithParent, MessageType.Error, ButtonsType.Ok, "Mapping must be empty (not used) or 2 characters.")
md.Show()
PropertyService.Set(escapeMappingKeyTimeout, int widget.EscapeMappingEntryTimeout.Text)
PropertyService.Set(disableAutoComplete, widget.DisableAutoCompleteNormalMode.Active)
PropertyService.Set(keyboardLayout, widget.KeyboardLayout.ActiveText)
================================================
FILE: XSVim/SubstituteCommand.fs
================================================
namespace XSVim
open System
open MonoDevelop.Core
open MonoDevelop.Core.ProgressMonitoring
open MonoDevelop.Ide
open MonoDevelop.Ide.FindInFiles
type SubstituteScope = Selection | Document
type Substitution = { find: string; replace: string; scope: SubstituteScope }
module Substitute =
let substitute substitution =
let find = FindInFiles.FindReplace()
let options = FindInFiles.FilterOptions()
options.RegexSearch <- true
if not(find.ValidatePattern(options, substitution.find)) then
MessageService.ShowError (GettextCatalog.GetString ("Search pattern is invalid"));
false
elif not(find.ValidatePattern(options, substitution.replace)) then
MessageService.ShowError (GettextCatalog.GetString ("Replace pattern is invalid"));
false
else
use monitor =
match IdeApp.Workbench with
| null -> new ConsoleProgressMonitor() :> ProgressMonitor
| workbench ->
let monitor = workbench.ProgressMonitors.GetSearchProgressMonitor (true)
monitor.PathMode <- PathMode.Hidden
monitor :> ProgressMonitor
let scope =
match substitution.scope with
| Document -> DocumentScope() :> Scope
| Selection -> SelectionScope() :> Scope
find.FindAll(scope, monitor, substitution.find, substitution.replace, options, Threading.CancellationToken.None)
|> Seq.iter(fun res ->
match monitor with
| :? SearchProgressMonitor as mon ->
mon.ReportResult res
| _ -> printfn "%A" res)
true
================================================
FILE: XSVim/TreeViewPads.fs
================================================
namespace XSVim
open System
open Gtk
open MonoDevelop.Ide
open MonoDevelop.Ide.Gui.Components
open MonoDevelop.Ide.Gui.Pads
open Reflection
/// Set up TreeViewPads to accept hjkl keys
module treeViewPads =
let getTreeViewPads() =
match IdeApp.Workbench |> Option.ofObj with
| Some workbench ->
workbench.Pads
|> List.ofSeq
|> List.choose(fun pad ->
try
// fetching pad.Content can throw when there is an exception
// when initializing the pad
match pad.Content with
| :? TreeViewPad as pad -> Some pad
| _ -> None
with
| _ -> None)
| None -> List.empty
let select (tree:TreeView) path =
if tree.Selection.GetSelectedRows().Length > 0 then
tree.Selection.UnselectAll()
let column = tree.Columns.[0]
tree.Selection.SelectPath path
tree.SetCursor(path, column, false)
let getSelectedNode (pad:TreeViewPad) =
let (node:ITreeNavigator) = pad.TreeView?GetSelectedNode()
node
let getPath (tree:TreeView) =
tree.Selection.GetSelectedRows().[0]
let pathExists (store:TreeStore) path =
let iter : TreeIter ref = ref Unchecked.defaultof<_>
store.GetIter (iter, path)
let moveDown (tree:TreeView) (store:TreeStore) pad =
let selected = tree.Selection.GetSelectedRows()
let pathExists = pathExists store
if selected.Length > 0 then
let path = selected.[0]
path.Down()
let node = getSelectedNode pad
let res = pathExists path
if res && node.Expanded then
select tree path
else
let path = getPath tree
path.Next()
let res = pathExists path
if res then
select tree path
else
// parent, then sibling
let path = getPath tree
let _res = path.Up()
let res = pathExists path
if res then
path.Next()
let res = pathExists path
if res then
select tree path
let moveUp (tree:TreeView) (store:TreeStore) pad =
let selected = tree.Selection.GetSelectedRows()
let pathExists = pathExists store
if selected.Length > 0 then
let path = selected.[0]
let prev = path.Prev()
let res = pathExists path
if prev && res then
select tree path
let node = getSelectedNode pad
if node.Expanded then
// move to last child
let path = getPath tree
path.Down()
let rec moveNext lastPath =
path.Next()
let res = pathExists path
if res then
moveNext (path.Copy())
else
select tree lastPath
moveNext (path.Copy())
else
let path = getPath tree
if path.Depth > 1 then
let up = path.Up()
let res = pathExists path
if res && up then
select tree path
let mutable initialized = false
/// Set up TreeViewPads to accept hjkl keys
let initialize() =
if not initialized then
initialized <- true
let errorPad = IdeApp.Workbench.Pads.ErrorsPad.Content
let errorPadTree = errorPad?view
if errorPadTree <> null then
padTreeViews.initialize errorPadTree
for pad in getTreeViewPads() do
let (tree:TreeView) = pad.TreeView?Tree
let (store:TreeStore) = pad.TreeView?Store
let processKey (key:KeyPressEventArgs) =
match key.Event.Key with
| Gdk.Key.Escape ->
dispatchCommand "MonoDevelop.Ide.Commands.ViewCommands.FocusCurrentDocument"
key.RetVal <- false
| Gdk.Key.l ->
pad.TreeView?ExpandCurrentItem()
key.RetVal <- true
| Gdk.Key.h ->
pad.TreeView?CollapseCurrentItem()
key.RetVal <- true
| Gdk.Key.j ->
moveDown tree store pad
key.RetVal <- true
| Gdk.Key.k ->
moveUp tree store pad
key.RetVal <- true
| _ -> ()
tree.KeyPressEvent.Add processKey
================================================
FILE: XSVim/Types.fs
================================================
namespace XSVim
open System
open System.Threading
open System.Threading.Tasks
open MonoDevelop.Ide
open MonoDevelop.Ide.Editor
type BeforeOrAfter = Before | After | OverSelection
type CaretMode = Insert | Block
type Selection = {
linewise : bool
content: string
}
type InsertModeEscapeKeyCombo = {
insertModeEscapeKey1: string
insertModeEscapeKey2: string
insertModeEscapeTimeout: int
}
type KeyboardLayout =
| Qwerty
| Colemak
| Dvorak
type Config = {
insertModeEscapeKey: InsertModeEscapeKeyCombo option
keyboardLayout: KeyboardLayout
} with
static member Default = { insertModeEscapeKey = None; keyboardLayout = Qwerty }
type Register =
| Register of char
| EmptyRegister
type VimMode =
| NormalMode
| VisualMode
| VisualBlockMode
| VisualLineMode
| InsertMode
| ReplaceMode
| ExMode of string // initial char typed to get to command line
type MoveRightBehaviour = StopAtEndOfLine | MoveToNextLineAtEnd | IncludeDelimiter
type MarkerJumpType = Offset | StartOfLine
type Jump =
| StartOfLineNumber of int
| StartOfDocument
| ToMark of string * MarkerJumpType
| ToSearch of string
| ToSearchBackwards of string
| SearchAgain
| SearchAgainBackwards
| HalfPageUp
| HalfPageDown
| PageUp
| PageDown
| LastLine
| FirstVisibleLine
| MiddleVisibleLine
| LastVisibleLine
| ParagraphForwards
| ParagraphBackwards
type VimKey =
| Esc
| Ret
| Left
| Down
| Up
| Right
| Backspace
| Delete
| Control of char
| Super of char
| Key of char
| EscapeKey of char // The key is part of an insert escape mapping
override x.ToString() =
match x with
| Esc -> "<esc>"
| Backspace -> "<bs>"
| Ret -> "<ret>"
| Delete -> "<del>"
| Control k -> sprintf "<C-%c>" k
| Super k -> sprintf "<D-%c>" k
| Down -> "<down>"
| Up -> "<up>"
| Left -> "<left>"
| Right -> "<right>"
| EscapeKey c -> string c
| Key c -> string c
type Repeat = int
type Offset = int
type TextObject =
| Jump of Jump
| Character of Repeat
| AWord
| InnerWord
| AWORD
| InnerWORD
| ASentence
| InnerSentence
| AParagraph
| InnerParagraph
| ABlock of string * string
| InnerBlock of string * string
| AQuotedBlock of char
| InnerQuotedBlock of char
| WholeLine
| WholeLineIncludingDelimiter
| ATag
| InnerTag
// motions
| Up
| Down
| Left
| Right of MoveRightBehaviour
| FirstNonWhitespace
| StartOfLine
| EndOfLine
| EndOfLineIncludingDelimiter
| ToCharInclusive of string
| ToCharInclusiveBackwards of string
| ToCharExclusive of string
| ToCharExclusiveBackwards of string
| WordForwards
| WORDForwards
| WordBackwards
| WORDBackwards
| ForwardToEndOfWord
| ForwardToEndOfWORD
| BackwardToEndOfWord
| BackwardToEndOfWORD
| Nothing
| CurrentLocation
| SelectedText
| SelectionStart
| MatchingBrace
| PrevUnmatchedBrace
| NextUnmatchedBrace
| PrevUnmatchedParen
| NextUnmatchedParen
| Offset of Offset
| Range of Offset * Offset
type CommandType =
| Move
| Visual
| Yank of Register
| Put of BeforeOrAfter
| Delete
| Substitute
| DeleteWholeLines
| DeleteLeft
| BlockInsert of BeforeOrAfter
| Change
| SwitchMode of VimMode
| Undo
| Redo
| JoinLines
| Dispatch of obj
| InsertLine of BeforeOrAfter
| ReplaceChar of string
| ResetKeys
| DoNothing
| Star of BeforeOrAfter
| ToggleCase
| InsertChar of VimKey
| IncrementNumber
| DecrementNumber
| SetMark of string
| IncrementalSearch of string
| IncrementalSearchBackwards of string
| SetSearchAction of CommandType
| MacroStart of char
| MacroEnd
| ReplayMacro of char
| NextTab
| PreviousTab
| Func of (unit -> unit)
| EditorFunc of (TextEditor -> unit)
| GotoPad of string
| DelayedFunc of (TextEditor -> unit) * int
| CancelFunc
| ChangeState of VimState
| Indent
| UnIndent
| EqualIndent
| SelectionOtherEnd
and VimAction = {
repeat: int option
commandType: CommandType
textObject: TextObject
}
and Macro = Macro of char
and VimSelection = { start: int; finish: int; mode: VimMode }
and VimState = {
keys: VimKey list
mode: VimMode
visualStartOffset: int
lastSelection: VimSelection option
findCharCommand: VimAction option // f,F,t or T command to be repeated with ;
lastAction: VimAction list // used by . command to repeat the last action
desiredColumn: int option
undoGroup: IDisposable option
statusMessage: string option
searchAction: CommandType option // Delete, Change, Visual, Yank or Move when / or ? is pressed
lastSearch: TextObject option // Last term searched for with / or ?
macro: Macro option
insertModeCancellationTokenSource: CancellationTokenSource option
} with
static member Default =
{ keys=[]
mode=NormalMode
visualStartOffset=0
lastSelection=None
findCharCommand=None
lastAction=[]
desiredColumn=None
undoGroup=None
statusMessage=None
lastSearch=None
searchAction=None
macro=None
insertModeCancellationTokenSource=None }
// shim for the build server which runs Mono 4.6.1
module Option =
let inline defaultValue value =
function
| Some v -> v
| None -> value
[<AutoOpen>]
module commandHelpers =
let getCommand repeat commandType textObject =
{ repeat=repeat; commandType=commandType; textObject=textObject }
let runOnce = getCommand (Some 1)
let typeChar c = runOnce (InsertChar c) Nothing
let wait = [ getCommand None DoNothing Nothing ]
let switchMode mode = runOnce (SwitchMode mode) Nothing
let dispatch command = runOnce (Dispatch command) Nothing
let resetKeys = [ runOnce ResetKeys Nothing ]
let func f = runOnce (Func f) Nothing
let delayedFunc f ms = runOnce (DelayedFunc (f, ms)) Nothing
let dispatchCommand command = IdeApp.CommandService.DispatchCommand command |> ignore
let gotoPad padId = runOnce (GotoPad padId) Nothing
================================================
FILE: XSVim/WindowManagement.fs
================================================
namespace XSVim
open MonoDevelop.Core
open MonoDevelop.Ide
open MonoDevelop.Ide.Commands
open Reflection
module Window =
type Notebook = {
isActive: bool
activeTab: int
tabs: string list
}
let dispatch command = IdeApp.CommandService.DispatchCommand command |> ignore
let openDocument fileName =
let (project:MonoDevelop.Projects.Project) = Unchecked.defaultof<_>
IdeApp.Workbench.OpenDocument(fileName |> FilePath, project).Wait(System.Threading.CancellationToken.None)
let switchToNotebook notebook =
openDocument notebook.tabs.[notebook.activeTab]
let forceClose() = IdeApp.Workbench.ActiveDocument.Close true |> Async.AwaitTask |> Async.Ignore
let getNotebooks() =
let (dockNotebookContainer: obj seq) = IdeApp.Workbench?RootWindow?TabControl?Container?GetNotebooks()
let getFiles notebook =
let tabs = notebook?Tabs
let tabs' = tabs :> seq<obj>
tabs' |> Seq.map(fun tab -> tab?Tooltip) |> List.ofSeq
dockNotebookContainer
|> Seq.map(fun notebook ->
let firstChild = notebook?Children |> Array.tryHead
let isActive =
firstChild
|> Option.map(fun tabstrip -> tabstrip?IsActiveNotebook)
|> Option.defaultValue false
{ isActive=isActive; activeTab=notebook?CurrentTabIndex; tabs=getFiles notebook } )
|> List.ofSeq
let tryFindActiveNoteBook() =
getNotebooks()
|> List.tryFind(fun notebook -> notebook.isActive)
let tryActiveInactiveNoteBooks() =
let notebooks = getNotebooks()
notebooks |> List.tryFind(fun notebook -> notebook.isActive),
notebooks |> List.tryFind(fun notebook -> not notebook.isActive)
let nextTab() =
tryFindActiveNoteBook() |> Option.iter(fun notebook ->
let tabCount = notebook.tabs.Length
let currentTabIndex = notebook.activeTab
let index =
if currentTabIndex < (tabCount-1) then
currentTabIndex + 1
else
0
openDocument notebook.tabs.[index])
let previousTab() =
tryFindActiveNoteBook() |> Option.iter(fun notebook ->
let tabCount = notebook.tabs.Length
let currentTabIndex = notebook.activeTab
let index =
if currentTabIndex > 0 then
currentTabIndex - 1
else
tabCount - 1
openDocument notebook.tabs.[index])
let switchWindow() =
let notebook =
getNotebooks()
|> List.tryFind(fun notebook -> not notebook.isActive)
notebook |> Option.iter switchToNotebook
let leftWindow() =
let notebooks = getNotebooks()
if notebooks.Length = 2 && notebooks.[1].isActive then
switchToNotebook notebooks.[0]
let rightWindow() =
let notebooks = getNotebooks()
if notebooks.Length = 2 && notebooks.[0].isActive then
switchToNotebook notebooks.[1]
let private closeTabWithForce force =
let closeFunc() =
match force with
| true -> forceClose() |> Async.RunSynchronously
| false -> dispatch FileCommands.CloseFile
match tryActiveInactiveNoteBooks() with
| Some active, Some inactive when active.tabs.Length = 1 ->
closeFunc()
switchToNotebook inactive
| Some active, _ when active.activeTab > 0 ->
closeFunc()
openDocument active.tabs.[active.activeTab-1]
| Some active, _ when active.activeTab = 0 && active.tabs.Length > 1 ->
closeFunc()
openDocument active.tabs.[1]
| _ -> closeFunc()
let closeTab() = closeTabWithForce false
let forceCloseTab() = closeTabWithForce true
let gotoPad padId =
IdeApp.Workbench.Pads
|> Seq.tryFind(fun p -> p.Id = padId)
|> Option.iter(fun pad -> pad.BringToFront(true))
================================================
FILE: XSVim/XSVim.fs
================================================
namespace XSVim
open System
open System.Collections.Generic
open System.Text.RegularExpressions
open System.Threading
open MonoDevelop.Core
open MonoDevelop.Core.Text
open MonoDevelop.Ide
open MonoDevelop.Ide.Commands
open MonoDevelop.Ide.Editor
open MonoDevelop.Ide.Editor.Extension
open Reflection
[<AutoOpen>]
module VimHelpers =
let commandManager = IdeApp.CommandService |> Option.ofObj
let dispatchCommand command =
commandManager
|> Option.iter(fun c -> c.DispatchCommand command |> ignore)
let closingBraces = [')'; '}'; ']'] |> set
let openingbraces = ['('; '{'; '[' ] |> set
let markDict = Dictionary<string, Marker>()
let findNextBraceForwardsOnLine (editor:TextEditor) (line:IDocumentLine) =
if closingBraces.Contains(editor.[editor.CaretOffset]) then
Some editor.CaretOffset
else
seq { editor.CaretOffset .. line.EndOffset }
|> Seq.tryFind(fun index -> openingbraces.Contains(editor.[index]))
let findCharForwardsOnLine (editor:TextEditor) (line:IDocumentLine) startOffset character =
let ch = char character
seq { startOffset+1 .. line.EndOffset }
|> Seq.tryFind(fun index -> editor.[index] = ch)
let findCharBackwardsOnLine startOffset (editor:TextEditor) (line:IDocumentLine) matcher =
seq { startOffset .. -1 .. line.Offset }
|> Seq.tryFind (fun i -> matcher editor.[i])
let findCharBackwardsOnLineExclusive (editor:TextEditor) startOffset = findCharBackwardsOnLine (startOffset-1) editor
let findCharBackwardsOnLineInclusive (editor:TextEditor) = findCharBackwardsOnLine editor.CaretOffset editor
let findStringCharBackwardsOnLine (editor:TextEditor) (line:IDocumentLine) startOffset character =
let ch = char character
let f = findCharBackwardsOnLineExclusive editor startOffset
f line ((=) ch)
let findCharForwards (editor:TextEditor) character =
let ch = char character
seq { editor.CaretOffset+1 .. editor.Length-1 }
|> Seq.tryFind(fun index -> editor.[index] = ch)
let findCharBackwards (editor:TextEditor) character =
let ch = char character
seq { editor.CaretOffset .. -1 .. 0 }
|> Seq.tryFind(fun index -> editor.[index] = ch)
let findUnmatchedBlockDelimiter(editor:TextEditor) pos blockStartDelimiter blockEndDelimiter op =
let blockStartShift = (blockStartDelimiter |> String.length) - 1
let blockEndShift = (blockEndDelimiter |> String.length) - 1
let text = editor.Text
let rec findRec startCount endCount at =
if (text.Length <= at || at < 0) then None else
let next = op at 1
let st = try text.[at..(at+blockStartShift)] with | _ -> ""
let en = try text.[at..(at+blockEndShift)] with | _ -> ""
match st, en with
| _, e when e = blockEndDelimiter && startCount < (endCount+1) -> Some at
| _, e when e = blockEndDelimiter -> findRec startCount (endCount+1) next
| s, _ when s = blockStartDelimiter -> findRec (startCount+1) endCount next
| _,_ -> findRec startCount endCount next
findRec 0 0 pos
let rec findUnmatchedBlockStartDelimiter(editor:TextEditor) pos blockStartDelimiter blockEndDelimiter =
findUnmatchedBlockDelimiter editor pos blockEndDelimiter blockStartDelimiter (-)
let findUnmatchedBlockEndDelimiter(editor:TextEditor) pos blockStartDelimiter blockEndDelimiter =
findUnmatchedBlockDelimiter editor pos blockStartDelimiter blockEndDelimiter (+)
let isWordChar c = Char.IsLetterOrDigit c || c = '-' || c = '_'
let isWORDChar c = not (Char.IsWhiteSpace c)
let isNonBlankButNotWordChar c = isWORDChar c && not (isWordChar c)
let isEOLChar c = c = '\r' || c = '\n'
let isSpaceOrTab c = c = ' ' || c = '\t'
let (|WhiteSpace|_|) c =
if Char.IsWhiteSpace c then Some WhiteSpace else None
let (|IsWordChar|_|) c =
if isWordChar c then Some IsWordChar else None
let (|EOLChar|_|) c =
if isEOLChar c then Some EOLChar else None
let findWordForwards (editor:TextEditor) commandType fWordChar =
let findFromNonLetterChar index =
match editor.[index], commandType with
| EOLChar, Delete -> Some index
| WhiteSpace, Move
| WhiteSpace, Delete ->
seq { index+1 .. editor.Length-1 }
|> Seq.tryFind(fun index -> not (isSpaceOrTab editor.[index]))
|> Option.bind(fun newIndex ->
let findFirstWordOnLine startOffset =
match editor.[startOffset] with
| WhiteSpace ->
seq { startOffset .. editor.Length-1 }
|> Seq.tryFind(fun index -> let c = editor.[index]
fWordChar c || c = '\n')
| _ -> Some newIndex
match editor.[newIndex] with
| '\r' -> findFirstWordOnLine (newIndex + 2)
| '\n' -> findFirstWordOnLine (newIndex + 1)
| _ -> Some newIndex)
| _ -> Some index
if not (fWordChar editor.[editor.CaretOffset]) && fWordChar editor.[editor.CaretOffset + 1] then
editor.CaretOffset + 1 |> Some
else
seq { editor.CaretOffset+1 .. editor.Length-1 }
|> Seq.tryFind(fun index -> index = editor.Length || not (fWordChar editor.[index]))
|> Option.bind findFromNonLetterChar
let findWordBackwards (editor:TextEditor) commandType fWordChar =
let findFromNonLetterChar index =
match editor.[index], commandType with
| WhiteSpace, Move ->
seq { index .. -1 .. 0 }
|> Seq.tryFind(fun index -> not (Char.IsWhiteSpace editor.[index]))
| _ -> Some index
if not (fWordChar editor.[editor.CaretOffset]) && fWordChar editor.[editor.CaretOffset - 1] then
editor.CaretOffset - 1 |> Some
else
seq { editor.CaretOffset .. -1 .. 0 }
|> Seq.tryFind(fun index -> index = editor.Length+1 || not (fWordChar editor.[index]))
|> Option.bind findFromNonLetterChar
let findPrevWord (editor:TextEditor) fWordChar =
let result = Math.Max(editor.CaretOffset - 1, 0)
let previous = fWordChar editor.[result]
let rec findStartBackwards index previous isInIdentifier =
let ch = editor.[index]
let current = fWordChar ch
match previous with
| _ when index = 0 -> 0
| false when isInIdentifier -> index + 2
| _ -> findStartBackwards (index - 1) current previous
findStartBackwards result previous previous
let findCurrentWordEnd (editor:TextEditor) fWordChar =
seq { editor.CaretOffset .. editor.Length - 2 }
|> Seq.tryFind(fun index -> not (fWordChar editor.[index+1]))
|> Option.defaultValue (editor.Length-1)
let findWordEnd (editor:TextEditor) fWordChar =
let currentWordEnd = findCurrentWordEnd editor fWordChar
if editor.CaretOffset = currentWordEnd then
let nextWordOffset = findWordForwards editor Move fWordChar
match nextWordOffset with
| Some offset ->
let f =
if fWordChar editor.[offset] then fWordChar else isNonBlankButNotWordChar
editor.CaretOffset <- offset
findCurrentWordEnd editor f
| None -> editor.Length
else
currentWordEnd
let findCurrentWordStart (editor:TextEditor) fWordChar =
seq { editor.CaretOffset .. -1 .. 1 }
|> Seq.tryFind(fun index -> not (fWordChar editor.[index-1]))
|> Option.defaultValue 0
let paragraphBackwards (editor:TextEditor) =
seq { editor.CaretLine-1 .. -1 .. 1 }
|> Seq.tryFind(fun lineNr -> let line = editor.GetLineText lineNr
String.IsNullOrWhiteSpace line)
|> Option.bind(fun lineNr -> Some (editor.GetLine lineNr).Offset)
let paragraphForwards (editor:TextEditor) =
seq { editor.CaretLine+1 .. editor.LineCount }
|> Seq.tryFind(fun lineNr -> let line = editor.GetLineText lineNr
String.IsNullOrWhiteSpace line)
|> Option.bind(fun lineNr -> Some (editor.GetLine lineNr).Offset)
let getVisibleLines editor =
let (lines:IDocumentLine seq) = editor?VisibleLines
lines
let getSortedVisibleLines editor =
getVisibleLines editor |> Seq.sortBy(fun l -> l.LineNumber) /// the lines come back in random order
let getVisibleLineCount editor =
getVisibleLines editor |> Seq.length
let getComparisonType (search:string) =
match search with
| s when s.ToLower() = s -> StringComparison.CurrentCultureIgnoreCase
| _ -> StringComparison.CurrentCulture
let findNextSearchOffset (editor:TextEditor) (search:string) (startOffset:int) =
let comparison = getComparisonType search
let index = editor.Text.IndexOf(search, startOffset, comparison)
if index > -1 then
Some index
else
let index = editor.Text.IndexOf(search, comparison)
if index > -1 then Some index else None
let findNextSearchOffsetBackwards (editor:TextEditor) (search:string) (startOffset:int) =
let comparison = getComparisonType search
let index = editor.Text.LastIndexOf(search, startOffset, comparison)
if index > -1 then
Some index
else
let index = editor.Text.LastIndexOf(search, editor.Length, comparison)
if index > -1 then Some index else None
let wordAtCaret (editor:TextEditor) =
if isWordChar (editor.[editor.CaretOffset]) then
let start = findCurrentWordStart editor isWordChar
let finish = (findCurrentWordEnd editor isWordChar)
let word = editor.GetTextAt(start, finish - start + 1)
Some word
else
None
let eofOnLine (line: IDocumentLine) = line.DelimiterLength = 0
let findQuoteTriplet (editor:TextEditor) line quoteChar =
let firstBackwards = findCharBackwardsOnLine editor.CaretOffset editor line ((=) quoteChar)
let firstForwards = findCharForwardsOnLine editor line editor.CaretOffset (string quoteChar)
let secondForwards =
match firstForwards with
| Some offset when offset + 1 < editor.Length ->
findCharForwardsOnLine editor line offset (string quoteChar)
| _ -> None
firstBackwards, firstForwards, secondForwards
let inferDelimiter (editor:TextEditor) =
editor.GetLines()
|> Seq.tryFind(fun line -> line.DelimiterLength > 0)
|> Option.map(fun line -> match line.UnicodeNewline with
| UnicodeNewline.LF -> "\n"
| UnicodeNewline.CRLF -> "\r\n"
| UnicodeNewline.CR -> "\r"
| _ -> editor.Options.DefaultEolMarker)
|> Option.defaultValue editor.Options.DefaultEolMarker
/// Get the range of the trailing or leading whitespace
/// around a word when aw or aW is used
let getAroundWordRange (editor:TextEditor) wordStart wordEnd =
let hasLeadingWhiteSpace =
wordStart > 1 && Char.IsWhiteSpace editor.[wordStart-1]
let hasTrailingWhiteSpace =
wordEnd < editor.Length && Char.IsWhiteSpace editor.[wordEnd]
let line = editor.GetLine editor.CaretLine
match hasTrailingWhiteSpace, hasLeadingWhiteSpace with
| true, _ ->
let finish =
seq { wordEnd .. line.EndOffset - 2 }
|> Seq.tryFind(fun index -> not (Char.IsWhiteSpace editor.[index+1]))
|> Option.defaultValue (line.EndOffset-1)
wordStart, finish + 1
| false, true ->
let start =
seq { wordStart .. -1 .. line.Offset }
|> Seq.tryFind(fun index -> not (Char.IsWhiteSpace editor.[index-1]))
|> Option.defaultValue line.Offset
start, wordEnd
| _ -> wordStart, wordEnd
let getWordRange (editor:TextEditor) fWordChar =
let wordStart = findCurrentWordStart editor fWordChar
let wordEnd = findCurrentWordEnd editor fWordChar
wordStart, wordEnd + 1
let getWhitespaceRange (editor:TextEditor) fWordChar =
let prevWordEnd = findWordBackwards editor Move fWordChar |> Option.defaultValue editor.CaretOffset
let nextWordEnd = findWordEnd editor fWordChar
prevWordEnd + 1, nextWordEnd + 1
let rec findEnclosingTag(editor:TextEditor) pos =
let search = seq { pos .. -1 .. 0 } |> Seq.tryFind(fun index -> editor.[index] = '<')
match search with
| Some startTagStart ->
let m = Regex.Match(editor.GetTextBetween(startTagStart, editor.Length), "<([\w|:|\.]+).*?>", RegexOptions.Singleline)
if m.Success then
let tagName = m.Groups.[1].Value;
let endTag = "</" + tagName + ">"
let startTagEnd = startTagStart + m.Length - 1
match findUnmatchedBlockEndDelimiter editor startTagEnd ("<" + tagName) endTag with
| Some endTagStart -> Some (startTagStart, startTagEnd, endTagStart, endTagStart + endTag.Length)
| None -> findEnclosingTag editor (startTagStart - 1)
else
None
| None -> None
let rec getRange (config:Config) (vimState:VimState) (editor:TextEditor) (command:VimAction) =
let line = editor.GetLine editor.CaretLine
let noOp = (editor.CaretOffset, editor.CaretOffset)
match command.textObject with
| Right behaviour ->
let line = editor.GetLine editor.CaretLine
let endOffset =
match behaviour with
| StopAtEndOfLine when editor.CaretColumn >= line.Length -> editor.CaretOffset
| MoveToNextLineAtEnd when editor.[editor.CaretOffset+1] = '\r' -> editor.CaretOffset + 3
| MoveToNextLineAtEnd when editor.[editor.CaretOffset+1] = '\n' -> editor.CaretOffset + 2
| IncludeDelimiter ->
let line = editor.GetLine editor.CaretLine
if line.Length > 0 && editor.CaretColumn <= line.LengthIncludingDelimiter then
editor.CaretOffset + 1
else
editor.CaretOffset
| _ -> editor.CaretOffset + 1
editor.CaretOffset, endOffset
| Left ->
editor.CaretOffset,
if editor.CaretColumn > DocumentLocation.MinColumn && editor.[editor.CaretOffset-1] <> '\n' then
editor.CaretOffset - 1
else
editor.CaretOffset
| Up ->
editor.CaretOffset,
if editor.CaretLine > DocumentLocation.MinLine then
let column =
let line = editor.GetLine (editor.CaretLine - 1)
let desiredColumn = vimState.desiredColumn |> Option.defaultValue editor.CaretColumn
if desiredColumn <= line.Length then
desiredColumn
else
line.Length
editor.LocationToOffset (new DocumentLocation(editor.CaretLine - 1, column))
else
editor.CaretOffset
| Down ->
editor.CaretOffset,
if editor.CaretLine < editor.LineCount then
let column =
let line = editor.GetLine (editor.CaretLine + 1)
let desiredColumn = vimState.desiredColumn |> Option.defaultValue editor.CaretColumn
if desiredColumn <= line.Length then
desiredColumn
else
line.Length
editor.LocationToOffset (new DocumentLocation(editor.CaretLine + 1, column))
else
editor.CaretOffset
| EndOfLine -> editor.CaretOffset, line.EndOffset-1
| Character repeat ->
let line = editor.GetLine editor.CaretLine
let endOffset = min line.EndOffset (editor.CaretOffset + repeat)
editor.CaretOffset, endOffset
| EndOfLineIncludingDelimiter ->
editor.CaretOffset,
if eofOnLine line then
line.EndOffsetIncludingDelimiter
else
line.EndOffsetIncludingDelimiter-1
| StartOfLine -> editor.CaretOffset, line.Offset
| Jump (StartOfLineNumber lineNumber) ->
let line = editor.GetLine lineNumber
editor.CaretOffset, line.Offset + editor.GetLineIndent(lineNumber).Length
| Jump StartOfDocument -> editor.CaretOffset, 0
| FirstNonWhitespace -> editor.CaretOffset, line.Offset + editor.GetLineIndent(editor.CaretLine).Length
| WholeLine ->
line.Offset, line.EndOffset
| WholeLineIncludingDelimiter ->
if eofOnLine line && line.LineNumber <> 1 then
let delimiter = inferDelimiter editor
line.Offset-delimiter.Length, line.EndOffsetIncludingDelimiter
else
line.Offset, line.EndOffsetIncludingDelimiter
| Jump LastLine ->
let lastLine = editor.GetLine editor.LineCount
editor.CaretOffset, lastLine.Offset
| Jump FirstVisibleLine ->
let firstLine = getSortedVisibleLines editor |> Seq.head
editor.CaretOffset, firstLine.Offset
| Jump MiddleVisibleLine ->
let firstLine = getSortedVisibleLines editor |> Seq.head
let lastLine = getSortedVisibleLines editor |> Seq.last
let middleLineNumber = (lastLine.LineNumber - firstLine.LineNumber) / 2 + firstLine.LineNumber
let middleLine = editor.GetLine middleLineNumber
editor.CaretOffset, middleLine.Offset
| Jump LastVisibleLine ->
let lastLine = getSortedVisibleLines editor |> Seq.last
editor.CaretOffset, lastLine.Offset
| ToCharInclusiveBackwards c ->
match findStringCharBackwardsOnLine editor line (editor.CaretOffset-1) c with
| Some index -> editor.CaretOffset, index
| None -> editor.CaretOffset, editor.CaretOffset
| ToCharExclusiveBackwards c ->
let startOffset =
match config.keyboardLayout, vimState.keys with
| Qwerty, Key ';' :: _ when c = editor.[editor.CaretOffset-1].ToString() ->
editor.CaretOffset-1
| Colemak, Key 'o' :: _ when c = editor.[editor.CaretOffset-1].ToString() ->
editor.CaretOffset-1
| Dvorak, Key 's' :: _ when c = editor.[editor.CaretOffset-1].ToString() ->
editor.CaretOffset-1
| _, _ -> editor.CaretOffset
match findStringCharBackwardsOnLine editor line startOffset c with
| Some index -> editor.CaretOffset, index+1
| None -> editor.CaretOffset, editor.CaretOffset
| ToCharInclusive c ->
match findCharForwardsOnLine editor line editor.CaretOffset c with
| Some index -> editor.CaretOffset, index
| None -> editor.CaretOffset, editor.CaretOffset
| ToCharExclusive c ->
let startOffset =
match config.keyboardLayout, vimState.keys with
| Qwerty, Key ';' :: _ when c = editor.[editor.CaretOffset+1].ToString() ->
editor.CaretOffset+1
| Colemak, Key 'o' :: _ when c = editor.[editor.CaretOffset+1].ToString() ->
editor.CaretOffset+1
| Dvorak, Key 's' :: _ when c = editor.[editor.CaretOffset+1].ToString() ->
editor.CaretOffset+1
| _, _ -> editor.CaretOffset
match findCharForwardsOnLine editor line startOffset c with
| Some index -> editor.CaretOffset, index-1
| None -> editor.CaretOffset, editor.CaretOffset
| InnerBlock (startChar, endChar) ->
let opening = findUnmatchedBlockStartDelimiter editor editor.CaretOffset startChar endChar
let closing = findUnmatchedBlockEndDelimiter editor editor.CaretOffset startChar endChar
match opening, closing with
| Some start, Some finish -> start+1, finish
| _, _ -> editor.CaretOffset, editor.CaretOffset
| ABlock (startChar, endChar) ->
let opening = findUnmatchedBlockStartDelimiter editor editor.CaretOffset startChar endChar
let closing = findUnmatchedBlockEndDelimiter editor editor.CaretOffset startChar endChar
match opening, closing with
| Some start, Some finish when finish < editor.Length -> start, finish+1
| _, _ -> editor.CaretOffset, editor.CaretOffset
| InnerQuotedBlock c ->
match findQuoteTriplet editor line c with
| Some start, Some finish, _ -> start + 1, finish // we're inside quotes
| None, Some start, Some finish -> start + 1, finish // there's quoted text to the right
| _, _,_ -> editor.CaretOffset, editor.CaretOffset
| AQuotedBlock c ->
match findQuoteTriplet editor line c with
| Some start, Some finish, _ -> start, finish + 1 // we're inside quotes
| None, Some start, Some finish -> start, finish + 1 // there's quoted text to the right
| _, _,_ -> editor.CaretOffset, editor.CaretOffset
| WordForwards ->
match findWordForwards editor command.commandType isWordChar with
| Some index -> editor.CaretOffset, index
| None -> editor.CaretOffset, editor.Length
| WORDForwards ->
match findWordForwards editor command.commandType isWORDChar with
| Some index -> editor.CaretOffset, index
| None -> editor.CaretOffset, editor.CaretOffset
| WordBackwards -> editor.CaretOffset, findPrevWord editor isWordChar
| WORDBackwards -> editor.CaretOffset, findPrevWord editor isWORDChar
| BackwardToEndOfWord ->
match findWordBackwards editor command.commandType isWordChar with
| Some index -> editor.CaretOffset, index
| None -> editor.CaretOffset, 0
| BackwardToEndOfWORD ->
match findWordBackwards editor command.commandType isWORDChar with
| Some index -> editor.CaretOffset, index
| None -> editor.CaretOffset, 0
| Jump ParagraphBackwards ->
match paragraphBackwards editor with
| Some index -> editor.CaretOffset, index
| None -> editor.CaretOffset, 0
| Jump ParagraphForwards ->
match paragraphForwards editor with
| Some index -> editor.CaretOffset, index
| None -> editor.CaretOffset, editor.CaretOffset
| InnerWord ->
let getWordCharFunc = function
| IsWordChar -> isWordChar
| _ -> isNonBlankButNotWordChar
match editor.[editor.CaretOffset] with
| WhiteSpace ->
let matchFunc c = Char.IsWhiteSpace c && (not (isEOLChar c))
getWordRange editor matchFunc
| _ ->
let fWordChar = getWordCharFunc editor.[editor.CaretOffset]
getWordRange editor fWordChar
| InnerWORD ->
match editor.[editor.CaretOffset] with
| WhiteSpace ->
let matchFunc c = Char.IsWhiteSpace c && (not (isEOLChar c))
getWordRange editor matchFunc
| _ ->
getWordRange editor isWORDChar
| AWord ->
let getWordCharFunc = function
| IsWordChar -> isWordChar
| _ -> isNonBlankButNotWordChar
match editor.[editor.CaretOffset] with
| WhiteSpace -> getWhitespaceRange editor isWordChar
| _ ->
let fWordChar = getWordCharFunc editor.[editor.CaretOffset]
let wordStart, wordEnd = getWordRange editor fWordChar
getAroundWordRange editor wordStart wordEnd
| AWORD ->
match editor.[editor.CaretOffset] with
| WhiteSpace -> getWhitespaceRange editor isWORDChar
| _ ->
let wordStart, wordEnd = getWordRange editor isWORDChar
getAroundWordRange editor wordStart wordEnd
| ForwardToEndOfWord ->
let isWordCharAtOffset offset = isWordChar (editor.[offset])
match command.commandType, isWordCharAtOffset editor.CaretOffset, Char.IsWhiteSpace (editor.[editor.CaretOffset+1]) with
| Change, true, true
| Delete, true, true ->
editor.CaretOffset, editor.CaretOffset
| _ -> editor.CaretOffset, findWordEnd editor isWordChar
| ForwardToEndOfWORD -> editor.CaretOffset, findWordEnd editor isWORDChar
| ATag ->
match findEnclosingTag editor editor.CaretOffset with
| Some (startTagStart, _, _, endTagEnd) -> startTagStart, endTagEnd
| None -> noOp
| InnerTag ->
match findEnclosingTag editor editor.CaretOffset with
| Some (_, startTagEnd, endTagStart, _) -> startTagEnd + 1, endTagStart
| None -> noOp
| Jump HalfPageUp ->
let visibleLineCount = getVisibleLineCount editor
let halfwayUp = max 1 (editor.CaretLine - visibleLineCount / 2)
editor.CaretOffset, editor.GetLine(halfwayUp).Offset
| Jump HalfPageDown ->
let visibleLineCount = getVisibleLineCount editor
let halfwayDown = min editor.LineCount (editor.CaretLine + visibleLineCount / 2)
editor.CaretOffset, editor.GetLine(halfwayDown).Offset
| Jump PageUp ->
let visibleLineCount = getVisibleLineCount editor
let pageUp = max 1 (editor.CaretLine - visibleLineCount)
editor.CaretOffset, editor.GetLine(pageUp).Offset
| Jump PageDown ->
let visibleLineCount = getVisibleLineCount editor
let pageDown = min editor.LineCount (editor.CaretLine + visibleLineCount)
editor.CaretOffset, editor.GetLine(pageDown).Offset
| CurrentLocation -> editor.CaretOffset, editor.CaretOffset+1
| SelectedText ->
let selection = editor.Selections |> Seq.head
let lead = editor.LocationToOffset selection.Lead
let anchor = editor.LocationToOffset selection.Anchor
min lead anchor, max lead anchor
| SelectionStart -> editor.CaretOffset, vimState.visualStartOffset
| MatchingBrace ->
match findNextBraceForwardsOnLine editor line with
| Some offset ->
let startOffset = editor.CaretOffset
editor.CaretOffset <- offset
EditActions.GotoMatchingBrace editor
startOffset, editor.CaretOffset
| _ -> editor.CaretOffset, editor.CaretOffset
| PrevUnmatchedBrace ->
match findUnmatchedBlockStartDelimiter editor editor.CaretOffset "{" "}" with
| Some jumpPos -> editor.CaretOffset, jumpPos
| None -> noOp
| NextUnmatchedBrace ->
match findUnmatchedBlockEndDelimiter editor editor.CaretOffset "{" "}" with
| Some jumpPos -> editor.CaretOffset, jumpPos
| None -> noOp
| PrevUnmatchedParen ->
match findUnmatchedBlockStartDelimiter editor editor.CaretOffset "(" ")" with
| Some jumpPos -> editor.CaretOffset, jumpPos
| None -> noOp
| NextUnmatchedParen ->
match findUnmatchedBlockEndDelimiter editor editor.CaretOffset "(" ")" with
| Some jumpPos -> editor.CaretOffset, jumpPos
| None -> noOp
| Jump (ToMark (c, jumpType)) ->
match markDict.TryGetValue c with
| true, mark ->
let offset =
match jumpType with
| MarkerJumpType.Offset -> mark.Offset
| MarkerJumpType.StartOfLine ->
let line = editor.GetLineByOffset mark.Offset
line.Offset + editor.GetLineIndent(line).Length
if editor.FileName.FullPath.ToString() = mark.FileName then
editor.CaretOffset, offset
else
let document = IdeApp.Workbench.GetDocument(FilePath mark.FileName)
let fileInfo = new MonoDevelop.Ide.Gui.FileOpenInformation (document.FileName)
IdeApp.Workbench.OpenDocument(fileInfo) |> ignore
editor.CaretOffset, offset
| _ -> editor.CaretOffset, editor.CaretOffset
| Offset offset -> editor.CaretOffset, offset
| Range (startOffset, endOffset) -> startOffset, endOffset
| Jump (ToSearch search) ->
let startOffset =
match config.keyboardLayout, vimState.keys with
| Qwerty, [Key 'n']
| Qwerty, [Key 'N']
| Colemak, [Key 'k']
| Colemak, [Key 'K']
| Dvorak, [Key 'b']
| Dvorak, [Key 'B'] -> editor.CaretOffset + 1
| _ -> editor.CaretOffset
let offset = findNextSearchOffset editor search startOffset |> Option.defaultValue editor.CaretOffset
editor.CaretOffset, offset
| Jump (ToSearchBackwards search) ->
let offset = findNextSearchOffsetBackwards editor search editor.CaretOffset |> Option.defaultValue editor.CaretOffset
editor.CaretOffset, offset
| Jump SearchAgain ->
match vimState.lastSearch with
| Some search -> getRange config vimState editor { command with textObject = search }
| None -> editor.CaretOffset, editor.CaretOffset
| Jump SearchAgainBackwards ->
match vimState.lastSearch with
| Some search ->
let reverseSearch =
match search with
| Jump (ToSearch s) -> Jump (ToSearchBackwards s)
| Jump (ToSearchBackwards s) -> Jump (ToSearch s)
| _ -> failwith "Invalid search"
getRange config vimState editor { command with textObject = reverseSearch }
| None -> editor.CaretOffset, editor.CaretOffset
| _ -> editor.CaretOffset, editor.CaretOffset
module Vim =
LoggingService.LogInfo ("XSVim " + version)
let registers = Dictionary<Register, XSVim.Selection>()
let editorStates = Dictionary<FilePath, VimState>()
registers.[EmptyRegister] <- { linewise=false; content="" }
let macros = Dictionary<char, VimAction list>()
let (|VisualModes|_|) = function
| VisualMode | VisualLineMode | VisualBlockMode -> Some VisualModes
| _ -> None
let (|NotInsertMode|_|) = function
| InsertMode | ReplaceMode | ExMode _ -> None
| _ -> Some NotInsertMode
let setSelection vimState (editor:TextEditor) (command:VimAction) (start:int) finish =
match vimState.mode, command.commandType with
| VisualMode, Move | VisualMode, SwitchMode _ ->
let start, finish =
if finish < vimState.visualStartOffset then
finish, vimState.visualStartOffset + 1
else
vimState.visualStartOffset, finish + if command.textObject = EndOfLine then 0 else 1
editor.SetSelection(start, min finish editor.Length)
if editor.SelectionMode = SelectionMode.Block then EditActions.ToggleBlockSelectionMode editor
| VisualBlockMode, Move | VisualBlockMode, SwitchMode _ ->
let selectionStartLocation = editor.OffsetToLocation vimState.visualStartOffset
let leftColumn, rightColumn =
if editor.CaretColumn < selectionStartLocation.Column then
editor.CaretColumn, selectionStartLocation.Column+1
else
selectionStartLocation.Column, editor.CaretColumn+1
let topLine = min selectionStartLocation.Line editor.CaretLine
let bottomLine = max selectionStartLocation.Line editor.CaretLine
editor.SetSelection(DocumentLocation (topLine, leftColumn), DocumentLocation (bottomLine, rightColumn))
if editor.SelectionMode = SelectionMode.Normal then EditActions.ToggleBlockSelectionMode editor
| VisualLineMode, Move | VisualLineMode, SwitchMode _ ->
let startPos = min finish vimState.visualStartOffset
let endPos = max finish vimState.visualStartOffset
let startLine = editor.GetLineByOffset startPos
let endLine = editor.GetLineByOffset endPos
editor.SetSelection(startLine.Offset, endLine.EndOffsetIncludingDelimiter)
if editor.SelectionMode = SelectionMode.Block then EditActions.ToggleBlockSelectionMode editor
| _ -> editor.SetSelection(start, min finish editor.Length)
let (|MoveUpOrDown|_|) = function
| { commandType=Move; textObject=Up }
| { commandType=Move; textObject=Down } -> Some MoveUpOrDown
| _ -> None
let (|LineWise|_|) = function
| Up | Down
| WholeLine
| WholeLineIncludingDelimiter
| Jump (ToMark (_, MarkerJumpType.StartOfLine))
| Jump LastLine -> Some LineWise
| _ -> None
let (|StartsWithDelimiter|_|) (s:string) =
if s.StartsWith "\r\n" then Some "\r\n"
elif s.StartsWith "\n" then Some "\n"
else None
let (|EndsWithDelimiter|_|) (s:string) =
if s.EndsWith "\r\n" then Some "\r\n"
elif s.EndsWith "\n" then Some "\n"
else None
let isLineWise vimState command =
match vimState.mode with
| VisualLineMode -> true
| _ ->
match command.textObject with
| LineWise -> true
| _ -> false
let getSelectedText vimState (editor: TextEditor) command =
let linewise = isLineWise vimState command
{ linewise=linewise; content=editor.SelectedText }
let getCaretMode (editor:TextEditor) =
let caret = editor.Carets.[0]
let caretMode:bool = caret?IsInInsertMode
match caretMode with
| true -> Insert
| false -> Block
let setCaretMode (editor:TextEditor) caretMode =
let currentMode = getCaretMode editor
match currentMode, caretMode with
| Block, Insert -> EditActions.SwitchCaretMode editor
| Insert, Block -> EditActions.SwitchCaretMode editor
| _ -> ()
let setAutoCompleteOnKeystroke value =
if SettingsPanel.AutoCompleteInNormalModeIsDisabled() then
IdeApp.Preferences.EnableAutoCodeCompletion.Set value |> ignore
let switchToInsertMode (editor:TextEditor) state isInitial =
let group =
if isInitial
then editor.OpenUndoGroup() |> Some
else
state.undoGroup
setAutoCompleteOnKeystroke true
setCaretMode editor Insert
{ state with mode = InsertMode; statusMessage = "-- INSERT --" |> Some; keys = []; undoGroup = group }
let switchToNormalMode (editor:TextEditor) vimState =
let lastSelection =
match vimState.mode with
| VisualModes ->
Some { start = vimState.visualStartOffset; finish = editor.CaretOffset; mode = vimState.mode }
| _ -> vimState.lastSelection
editor.ClearSelection()
setAutoCompleteOnKeystroke false
setCaretMode editor Block
// stupid hack to prevent intellisense in normal mode
// https://github.com/mono/monodevelop/blob/fdbfbe89529bd9076e1906e7b70fdb51a9ae6b99/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs#L153
if editor.SelectionMode = SelectionMode.Normal then EditActions.ToggleBlockSelectionMode editor
vimState.undoGroup |> Option.iter(fun d -> d.Dispose())
if vimState.mode = InsertMode && editor.CaretColumn > 1 then
EditActions.MoveCaretLeft editor
{ vimState with mode = NormalMode; lastSelection = lastSelection; undoGroup = None; statusMessage = None }
let processVimKey (editor:TextEditor) =
function
| Key k -> editor.InsertAtCaret (string k)
| VimKey.Delete -> EditActions.Delete editor
| VimKey.Backspace -> EditActions.Backspace editor
| VimKey.Left -> EditActions.MoveCaretLeft editor
| VimKey.Right -> EditActions.MoveCaretRight editor
| VimKey.Up -> EditActions.MoveCaretUp editor
| VimKey.Down -> EditActions.MoveCaretDown editor
| Ret -> EditActions.InsertNewLine editor
| Super _
| Esc
| EscapeKey _
| Control _ -> ()
let runCommand config vimState editor command =
let delete state start finish =
let finish =
match command.textObject with
| ForwardToEndOfWord
| ForwardToEndOfWORD
| EndOfLine
| MatchingBrace
| PrevUnmatchedBrace
| NextUnmatchedBrace
| PrevUnmatchedParen
| NextUnmatchedParen
| ToCharInclusive _
| ToCharExclusive _ -> finish + 1
| _ -> finish
if start <> finish then
if command.textObject <> SelectedText then
setSelection state editor command start finish
registers.[EmptyRegister] <- getSelectedText state editor command
EditActions.ClipboardCut editor
state
let setMark c =
if markDict.ContainsKey c then
let marker = markDict.[c]
markDict.Remove c |> ignore
marker.Remove()
let marker = Marker(editor, c)
markDict.Add (c, marker) |> ignore
let toggleCase state start finish =
if command.textObject <> SelectedText then
setSelection state editor command start finish
let toggleChar = function
| c when Char.IsUpper c -> Char.ToLower c
| c when Char.IsLower c -> Char.ToUpper c
| c -> c
match state.mode with
| VisualBlockMode ->
let selectionStartLocation = editor.OffsetToLocation vimState.visualStartOffset
let topLine = Math.Min(selectionStartLocation.Line, editor.CaretLine)
let bottomLine = Math.Max(selectionStartLocation.Line, editor.CaretLine)
editor.CaretColumn <- Math.Min(editor.CaretColumn, selectionStartLocation.Column)
let offsets = [ topLine .. bottomLine ] |> List.map (fun c -> editor.LocationToOffset(c, editor.CaretColumn))
for i in offsets do
let currentLetter = editor.[i]
let isLetter = Char.IsLetter editor.[i]
if isLetter then
let c = toggleChar currentLetter
editor.SetSelection(i, i+1)
EditActions.Delete editor
editor.InsertAtCaret (string c)
EditActions.MoveCaretLeft editor
| _ ->
let swappedChars = editor.SelectedText |> Seq.map toggleChar |> Array.ofSeq
EditActions.Delete editor
editor.InsertAtCaret (swappedChars |> String)
if command.textObject = SelectedText then
editor.CaretOffset <- state.visualStartOffset
state
let modifyNumber f =
let line = editor.GetLine editor.CaretLine
let startOffset =
let f = findCharBackwardsOnLineInclusive editor
f line (fun c -> (c < '0' || c > '9') && c <> '-')
let offset =
match startOffset with
| Some i -> i+1
| None -> editor.CaretOffset
let lineToEnd = editor.Text.[offset .. line.EndOffset-1]
let matches = Regex.Matches(lineToEnd, "-?[0-9]+", RegexOptions.Compiled) |> Seq.cast<Match>
match matches |> Seq.tryHead with
| Some m ->
let i = Convert.ToInt32 (lineToEnd.[m.Index .. m.Index+m.Length-1])
let replacement = (f i) |> string
editor.ReplaceText(line.Offset+m.Index+(line.Length-lineToEnd.Length), m.Length, replacement)
let line = editor.GetLine editor.CaretLine
editor.CaretOffset <- line.Offset + m.Index+(line.Length-lineToEnd.Length)+m.Length-1
vimState
| None -> vimState
let rec processCommands config count vimState command isInitial =
let blockInsert fColumnSelect =
let selectionStartLocation = editor.OffsetToLocation vimState.visualStartOffset
let topLine = min selectionStartLocation.Line editor.CaretLine
let bottomLine = max selectionStartLocation.Line editor.CaretLine
editor.CaretColumn <- fColumnSelect editor.CaretColumn selectionStartLocation.Column
editor.SetSelection(DocumentLocation (topLine, editor.CaretColumn), DocumentLocation (bottomLine, editor.CaretColumn))
if editor.SelectionMode = SelectionMode.Normal then EditActions.ToggleBlockSelectionMode editor
switchToInsertMode editor vimState isInitial
let start, finish =
if editor.Length > 0 then
VimHelpers.getRange config vimState editor command
else
// editor can have zero length when a tab containing it has just been closed
0, 0
let newState =
match command.commandType with
| Move ->
match vimState.mode with
| VisualModes ->
editor.CaretOffset <- finish
let finish =
match command.textObject with
| EndOfLine -> finish + 1
| _ -> finish
setSelection vimState editor command start finish
| _ -> ()
match command.textObject with
| Jump _ ->
setMark "\'"
setMark "`"
| _ -> ()
let newState =
match command, vimState.desiredColumn with
// don't change desired column if we already started moving up or down
| MoveUpOrDown, Some _c ->
editor.CaretOffset <- finish
vimState
| MoveUpOrDown, None ->
let res = { vimState with desiredColumn = Some editor.CaretColumn }
editor.CaretOffset <- finish
res
| _ ->
editor.CaretOffset <- finish
{ vimState with desiredColumn = Some editor.CaretColumn }
newState
| Delete ->
let linewise = isLineWise vimState command
let start, finish =
match linewise with
| true ->
let min = min start finish
let maxOffset = max start finish
let line = editor.GetLineByOffset min
let finish = editor.GetLineByOffset(maxOffset).EndOffsetIncludingDelimiter
if eofOnLine line && line.LineNumber <> 1 then
let delimiter = inferDelimiter editor
line.Offset-delimiter.Length, finish
else
line.Offset, finish
| false -> start, finish
let newState = delete vimState start finish
let offsetBeforeDelimiter =
match linewise with
| true ->
let line = editor.GetLineByOffset(editor.CaretOffset)
let line =
if editor.CaretOffset = editor.Length && editor.Length > 0 && editor.[editor.Length-1] = '\n' && line.PreviousLine <> null then
line.PreviousLine
else
line
line.Offset + editor.GetLineIndent(line.LineNumber).Length
| false when editor.CaretOffset < editor.Length && editor.CaretOffset > 0 ->
let charAtCaret = editor.[editor.CaretOffset]
let previous = editor.[editor.CaretOffset - 1]
if isEOLChar charAtCaret && not (isEOLChar previous) then
editor.CaretOffset - 1
else
editor.CaretOffset
| _ -> editor.CaretOffset
editor.CaretOffset <- max offsetBeforeDelimiter 0
newState
| Indent ->
let line = editor.GetLineByOffset(finish)
setSelection vimState editor command start line.EndOffset
EditActions.IndentSelection editor
editor.ClearSelection()
vimState
| UnIndent ->
let line = editor.GetLineByOffset(finish)
setSelection vimState editor command start line.EndOffset
EditActions.UnIndentSelection editor
editor.ClearSelection()
vimState
| EqualIndent ->
// Always work top to bottom
setSelection vimState editor command start finish
dispatchCommand CodeFormatting.CodeFormattingCommands.FormatBuffer
editor.ClearSelection()
processCommands config 1 vimState (runOnce Move FirstNonWhitespace) false
| Substitute ->
let newState = delete vimState start finish
switchToInsertMode editor newState isInitial
| ToggleCase ->
toggleCase vimState start finish
| DeleteWholeLines ->
let min = min start finish
let max = max start finish
let start = editor.GetLineByOffset(min).Offset
let finish = editor.GetLineByOffset(max).EndOffsetIncludingDelimiter
delete vimState start finish
| DeleteLeft -> if editor.CaretColumn > 1 then delete vimState (editor.CaretOffset - 1) editor.CaretOffset else vimState
| Change ->
let finish =
if count <> 1 && editor.[finish] = ' ' then
finish + 1
else
finish
let state = delete vimState start finish
switchToInsertMode editor state isInitial
| Yank register ->
let finish =
match command.textObject with
| ForwardToEndOfWord
| ForwardToEndOfWORD
| EndOfLine
| ToCharInclusive _
| ToCharExclusive _ -> finish + 1
| _ -> finish
if command.textObject <> SelectedText then
setSelection vimState editor command start finish
registers.[register] <- getSelectedText vimState editor command
if register = EmptyRegister then
EditActions.ClipboardCopy editor
editor.ClearSelection()
match vimState.mode with
| VisualModes -> editor.CaretOffset <- vimState.visualStartOffset
| _ -> ()
processCommands config 1 vimState (runOnce (SwitchMode NormalMode) Nothing) false
| Put Before ->
if registers.[EmptyRegister].linewise then
editor.CaretOffset <- editor.GetLine(editor.CaretLine).Offset
EditActions.ClipboardPaste editor
EditActions.MoveCaretUp editor
EditActions.MoveCaretToLineStart editor
else
EditActions.ClipboardPaste editor
vimState
| Put After ->
if registers.[EmptyRegister].linewise then
if editor.CaretLine = editor.LineCount then
let line = editor.GetLine(editor.CaretLine)
let delimiter = inferDelimiter editor
match registers.[EmptyRegister].content with
| StartsWithDelimiter _delimiter -> ()
| _ -> editor.InsertText(editor.Length, delimiter)
editor.CaretOffset <- editor.Length
EditActions.ClipboardPaste editor
if eofOnLine line then
match registers.[EmptyRegister].content with
| EndsWithDelimiter clipboardDelimiter ->
editor.RemoveText(editor.Length-clipboardDelimiter.Length, clipboardDelimiter.Length)
| _ -> ()
editor.CaretOffset <- line.Offset
EditActions.MoveCaretDown editor
EditActions.MoveCaretToLineStart editor
else
let line = editor.GetLine(editor.CaretLine)
editor.CaretOffset <- line.EndOffsetIncludingDelimiter
EditActions.ClipboardPaste editor
editor.CaretOffset <- line.Offset
EditActions.MoveCaretDown editor
EditActions.MoveCaretToLineStart editor
else
EditActions.MoveCaretRight editor
EditActions.ClipboardPaste editor
EditActions.MoveCaretLeft editor
vimState
| Put OverSelection ->
EditActions.ClipboardPaste editor
{ vimState with mode = NormalMode }
| SelectionOtherEnd ->
let offset = editor.CaretOffset
editor.CaretOffset <- vimState.visualStartOffset
let start, finish =
if offset > vimState.visualStartOffset then
vimState.visualStartOffset, offset + 1
else
offset, vimState.visualStartOffset + 1
editor.SetSelection(start, finish)
{ vimState with visualStartOffset = offset }
| Visual ->
editor.SetSelection(start, finish); vimState
| Undo -> EditActions.Undo editor; editor.ClearSelection(); vimState
| Redo -> EditActions.Redo editor; vimState
| JoinLines ->
let lastColumn = editor.GetLine(editor.CaretLine).Length
EditActions.JoinLines editor
editor.CaretColumn <- lastColumn + 1
vimState
| ReplaceChar c ->
if editor.CaretOffset < editor.Length && not (isEOLChar editor.[editor.CaretOffset]) then
editor.SetSelection(editor.CaretOffset, editor.CaretOffset+1)
EditActions.Delete editor
match c with
| StartsWithDelimiter _ ->
EditActions.InsertNewLine editor
| _ ->
editor.InsertAtCaret c
EditActions.MoveCaretLeft editor
vimState
| SetMark c ->
setMark c
vimState
| InsertLine Before ->
EditActions.InsertNewLineAtEnd editor
vimState
| InsertLine After ->
if editor.CaretLine = 1 then
EditActions.MoveCaretToLineStart editor
EditActions.InsertNewLine editor
EditActions.MoveCaretUp editor
vimState
else
EditActions.MoveCaretUp editor
EditActions.InsertNewLineAtEnd editor
vimState
| Dispatch command -> dispatchCommand command ; vimState
| ResetKeys -> { vimState with keys = [] }
| BlockInsert Before -> blockInsert min
| BlockInsert After -> blockInsert (fun s f -> (max s f) + 1)
| SwitchMode mode ->
match mode with
| NormalMode ->
let state = switchToNormalMode editor vimState
if vimState.mode = InsertMode then
MonoDevelop.Ide.CodeCompletion.CompletionWindowManager.HideWindow ()
processCommands config 1 state (runOnce (SetMark ".") Nothing) false
else
state
| VisualMode | VisualLineMode | VisualBlockMode ->
setCaretMode editor Block
let start, finish = VimHelpers.getRange config vimState editor command
let statusMessage =
match mode with
| VisualMode -> Some "-- VISUAL --"
| VisualLineMode -> Some "-- VISUAL LINE --"
| VisualBlockMode -> Some "-- VISUAL BLOCK --"
| _ -> None
let newState = { vimState with mode = mode; visualStartOffset = editor.CaretOffset; statusMessage = statusMessage }
setAutoCompleteOnKeystroke false
setSelection newState editor command start finish
match mode, editor.SelectionMode with
| VisualBlockMode, SelectionMode.Normal -> EditActions.ToggleBlockSelectionMode editor
| _, SelectionMode.Block -> EditActions.ToggleBlockSelectionMode editor
| _ -> ()
newState
| InsertMode ->
switchToInsertMode editor vimState isInitial
| ReplaceMode ->
let undoGroup =
if isInitial
then editor.OpenUndoGroup() |> Some
else
vimState.undoGroup
{ vimState with mode = ReplaceMode; statusMessage = "-- REPLACE --"|> Some; undoGroup = undoGroup }
| ExMode c ->
{ vimState with mode = (ExMode c); statusMessage = string c |> Some }
| Star After ->
match wordAtCaret editor with
| Some word ->
let matches = Regex.Matches(editor.Text, sprintf @"\b%s\b" word)
|> Seq.cast<Match>
let m =
matches
|> Seq.tryFind(fun m -> m.Index > editor.CaretOffset)
let offset =
match m with
| Some m -> m.Index
| None ->
let m = matches |> Seq.head
m.Index
editor.CaretOffset <- offset
{ vimState with lastSearch = Some (Jump (ToSearch word)) }
| None ->
processCommands config 1 vimState (runOnce Move WordForwards) isInitial
| Star Before ->
match wordAtCaret editor with
| Some word ->
let matches = Regex.Matches(editor.Text, sprintf @"\b%s\b" word)
|> Seq.cast<Match>
let start = findCurrentWordStart editor isWordChar
let m =
matches
|> Seq.tryFindBack(fun m -> m.Index < start)
let offset =
match m with
| Some m -> m.Index
| None ->
let m = matches |> Seq.last
m.Index
editor.CaretOffset <- offset
{ vimState with lastSearch = Some (Jump (ToSearchBackwards word)) }
| None -> vimState
| InsertChar c ->
match vimState.mode with
| InsertMode ->
processVimKey editor c
vimState
| _ -> vimState
| IncrementNumber -> modifyNumber (fun i -> i + 1)
| DecrementNumber -> modifyNumber (fun i -> i - 1)
| IncrementalSearch search ->
findNextSearchOffset editor search editor.CaretOffset
|> Option.iter(fun index ->
editor.SetSelection(index, index + search.Length)
editor.ScrollTo(index))
vimState
| IncrementalSearchBackwards search ->
findNextSearchOffsetBackwards editor search editor.CaretOffset
|> Option.iter(fun index ->
editor.SetSelection(index, index + search.Length)
editor.ScrollTo(index))
vimState
| SetSearchAction command -> { vimState with searchAction = Some command }
| MacroStart c ->
macros.[c] <- []
{ vimState with macro = Macro (char c) |> Some }
| MacroEnd ->
{ vimState with macro = None }
| ReplayMacro c ->
let getCount repeat = repeat |> Option.defaultValue 1
let rec runMacro state actions =
match actions with
| [ only ] ->
processCommands config (getCount only.repeat) state only false
| h :: t ->
let newState = processCommands config (getCount h.repeat) state h false
runMacro newState t
| [] -> state
runMacro vimState macros.[c]
| NextTab ->
Window.nextTab()
vimState
| PreviousTab ->
Window.previousTab()
vimState
| Func f ->
f()
vimState
| EditorFunc f ->
f editor
vimState
| GotoPad padId ->
Window.gotoPad padId
vimState
| ChangeState s -> s
| DelayedFunc (f, ms) ->
let token = new CancellationTokenSource()
let work =
async {
do! Async.Sleep ms
if (not token.IsCancellationRequested) then
Runtime.RunInMainThread(fun _ -> f editor) |> ignore
}
Async.Start(work, token.Token)
{ vimState with insertModeCancellationTokenSource = Some token }
| CancelFunc ->
vimState.insertModeCancellationTokenSource
|> Option.iter(fun token -> token.Cancel())
{ vimState with insertModeCancellationTokenSource = None }
| _ -> vimState
match count with
| 1 -> newState
| _ -> processCommands config (count-1) newState command false
let count = command.repeat |> Option.defaultValue 1
processCommands config count vimState command true
let (|Digit|_|) character =
if character >= "0" && character <= "9" then
Some (Convert.ToInt32 character)
else
None
let (|OneToNine|_|) character =
if character >= "1" && character <= "9" then
Some (Convert.ToInt32 character)
else
None
let (|RegisterMatch|_|) = function
| c -> Some (Register (Char.Parse c))
let (|BlockDelimiter|_|) layout character =
let pairs =
[
"[", ("[", "]")
"]", ("[", "]")
"(", ("(", ")")
")", ("(", ")")
"b", ("(", ")")
"{", ("{", "}")
"}", ("{", "}")
"B", ("{", "}")
"<", ("<", ">")
">", ("<", ">")
] |> dict
let mappedChar = remap layout character
if pairs.ContainsKey mappedChar then
Some pairs.[mappedChar]
else
None
let (|QuoteDelimiter|_|) layout character =
let mappedChar = remap layout character
if Array.contains mappedChar [| "\""; "'"; "`"|] then
Some mappedChar
else
None
let (|Movement|_|) layout keys =
let remappedKeys = keys |> List.map (fun k -> remap layout k)
match remappedKeys with
| ["<left>"]
| ["h"] -> Some Left
| ["<down>"]
| ["j"] -> Some Down
| ["<up>"]
| ["k"] -> Some Up
| ["<right>"]
| ["l"] -> Some (Right StopAtEndOfLine)
| [" "] -> Some (Right MoveToNextLineAtEnd)
| ["$"] -> Some EndOfLine
| ["^"] -> Some FirstNonWhitespace
| ["0"] -> Some StartOfLine
| ["_"] -> Some FirstNonWhitespace
| ["w"] -> Some WordForwards
| ["W"] -> Some WORDForwards
| ["b"] -> Some WordBackwards
| ["B"] -> Some WORDBackwards
| ["e"] -> Some ForwardToEndOfWord
| ["E"] -> Some ForwardToEndOfWORD
| ["g"; "e"] -> Some BackwardToEndOfWord
| ["g"; "E"] -> Some BackwardToEndOfWORD
| ["{"] -> Some (Jump ParagraphBackwards)
| ["}"] -> Some (Jump ParagraphForwards)
| ["%"] -> Some MatchingBrace
| ["["; "{"] -> Some PrevUnmatchedBrace
| ["]"; "}"] -> Some NextUnmatchedBrace
| ["["; "("] -> Some PrevUnmatchedParen
| ["]"; ")"] -> Some NextUnmatchedParen
| ["G"] -> Some (Jump LastLine)
| ["H"] -> Some (Jump FirstVisibleLine)
| ["M"] -> Some (Jump MiddleVisibleLine)
| ["L"] -> Some (Jump LastVisibleLine)
| ["<C-d>"] -> Some (Jump HalfPageDown)
| ["<C-u>"] -> Some (Jump HalfPageUp)
| ["<C-f>"] -> Some (Jump PageDown)
| ["<C-b>"] -> Some (Jump PageUp)
| ["n"] -> Some (Jump SearchAgain)
| ["N"] -> Some (Jump SearchAgainBackwards)
| ["'"; c] -> Some (Jump (ToMark (c, MarkerJumpType.StartOfLine)))
| ["`"; c] -> Some (Jump (ToMark (c, MarkerJumpType.Offset)))
| _ -> None
let unfinishedMovements = [ "g"; "["; "]"; "@"; "m"; "`"; "'" ] |> set
let (|UnfinishedMovement|_|) layout character =
let remappedCharacter = remap layout character
if unfinishedMovements.Contains remappedCharacter then
Some UnfinishedMovement
else
None
let (|IndentChar|_|) layout key =
match remap layout key with
| ">" -> Some Indent
| "<" -> Some UnIndent
| "=" -> Some EqualIndent
| _ -> None
let (|FindChar|_|) layout key =
match remap layout key with
| "f" -> Some ToCharInclusive
| "F" -> Some ToCharInclusiveBackwards
| "t" -> Some ToCharExclusive
| "T" -> Some ToCharExclusiveBackwards
| _ -> None
let (|SearchChar|_|) layout key =
match remap layout key with
| "/" -> Some (SearchChar '/')
| "?" -> Some (SearchChar '?')
| _ -> None
let (|Action|_|) layout key =
match remap layout key with
| "d" -> Some Delete
| "c" -> Some Change
| "v" -> Some Visual
| "y" -> Some (Yank EmptyRegister)
| ">" -> Some Indent
| "<" -> Some UnIndent
| _ -> None
let (|ModeChange|_|) layout key =
match remap layout key with
| "i" -> Some InsertMode
| "v" -> Some VisualMode
| "<C-v>" -> Some VisualBlockMode
| "<C-q>" -> Some VisualBlockMode
| "V" -> Some VisualLineMode
| "R" -> Some ReplaceMode
| _ -> None
let (|Escape|_|) = function
| "<esc>" | "<C-c>" | "<C-[>" -> Some Escape
| _ -> None
let (|RemappedMatches|_|) layout matchList unmappedList =
let mappedList = unmappedList |> List.map (fun x -> remap layout x)
if mappedList = matchList then
Some matchList
else
None
let (|RemappedMatchesChar|_|) layout matchChar unmappedChar = //
let mappedChar = remap layout unmappedChar
if mappedChar = matchChar then
Some matchChar
else
None
let getInsertModeEscapeCombo config =
match config.insertModeEscapeKey with
| Some combo ->
combo.insertModeEscapeKey1, combo.insertModeEscapeKey2, combo.insertModeEscapeTimeout
| None -> "", "", 0
let parseKeys (state:VimState) (config: Config) =
let layout = config.keyboardLayout;
let keyList = state.keys |> List.map string
let numericArgument, keyList =
match keyList, state.mode with
| "r" :: _, _
| [ _ ], ReplaceMode
| FindChar layout _ :: _, _ -> None, keyList
// 2dw -> 2, dw
| OneToNine d1 :: Digit d2 :: Digit d3 :: Digit d4 :: t, _ ->
Some (d1 * 1000 + d2 * 100 + d3 * 10 + d4), t
| OneToNine d1 :: Digit d2 :: Digit d3 :: t, _ ->
Some (d1 * 100 + d2 * 10 + d3), t
| OneToNine d1 :: Digit d2 :: t, _ ->
Some (d1 * 10 + d2), t
| OneToNine d :: t, _ -> Some (d), t
// d2w -> 2, dw
| c :: OneToNine d1 :: Digit d2 :: Digit d3 :: Digit d4 :: t, _ ->
Some (d1 * 1000 + d2 * 100 + d3 * 10 + d4), c::t
| c :: OneToNine d1 :: Digit d2 :: Digit d3 :: t, _ ->
Some (d1 * 100 + d2 * 10 + d3), c::t
| c :: OneToNine d1 :: Digit d2 :: t, _ ->
Some (d1 * 10 + d2), c::t
| c :: OneToNine d :: t, _ ->
Some d, c::t
| _ -> None, keyList
let run = getCommand numericArgument
let runInVisualMode actions = [ yield switchMode VisualMode; yield! actions; yield switchMode NormalMode ]
let runInVisualLineMode actions = [ yield switchMode VisualLineMode; yield! actions; yield switchMode NormalMode ]
LoggingService.LogDebug (sprintf "%A %A" state.mode keyList)
let newState =
match keyList with
| [ FindChar layout m; c ] -> { state with findCharCommand = run Move ( m c ) |> Some }
| _ -> state
let insertModeEscapeFirstChar, insertModeEscapeSecondChar, insertModeTimeout =
getInsertModeEscapeCombo config
let action =
match state.mode, keyList with
| VisualBlockMode, [ Escape ] -> [ switchMode NormalMode; run Move SelectionStart ]
| NormalMode, [ Escape ] -> [ yield! resetKeys; yield dispatch "MonoDevelop.Ide.Commands.ViewCommands.FocusCurrentDocument" ]
| _, [ Escape ] -> [ switchMode NormalMode ]
| InsertMode, [ c ] when c = insertModeEscapeFirstChar ->
delayedFunc (fun editor ->
editor.InsertAtCaret insertModeEscapeFirstChar
let oldState = editorStates.[editor.FileName]
editorStates.[editor.FileName] <- { oldState with keys = [] } ) insertModeTimeout :: wait
| InsertMode, [ c1; c2 ] when c1 = insertModeEscapeFirstChar && c2 = insertModeEscapeSecondChar ->
[ run CancelFunc Nothing; switchMode NormalMode ]
| InsertMode, [ c; _ ] when c = insertModeEscapeFirstChar ->
[ run CancelFunc Nothing
run (ChangeState { state with keys = [] }) Nothing
typeChar (Key (char insertModeEscapeFirstChar)) ]
| NotInsertMode, RemappedMatches layout [ "G" ] _ ->
match numericArgument with
| Some lineNumber -> [ runOnce Move (Jump (StartOfLineNumber lineNumber)) ]
| None -> [ runOnce Move (Jump LastLine) ]
| NormalMode, [ IndentChar layout _ ] -> wait
| NormalMode, [ IndentChar layout _ ; RemappedMatchesChar layout "g" _ ] -> wait
| NormalMode, [ IndentChar layout indent; RemappedMatchesChar layout "G" _ ] ->
match numericArgument with
| Some lineNumber -> [ runOnce Indent (Jump (StartOfLineNumber lineNumber)) ]
| None -> [ runOnce indent (Jump LastLine) ]
| NormalMode, [ IndentChar layout indent; RemappedMatchesChar layout "g" _; RemappedMatchesChar layout "g" _ ] ->
let lineNumber = match numericArgument with Some n -> n | None -> 1
[ runOnce indent (Jump (StartOfLineNumber lineNumber)) ]
| NormalMode, RemappedMatches layout [ ">"; ">" ] _ -> [ run Indent WholeLine ]
| NormalMode, RemappedMatches layout [ "<"; "<" ] _ -> [ run UnIndent WholeLine ]
| NormalMode, RemappedMatches layout [ "="; "=" ] _ -> [ run EqualIndent WholeLine ]
| NormalMode, RemappedMatches layout [ "V" ] _ ->
match numericArgument with
| Some lines -> [ switchMode VisualLineMode; getCommand (lines-1 |> Some) Move Down ]
| None -> [ switchMode VisualLineMode ]
| NormalMode, RemappedMatches layout [ "v" ] _ ->
match numericArgument with
| Some chars -> [ switchMode VisualMode; getCommand (chars-1 |> Some) Move (Right StopAtEndOfLine) ]
| None -> [ switchMode VisualMode ]
| NormalMode, RemappedMatches layout [ "d"; "G" ] _ -> [ runOnce DeleteWholeLines (Jump LastLine)]
| NormalMode, RemappedMatches layout [ "d"; "j" ] _ ->
let numberOfLines =
match numericArgument with
| Some lines -> lines
| None -> 1
runInVisualLineMode [ getCommand (numberOfLines |> Some) Move Down; runOnce Delete SelectedText ]
| NormalMode, RemappedMatches layout [ "d"; "k" ] _ ->
let numberOfLines =
match numericArgument with
| Some lines -> lines
| None -> 1
runInVisualLineMode [ getCommand (numberOfLines |> Some) Move Up; runOnce Delete SelectedText; ]
| NotInsertMode, [ (Action layout _) ; UnfinishedMovement layout _] -> wait
| NotInsertMode, [ UnfinishedMovement layout _ ] -> wait
| NormalMode, RemappedMatches layout [ "d"; "g"; "g" ] _ -> [ runOnce DeleteWholeLines (Jump StartOfDocument)]
| ReplaceMode, [ c ] -> [ run (ReplaceChar c) Nothing; run Move (Right IncludeDelimiter) ]
| NotInsertMode, Movement layout m -> [ run Move m ]
| NotInsertMode, [ FindChar layout m; c ] -> [ run Move (m c) ]
| NormalMode, IndentChar layout indent :: Movement layout m ->
match numericArgument with
| None -> [ run indent m ]
| Some lines ->
runInVisualMode [ getCommand (lines-1 |> Some) Move Down; runOnce indent SelectedText ]
| NormalMode, Action layout action :: Movement layout m when numericArgument = None -> [ run action m ]
| NormalMode, Action layout action :: Movement layout m ->
match action, m with
| Delete, _
| Yank _, _ ->
match m with
| WordForwards -> runInVisualMode [ run Move ForwardToEndOfWord; runOnce Move (Right StopAtEndOfLine); runOnce action SelectedText; ]
| WORDForwards -> runInVisualMode [ run Move ForwardToEndOfWORD; runOnce Move (Right StopAtEndOfLine); runOnce action SelectedText; ]
| WordBackwards -> runInVisualMode [ runOnce Move Left; run Move ForwardToEndOfWord; runOnce action SelectedText; ]
| WORDBackwards -> runInVisualMode [ runOnce Move Left; run Move ForwardToEndOfWORD; runOnce action SelectedText; ]
| _ -> runInVisualMode [ run Move m; runOnce action SelectedText; ]
| _ -> [ run action m ]
| NormalMode, RemappedMatches layout [ "u" ] _ -> [ run Undo Nothing ]
| NormalMode, [ "<C-r>" ] -> [ run Redo Nothing ]
| NormalMode, RemappedMatches layout [ "d"; "d" ] _ ->
match numericArgument with
| None -> [ run Delete WholeLine ]
| Some lines ->
[ switchMode VisualLineMode
getCommand (lines-1 |> Some) Move Down
runOnce Delete SelectedText
switchMode NormalMode
runOnce Move FirstNonWhitespace ]
| NormalMode, RemappedMatches layout [ "c"; "c" ] _ -> [ run Change WholeLine ]
| NormalMode, RemappedMatches layout ["\""] _ -> wait
| NormalMode, [RemappedMatchesChar layout "\"" _; _ ] -> wait
| NormalMode, [RemappedMatchesChar layout "\"" _; _; RemappedMatchesChar layout "y" _ ] -> wait
| NormalMode, RemappedMatchesChar layout "\"" _ :: (RegisterMatch r) :: RemappedMatchesChar layout "y" _ :: (Movement layout m) -> [ run (Yank r) m]
| NormalMode, RemappedMatches layout [ "y"; "y" ] _
| NormalMode, RemappedMatches layout [ "Y" ] _ ->
match numericArgument with
| Some lines -> runInVisualLineMode [ getCommand (lines-1 |> Some) Move Down; runOnce (Yank EmptyRegister) SelectedText ]
| None -> [ runOnce (Yank EmptyRegister) WholeLineIncludingDelimiter ]
| NormalMode, RemappedMatches layout [ "C" ] _ -> [ run Change EndOfLine ]
| NormalMode, RemappedMatches layout [ "D" ] _ -> [ run Delete EndOfLine ]
| NormalMode, RemappedMatches layout [ "x" ] _ -> [ runOnce Delete (Character (numericArgument |> Option.defaultValue 1)) ]
| NormalMode, RemappedMatches layout [ "X" ] _ -> [ run DeleteLeft Nothing ]
| NormalMode, RemappedMatches layout [ "s" ] _ -> [ run Substitute CurrentLocation]
| NormalMode, RemappedMatches layout [ "S" ] _ -> [ run Delete WholeLine; runOnce (InsertLine After) Nothing; switchMode InsertMode ]
| NormalMode, RemappedMatches layout [ "p" ] _ -> [ run (Put After) Nothing ]
| NormalMode, RemappedMatches layout [ "P" ] _ -> [ run (Put Before) Nothing ]
| VisualModes, RemappedMatches layout [ "p" ] _ -> [ run (Put OverSelection) Nothing ]
| VisualModes, RemappedMatches layout [ "P" ] _ -> [ run (Put OverSelection) Nothing ]
| NormalMode, RemappedMatches layout [ "J" ] _ -> [ run JoinLines Nothing ]
| NotInsertMode, RemappedMatches layout [ "*" ] _ -> [ run (Star After) Nothing ]
| NotInsertMode, RemappedMatches layout [ "#" ] _ -> [ run (Star Before) Nothing ]
| NotInsertMode, RemappedMatches layout [ "£" ] _ -> [ run (Star Before) Nothing ]
| NotInsertMode, [ SearchChar layout c ] -> [ switchMode (ExMode (string c)); runOnce (SetSearchAction Move) Nothing ]
| VisualModes, RemappedMatches layout [ ":" ] _ -> [ switchMode (ExMode ":'<,'>") ]
| NotInsertMode, RemappedMatches layout [ ":" ] _ -> [ switchMode (ExMode ":") ]
| NotInsertMode, [ Action layout action; SearchChar layout c ] -> [ switchMode (ExMode (string c)); runOnce (SetSearchAction action) Nothing ]
| NormalMode, RemappedMatches layout [ "z"; "z" ] _ -> [ dispatch ViewCommands.CenterAndFocusCurrentDocument ]
| NormalMode, RemappedMatches layout [ "z"; ] _ -> wait
| NormalMode, [ "<C-y>" ] -> [ dispatch TextEditorCommands.ScrollLineUp ]
| NormalMode, [ "<C-e>" ] -> [ dispatch TextEditorCommands.ScrollLineDown ]
| NormalMode, [ "<C-o>" ] -> [ dispatch NavigationCommands.NavigateBack ]
| NormalMode, [ "<C-i>" ] -> [ dispatch NavigationCommands.NavigateForward ]
| NormalMode, RemappedMatches layout [ "r" ] _ -> wait
| NormalMode, [ RemappedMatchesChar layout "r" _; "<ret>" ] -> [ run (ReplaceChar "\n" ) Nothing ]
| NormalMode, [ RemappedMatchesChar layout "r" _; c ] -> [ run (ReplaceChar c) Nothing ]
| NormalMode, [ RemappedMatchesChar layout "m" _; c ] -> [ run (SetMark c) Nothing ]
| NotInsertMode, [ Action layout action; FindChar layout m; c ] -> [ run action (m c) ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "i" _; BlockDelimiter layout c ] -> [ run action (InnerBlock c) ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "a" _; BlockDelimiter layout c ] -> [ run action (ABlock c) ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "i" _; QuoteDelimiter layout c ] -> [ run action (InnerQuotedBlock (char c)) ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "a" _; QuoteDelimiter layout c ] -> [ run action (AQuotedBlock (char c)) ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "i" _; RemappedMatchesChar layout "w" _ ] -> [ run action InnerWord ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "a" _; RemappedMatchesChar layout "w" _ ] -> [ run action AWord ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "i" _; RemappedMatchesChar layout "W" _ ] -> [ run action InnerWORD ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "a" _; RemappedMatchesChar layout "W" _ ] -> [ run action AWORD ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "a" _; RemappedMatchesChar layout "t" _ ] -> [ run action ATag ]
| NotInsertMode, [ Action layout action; RemappedMatchesChar layout "i" _; RemappedMatchesChar layout "t" _ ] -> [ run action InnerTag ]
| VisualMode, RemappedMatches layout [ "i"; "w" ] _ -> [ run Visual InnerWord ]
| VisualMode, RemappedMatches layout [ "a"; "w" ] _ -> [ run Visual AWord ]
| VisualMode, RemappedMatches layout [ "i"; "W" ] _ -> [ run Visual InnerWORD ]
| VisualMode, RemappedMatches layout [ "a"; "W" ] _ -> [ run Visual AWORD ]
| VisualMode, RemappedMatches layout [ "a"; "t" ] _ -> [ run Visual ATag ]
| VisualMode, RemappedMatches layout [ "i"; "t" ] _ -> [ run Visual InnerTag ]
| VisualMode, RemappedMatches layout [ "u"] _ -> [ dispatch EditCommands.LowercaseSelection ]
| VisualMode, RemappedMatches layout [ "U"] _ -> [ dispatch EditCommands.UppercaseSelection ]
| NormalMode, [ ModeChange layout mode ] -> [ switchMode mode ]
| NormalMode, RemappedMatches layout [ "a" ] _ -> [ run Move (Right IncludeDelimiter); switchMode InsertMode ]
| NormalMode, RemappedMatches layout [ "A" ] _ -> [ run Move EndOfLineIncludingDelimiter; switchMode InsertMode ]
| NormalMode, RemappedMatches layout [ "O" ] _ -> [ run (InsertLine After) Nothing; switchMode InsertMode ]
| NormalMode, RemappedMatches layout [ "o" ] _ -> [ run (InsertLine Before) Nothing; switchMode InsertMode ]
| NormalMode, RemappedMatches layout [ "I" ] _ -> [ run Move FirstNonWhitespace; switchMode InsertMode ]
| NormalMode, [ Action layout _ ] -> wait
| NotInsertMode, [ Action layout _; RemappedMatchesChar layout "i" _ ] -> wait
| NotInsertMode, [ Action layout _; "a" ] -> wait
| VisualMode, RemappedMatches layout [ "i" ] _ | VisualMode, [ "a" ] -> wait
| NotInsertMode, [ FindChar layout _; ] -> wait
| NotInsertMode, [ Action layout _; FindChar layout _; ] -> wait | NotInsertMode, [ "<ret>" ] -> [ run Move Down; run Move FirstNonWhitespace ]
| NotInsertMode, RemappedMatches layout [ "q" ] _ when state.macro.IsNone -> wait
| NotInsertMode, [ RemappedMatchesChar layout "q" _; c ] -> [ run (MacroStart (char c)) Nothing ]
| NotInsertMode, RemappedMatches layout [ "q" ] _ -> [ run MacroEnd Nothing ]
| NotInsertMode, [ "@"; c ] -> [ run (ReplayMacro (char c)) Nothing ]
| NotInsertMode, RemappedMatches layout [ "g"; "g" ] _ ->
let lineNumber = match numericArgument with Some n -> n | None -> 1
[ runOnce Move (Jump (StartOfLineNumber lineNumber)) ]
| NotInsertMode, RemappedMatches layout [ "g"; "d" ] _ -> [ dispatch "MonoDevelop.Refactoring.RefactoryCommands.GotoDeclaration" ]
| NotInsertMode, RemappedMatches layout [ "g"; "u" ] _ -> [ dispatch "MonoDevelop.Refactoring.RefactoryCommands.FindReferences" ]
| NotInsertMode, RemappedMatches layout [ "g"; "b" ] _ -> [ dispatch "MonoDevelop.RefactoryCommands.NavigationCommands.FindBaseSymbols" ]
| NotInsertMode, RemappedMatches layout [ "g"; "t" ] _ -> [ func Window.nextTab ]
| NotInsertMode, RemappedMatches layout [ "g"; "T" ] _ -> [ func Window.previousTab ]
| NotInsertMode, RemappedMatches layout [ "z"; "z" ] _ -> [ dispatch TextEditorCommands.RecenterEditor ]
| NotInsertMode, RemappedMatches layout [ "z"; "a" ] _ -> [ dispatch EditCommands.ToggleAllFoldings ]
| NotInsertMode, RemappedMatches layout [ "z"; "o" ] _ -> [ dispatch EditCommands.ToggleFolding ]
| NotInsertMode, RemappedMatches layout [ "z"; "c" ] _ -> [ dispatch EditCommands.ToggleFolding ]
| NotInsertMode, RemappedMatches layout [ "g"; "h" ] _ -> [ dispatch TextEditorCommands.ShowQuickInfo ]
| NotInsertMode, RemappedMatches layout [ "g"; "v" ] _ ->
match state.lastSelection with
| Some selection -> [ run Move (Offset selection.start)
switchMode selection.mode
run Move (Offset selection.finish) ]
| None -> resetKeys
| NotInsertMode, RemappedMatches layout [ "." ] _ -> state.lastAction @ [ switchMode NormalMode ]
| NotInsertMode, RemappedMatches layout [ ";" ] _ -> match state.findCharCommand with Some command -> [ command ] | None -> []
| NotInsertMode, RemappedMatches layout [ "," ] _ ->
match state.findCharCommand with
| Some command ->
let findCommand =
match command.textObject with
| ToCharInclusive c -> ToCharInclusiveBackwards c
| ToCharInclusiveBackwards c -> ToCharInclusive c
| ToCharExclusive c -> ToCharExclusiveBackwards c
| ToCharExclusiveBackwards c -> ToCharExclusive c
| _ -> failwith "Invalid find command"
[ { command with textObject=findCommand } ]
| None -> []
| VisualModes, Movement layout m -> [ run Move m ]
| VisualBlockMode, RemappedMatches layout [ "I" ] _ -> [ run (BlockInsert Before) Nothing ]
| VisualBlockMode, [ "A" ] -> [ run (BlockInsert After) Nothing ]
| VisualModes, [ RemappedMatchesChar layout "i" _; BlockDelimiter layout c ] -> [ run Visual (InnerBlock c) ]
| VisualModes, [ "a"; BlockDelimiter layout c ] -> [ run Visual (ABlock c) ]
| VisualModes, [ RemappedMatchesChar layout "i" _; QuoteDelimiter layout c ] -> [ run Visual (InnerQuotedBlock (char c)) ]
| VisualModes, [ "a"; QuoteDelimiter layout c ] -> [ run Visual (AQuotedBlock (char c)) ]
| VisualModes, RemappedMatches layout [ "x" ] _ -> [ run Delete SelectedText; switchMode NormalMode ]
| VisualModes, RemappedMatches layout [ "d" ] _ -> [ run Delete SelectedText; switchMode NormalMode ]
| VisualModes, RemappedMatches layout [ "D" ] _ -> [ run Delete EndOfLine; switchMode NormalMode ]
| VisualModes, RemappedMatches layout [ "c" ] _
| VisualModes, RemappedMatches layout [ "s" ] _ -> [ run Change SelectedText ]
| VisualModes, RemappedMatches layout [ "o" ] _ -> [ run SelectionOtherEnd Nothing ]
| NormalMode, [ "~" ] -> [ run ToggleCase CurrentLocation ]
| VisualModes, [ "~" ] -> [ run ToggleCase SelectedText; switchMode NormalMode ]
| VisualModes, RemappedMatches layout [ "y" ] _ -> [ run (Yank EmptyRegister) SelectedText; switchMode NormalMode ]
| VisualModes, RemappedMatches layout [ "Y" ] _ -> [ run (Yank EmptyRegister) WholeLine; switchMode NormalMode ]
| VisualModes, RemappedMatches layout [ ">" ] _ -> [ run (EditorFunc EditActions.IndentSelection) Nothing; switchMode NormalMode ]
| VisualModes, RemappedMatches layout [ "<" ] _ -> [ run (EditorFunc EditActions.UnIndentSelection) Nothing; switchMode NormalMode ]
| VisualModes, RemappedMatches layout [ "=" ] _ -> [ run EqualIndent SelectedText; switchMode NormalMode ]
| NotInsertMode, RemappedMatches layout [ "Z" ] _ -> wait
| NotInsertMode, RemappedMatches layout ["Z"; "Z" ] _ -> [ func Window.closeTab ]
| NotInsertMode, [ "<C-p>" ] -> [ dispatch SearchCommands.GotoFile ]
| NotInsertMode, [ "<C-w>" ] -> wait
| NotInsertMode, [ "<C-w>"; "w" ]
| NotInsertMode, [ "<C-w>"; "<C-w>" ] -> [ func Window.switchWindow ]
| NotInsertMode, [ "<C-w>"; "h" ] -> [ func Window.leftWindow ]
| NotInsertMode, [ "<C-w>"; "l" ] -> [ func Window.rightWindow ]
// These commands don't work the same way as vim yet, but better than nothing
| NotInsertMode, [ "<C-w>"; "o" ] -> [ dispatch FileTabCommands.CloseAllButThis ]
| NotInsertMode, [ "<C-w>"; "c" ] -> [ func Window.closeTab ]
| NotInsertMode, [ "<C-w>"; "v" ]
| NotInsertMode, [ "<C-w>"; "s" ]
| NotInsertMode, [ "<C-w>"; "<C-v>" ]
| NotInsertMode, [ "<C-w>"; "<C-s>" ] ->
let notebooks = Window.getNotebooks()
if notebooks.Length < 2 then
[ dispatch "MonoDevelop.Ide.Commands.ViewCommands.SideBySideMode" ]
else
resetKeys
| InsertMode, [ "<C-n>" ] -> [ dispatch TextEditorCommands.DynamicAbbrev ]
| NotInsertMode, [ "<C-a>" ] -> [ run IncrementNumber Nothing; switchMode NormalMode ]
| NotInsertMode, [ "<C-x>" ] -> [ run DecrementNumber Nothing; switchMode NormalMode ]
| NotInsertMode, RemappedMatches layout [ "g"; "p" ] _ -> wait
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "s" ] _ -> [ gotoPad "ProjectPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "c" ] _ -> [ gotoPad "ClassPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "e" ] _ -> [ gotoPad "MonoDevelop.Ide.Gui.Pads.ErrorListPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "t" ] _ -> [ gotoPad "MonoDevelop.Ide.Gui.Pads.TaskListPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "p" ] _ -> [ gotoPad "MonoDevelop.DesignerSupport.PropertyPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "o" ] _ -> [ gotoPad "MonoDevelop.DesignerSupport.DocumentOutlinePad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "b" ] _ -> [ gotoPad "MonoDevelop.Debugger.BreakpointPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "l" ] _ -> [ gotoPad "MonoDevelop.Debugger.LocalsPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "w" ] _ -> [ gotoPad "MonoDevelop.Debugger.WatchPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "i" ] _ -> [ gotoPad "MonoDevelop.Debugger.ImmediatePad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "n" ] _ -> [ gotoPad "MonoDevelop.FSharp.FSharpInteractivePad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "f" ] _ ->
let searchResultPads = IdeApp.Workbench.Pads |> Seq.filter(fun p -> p.Content :? MonoDevelop.Ide.FindInFiles.SearchResultPad)
match searchResultPads |> Seq.length with
| 0 -> resetKeys
| 1 -> [ gotoPad "SearchPad - Search Results - 0" ]
| _ -> wait
| NotInsertMode, [ RemappedMatchesChar layout "g" _; RemappedMatchesChar layout "p" _; RemappedMatchesChar layout "f" _; OneToNine d ] ->
[ gotoPad (sprintf "SearchPad - Search Results - %d" (d-1)) ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "d" ] _ -> wait
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "d"; "t" ] _ -> [ gotoPad "MonoDevelop.Debugger.ThreadsPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "d"; "s" ] _ -> [ gotoPad "MonoDevelop.Debugger.StackTracePad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "d"; "c" ] _ -> [ gotoPad "MonoDevelop.Debugger.StackTracePad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "u" ] _ -> wait
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "u"; "t" ] _ -> [ gotoPad "MonoDevelop.UnitTesting.TestPad" ]
| NotInsertMode, RemappedMatches layout [ "g"; "p"; "u"; "r" ] _-> [ gotoPad "MonoDevelop.UnitTesting.TestResultsPad" ]
| _, [] when numericArgument.IsSome -> wait
| _ -> resetKeys
action, newState
let keyPressToVimKey (keyPress:KeyDescriptor) =
match keyPress.KeyChar with
| c when keyPress.ModifierKeys = ModifierKeys.Control ->
Control c
| c when keyPress.ModifierKeys = ModifierKeys.Command ->
Super c
| 'z' when keyPress.ModifierKeys = ModifierKeys.Command ->
Key 'u'
| c when keyPress.KeyChar <> '\000' ->
Key c
| _ ->
match keyPress.SpecialKey with
| SpecialKey.Escape -> Esc
| SpecialKey.Return -> Ret
| SpecialKey.Left -> VimKey.Left
| SpecialKey.Down -> VimKey.Down
| SpecialKey.Up -> VimKey.Up
| SpecialKey.Right -> VimKey.Right
| SpecialKey.BackSpace -> Backspace
| SpecialKey.Delete -> VimKey.Delete
| _ -> Key keyPress.KeyChar
let handleKeyPress state (keyPress:KeyDescriptor) (editor:TextEditor) config =
let fileName = editor.FileName
let vimKey =
match state.mode, keyPress.KeyChar, config.insertModeEscapeKey with
| InsertMode, c, Some combo when (string c) = combo.insertModeEscapeKey1 ->
EscapeKey c
| InsertMode, c, Some combo when state.keys |> List.tryHead = Some (Key (char combo.insertModeEscapeKey1)) ->
EscapeKey c
| _ -> keyPressToVimKey keyPress
let newState = { state with keys = state.keys @ [ vimKey ] }
let action, newState = parseKeys newState config
let newState =
match state.statusMessage, state.mode, newState.mode with
| Some _, NormalMode, NormalMode -> { newState with statusMessage = None }
| _ -> newState
LoggingService.LogDebug (sprintf "%A" action)
let rec performActions config actions' state handled =
match actions' with
| [] -> state, handled
| [ only ] ->
match only.commandType with
| DoNothing -> state, true
| _ ->
let newState = runCommand config state editor only
{ newState with keys = [] }, true
| h::t ->
let newState = runCommand config state editor h
performActions config t newState true
let newState, handled =
let processKey() =
use group = editor.OpenUndoGroup()
state.macro
|> Option.iter(fun (Macro c) -> macros.[c] <- macros.[c] @ action)
performActions config action newState false
match state.mode, newState.keys |> List.map string with
| ExMode _, [ Escape ] -> processKey()
| ExMode _, _ ->
let state, actions = exMode.processKey state keyPress
performActions config actions state true
| _ -> processKey()
let firstAction = action |> List.head
let newState =
match config.keyboardLayout, state.mode, vimKey, firstAction.commandType with
| _, InsertMode, _, _ ->
newState.macro |> Option.iter(fun (Macro m) ->
macros.[m] <- macros.[m] @ [ typeChar vimKey ])
//{ newState with lastAction = newState.lastAction @ [ typeChar vimKey ]}
newState
| _, NotInsertMode, _, SwitchMode VisualModes
| _, NotInsertMode, _, Delete
| _, NotInsertMode, _, Change
| _, NotInsertMode, _, Indent
| _, NotInsertMode, _, UnIndent
| _, NotInsertMode, _, Put _
| _, NotInsertMode, _, ReplaceChar _
| Qwerty, NotInsertMode, Key 'a', _
| Qwerty, NotInsertMode, Key 'i', _
| Qwerty, NotInsertMode, Key 'I', _
| Qwerty, NotInsertMode, Key 'o', _
| Qwerty, NotInsertMode, Key 'O', _
| Qwerty, NotInsertMode, Key 'A', _
| Colemak, NotInsertMode, Key 'a', _
| Colemak, NotInsertMode, Key 'u', _
| Colemak, NotInsertMode, Key 'U', _
| Colemak, NotInsertMode, Key 'y', _
| Colemak, NotInsertMode, Key 'Y', _
| Colemak, NotInsertMode, Key 'A', _
| Dvorak, NotInsertMode, Key 'a', _
| Dvorak, NotInsertMode, Key 'c', _
| Dvorak, NotInsertMode, Key 'C', _
| Dvorak, NotInsertMode, Key 'r', _
| Dvorak, NotInsertMode, Key 'R', _
| Dvorak, NotInsertMode, Key 'A', _-> { newState with lastAction = action }
| _ -> newState
editorStates.[fileName] <- newState
newState, handled
================================================
FILE: XSVim/XSVim.fsproj
================================================
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{9DB313D4-4CD1-455F-846F-42CD234DE626}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>XSVim</RootNamespace>
<AssemblyName>XSVim</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<UseMSBuildEngine>false</UseMSBuildEngine>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<PlatformTarget></PlatformTarget>
<StartAction>Program</StartAction>
<OtherFlags>--warnon:1182</OtherFlags>
<StartAction>Program</StartAction>
<StartProgram>/Applications/Visual Studio.app/Contents/Resources/lib/monodevelop/bin/VisualStudio.exe</StartProgram>
<StartWorkingDirectory>/Applications/Visual Studio.app/Contents/Resources/lib/monodevelop/bin</StartWorkingDirectory>
<Commandlineparameters>-no-redirect</Commandlineparameters>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<DefineConstants></DefineConstants>
<ErrorReport>prompt</ErrorReport>
<GenerateTailCalls>true</GenerateTailCalls>
<PlatformTarget></PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="MonoDevelop.SourceEditor">
<HintPath>..\lib\MonoDevelop.SourceEditor.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="MonoDevelop.Core">
<HintPath>..\lib\MonoDevelop.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="MonoDevelop.Ide">
<HintPath>..\lib\MonoDevelop.Ide.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="Mono.Addins">
<HintPath>..\lib\Mono.Addins.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="System.Collections.Immutable">
<HintPath>..\lib\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Mac">
<HintPath>..\lib\Xamarin.Mac.dll</HintPath>
</Reference>
<Reference Include="Xwt">
<HintPath>..\lib\Xwt.dll</HintPath>
</Reference>
<Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="pango-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="Mono.Cairo" />
<Reference Include="FSharp.Core">
<HintPath>..\packages\FSharp.Core.4.5.4\lib\net45\FSharp.Core.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.fs" />
<Compile Include="Properties\AddinInfo.fs" />
<EmbeddedResource Include="Properties\Manifest.addin.xml" />
<Compile Include="SettingsPanel.fs" />
<Compile Include="Reflection.fs" />
<Compile Include="Classes.fs" />
<Compile Include="Types.fs" />
<Compile Include="WindowManagement.fs" />
<Compile Include="SubstituteCommand.fs" />
<Compile Include="ExMode.fs" />
<Compile Include="PadTreeViews.fs" />
<Compile Include="TreeViewPads.fs" />
<Compile Include="Mapping.fs" />
<Compile Include="XSVim.fs" />
<Compile Include="Addin.fs" />
<EmbeddedResource Include="KeyBindingSchemeVim.xml" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(FSharpTargetsPath)" />
</Project>
================================================
FILE: XSVim/packages.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FSharp.Core" version="4.5.4" targetFramework="net472" />
</packages>
================================================
FILE: XSVim.Tests/ChangeTests.fs
================================================
namespace XSVim.Tests
open NUnit.Framework
open System.Runtime.CompilerServices
open System.Threading.Tasks
[<TestFixture>]
module ``Change tests`` =
[<SetUp;AsyncStateMachine(typeof<Task>)>]
let ``run before tests``() =
FixtureSetup.initialiseMonoDevelop()
[<Test>]
let ``cc non empty line``() =
assertText "abc\nd$ef\nghi" "cc" "abc\n|\nghi"
[<Test>]
let ``cw changes word``() =
assertText "a$bc def" "cw" "| def"
[<Test>]
let ``C changes last character``() =
assertText "abc$" "C" "ab|"
[<Test>]
let ``cw changes space``() =
assertText "abc $def" "cw" "abc|def"
[<Test>]
let ``c2w changes two words``() =
assertText "a$bc def ghi" "c2w" "| ghi"
[<Test>]
let ``undo works after cw``() =
assertText "a$bc def ghi" "cw<esc>u" "a$bc def ghi"
[<Test>]
let ``undo works after c2w``() =
assertText "a$bc def ghi" "c2w<esc>u" "a$bc def ghi"
[<Test>]
let ``ce changes word``() =
assertText "a$bc def" "ce" "| def"
[<Test>]
let ``ce changes last character``() =
assertText "a$ bcd" "ce" "| bcd"
[<Test>]
let ``c% changes to matching parens``() =
assertText "abc($def)ghi" "c%" "abc|ghi"
[<Test>]
let ``Change to end of word does not include dot``() =
assertText "open Mon$o.Addins" "ce" "open Mo|.Addins"
[<Test>]
let ``Change to end of word includes dot``() =
assertText "open Mono$.Addins" "ce" "open Mon|Addins"
[<Test>]
let ``cc empty line``() =
assertText "abc\n\n$def" "cc" "abc\n|\ndef"
[<Test>]
let ``ci backtick``() =
assertText "``some t$ext``" "ci`" "``|``"
[<Test>]
let ``S changes entire line``() =
assertText " line1 \n line2$ \n line3 " "S" " line1 \n |\n line3 "
[<Test>]
let ``2S changes two lines``() =
assertText " line1 \n line2$ \n line3 \n line4 " "2S" " line1 \n |\n line4 "
[<Test>]
let ``s before the end of line``() =
assertText "a$b" "s" "|b"
[<Test>]
let ``s at the end of line``() =
assertText "ab$\n" "s" "a|\n"
================================================
FILE: XSVim.Tests/DeleteTests.fs
================================================
namespace XSVim.Tests
open NUnit.Framework
open System.Runtime.CompilerServices
open System.Threading.Tasks
[<TestFixture>]
module ``Delete tests`` =
[<SetUp;AsyncStateMachine(typeof<Task>)>]
let ``run before tests``() =
FixtureSetup.initialiseMonoDevelop()
[<Test>]
let ``Vjd test``() =
let source =
@"aaaaaa
bb$bbbb
cccccc
dddddd
eeeeee";
let expected =
@"aaaaaa
d$ddddd
eeeeee";
assertText source "Vjd" expected
[<Test>]
let ``Delete line and line below``() =
let source =
@"aaaaaa
bb$bbbb
cccccc
dddddd
eeeeee";
let expected =
@"aaaaaa
d$ddddd
eeeeee";
assertText source "dj" expected
[<Test>]
let ``Delete line and next two lines``() =
let source =
@"aaaaaa
bb$bbbb
cccccc
dddddd
eeeeee";
let expected =
@"aaaaaa
e$eeeee";
assertText source "d2j" expected
[<Test>]
let ``dd first line``() =
assertText "ab$c\n def" "dd" " d$ef"
[<Test>]
let ``dd next line is blank``() =
assertText
"""
{
fo$o
bar
}
"""
"dd"
// $ is over \n here
"""
{
$ bar
}
"""
[<Test>]
let ``2dd deletes 2 lines``() =
assertText "ab$c\ndef\nghi" "2dd" "g$hi"
[<Test>]
let ``2ddp puts 2 lines back``() =
assertText "abc\nde$f\nghi" "2ddp" "abc\nd$ef\nghi"
[<Test>]
let ``dd last line at EOF``() =
assertText "abc\ndef\ngh$i" "dd" "abc\nd$ef"
[<Test>]
let ``dd only line``() =
assertText "a$bc" "dd" "$"
[<Test>]
let ``Delete char under caret``() =
assertText "abc$def" "x" "abd$ef"
[<Test>]
let ``Delete char at EOL``() =
assertText "abcdef$\n" "xx" "abcd$\n"
[<Test>]
let ``x with multiplier stops at EOL (caret at EOL)``() =
assertText "abcdef$\n" "4x" "abcde$\n"
[<Test>]
let ``x with multiplier stops at EOL``() =
assertText "ab$cdef\n" "100x" "a$\n"
[<Test>]
let ``Delete char to left of caret``() =
assertText "abc$def" "X" "ac$def"
[<Test>]
let ``Delete to end of line``() =
assertText "abc$ def\nghi" "d$" "ab$\nghi"
[<Test>]
let ``Delete to end of document``() =
assertText "abc\nde$f\nghi" "dG" "abc\n$"
[<Test>]
let ``Delete to start of document``() =
assertText "abc\nde$f\nghi" "dgg" "g$hi"
[<Test>]
let ``Delete to end of line using D``() =
assertText "abc$ def\nghi" "D" "ab$\nghi"
[<Test>]
let ``Delete to end of line from start keeps caret on current line``() =
assertText "abc\nd$ef\nghi" "D" "abc\n\n$ghi"
[<Test>]
let ``Deletes word``() =
assertText "a$bc def" "dw" "d$ef"
[<Test>]
let ``Delete to end of word``() =
assertText "ab$c def" "de" "a $def"
[<Test>]
let ``Delete to end of word does not include dot``() =
assertText "open Mon$o.Addins" "de" "open Mo.$Addins"
[<Test>]
let ``Delete to end of word includes dot``() =
assertText "open Mono$.Addins" "de" "open MonA$ddins"
[<Test>]
let ``Delete word does not include dot``() =
assertText "open Mo$no.Addins" "dw" "open M.$Addins"
[<Test>]
let ``Delete to end of WORD``() =
assertText "ab$c.def ghi" "dE" "a $ghi"
[<Test>]
let ``Deleting last word doesn't delete delimiter'``() =
assertText "abc d$ef \nghi" "dw" "abc $\nghi"
[<Test>]
let ``Deleting last word touching EOL doesn't delete delimiter'``() =
assertText "abc d$ef\nghi" "dw" "abc $\nghi"
[<Test>]
let ``Delete char to left doesn't delete past start of line``() =
assertText "abcdef\nab$cdef" "XXX" "abcdef\nb$cdef"
[<Test>]
let ``dw last word at EOF``() =
assertText "a$bc" "dw" "$"
[<Test>]
let ``dw to brace #167``() =
assertText "abc\n $ {" "dw" "abc\n{$"
[<Test>]
let ``d]) deletes to next unmatched )``() =
assertText "if (a$ == (b)c)" "d])" "if ($"
[<Test>]
let ``dib deletes nested brackets``() =
assertText "(fo$o (bar (foo) ) )" "dib" "()$"
[<Test>]
let ``dib deletes nested brackets backwards``() =
assertText "(foo (bar (foo) ) $ )" "dib" "()$"
[<Test>]
let ``dib outside brackets does nothing``() =
assertText "don't crash$ me" "dib" "don't crash$ me"
================================================
FILE: XSVim.Tests/ExModeTests.fs
================================================
namespace XSVim.Tests
open NUnit.Framework
open XSVim
open System.Runtime.CompilerServices
open System.Threading.Tasks
[<TestFixture>]
module ``Ex mode tests`` =
[<SetUp;AsyncStateMachine(typeof<Task>)>]
let ``run before tests``() =
FixtureSetup.initialiseMonoDevelop()
[<Test>]
let ``/ searches for word``() =
assertText "ab$c abc" "/abc<ret>" "abc a$bc"
[<Test>]
let ``/ is case insensitive``() =
assertText "ab$c ABC" "/abc<ret>" "abc A$BC"
[<Test>]
let ``/ is case sensitive``() =
assertText "ab$c ABC Abc" "/Abc<ret>" "abc ABC A$bc"
[<Test>]
let ``deletes to search term``() =
assertText "ab$c ABC Abc 123" "d/123<ret>" "a1$23"
[<Test>]
let ``n searches for next word``() =
assertText "ab$c abc abc" "/abc<ret>n" "abc abc a$bc"
[<Test>]
let ``n wraps to start``() =
assertText "ab$c abc abc" "/abc<ret>nn" "a$bc abc abc"
[<Test>]
let ``N searches for previous word``() =
assertText "ab$c abc abc" "/abc<ret>N" "a$bc abc abc"
[<Test>]
let ``n searches for previous word after ?``() =
assertText "abc abc a$bc" "?abc<ret>n" "a$bc abc abc"
[<Test>]
let ``? searches for word backwards``() =
assertText "abc abc a$bc" "?abc<ret>" "abc a$bc abc"
[<Test>]
let ``:2 jumps to line 2``() =
assertText "l$ine1\nline2" ":2<ret>" "line1\nl$ine2"
[<Test>]
let ``Backspacing ex mode returns to normal mode``() =
let _, state, _ = test "abc abc a$bc" "/a<bs><bs>"
state.mode |> should equal NormalMode
[<Test>]
let ``<esc> returns to normal mode``() =
let _, state, _ = test "abc abc a$bc" "/<esc>"
state.mode |> should equal NormalMode
[<Test>]
let ``<C-c> returns to normal mode``() =
let _, state, _ = test "abc abc a$bc" "/<C-c>"
state.mode |> should equal NormalMode
[<Test>]
let ``<C-[> returns to normal mode``() =
let _, state, _ = test "abc abc a$bc" "/<C-[>"
state.mode |> should equal NormalMode
[<Test>]
let ``Displays could not parse message``() =
let _, state, _ = test "a$bc" ":garbage<ret>"
state.statusMessage.Value |> should equal "Could not parse :garbage"
[<Test>]
let ``Could not parse message is reset``() =
let _, state, _ = test "a$bc" ":garbage<ret>l"
state.statusMessage |> should equal None
[<Test>]
let ``Deletes lines 2 to 4``() =
assertText
"""11111
22222
33333
44444
55555$"""
":2,4d<ret>"
"""11111
5$5555"""
[<Test>]
let ``Switching to substitute command mode with a selection``() =
let _, state, _ = test "a$bc" "v:"
state.statusMessage |> should equal (Some ":'<,'>")
================================================
FILE: XSVim.Tests/IndentationTests.fs
================================================
namespace XSVim.Tests
open NUnit.Framework
open XSVim
open System.Runtime.CompilerServices
open System.Threading.Tasks
[<TestFixture>]
module ``Indentation tests`` =
[<SetUp;AsyncStateMachine(typeof<Task>)>]
let ``run before tests``() =
FixtureSetup.initialiseMonoDevelop()
[<Test>]
let ``>> indents right in normal mode``() =
assertText "a$bc" ">>" " a$bc"
[<Test>]
let ``indent is repeatable``() =
assertText "a$bc" ">>." " a$bc"
[<Test>]
let ``V> indents line right``() =
let text, state, _ = test "a$bc\ndef" "V>"
text |> should equal " a$bc\ndef"
state.mode |> should equal NormalMode
[<Test>]
let ``V2> indents line right twice``() =
assertText "a$bc\ndef" "V2>" " a$bc\ndef"
[<Test>]
let ``>j indents current line and line below``() =
assertText "a$bc\ndef" ">j" " a$bc\n def"
[<Test>]
let ``<j unindents current line and line below``() =
assertText " a$bc\n def" "<j" "a$bc\ndef"
[<Test;Ignore("Doesn't place caret at correct location")>]
let ``>2j indents current line and two lines below``() =
assertText "a$bc\ndef\nghi" ">2j" " a$bc\n def\n ghi"
[<Test>]
let ``>gg indents to top of file``() =
assertText "abc\ndef\ngh$i" ">gg" " abc\n def\n gh$i"
[<Test>]
let ``V> indents line``() =
assertText "abc\ndef\ngh$i" ">gg" " abc\n def\n gh$i"
[<Test>]
let ``>2gg indents to line 2``() =
assertText "abc\ndef\ngh$i" ">2gg" "abc\n def\n gh$i"
[<Test>]
let ``>2G indents to line 2``() =
assertText "abc\ndef\ngh$i" ">2G" "abc\n def\n gh$i"
[<Test>]
let ``== autoindents line``() =
assertText "abc\n def\ngh$i" "==" "abc\n def\n g$hi"
[<Test>]
let ``= autoindents selection``() =
assertText "abc\n def\ngh$i" "V=" "abc\n def\n g$hi"
[<Test>]
let ``= autoindents multiple line selection``() =
assertText "abc\n de$f\n ghi\n jkl" "Vj=" "abc\nd$ef\nghi\n jkl"
[<Test>]
let ``=gg indents to top of file``() =
assertText "abc\n def\n gh$i" "=gg" "a$bc\ndef\nghi"
================================================
FILE: XSVim.Tests/InsertionTests.fs
================================================
namespace XSVim.Tests
open NUnit.Framework
open XSVim
open System.Runtime.CompilerServices
open System.Threading.Tasks
[<TestFixture>]
module ``Insertion tests`` =
[<SetUp;AsyncStateMachine(typeof<Task>)>]
let ``run before tests``() =
FixtureSetup.initialiseMonoDevelop()
[<Test>]
let ``'O' should insert line above``() =
//TODO: 'O' is broken on the top line
assertText " \n a$bcdef" "O" " \n |\n abcdef"
[<Test>]
let ``'O' inserts line above``() =
assertText "abc$def\n" "O" "|\nabcdef\n"
================================================
FILE: XSVim.Tests/KeyParsing.fs
================================================
namespace XSVim.Tests
open XSVim
open NUnit.Framework
[<TestFixture>]
module ``Key parsing tests`` =
let test keys =
let keys = [for c in keys -> Key c]
let state = { VimState.Default with keys=keys }
let config = Config.Default
let action, _state = Vim.parseKeys state config
let first = action.Head
first.repeat, first.commandType, first.textObject
[<Test>]
let ``10j``() =
test "10j" |> should equal (Some 10, Move, Down)
[<Test>]
let ``11G``() =
test "11G" |> should equal (Some 1, Move, Jump (StartOfLineNumber 11))
================================================
FILE: XSVim.Tests/KeyboardMap.fs
================================================
namespace XSVim.Tests
open XSVim
module mapFromQwerty =
let qwertyToColemak = function
| 'e' -> 'f'
| 'r' -> 'p'
| 't' -> 'g'
| 'y' -> 'j'
| 'u' -> 'l'
| 'i' -> 'u'
| 'o' -> 'y'
| 'p' -> ';'
| 's' -> 'r'
| 'd' -> 's'
| 'f' -> 't'
| 'g' -> 'd'
| 'j' -> 'n'
| 'k' -> 'e'
| 'l' -> 'i'
| ';' -> 'o'
| 'n' -> 'k'
| 'E' -> 'F'
| 'R' -> 'P'
| 'T' -> 'G'
| 'Y' -> 'J'
| 'U' -> 'L'
| 'I' -> 'U'
| 'O' -> 'Y'
| 'P' -> ':'
| 'S' -> 'R'
| 'D' -> 'S'
| 'F' -> 'T'
| 'G' -> 'D'
| 'J' -> 'N'
| 'K' -> 'E'
| 'L' -> 'I'
| ':' -> 'O'
| 'N' -> 'K'
| c -> c
let qwertyToDvorak = function
| '-' -> '['
| '=' -> ']'
| 'q' -> '''
| 'w' -> ','
| 'e' -> '.'
| 'r' -> 'p'
| 't' -> 'y'
| 'y' -> 'f'
| 'u' -> 'g'
| 'i' -> 'c'
| 'o' -> 'r'
| 'p' -> 'l'
| '[' -> '/'
| ']' -> '='
| 's' -> 'o'
| 'd' -> 'e'
| 'f' -> 'u'
| 'g' -> 'i'
| 'h' -> 'd'
| 'j' -> 'h'
| 'k' -> 't'
| 'l' -> 'n'
| ';' -> 's'
| ''' -> '-'
| 'z' -> ';'
| 'x' -> 'q'
| 'c' -> 'j'
| 'v' -> 'k'
| 'b' -> 'x'
| 'n' -> 'b'
| ',' -> 'w'
| '.' -> 'v'
| '/' -> 'z'
| '_' -> '{'
| '+' -> '}'
| 'Q' -> '\''
| 'W' -> '<'
| 'E' -> '>'
| 'R' -> 'P'
| 'T' -> 'Y'
| 'Y' -> 'F'
| 'U' -> 'G'
| 'I' -> 'C'
| 'O' -> 'R'
| 'P' -> 'L'
| '{' -> '?'
| '}' -> '+'
| 'S' -> 'O'
| 'D' -> 'E'
| 'F' -> 'U'
| 'G' -> 'I'
| 'H' -> 'D'
| 'J' -> 'H'
| 'K' -> 'T'
| 'L' -> 'N'
| ':' -> 'S'
| '"' -> '_'
| 'Z' -> ':'
| 'X' -> 'Q'
| 'C' -> 'J'
| 'V' -> 'K'
| 'B' -> 'X'
| 'N' -> 'B'
| 'M' -> 'M'
| '<' -> 'W'
| '>' -> 'V'
| '?' -> 'Z'
| c -> c
let remap layout key =
match layout with
| Colemak -> qwertyToColemak key
| Dvorak -> qwertyToDvorak key
| _ -> key
================================================
FILE: XSVim.Tests/MacrosTests.fs
================================================
namespace XSVim.Tests
open NUnit.Framework
open XSVim
open System.Runtime.CompilerServices
open System.Threading.Tasks
[<TestFixture>]
module ``Macro tests`` =
[<SetUp;AsyncStateMachine(typeof<Task>)>]
let ``run before tests``() =
FixtureSetup.initialiseMonoDevelop()
[<Test>]
let ``Start recording macro q``() =
let _, state, _ = test " $" "qq"
state.macro |> should equal (Some (Macro 'q'))
[<Test>]
let ``Stop recording macro q``() =
let _, state, _ = test " $" "qqq"
state.macro |> should equal None
[<Test>]
let ``Replay macro q``() =
assertText "a$bc abc" "qqfcad<esc>q@q" "abcd abcd$"
[<Test>]
let ``Macros are repeatable``() =
assertText "a$bc abc abc abc" "qqfcad<esc>q3@q" "abcd abcd abcd abcd$"
[<Test>]
let ``Macros containing repeats are repeatable``() =
assertText " $aa aa aa aa" "qq2faab<esc>q3@q" " aab aab aab aab$"
================================================
FILE: XSVim.Tests/MarkerTests.fs
================================================
namespace XSVim.Tests
open NUnit.Framework
open XSVim
open System.Runtime.CompilerServices
open System.Threading.Tasks
[<TestFixture>]
module ``Marker tests`` =
[<SetUp;AsyncStateMachine(typeof<Task>)>]
let ``run before tests``() =
FixtureSetup.initialiseMonoDevelop()
[<Test>]
let ``ma adds marker a``() =
assertText "a$bc" "mall`a" "a$bc"
[<Test>]
let ``'a moves to start of line of marker a``() =
assertText "123 a$bc" "mall'a" "1$23 abc"
[<Test>]
let ``'. jumps to last edit line``() =
assertText " 123 a$bc" "i<esc>'." " 1$23 abc"
[<Test>]
let ``'' jumps to last jump location line``() =
assertText "ab$c\ndef" "G''" "a$bc\ndef"
[<Test>]
let ``Deletes from 4 up to marker a``() =
assertText "1234XXXXXXXa$5678" "maF4d`a" "123a$5678"
[<Test>]
let ``Selects from 4 up to marker a``() =
let _ = test "1234XXXXXXXa$5678" "maF4v`ay"
Vim.registers.[EmptyRegister].content
|> should equal "4XXXXXXXa"
[<Test>]
let ``Deletes linewise between marker a and marker b``() =
assertText
"""
.........
aaaaa$aaaa
.........
.........
bbbbbbbbb
.........
"""
"ma/b<ret>mb:'a,'bd<ret>"
"""
.........
.$........
"""
================================================
FILE: XSVim.Tests/MiscTests.fs
================================================
namespace XSVim.Tests
open NUnit.Framework
open XSVim
open System.Runtime.CompilerServices
open System.Threading.Tasks
[<TestFixture>]
module ``Miscellaneous tests`` =
[<SetUp;AsyncStateMachine(typeof<Task>)>]
let ``run before tests``() =
FixtureSetup.initialiseMonoDevelop()
[<Test>]
let ``'A' should put caret at end of the line``() =
assertText "abc$def\n" "A" "abcdef|\n"
[<Test>]
let ``'A' should put caret at EOF``() =
assertText "abc$def" "A" "abcdef|"
[<Test>]
let ``'a' should append after``() =
assertText "a$bcdef" "a" "a|bcdef"
[<Test>]
let ``'a' should append after last char``() =
assertText "abcdef$\n" "a" "abcdef|\n"
[<Test>]
let ``'a' should append before EOF``() =
assertText "abcdef$" "a" "abcdef|"
[<Test>]
let ``'a' on empty line should keep cursor on the current line``() =
assertText "\n$abc" "a" "|\nabc"
[<Test>]
let ``'I' should insert at first non whitespace``() =
assertText " abcdef$" "I" " |abcdef"
[<Test>]
let ``Undo repeat``() =
assertText "a$bc def ghi" "3dwu" "a$bc def ghi"
[<Test>]
let ``Repeat typed chars``() =
assertText "d$" "iabc <esc>." "abcabc $ d"
[<Test>]
let ``Repeat intellisense``() =
let _, state, editor =
test
"""
System$
System
""" "a.Coll"
// simulate ctrl-space intellisense inserting "Collections" by removing
// "Coll" and then inserting "Collections"
let offset = editor.Text.IndexOf("Coll")
editor.RemoveText(offset,4) // delete "Coll"
editor.InsertText(editor.CaretOffset, "Collections")
let config = Config.Default
let _, newState, _ = sendKeysToEditor editor "<esc>j." config
let text = getEditorText editor newState
text
|> should equal
"""
System.Collections
System.Collections$
"""
[<Test>]
let ``Large text addition is not too slow!``() =
let _, state, editor =
test " $" ""
editor.InsertText(0, new System.String('x', 200000))
[<Test>]
let ``backspace is repeated``() =
assertText "d$" "iabc<bs> <esc>." "abab $ d"
[<Test; Ignore("TODO")>]
let ``delete key is repeated``() =
assertText "d$" "i<del>abc<esc>." "ababc$"
[<Test>]
let ``<C-[> escapes``() =
assertText " abc$" "i<C-[>" " ab$c"
[<Test>]
let ``Return to normal mode doesn't move past start of line``() =
assertText "abc\nd$ef" "i<esc>" "abc\nd$ef"
[<Test>]
let ``dot repeats at start of line``() =
assertText
"""
def$
def
"""
"Iabc<esc>j."
"""
abcdef
abc$def
"""
[<Test>]
let ``dot repeats at end of line``() =
assertText
"""
a$bc
a$bc
"""
"Adef<esc>j."
"""
abcdef
abcdef$
"""
[<Test>]
let ``Repeat delete word``() =
assertText "a$bc de fgh" "dww." "de $"
[<Test>]
let ``Repeat change word``() =
assertText "a$bc de fgz " "cwxxx<esc>ww." "xxx de xxx$ "
[<Test>]
let ``r should be repeatable``() =
assertText "a$aaa" "rb$." "baab$"
[<Test>]
let ``r<ret> inserts <ret> and indents``() =
assertText " aaa$\nbbb" "r<ret>" " aa\n \n$bbb"
[<Test>]
let ``R switches to replace mode``() =
let _, state, _ = test "a$bc" "R"
state.mode |> should equal ReplaceMode
[<Test>]
let ``R replaces characters``() =
assertText "a$bc" "RABCD" "ABCD$"
[<Test>]
let ``R replaces digits``() =
assertText "a$bc" "R123" "123$"
[<Test>]
let ``Replace mode inserts at end of line``() =
assertText "a$bc\ndef" "RABCD" "ABCD\n$def"
[<Test>]
let ``Replace mode is undoable``() =
assertText "a$bc\ndef" "RABCD<esc>u" "a$bc\ndef"
[<Test>]
let ``Undo insert mode``() =
assertText "abc$" "adef ghi jkl<esc>u" "abc$"
[<Test>]
let ``J puts caret between joined lines``() =
assertText "a$bc\ndef" "J" "abc $def"
[<Test>]
let ``* finds next word``() =
assertText " $ abc" "*" " a$bc"
[<Test>]
let ``* does not match substring``() =
assertText "a$bc abcde abc" "*" "abc abcde a$bc"
[<Test>]
let ``* finds next word at caret``() =
assertText "a$bc abc" "*" "abc a$bc"
[<Test>]
let ``n finds next match``() =
assertText "a$bc abc cba abc" "*n" "abc abc cba a$bc"
[<Test>]
let ``* finds next word when on last word char``() =
assertText "abc$ abc" "*" "abc a$bc"
[<Test>]
let ``* wraps to start``() =
assertText "abc a$bc" "*" "a$bc abc"
[<Test>]
let ``* finds next word at EOF``() =
assertText "abc abc$" "*" "a$bc abc"
[<Test>]
let ``# finds previous word at caret``() =
assertText "abc abc a$bc" "#" "abc a$bc abc"
[<Test>]
let ``# matches exact word``() =
assertText "abc abcde a$bc" "#" "a$bc abcde abc"
[<Test>]
let ``£ wraps to end``() =
assertText "a$bc abc" "£" "abc a$bc"
[<Test>]
let ``~ toggles case of char at caret``() =
assertText "a$bc abc" "~" "Ab$c abc"
[<Test>]
let ``~ toggles case of selection``() =
assertText "A$bC abc" "vll~" "a$Bc abc"
[<Test>]
let ``<esc> doesn't move caret left onto newline'``() =
assertText "\nA$bC abc" "o<esc>" "\nAbC abc\n$"
[<Test>]
let ``<C-a> increments next number``() =
assertText "a$bc 9 " "<C-a>" "abc 10$ "
[<Test>]
let ``<C-a> increments second number``() =
assertText "abc 0 $1 " "<C-a>" "abc 0 2$ "
[<Test>]
let ``<C-a> increments same width number``() =
assertText "a$bc 0 " "<C-a>" "abc 1$ "
[<Test>]
let ``<C-a> increments number at caret``() =
assertText "abc 9$ " "<C-a>" "abc 10$ "
[<Test>]
let ``<C-a> increments next negative number``() =
assertText "a$bc -1 " "<C-a>" "abc 0$ "
[<Test>]
let ``<C-x> decrements next number``() =
assertText "a$bc 10 " "<C-x>" "abc 9$ "
[<Test>]
let ``<C-x> decrements -1``() =
assertText "abc -1$ " "<C-x>" "abc -2$ "
[<Test>]
let ``dot repeats 2dd``() =
assertText
"""
a$aaaa
aaaaa
bbbbb
bbbbb
ccccc
"""
"2dd."
"""
c$cccc
"""
[<Test>]
let ``dot repeats 2dj``() =
assertText
"""
a$aaaa
aaaaa
aaaaa
bbbbb
bbbbb
bbbbb
"""
"2dj."
"""
$"""
[<Test>]
let ``dot repeats 3S``() =
assertText
"""
a$aaaa
aaaaa
bbbbb
bbbbb
ccccc
ccccc"""
"3S<esc>."
"""
$ ccccc"""
================================================
FILE: XSVim.Tests/Movement.fs
================================================
namespace XSVim.Tests
open NUnit.Framework
open XSVim
open System.Runtime.CompilerServices
open System.Threading.Tasks
[<TestFixture>]
module ``Movement tests`` =
[<SetUp;AsyncStateMachine(typeof<Task>)>]
let ``run before tests``() =
FixtureSetup.initialiseMonoDevelop()
[<Test>]
let ``Move to next word``() =
assertText "aa$a bbb" "w" "aaa b$bb"
[<Test>]
let ``Move to next word on next line``() =
assertText "aa$a\n bbb" "w" "aaa\n b$bb"
[<Test>]
let ``Move to empty line``() =
assertText "aa$a\n\nbbb" "w" "aaa\n\n$bbb"
[<Test>]
let ``Moves to EOF when there is no next word``() =
assertText "aa$aa" "w" "aaaa$"
[<Test>]
let ``w skips over tabs``() =
assertText "\t$\t\taaaa" "w" "\t\t\ta$aaa"
[<Test>]
let ``Move to word end``() =
assertText "aa$a bbb" "e" "aaa$ bbb"
[<Test>]
let ``Move to next word end``() =
assertText "aaa$ bbb" "e" "aaa bbb$"
[<Test>]
let ``Move to second word end``() =
assertText "aa$a bbb" "ee" "aaa bbb$"
[<Test>]
let ``e jumps over punctuation``() =
assertText "int model$);\n " "e" "int model);$\n "
[<Test>]
let ``e jumps over chevrons``() =
assertText "Task<List<SomeWord$>> nextWord" "e" "Task<List<SomeWord>>$ nextWord"
[<Test>]
let ``e jumps from chevron to end of next word``() =
assertText "Task<List<SomeWord>>$ nextWord" "e" "Task<List<SomeWord>> nextWord$"
[<Test>]
let ``e jumps over spaces``() =
assertText " $ abcde" "e" " abcde$"
[<Test>]
let ``e stops before dot``() =
assertText "open$ System.Collections.Generic" "e" "open System$.Collections.Generic"
[<Test>]
let ``Move to end of line``() =
assertText "aa$a aaa\nbbb" "$" "aaa aaa$\nbbb"
[<Test>]
let ``Move to end of document``() =
assertText "aa$aaaa\nbbbbbb" "G" "aaaaaa\nb$bbbbb"
[<Test>]
let ``Move to start of document``() =
assertText "aaaaaa\nbb$bbbb" "gg" "a$aaaaa\nbbbbbb"
[<Test>]
let ``Move to line 2``() =
assertText "a$aaaaa\n bbbbbb" "2gg" "aaaaaa\n b$bbbbb"
[<Test>]
let ``Move to line 3``() =
assertText "a$aaaaa\nbbbbbb\ncccccc\ndddddd" "3G" "aaaaaa\nbbbbbb\nc$ccccc\ndddddd"
[<Test>]
let ``Move down to desired column``() =
assertText "12345$6\n123\n123456" "jj" "123456\n123\n12345$6"
[<Test>]
let ``Move down to last column``() =
assertText "12345$6\n123\n123456" "j" "123456\n123$\n123456"
[<Test>]
let ``Move across then down``() =
assertText "1$2\n12\n" "lj" "12\n12$\n"
[<Test>]
let ``Move ten right``() =
assertText "a$bcdefghijkl" "10l" "abcdefghijk$l"
[<Test>]
let ``Does not move right past delimiter``() =
assertText "a$b\n" "ll" "ab$\n"
[<Test>]
let ``Find moves to digit``() =
assertText "abc$ d1 d2 d3" "f2" "abc d1 d2$ d3"
[<Test>]
let ``Reverse find moves to digit``() =
assertText "abc d1 d2 d$3" "F1" "abc d1$ d2 d3"
[<Test>]
let ``Till moves to digit``() =
assertText "abc$ d1 d2 d3" "t2" "abc d1 d$2 d3"
[<Test>]
let ``Reverse till moves to digit``() =
assertText "abc d1 d2 d$3" "T1" "abc d1 $d2 d3"
[<Test>]
let ``2fd moves to second d``() =
assertText "abc$ d1 d2 d3" "2fd" "abc d1 d$2 d3"
[<Test>]
let ``F finds previous char``() =
assertText "a a$" "Fa" "a$ a"
[<Test>]
let ``f is repeatable with ;``() =
assertText " $ a1 a2" "fa;" " a1 a$2"
[<Test>]
let ``f is reversed with ,``() =
assertText " $ a1 a2" "fa;," " a$1 a2"
[<Test>]
let ``t does not move if caret is already just before search char``() =
assertText " $a1 a2" "ta" " $a1 a2"
[<Test>]
let ``T does not move if caret is already just after search char``() =
assertText "a1 a2$" "Ta" "a1 a2$"
[<Test>]
let ``t is repeatable with ;``() =
assertText " $a1 a2" "ta;" " a1 $a2"
[<Test>]
let ``T is repeatable with ;``() =
assertText "a1 a2$" "Ta;" "a1$ a2"
[<Test>]
let ``ge moves back to end of last word``() =
assertText "abc de$f" "ge" "abc$ def"
[<Test>]
let ``ge between words moves back to end of last word``() =
assertText "abc $def" "ge" "abc$ def"
[<Test>]
let ``ge stops at first character``() =
assertText "abc$" "ge" "a$bc"
[<Test>]
let ``gE moves back to end of last WORD``() =
assertText "abc def.gh$i" "gE" "abc$ def.ghi"
[<Test>]
let ``l stops at EOL``() =
assertText "abc$\ndef" "l" "abc$\ndef"
[<Test>]
let ``space moves past EOL``() =
assertText "abc$\ndef" " " "abc\nd$ef"
[<Test>]
let ``% moves to matching parens``() =
assertText "($foo(bar))" "%" "(foo(bar))$"
[<Test>]
let ``[{ goes to previous unmatched {``() =
assertText "func { case { a } case$ { b } }" "[{" "func {$ case { a } case { b } }"
[<Test>]
let ``[( goes to previous unmatched (``() =
assertText "if (a == (b)c$)" "[(" "if ($a == (b)c)"
[<Test>]
let ``]} goes to next unmatched }``() =
assertText "func { case$ { a } case { b } }" "]}" "func { case { a } case { b } }$"
[<Test>]
let ``]) goes to next unmatched )``() =
assertText "if (a$ == (b)c)" "])" "if (a == (b)c)$"
================================================
FILE: XSVim.Tests/Properties/AssemblyInfo.fs
================================================
namespace XSVim.Tests
open System.Reflection
open System.Runtime.CompilerServices
[<assembly: AssemblyTitle("XSVim.Tests")>]
[<assembly: AssemblyDescription("")>]
[<assembly: AssemblyConfiguration("")>]
[<assembly: AssemblyCompany("")>]
[<assembly: AssemblyProduct("")>]
[<assembly: AssemblyCopyright("(c) Jason Imison")>]
[<assembly: AssemblyTrademark("")>]
// The assembly version has the format {Major}.{Minor}.{Build}.{Revision}
[<assembly: AssemblyVersion("1.0.0.0")>]
//[<assembly: AssemblyDelaySign(false)>]
//[<assembly: AssemblyKeyFile("")>]
()
================================================
FILE: XSVim.Tests/TestHelpers.fs
================================================
namespace XSVim.Tests
open System
open System.Text.RegularExpressions
open System.Threading.Tasks
open MonoDevelop.Ide.Editor.Extension
open NUnit.Framework
open XSVim
open MonoDevelop.Core
open MonoDevelop.Ide.Editor
[<AutoOpen>]
module FsUnit =
open System.Diagnostics
open NUnit.Framework.Constraints
[<DebuggerNonUserCode>]
let should (f : 'a -> #Constraint) x (y : obj) =
let c = f x
let y =
match y with
| :? (unit -> unit) -> box (new TestDelegate(y :?> unit -> unit))
| _ -> y
Assert.That(y, c)
let shouldnot (f : 'a -> #Constraint) x (y : obj) =
let c = f x
let y =
match y with
| :? (unit -> unit) -> box (new TestDelegate(y :?> unit -> unit))
| _ -> y
Assert.That(y, new NotConstraint(c))
let equal x = new EqualConstraint(x)
// like "should equal", but validates same-type
let shouldEqual (x: 'a) (y: 'a) = Assert.AreEqual(x, y, sprintf "Expected: %A\nActual: %A" x y)
let replaceLineEnding (s:string) =
s.Replace("\r\n", "\n")
let shouldEqualIgnoringLineEndings (x: string) (y: string) =
Assert.AreEqual((replaceLineEnding x), (replaceLineEnding y), sprintf "Expected: %A\nActual: %A" x y)
let notEqual x = new NotConstraint(new EqualConstraint(x))
let NOT c = new NotConstraint(c)
let contain x = new ContainsConstraint(x)
let haveLength n = Has.Length.EqualTo(n)
let haveCount n = Has.Count.EqualTo(n)
let NotEmpty = Has.Length.GreaterThan(0)
let endWith (s:string) = new EndsWithConstraint(s)
let startWith (s:string) = new StartsWithConstraint(s)
let be = id
let Null = new NullConstraint()
let Empty = new EmptyConstraint()
let EmptyString = new EmptyStringConstraint()
let NullOrEmptyString = new NullOrEmptyStringConstraint()
let True = new TrueConstraint()
let False = new FalseConstraint()
let sameAs x = new SameAsConstraint(x)
let throw = Throws.TypeOf
module FixtureSetup =
let firs
gitextract_1b8pllvk/ ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── XSVim/ │ ├── Addin.fs │ ├── Classes.fs │ ├── ExMode.fs │ ├── KeyBindingSchemeVim.xml │ ├── Mapping.fs │ ├── PadTreeViews.fs │ ├── Properties/ │ │ ├── AddinInfo.fs │ │ ├── AssemblyInfo.fs │ │ └── Manifest.addin.xml │ ├── Reflection.fs │ ├── SettingsPanel.fs │ ├── SubstituteCommand.fs │ ├── TreeViewPads.fs │ ├── Types.fs │ ├── WindowManagement.fs │ ├── XSVim.fs │ ├── XSVim.fsproj │ └── packages.config ├── XSVim.Tests/ │ ├── ChangeTests.fs │ ├── DeleteTests.fs │ ├── ExModeTests.fs │ ├── IndentationTests.fs │ ├── InsertionTests.fs │ ├── KeyParsing.fs │ ├── KeyboardMap.fs │ ├── MacrosTests.fs │ ├── MarkerTests.fs │ ├── MiscTests.fs │ ├── Movement.fs │ ├── Properties/ │ │ └── AssemblyInfo.fs │ ├── TestHelpers.fs │ ├── TextObjectSelectionTests.fs │ ├── VisualTests.fs │ ├── XSVim.Tests.fsproj │ ├── YankAndPut.fs │ └── packages.config ├── XSVim.sln ├── addin-project.xml ├── build.sh ├── copy-assemblies.sh ├── run-from-source.sh └── test.sh
Condensed preview — 47 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (236K chars).
[
{
"path": ".gitattributes",
"chars": 569,
"preview": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Custom for Visual Studio\n*.cs diff=csharp\n*.sln"
},
{
"path": ".gitignore",
"chars": 207,
"preview": "# F#\n[Bb]in/\n[Oo]bj/\n.fake/\nrepository/*\n*.suo\n*.pidb\n*.userprefs\n*.GhostDoc.xml\n*.user\n*.dll\n*.pdb\n*.cache\n*.swp\n*.swo\n"
},
{
"path": ".travis.yml",
"chars": 1429,
"preview": "language: csharp\nos: osx\nmono: latest\nsolution: XSVim.sln\ninstall:\n- nuget restore XSVim.sln\n- wget https://download.vi"
},
{
"path": "LICENSE",
"chars": 1069,
"preview": "MIT License\n\nCopyright (c) 2016 Jason Imison\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
},
{
"path": "README.md",
"chars": 4233,
"preview": "# XSVim [](https://gitter.im/XSVim/Lobby?utm_source=badge&utm_medium="
},
{
"path": "XSVim/Addin.fs",
"chars": 9632,
"preview": "namespace XSVim\n\nopen System\nopen System.Collections.Generic\nopen MonoDevelop.Components.Commands\nopen MonoDevelop.Core"
},
{
"path": "XSVim/Classes.fs",
"chars": 1117,
"preview": "namespace XSVim\nopen MonoDevelop.Core\nopen MonoDevelop.Core.Text\nopen MonoDevelop.Ide\nopen MonoDevelop.Ide.Editor\nopen "
},
{
"path": "XSVim/ExMode.fs",
"chars": 7219,
"preview": "namespace XSVim\nopen System\nopen System.Text.RegularExpressions\nopen Reflection\nopen MonoDevelop.Ide\nopen MonoDevelop.I"
},
{
"path": "XSVim/KeyBindingSchemeVim.xml",
"chars": 1692,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<scheme version=\"1.0\">\n <!-- Vim keybinding scheme containing all the keys t"
},
{
"path": "XSVim/Mapping.fs",
"chars": 2428,
"preview": "namespace XSVim\n\n[<AutoOpen>]\nmodule mapping =\n let colemakToQwerty = function\n | \"f\" -> \"e\"\n | \"p\" ->"
},
{
"path": "XSVim/PadTreeViews.fs",
"chars": 1096,
"preview": "namespace XSVim\n\nopen Gtk\nopen MonoDevelop.Ide.Gui.Components\n\nmodule padTreeViews =\n let select (tree:TreeView) pat"
},
{
"path": "XSVim/Properties/AddinInfo.fs",
"chars": 586,
"preview": "namespace XSVim\n\nopen Mono.Addins\nopen MonoDevelop\n[<assembly:Addin (\n \"XSVim\",\n Namespace = \"XSVim\",\n Version = ver"
},
{
"path": "XSVim/Properties/AssemblyInfo.fs",
"chars": 321,
"preview": "namespace XSVim\nopen System.Reflection\nopen System.Runtime.CompilerServices\n\n[<AutoOpen>]\nmodule AddinVersion =\n [<L"
},
{
"path": "XSVim/Properties/Manifest.addin.xml",
"chars": 2224,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ExtensionModel>\n <Runtime>\n </Runtime>\n <Extension path=\"/MonoDevelop/"
},
{
"path": "XSVim/Reflection.fs",
"chars": 4928,
"preview": "namespace XSVim\nopen System\nopen System.Reflection\nopen Microsoft.FSharp.Reflection\n\nmodule Reflection =\n // Various"
},
{
"path": "XSVim/SettingsPanel.fs",
"chars": 4281,
"preview": "namespace XSVim\nopen Gtk\nopen MonoDevelop.Components\nopen MonoDevelop.Core\nopen MonoDevelop.Ide.Gui.Dialogs\n\ntype Setti"
},
{
"path": "XSVim/SubstituteCommand.fs",
"chars": 1630,
"preview": "namespace XSVim\nopen System\nopen MonoDevelop.Core\nopen MonoDevelop.Core.ProgressMonitoring\nopen MonoDevelop.Ide\nopen Mo"
},
{
"path": "XSVim/TreeViewPads.fs",
"chars": 4969,
"preview": "namespace XSVim\nopen System\nopen Gtk\nopen MonoDevelop.Ide\nopen MonoDevelop.Ide.Gui.Components\nopen MonoDevelop.Ide.Gui."
},
{
"path": "XSVim/Types.fs",
"chars": 6437,
"preview": "namespace XSVim\nopen System\nopen System.Threading\nopen System.Threading.Tasks\nopen MonoDevelop.Ide\nopen MonoDevelop.Ide."
},
{
"path": "XSVim/WindowManagement.fs",
"chars": 4146,
"preview": "namespace XSVim\nopen MonoDevelop.Core\nopen MonoDevelop.Ide\nopen MonoDevelop.Ide.Commands\nopen Reflection\n\nmodule Window"
},
{
"path": "XSVim/XSVim.fs",
"chars": 97248,
"preview": "namespace XSVim\n\nopen System\nopen System.Collections.Generic\nopen System.Text.RegularExpressions\nopen System.Threading\n"
},
{
"path": "XSVim/XSVim.fsproj",
"chars": 4499,
"preview": "<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n <Prop"
},
{
"path": "XSVim/packages.config",
"chars": 134,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"FSharp.Core\" version=\"4.5.4\" targetFramework=\"net472\" "
},
{
"path": "XSVim.Tests/ChangeTests.fs",
"chars": 2165,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n\n[<TestFixt"
},
{
"path": "XSVim.Tests/DeleteTests.fs",
"chars": 4715,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n\n[<TestFixt"
},
{
"path": "XSVim.Tests/ExModeTests.fs",
"chars": 2810,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n"
},
{
"path": "XSVim.Tests/IndentationTests.fs",
"chars": 2255,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n"
},
{
"path": "XSVim.Tests/InsertionTests.fs",
"chars": 551,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n"
},
{
"path": "XSVim.Tests/KeyParsing.fs",
"chars": 610,
"preview": "namespace XSVim.Tests\nopen XSVim\nopen NUnit.Framework\n\n[<TestFixture>]\nmodule ``Key parsing tests`` =\n let test keys"
},
{
"path": "XSVim.Tests/KeyboardMap.fs",
"chars": 2436,
"preview": "namespace XSVim.Tests\n\nopen XSVim\nmodule mapFromQwerty =\n let qwertyToColemak = function\n | 'e' -> 'f'\n "
},
{
"path": "XSVim.Tests/MacrosTests.fs",
"chars": 953,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n"
},
{
"path": "XSVim.Tests/MarkerTests.fs",
"chars": 1321,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n"
},
{
"path": "XSVim.Tests/MiscTests.fs",
"chars": 7308,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n"
},
{
"path": "XSVim.Tests/Movement.fs",
"chars": 5473,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n"
},
{
"path": "XSVim.Tests/Properties/AssemblyInfo.fs",
"chars": 560,
"preview": "namespace XSVim.Tests\nopen System.Reflection\nopen System.Runtime.CompilerServices\n\n[<assembly: AssemblyTitle(\"XSVim.Tes"
},
{
"path": "XSVim.Tests/TestHelpers.fs",
"chars": 7892,
"preview": "namespace XSVim.Tests\n\nopen System\nopen System.Text.RegularExpressions\nopen System.Threading.Tasks\nopen MonoDevelop.Ide"
},
{
"path": "XSVim.Tests/TextObjectSelectionTests.fs",
"chars": 9150,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n"
},
{
"path": "XSVim.Tests/VisualTests.fs",
"chars": 3091,
"preview": "namespace XSVim.Tests\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks\n"
},
{
"path": "XSVim.Tests/XSVim.Tests.fsproj",
"chars": 4056,
"preview": "<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n <Prop"
},
{
"path": "XSVim.Tests/YankAndPut.fs",
"chars": 3060,
"preview": "namespace XSVim.Tests\n\nopen NUnit.Framework\nopen XSVim\nopen System.Runtime.CompilerServices\nopen System.Threading.Tasks"
},
{
"path": "XSVim.Tests/packages.config",
"chars": 134,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"FSharp.Core\" version=\"4.6.2\" targetFramework=\"net472\" "
},
{
"path": "XSVim.sln",
"chars": 1582,
"preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 2012\nProject(\"{f2a71f9b-5d33-465a-a702-920d"
},
{
"path": "addin-project.xml",
"chars": 230,
"preview": "<AddinProject appVersion=\"7.4\">\n <Project>\n <AddinFile>XSVim/bin/Debug/XSVim.dll</AddinFile>\n <BuildFil"
},
{
"path": "build.sh",
"chars": 79,
"preview": "/Library/Frameworks/Mono.framework/Versions/Current/Commands/msbuild XSVim.sln\n"
},
{
"path": "copy-assemblies.sh",
"chars": 756,
"preview": "#!/usr/bin/env bash\n\ncp /Applications/Visual\\ Studio.app/Contents/Resources/lib/monodevelop/bin/Mono.Addins.dll lib/\ncp "
},
{
"path": "run-from-source.sh",
"chars": 179,
"preview": "#! /bin/bash\n\nMONODEVELOP_CONSOLE_LOG_LEVEL=All MONODEVELOP_DEV_ADDINS=$(pwd)/XSVim/bin/Debug /Applications/Visual\\ Stud"
},
{
"path": "test.sh",
"chars": 148,
"preview": "mono \"/Applications/Visual Studio.app/Contents/Resources/lib/monodevelop/bin/vstool.exe\" run-md-tests XSVim.Tests/bin/De"
}
]
About this extraction
This page contains the full source code of the nosami/XSVim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 47 files (218.4 KB), approximately 57.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.