-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TypingCommandHandler.cs
90 lines (78 loc) · 3.68 KB
/
TypingCommandHandler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Utilities;
namespace QuoteCompletionFix
{
[Export(typeof(ICommandHandler))]
[Name(nameof(TypingCommandHandler))]
[ContentType(ContentTypes.CSharp)]
[TextViewRole(PredefinedTextViewRoles.PrimaryDocument)]
internal class TypingCommandHandler : ICommandHandler<TypeCharCommandArgs>
{
public string DisplayName => nameof(TypingCommandHandler);
private static RatingPrompt _rating;
[Import]
public IEditorOperationsFactoryService EditorOperationsFactory { get; set; }
// Returning true means that further processing of the typed char should be stopped (we handled it).
// Returning false means that the built-in behavior should be used after this handler (we didn't handle it).
public bool ExecuteCommand(TypeCharCommandArgs args, CommandExecutionContext executionContext)
{
// Pass through if typed char isn't a quote
if (args.TypedChar is not '"')
{
return false;
}
ITextSnapshot snapshot = args.TextView.TextSnapshot;
var position = args.TextView.Caret.Position.BufferPosition.Position;
var next = position < snapshot.Length ? snapshot.GetText(position, 1)[0] : ' ';
var prev = position > 0 ? snapshot.GetText(position - 1, 1)[0] : ' ';
// Pass through to preserve current behavior when the caret is in between two quotation marks
if (prev == args.TypedChar && next == args.TypedChar)
{
return false;
}
// Pass through if the next char is a quote to support type-through (provisional text)
// TODO: Check for existence of provisional text and only return false if it exists
if (next == args.TypedChar)
{
return false;
}
// We're in a state where we can run our rules
return ApplyRules(args, position, next, prev);
}
private bool ApplyRules(TypeCharCommandArgs args, int position, char next, char prev)
{
// Rule #1: If the next char is a letter or digit, don't auto-complete the quote
if (char.IsLetterOrDigit(next) || char.IsLetterOrDigit(prev))
{
args.TextView.TextBuffer.Insert(position, args.TypedChar.ToString());
RegisterUseAsync().FireAndForget();
return true;
}
// Rule #2: If the previous char is a $ or @, auto-complete the quote
if (prev is '$' or '@')
{
args.TextView.TextBuffer.Insert(position, "" + args.TypedChar);
EditorOperationsFactory.GetEditorOperations(args.TextView).InsertProvisionalText("\"");
args.TextView.Caret.MoveToPreviousCaretPosition();
RegisterUseAsync().FireAndForget();
return true;
}
return false; // No rules applied from this command handler. Pass through to built-in command handlers
}
private static async Task RegisterUseAsync()
{
_rating ??= new("MadsKristensen.QuoteCompletionFix", Vsix.Name, await General.GetLiveInstanceAsync());
_rating.RegisterSuccessfulUsage();
}
public CommandState GetCommandState(TypeCharCommandArgs args)
{
return CommandState.Available;
}
}
}