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);
        }
예제 #3
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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);
        }
예제 #10
0
        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);
        }