private bool HandleBackspace(SelfClosingPair pair, CodeString original, out CodeString result) { result = null; var position = original.CaretPosition; var lines = original.Lines; var line = lines[original.CaretPosition.StartLine]; if (line.Length == 0) { // nothing to delete at caret position... bail out. return(false); } var previous = Math.Max(0, position.StartColumn - 1); var next = Math.Min(line.Length - 1, position.StartColumn); var previousChar = line[previous]; var nextChar = line[next]; if (original.CaretPosition.StartColumn < next && previousChar == pair.OpeningChar && nextChar == pair.ClosingChar) { if (line.Length == 2) { // entire line consists in the self-closing pair itself. result = new CodeString(string.Empty, default, Selection.Empty.ShiftRight());
private bool HandleOpeningChar(SelfClosingPair pair, CodeString original, out CodeString result) { var nextPosition = original.CaretPosition.ShiftRight(); var autoCode = new string(new[] { pair.OpeningChar, pair.ClosingChar }); var lines = original.Lines; var line = original.CaretLine; string newCode; if (string.IsNullOrEmpty(line)) { newCode = autoCode; } else if (pair.IsSymetric && original.CaretPosition.StartColumn < line.Length && line[original.CaretPosition.StartColumn] == pair.ClosingChar) { newCode = line; } else { newCode = original.CaretPosition.StartColumn >= line.Length ? line + autoCode : line.Insert(original.CaretPosition.StartColumn, autoCode); } lines[original.CaretPosition.StartLine] = newCode; result = new CodeString(string.Join("\r\n", lines), nextPosition, new Selection(original.SnippetPosition.StartLine, 1, original.SnippetPosition.EndLine, 1)); return(true); }
public bool Execute(SelfClosingPair pair, CodeString original, Keys input, out CodeString result) { result = null; if (original.IsComment) { // not handling backspace in comments return(false); } return(input == Keys.Back && HandleBackspace(pair, original, out result)); }
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); }
private bool HandleClosingChar(SelfClosingPair pair, CodeString original, out CodeString result) { result = null; if (pair.IsSymetric) { // a symetric pair would have already been handled with the opening character. return(false); } var nextIsClosingChar = original.CaretLine.Length > original.CaretCharIndex && original.CaretLine[original.CaretCharIndex] == pair.ClosingChar; if (nextIsClosingChar) { var nextPosition = original.CaretPosition.ShiftRight(); var newCode = original.Code; result = new CodeString(newCode, nextPosition, new Selection(original.SnippetPosition.StartLine, 1, original.SnippetPosition.EndLine, 1)); return(true); } return(false); }
/* * // note: this works... but the VBE makes an annoying DING! when the command isn't available. * // todo: implement our own intellisense, then uncomment this code. * private readonly IShowIntelliSenseCommand _showIntelliSense; * * public SelfClosingPairCompletionService(IShowIntelliSenseCommand showIntelliSense) * { * _showIntelliSense = showIntelliSense; * } */ public bool Execute(SelfClosingPair pair, CodeString original, char input, out CodeString result) { result = null; var previousCharIsClosingChar = original.CaretPosition.StartColumn > 0 && original.CaretLine[original.CaretPosition.StartColumn - 1] == pair.ClosingChar; var nextCharIsClosingChar = original.CaretPosition.StartColumn < original.CaretLine.Length && original.CaretLine[original.CaretPosition.StartColumn] == pair.ClosingChar; if (pair.IsSymetric && input != '\b' && original.Code.Length >= 1 && previousCharIsClosingChar && !nextCharIsClosingChar || original.IsComment || (original.IsInsideStringLiteral && !nextCharIsClosingChar)) { return(false); } if (input == pair.OpeningChar) { return(HandleOpeningChar(pair, original, out result)); } if (input == pair.ClosingChar) { return(HandleClosingChar(pair, original, out result)); } if (input == '\b') { return(Execute(pair, original, Keys.Back, out result)); } return(false); }
private bool HandleInternal(AutoCompleteEventArgs e, CodeString original, SelfClosingPair pair, out CodeString result) { if (!original.CaretPosition.IsSingleCharacter) { // todo: WrapSelectionWith(pair)? result = null; return(false); } // 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 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 && !reprettified.Equals(result)) { // 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); }