-
Notifications
You must be signed in to change notification settings - Fork 294
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[vi-mode] Supports the text-object command
diw
(#2059)
- Loading branch information
1 parent
4d78ce1
commit d7b9f82
Showing
13 changed files
with
709 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using System.Text; | ||
|
||
namespace Microsoft.PowerShell | ||
{ | ||
internal static class StringBuilderCharacterExtensions | ||
{ | ||
/// <summary> | ||
/// Returns true if the character at the specified position is a visible whitespace character. | ||
/// A blank character is defined as a SPACE or a TAB. | ||
/// </summary> | ||
/// <param name="buffer"></param> | ||
/// <param name="i"></param> | ||
/// <returns></returns> | ||
public static bool IsVisibleBlank(this StringBuilder buffer, int i) | ||
{ | ||
var c = buffer[i]; | ||
|
||
// [:blank:] of vim's pattern matching behavior | ||
// defines blanks as SPACE and TAB characters. | ||
|
||
return c == ' ' || c == '\t'; | ||
} | ||
|
||
/// <summary> | ||
/// Returns true if the character at the specified position is | ||
/// not present in a list of word-delimiter characters. | ||
/// </summary> | ||
/// <param name="buffer"></param> | ||
/// <param name="i"></param> | ||
/// <param name="wordDelimiters"></param> | ||
/// <returns></returns> | ||
public static bool InWord(this StringBuilder buffer, int i, string wordDelimiters) | ||
{ | ||
return Character.IsInWord(buffer[i], wordDelimiters); | ||
} | ||
|
||
/// <summary> | ||
/// Returns true if the character at the specified position is | ||
/// at the end of the buffer | ||
/// </summary> | ||
/// <param name="buffer"></param> | ||
/// <param name="i"></param> | ||
/// <returns></returns> | ||
public static bool IsAtEndOfBuffer(this StringBuilder buffer, int i) | ||
{ | ||
return i >= (buffer.Length - 1); | ||
} | ||
|
||
/// <summary> | ||
/// Returns true if the character at the specified position is | ||
/// a unicode whitespace character. | ||
/// </summary> | ||
/// <param name="buffer"></param> | ||
/// <param name="i"></param> | ||
/// <returns></returns> | ||
public static bool IsWhiteSpace(this StringBuilder buffer, int i) | ||
{ | ||
// Treat just beyond the end of buffer as whitespace because | ||
// it looks like whitespace to the user even though they haven't | ||
// entered a character yet. | ||
return i >= buffer.Length || char.IsWhiteSpace(buffer[i]); | ||
} | ||
} | ||
|
||
public static class Character | ||
{ | ||
/// <summary> | ||
/// Returns true if the character not present in a list of word-delimiter characters. | ||
/// </summary> | ||
/// <param name="c"></param> | ||
/// <param name="wordDelimiters"></param> | ||
/// <returns></returns> | ||
public static bool IsInWord(char c, string wordDelimiters) | ||
{ | ||
return !char.IsWhiteSpace(c) && wordDelimiters.IndexOf(c) < 0; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
using System; | ||
using System.Text; | ||
|
||
namespace Microsoft.PowerShell | ||
{ | ||
internal static class StringBuilderTextObjectExtensions | ||
{ | ||
private const string WhiteSpace = " \n\t"; | ||
|
||
/// <summary> | ||
/// Returns the position of the beginning of the current word as delimited by white space and delimiters | ||
/// This method differs from <see cref="ViFindPreviousWordPoint(string)"/>: | ||
/// - When the cursor location is on the first character of a word, <see cref="ViFindPreviousWordPoint(string)"/> | ||
/// returns the position of the previous word, whereas this method returns the cursor location. | ||
/// - When the cursor location is in a word, both methods return the same result. | ||
/// This method supports VI "iw" text object. | ||
/// </summary> | ||
public static int ViFindBeginningOfWordObjectBoundary(this StringBuilder buffer, int position, string wordDelimiters) | ||
{ | ||
// Cursor may be past the end of the buffer when calling this method | ||
// this may happen if the cursor is at the beginning of a new line. | ||
var i = Math.Min(position, buffer.Length - 1); | ||
|
||
// If starting on a word consider a text object as a sequence of characters excluding the delimiters, | ||
// otherwise, consider a word as a sequence of delimiters. | ||
var delimiters = wordDelimiters; | ||
var isInWord = buffer.InWord(i, wordDelimiters); | ||
|
||
if (isInWord) | ||
{ | ||
// For the purpose of this method, whitespace character is considered a delimiter. | ||
delimiters += WhiteSpace; | ||
} | ||
else | ||
{ | ||
char c = buffer[i]; | ||
if ((wordDelimiters + '\n').IndexOf(c) == -1 && char.IsWhiteSpace(c)) | ||
{ | ||
// Current position points to a whitespace that is not a newline. | ||
delimiters = WhiteSpace; | ||
} | ||
else | ||
{ | ||
delimiters += '\n'; | ||
} | ||
} | ||
|
||
var isTextObjectChar = isInWord | ||
? (Func<char, bool>)(c => delimiters.IndexOf(c) == -1) | ||
: c => delimiters.IndexOf(c) != -1; | ||
|
||
var beginning = i; | ||
while (i >= 0 && isTextObjectChar(buffer[i])) | ||
{ | ||
beginning = i--; | ||
} | ||
|
||
return beginning; | ||
} | ||
|
||
/// <summary> | ||
/// Finds the position of the beginning of the next word object starting from the specified position. | ||
/// If positioned on the last word in the buffer, returns buffer length + 1. | ||
/// This method supports VI "iw" text-object. | ||
/// iw: "inner word", select words. White space between words is counted too. | ||
/// </summary> | ||
public static int ViFindBeginningOfNextWordObjectBoundary(this StringBuilder buffer, int position, string wordDelimiters) | ||
{ | ||
// Cursor may be past the end of the buffer when calling this method | ||
// this may happen if the cursor is at the beginning of a new line. | ||
var i = Math.Min(position, buffer.Length - 1); | ||
|
||
// Always skip the first newline character. | ||
if (buffer[i] == '\n' && i < buffer.Length - 1) | ||
{ | ||
++i; | ||
} | ||
|
||
// If starting on a word consider a text object as a sequence of characters excluding the delimiters, | ||
// otherwise, consider a word as a sequence of delimiters. | ||
var delimiters = wordDelimiters; | ||
var isInWord = buffer.InWord(i, wordDelimiters); | ||
|
||
if (isInWord) | ||
{ | ||
delimiters += WhiteSpace; | ||
} | ||
else if (char.IsWhiteSpace(buffer[i])) | ||
{ | ||
delimiters = " \t"; | ||
} | ||
|
||
var isTextObjectChar = isInWord | ||
? (Func<char, bool>)(c => delimiters.IndexOf(c) == -1) | ||
: c => delimiters.IndexOf(c) != -1; | ||
|
||
// Try to skip a second newline characters to replicate vim behaviour. | ||
if (buffer[i] == '\n' && i < buffer.Length - 1) | ||
{ | ||
++i; | ||
} | ||
|
||
// Skip to next non-word characters. | ||
while (i < buffer.Length && isTextObjectChar(buffer[i])) | ||
{ | ||
++i; | ||
} | ||
|
||
// Make sure end includes the starting position. | ||
return Math.Max(i, position); | ||
} | ||
} | ||
} |
Oops, something went wrong.