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 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 void SourceCodeReturnsContentForModule() { var codeModuleMock = new Mock <ICodeModule>(); codeModuleMock.Setup(m => m.Content()).Returns("TestTestTest"); var codeModule = codeModuleMock.Object; var module = new QualifiedModuleName("TestProject", string.Empty, "TestModule"); var projectsProvider = TestProvider(module, codeModule); var codePaneSourceHandler = new CodePaneHandler(projectsProvider); var expected = "TestTestTest"; var actual = codePaneSourceHandler.SourceCode(module); Assert.AreEqual(expected, actual); }
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); }
public void RewriteDoesNotRewriteIfNotDirty() { var codeModule = new Mock <ICodeModule>(); codeModule.Setup(m => m.Content()).Returns(string.Empty); codeModule.Setup(m => m.Clear()); var module = new QualifiedModuleName("TestProject", string.Empty, "TestModule"); var projectsProvider = TestProvider(module, codeModule.Object); var codePaneSourceHandler = new CodePaneHandler(projectsProvider); var tokenStream = new CommonTokenStream(new ListTokenSource(new List <IToken>())); var sut = new ModuleRewriter(module, tokenStream, codePaneSourceHandler); sut.Rewrite(); codeModule.Verify(m => m.Clear(), Times.Never); }
private static ICodePaneHandler InitializeSut(TestCodeString original, TestCodeString prettified, out Mock <ICodeModule> module, out Mock <ICodePane> pane) { var builder = new MockVbeBuilder(); var project = builder.ProjectBuilder("TestProject1", ProjectProtection.Unprotected) .AddComponent("Module1", ComponentType.StandardModule, ""); var vbe = builder.AddProject(project.Build()).Build(); module = new Mock <ICodeModule>(); pane = new Mock <ICodePane>(); pane.SetupProperty(m => m.Selection); module.Setup(m => m.DeleteLines(original.SnippetPosition.StartLine, original.SnippetPosition.LineCount)); module.Setup(m => m.InsertLines(original.SnippetPosition.StartLine, original.Code)); module.Setup(m => m.CodePane).Returns(pane.Object); module.Setup(m => m.GetLines(original.SnippetPosition)).Returns(prettified.Code); var sut = new CodePaneHandler(new ProjectsRepository(vbe.Object)); return(sut); }
public void RewriteClearsEntireModule() { var codeModule = new Mock <ICodeModule>(); codeModule.Setup(m => m.Clear()); var module = new QualifiedModuleName("TestProject", string.Empty, "TestModule"); var projectsProvider = TestProvider(module, codeModule.Object); var codePaneSourceHandler = new CodePaneHandler(projectsProvider); var tokenStream = new CommonTokenStream(new ListTokenSource(new List <IToken>())); var sut = new ModuleRewriter(module, tokenStream, codePaneSourceHandler); sut.InsertAfter(0, "test"); if (!sut.IsDirty) { sut.InsertBefore(0, "foo"); } sut.Rewrite(); codeModule.Verify(m => m.Clear()); }
public void AfterSubstituteCodeTheCorrespondingModuleContainsTheCodeProvided() { var code = "TestTestTest"; var codeModuleMock = new Mock <ICodeModule>(); codeModuleMock.Setup(m => m.Content()).Returns(() => code); codeModuleMock.Setup(m => m.CountOfLines).Returns(1); codeModuleMock.Setup(m => m.Clear()).Callback(() => code = string.Empty); codeModuleMock.Setup(m => m.InsertLines(It.IsAny <int>(), It.IsAny <string>())) .Callback((int start, string str) => code = code.Insert(start - 1, str)); var codeModule = codeModuleMock.Object; var module = new QualifiedModuleName("TestProject", string.Empty, "TestModule"); var projectsProvider = TestProvider(module, codeModule); var codePaneSourceHandler = new CodePaneHandler(projectsProvider); codePaneSourceHandler.SubstituteCode(module, "NewNewNew"); var expected = "NewNewNew"; var actual = codeModule.Content(); Assert.AreEqual(expected, actual); }
private static SmartConcatenationHandler InitializeSut(TestCodeString original, TestCodeString prettified, out Mock <ICodeModule> module, out Mock <ICodePane> pane, out AutoCompleteSettings settings) { var builder = new MockVbeBuilder(); var project = builder.ProjectBuilder("TestProject1", ProjectProtection.Unprotected) .AddComponent("Module1", ComponentType.StandardModule, ""); var vbe = builder.AddProject(project.Build()).Build(); module = new Mock <ICodeModule>(); pane = new Mock <ICodePane>(); pane.SetupProperty(m => m.Selection); var paneSelection = new Selection(original.SnippetPosition.StartLine + original.CaretPosition.StartLine, original.CaretPosition.StartColumn + 1); pane.Object.Selection = paneSelection; module.Setup(m => m.DeleteLines(original.SnippetPosition.StartLine, original.SnippetPosition.LineCount)); module.Setup(m => m.InsertLines(original.SnippetPosition.StartLine, original.Code)); module.Setup(m => m.CodePane).Returns(pane.Object); for (var i = 0; i < original.SnippetPosition.LineCount; i++) { var index = i; module.Setup(m => m.GetLines(index + 1, 1)).Returns(original.Lines[index]); } module.Setup(m => m.GetLines(original.SnippetPosition)).Returns(prettified.Code); module.Setup(m => m.GetLines(paneSelection.StartLine, paneSelection.LineCount)).Returns(prettified.CaretLine); settings = new AutoCompleteSettings { IsEnabled = true }; settings.SmartConcat.IsEnabled = true; settings.SmartConcat.ConcatVbNewLineModifier = ModifierKeySetting.CtrlKey; settings.SmartConcat.ConcatMaxLines = AutoCompleteSettings.ConcatMaxLinesMaxValue; var handler = new CodePaneHandler(new ProjectsRepository(vbe.Object)); var sut = new SmartConcatenationHandler(handler); return(sut); }
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); }