private bool WillHandle(AutoCompleteEventArgs e) { Debug.Assert(_settings != null); if (!_enabled) { Logger.Warn("KeyDown controller is executing, but auto-completion service is disabled."); return(false); } if (_popupShown || e.Character == default && e.IsDeleteKey) { return(false); } var module = e.Module; using (var pane = module.CodePane) { if (pane.Selection.LineCount > 1) { return(false); } } return(true); }
private void ShowAutoComplete() { args = new AutoCompleteEventArgs { Cancel = false, AutoCompleteList = myAutoCompleteList }; // With... if ((listBox.SelectedIndex == -1)) { args.SelectedIndex = 0; System.Diagnostics.Debug.WriteLine("Always {0}", args.SelectedIndex); } else { args.SelectedIndex = listBox.SelectedIndex; System.Diagnostics.Debug.WriteLine("Not always {0}", args.SelectedIndex); } if (BeforeDisplayingAutoComplete != null) { BeforeDisplayingAutoComplete(this, args); } myAutoCompleteList = args.AutoCompleteList; if ((!args.Cancel && (args.AutoCompleteList != null) && args.AutoCompleteList.Count > 0)) { DoShowAuto(); } else { DoHideAuto(); } }
public override bool Execute(AutoCompleteEventArgs e, AutoCompleteSettings settings) { var result = base.Execute(e, settings); var module = e.CodeModule; using (var pane = module.CodePane) { var original = module.GetLines(e.CurrentSelection); var hasAsToken = Regex.IsMatch(original, $@"{Tokens.Property} {Tokens.Get}\s+\(.*\)\s+{Tokens.As}\s?", RegexOptions.IgnoreCase); var hasAsType = Regex.IsMatch(original, $@"{Tokens.Property} {Tokens.Get}\s+\w+\(.*\)\s+{Tokens.As}\s+(?<Identifier>\w+)", RegexOptions.IgnoreCase); var asTypeClause = hasAsToken && hasAsType ? string.Empty : hasAsToken ? $" {Tokens.Variant}" : $" {Tokens.As} {Tokens.Variant}"; if (result && Regex.IsMatch(original, $"{Tokens.Property} {Tokens.Get}")) { var code = original + asTypeClause; module.ReplaceLine(e.CurrentSelection.StartLine, code); var newCode = module.GetLines(e.CurrentSelection); if (code == newCode) { pane.Selection = new Selection(e.CurrentSelection.StartLine, code.Length - Tokens.Variant.Length + 1, e.CurrentSelection.StartLine, code.Length + 1); } } } return(result); }
public SelfClosingPairTestInfo( Mock <SelfClosingPairCompletionService> service, CodeString original, CodeString prettified, char input, CodeString rePrettified, bool isControlKeyDown = false, bool isDeleteKey = false) { Original = original; Prettified = prettified; Input = input; RePrettified = rePrettified; Settings = AutoCompleteSettings.AllEnabled; Service = service; Module = new Mock <ICodeModule>(); Handler = new Mock <ICodePaneHandler>(); Handler.Setup(e => e.GetCurrentLogicalLine(Module.Object)).Returns(original); Handler.SetupSequence(e => e.Prettify(Module.Object, It.IsAny <CodeString>())) .Returns(prettified) .Returns(rePrettified); Args = new AutoCompleteEventArgs(Module.Object, input, isControlKeyDown, isDeleteKey); }
public override bool Handle(AutoCompleteEventArgs e, AutoCompleteSettings settings, out CodeString result) { result = null; if (!settings.SelfClosingPairs.IsEnabled || !_scpInputLookup.TryGetValue(e.Character, out var pair) && e.Character != '\b') { // not an interesting keypress. return(false); } var original = CodePaneHandler.GetCurrentLogicalLine(e.Module); if (original == null || original.Lines.Length == MaximumLines) { // selection spans more than a single logical line, or // logical line somehow spans more than the maximum number of physical lines in a logical line of code (25). return(false); } if (!original.CaretPosition.IsSingleCharacter) { // here would be an opportunity to "wrap selection" with a SCP. // todo: WrapSelectionWith(pair)? result = null; return(false); } if (pair != null) { // found a SCP for the input key; see if we should handle it: if (!HandleInternal(e, original, pair, out result)) { return(false); } } else if (e.Character == '\b') { // backspace - see if SCP logic needs to intervene: foreach (var scp in _selfClosingPairs) { if (HandleInternal(e, original, scp, out result)) { break; } } } if (result == null) { // no meaningful output; let the input be handled by another handler, maybe. return(false); } // 1-based selection span in the code pane starts at column 1 but really encompasses the entire line. var snippetPosition = new Selection(result.SnippetPosition.StartLine, 1, result.SnippetPosition.EndLine, 1); result = new CodeString(result.Code, result.CaretPosition, snippetPosition); _scpService.ShowQuickInfo(); e.Handled = true; return(true); }
private void HandleTab() { var args = new AutoCompleteEventArgs(this.currentInput.ToString()); this.AutoCompleteRequired?.Invoke(this, args); if (args.Suggestions.Count == 0) { return; } var enumerator = args.Suggestions.GetEnumerator(); enumerator.MoveNext(); if (args.Suggestions.Count == 1) { this.currentInput.Clear().Append(enumerator.Current); this.Refresh(); } else { Console.WriteLine(); Console.Write(enumerator.Current); while (enumerator.MoveNext()) { Console.Write(", {0}", enumerator.Current); } this.Refresh(); } }
private void HandleKeyDown(object sender, AutoCompleteEventArgs e) { if (e.Character == default && !e.IsDelete) { return; } var module = e.CodeModule; var qualifiedSelection = module.GetQualifiedSelection(); Debug.Assert(qualifiedSelection != null, nameof(qualifiedSelection) + " != null"); var pSelection = qualifiedSelection.Value.Selection; if (_popupShown || pSelection.LineCount > 1 || e.IsDelete) { return; } var currentContent = module.GetLines(pSelection); if (HandleSmartConcat(e, pSelection, currentContent, module)) { return; } HandleSelfClosingPairs(e, module, pSelection); }
/// <summary> /// Adds a line continuation when {ENTER} is pressed inside a string literal; returns false otherwise. /// </summary> private bool HandleSmartConcat(AutoCompleteEventArgs e, Selection pSelection, string currentContent, ICodeModule module) { var shouldHandle = _settings.EnableSmartConcat && e.Character == '\r' && IsInsideStringLiteral(pSelection, ref currentContent); var lastIndexLeftOfCaret = currentContent.Length > 2 ? currentContent.Substring(0, pSelection.StartColumn - 1).LastIndexOf('"') : 0; if (shouldHandle && lastIndexLeftOfCaret > 0) { var indent = currentContent.NthIndexOf('"', 1); var whitespace = new string(' ', indent); var code = $"{currentContent.Substring(0, pSelection.StartColumn - 1)}\" & _\r\n{whitespace}\"{currentContent.Substring(pSelection.StartColumn - 1)}"; if (e.ControlDown) { code = $"{currentContent.Substring(0, pSelection.StartColumn - 1)}\" & vbNewLine & _\r\n{whitespace}\"{currentContent.Substring(pSelection.StartColumn - 1)}"; } module.ReplaceLine(pSelection.StartLine, code); using (var pane = module.CodePane) { pane.Selection = new Selection(pSelection.StartLine + 1, indent + currentContent.Substring(pSelection.StartColumn - 2).Length); e.Handled = true; return(true); } } return(false); }
public virtual bool Execute(AutoCompleteEventArgs e, AutoCompleteSettings settings) { var input = e.Character.ToString(); if (!IsMatch(input)) { return(false); } var module = e.CodeModule; using (var pane = module.CodePane) { var pSelection = pane.Selection; var zSelection = pSelection.ToZeroBased(); var original = module.GetLines(pSelection); var nextChar = zSelection.StartColumn == original.Length ? string.Empty : original.Substring(zSelection.StartColumn, 1); if (input == InputToken && (input != OutputToken || nextChar != OutputToken)) { string code; if (!StripExplicitCallStatement(ref original, ref pSelection)) { code = original.Insert(Math.Max(0, zSelection.StartColumn), InputToken + OutputToken); } else { code = original; } module.ReplaceLine(pSelection.StartLine, code); var newCode = module.GetLines(pSelection); if (newCode.Equals(code, StringComparison.OrdinalIgnoreCase)) { pane.Selection = new Selection(pSelection.StartLine, pSelection.StartColumn + 1); } else { // VBE added a space; need to compensate: pane.Selection = new Selection(pSelection.StartLine, GetPrettifiedCaretPosition(pSelection, code, newCode)); } e.Handled = true; return(true); } else if (input == OutputToken && nextChar == OutputToken) { // just move caret one character to the right & suppress the keypress pane.Selection = new Selection(pSelection.StartLine, GetPrettifiedCaretPosition(pSelection, original, original) + 1); e.Handled = true; return(true); } return(false); } }
private static TestCodeString Run(TestCodeString original, char input, bool isCtrlDown = false, bool isDeleteKey = false) { var sut = InitializeSut(original, out var module, out var settings); var args = new AutoCompleteEventArgs(module.Object, input, isCtrlDown, isDeleteKey); if (sut.Handle(args, settings, out var result)) { return(new TestCodeString(result)); } return(null); }
private bool HandleInternal(AutoCompleteEventArgs e, CodeString original, SelfClosingPair pair, out CodeString result) { if (!original.CaretPosition.IsSingleCharacter) { // todo: WrapSelection? result = null; return(false); } var isPresent = original.CaretLine.EndsWith($"{pair.OpeningChar}{pair.ClosingChar}"); if (!_scpService.Execute(pair, original, e.Character, out result)) { return(false); } var prettified = CodePaneHandler.Prettify(e.Module, original); if (!isPresent && original.CaretLine.Length + 2 == prettified.CaretLine.Length && prettified.CaretLine.EndsWith($"{pair.OpeningChar}{pair.ClosingChar}")) { // prettifier just added the pair for us; likely a Sub or Function statement. prettified = original; // pretend this didn't happen. note: probably breaks if original has extra whitespace. } if (!_scpService.Execute(pair, prettified, e.Character, out result)) { return(false); } result = CodePaneHandler.Prettify(e.Module, result); var currentLine = result.Lines[result.CaretPosition.StartLine]; if (!string.IsNullOrWhiteSpace(currentLine) && currentLine.EndsWith(" ") && result.CaretPosition.StartColumn == currentLine.Length) { result = result.ReplaceLine(result.CaretPosition.StartLine, currentLine.TrimEnd()); } if (pair.OpeningChar == '(' && e.Character == pair.OpeningChar && !result.CaretLine.EndsWith($"{pair.OpeningChar}{pair.ClosingChar}")) { // VBE eats it. bail out but still swallow the keypress, since we've already re-prettified. e.Handled = true; result = null; return(false); } return(true); }
public override bool Handle(AutoCompleteEventArgs e, AutoCompleteSettings settings, out CodeString result) { result = null; if (e.Character != '\r' || (!settings?.SmartConcat.IsEnabled ?? true)) { return(false); } var currentContent = CodePaneHandler.GetCurrentLogicalLine(e.Module); if ((!currentContent?.IsInsideStringLiteral ?? true) || currentContent.Lines.Length >= settings.SmartConcat.ConcatMaxLines) { // selection spans more than a single logical line, or spans too many lines to be legal; // too many line continuations throws COMException if we attempt to modify. return(false); } var lastIndexLeftOfCaret = currentContent.CaretLine.Length > 2 ? currentContent.CaretLine.Substring(0, currentContent.CaretPosition.StartColumn).LastIndexOf('"') : 0; if (lastIndexLeftOfCaret > 0) { var indent = currentContent.CaretLine.NthIndexOf('"', 1); var whitespace = new string(' ', indent); // todo: handle shift modifier? var concatVbNewLine = settings.SmartConcat.ConcatVbNewLineModifier.HasFlag(ModifierKeySetting.CtrlKey) && e.IsControlKeyDown; var autoCode = $"\" {(concatVbNewLine ? "& vbNewLine " : string.Empty)}& _\r\n{whitespace}\""; var left = currentContent.CaretLine.Substring(0, currentContent.CaretPosition.StartColumn); var right = currentContent.CaretLine.Substring(currentContent.CaretPosition.StartColumn); var caretLine = $"{left}{autoCode}{right}"; var lines = currentContent.Lines; lines[currentContent.CaretPosition.StartLine] = caretLine; var code = string.Join("\r\n", lines); var newContent = new CodeString(code, currentContent.CaretPosition, currentContent.SnippetPosition); var newPosition = new Selection(newContent.CaretPosition.StartLine + 1, indent + 1); e.Handled = true; result = new CodeString(newContent.Code, newPosition, new Selection(newContent.SnippetPosition.StartLine, 1, newContent.SnippetPosition.EndLine, 1)); CodePaneHandler.SubstituteCode(e.Module, result); var finalSelection = new Selection(result.SnippetPosition.StartLine, 1).Offset(result.CaretPosition); CodePaneHandler.SetSelection(e.Module, finalSelection); return(true); } return(false); }
public override bool Execute(AutoCompleteEventArgs e, AutoCompleteSettings settings) { var ignoreTab = e.Character == '\t' && !settings.CompleteBlockOnTab; var ignoreEnter = e.Character == '\r' && !settings.CompleteBlockOnEnter; if (IsInlineCharCompletion || e.IsDelete || ignoreTab || ignoreEnter) { return(false); } var module = e.CodeModule; using (var pane = module.CodePane) { var selection = pane.Selection; var originalCode = module.GetLines(selection); var code = originalCode.Trim().StripStringLiterals(); var hasComment = code.HasComment(out int commentStart); var isDeclareStatement = Regex.IsMatch(code, $"\\b{Tokens.Declare}\\b", RegexOptions.IgnoreCase); var isExitStatement = Regex.IsMatch(code, $"\\b{Tokens.Exit}\\b", RegexOptions.IgnoreCase); var isNamedArg = Regex.IsMatch(code, $"\\b{InputToken}\\:\\=", RegexOptions.IgnoreCase); if ((SkipPreCompilerDirective && code.StartsWith("#")) || isDeclareStatement || isExitStatement || isNamedArg) { return(false); } if (IsMatch(code) && !IsBlockCompleted(module, selection)) { var indent = originalCode.TakeWhile(c => char.IsWhiteSpace(c)).Count(); var newCode = OutputToken.PadLeft(OutputToken.Length + indent, ' '); var stdIndent = IndentBody ? IndenterSettings.Create().IndentSpaces : 0; module.InsertLines(selection.NextLine.StartLine, "\n" + newCode); module.ReplaceLine(selection.NextLine.StartLine, new string(' ', indent + stdIndent)); pane.Selection = new Selection(selection.NextLine.StartLine, indent + stdIndent + 1); e.Handled = true; return(true); } return(false); } }
private void HandleKeyDown(object sender, AutoCompleteEventArgs e) { if (!WillHandle(e)) { return; } foreach (var handler in _handlers) { if (TryHandle(e, handler)) { return; } } }
public override bool Handle(AutoCompleteEventArgs e, AutoCompleteSettings settings, out CodeString result) { result = null; if (!_scpInputLookup.TryGetValue(e.Character, out var pair) && e.Character != '\b') { return(false); } var original = CodePaneHandler.GetCurrentLogicalLine(e.Module); if (original == null || original.Lines.Length == MaximumLines) { // selection spans more than a single logical line, or // logical line somehow spans more than the maximum number of physical lines in a logical line of code (25). return(false); } if (pair != null) { if (!HandleInternal(e, original, pair, out result)) { return(false); } } else if (e.Character == '\b') { foreach (var scp in _selfClosingPairs) { if (HandleInternal(e, original, scp, out result)) { break; } } } if (result == null) { return(false); } var snippetPosition = new Selection(result.SnippetPosition.StartLine, 1, result.SnippetPosition.EndLine, 1); result = new CodeString(result.Code, result.CaretPosition, snippetPosition); e.Handled = true; return(true); }
private void HandleKeyDown(object sender, AutoCompleteEventArgs e) { if (!WillHandle(e)) { return; } foreach (var handler in _handlers) { if (!handler.Handle(e, _settings, out _)) { continue; } e.Handled = true; return; } }
private bool TryHandle(AutoCompleteEventArgs e, AutoCompleteHandlerBase handler) { try { if (!handler.Handle(e, _settings, out _)) { return(false); } e.Handled = true; return(true); } catch (Exception exception) { Logger.Error(exception); return(false); } }
private bool TryHandle(AutoCompleteEventArgs e, AutoCompleteHandlerBase handler) { try { if (!handler.Handle(e, _settings, out _)) { return(false); } Logger.Debug($"Keypress was handled by {handler.GetType().Name}."); e.Handled = true; return(true); } catch (Exception exception) { Logger.Error(exception); return(false); } }
private void HandleSelfClosingPairs(AutoCompleteEventArgs e, ICodeModule module, Selection pSelection) { if (!pSelection.IsSingleCharacter) { return; } var currentCode = e.CurrentLine; var currentSelection = e.CurrentSelection; //var surroundingCode = GetSurroundingCode(module, currentSelection); // todo: find a way to parse the current instruction var original = new CodeString(currentCode, new Selection(0, currentSelection.EndColumn - 1), new Selection(pSelection.StartLine, 1)); var prettifier = new CodeStringPrettifier(module); foreach (var selfClosingPair in _selfClosingPairs) { CodeString result; if (e.Character == '\b' && pSelection.StartColumn > 1) { result = _selfClosingPairCompletion.Execute(selfClosingPair, original, '\b'); } else { result = _selfClosingPairCompletion.Execute(selfClosingPair, original, e.Character, prettifier); } if (result != default) { using (var pane = module.CodePane) { module.DeleteLines(result.SnippetPosition); module.InsertLines(result.SnippetPosition.StartLine, result.Code); pane.Selection = result.SnippetPosition.Offset(result.CaretPosition); e.Handled = true; return; } } } }
protected void text_AutoComplete(object sender, AutoCompleteEventArgs e) { //just return 10 strings starting with the given one e.Results = Enumerable.Range(0, 10).Select(x => e.Parameter + x.ToString()); }
/// <summary> /// A method that returns <c>false</c> if the input isn't handled, <c>true</c> if it is. /// </summary> /// <param name="e">The autocompletion event info</param> /// <param name="settings">The current AC settings</param> /// <param name="result">If handled, the resulting <c>CodeString</c></param> /// <returns></returns> public abstract bool Handle(AutoCompleteEventArgs e, AutoCompleteSettings settings, out CodeString result);
private bool HandleInternal(AutoCompleteEventArgs e, CodeString original, SelfClosingPair pair, out CodeString result) { // if executing the SCP against the original code yields no result, we need to bail out. if (!_scpService.Execute(pair, original, e.Character, out result)) { return(false); } // let the VBE alter the original code if it wants to, then work with the prettified code. var prettified = CodePaneHandler.Prettify(e.Module, original); var isPresent = original.CaretLine.EndsWith($"{pair.OpeningChar}{pair.ClosingChar}"); if (!isPresent && original.CaretLine.Length + 2 == prettified.CaretLine.Length && prettified.CaretLine.EndsWith($"{pair.OpeningChar}{pair.ClosingChar}")) { // prettifier just added the pair for us; likely a Sub or Function statement. prettified = original; // pretend this didn't happen; we need to work out the caret position anyway. } if (prettified.CaretLine.Length == 0) { // prettifier destroyed the indent. need to reinstate it now. prettified = prettified.ReplaceLine( index: prettified.CaretPosition.StartLine, content: new string(' ', original.CaretLine.TakeWhile(c => c == ' ').Count()) ); } if (original.CaretLine.EndsWith(" ") && string.Equals(original.CaretLine, prettified.CaretLine + " ", StringComparison.InvariantCultureIgnoreCase)) { prettified = original; } // if executing the SCP against the prettified code yields no result, we need to bail out. if (!_scpService.Execute(pair, prettified, e.Character, out result)) { return(false); } var reprettified = CodePaneHandler.Prettify(e.Module, result); if (pair.OpeningChar == '(' && e.Character == pair.OpeningChar) { if (string.Equals(reprettified.Code, result.Code, StringComparison.InvariantCultureIgnoreCase)) { e.Handled = true; result = reprettified; return(true); } // VBE eats it. bail out but don't swallow the keypress. e.Handled = false; result = null; return(false); } var currentLine = reprettified.Lines[reprettified.CaretPosition.StartLine]; if (!string.IsNullOrWhiteSpace(currentLine) && currentLine.EndsWith(" ") && reprettified.CaretPosition.StartColumn == currentLine.Length) { result = reprettified.ReplaceLine(reprettified.CaretPosition.StartLine, currentLine.TrimEnd()); } if (pair.OpeningChar == '(' && e.Character == pair.OpeningChar && !result.CaretLine.EndsWith($"{pair.OpeningChar}{pair.ClosingChar}")) { // VBE eats it. bail out but still swallow the keypress; we already prettified the opening character into the editor. e.Handled = true; result = null; return(false); } return(true); }
private void ShowAutoComplete() { args = new AutoCompleteEventArgs {Cancel = false, AutoCompleteList = myAutoCompleteList}; // With... if ((listBox.SelectedIndex == -1)) { args.SelectedIndex = 0; System.Diagnostics.Debug.WriteLine("Always {0}", args.SelectedIndex); } else { args.SelectedIndex = listBox.SelectedIndex; System.Diagnostics.Debug.WriteLine("Not always {0}", args.SelectedIndex); } if (BeforeDisplayingAutoComplete != null) BeforeDisplayingAutoComplete(this, args); myAutoCompleteList = args.AutoCompleteList; if ((!args.Cancel && (args.AutoCompleteList != null) && args.AutoCompleteList.Count > 0)) { DoShowAuto(); } else { DoHideAuto(); } }
/// <summary> /// Handles and auto-complete request according to the result of the /// <see cref="AutoCompleteEvent"/> (also, see /// <see cref="CompletionList"/>) /// </summary> private void CmdAutoComplete() { if (AutoCompleteEvent == null) { InsertChar('\t'); return; } AutoCompleteEventArgs eventArgs = new AutoCompleteEventArgs( text.ToString().Substring(0, textPosition)); AutoCompleteEvent(this, eventArgs); string[] completions = eventArgs.Completions. SelectMany(c => c.Completions). Distinct(). OrderBy(s => s). ToArray(); int noOfCompletions = completions.Length; if (noOfCompletions == 0) { return; } if (completions.Length == 1) { InsertTextAtCursor(completions[0]); } else { string commonPrefix = eventArgs.Completions. Select(c => c.CommonPrefix). ElementAtMin(s => s.Length); int last = -1; for (int p = 0; p < completions[0].Length; p++) { char c = completions[0][p]; for (int i = 1; i < noOfCompletions; i++) { if (completions[i].Length < p) { goto mismatch; } if (completions[i][p] != c) { goto mismatch; } } last = p; } mismatch: if (last != -1) { InsertTextAtCursor(completions[0].Substring(0, last + 1)); } Console.WriteLine(); foreach (string s in completions) { Console.Write(commonPrefix); Console.Write(s); Console.WriteLine(); } Console.WriteLine(); Render(); MoveCursor(textPosition); } }