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);
        }
Exemplo n.º 7
0
        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);
        }