private void TabSelection(bool isShiftDown)
        {
            ITextSelection selection = CodeEditor.Document.Selection;

            ITextRange range          = selection.GetClone();
            int        selectionStart = range.StartPosition;

            range.StartOf(TextRangeUnit.Line, false);
            int lineStart = range.StartPosition;

            int charactersFromLineStart     = selectionStart - lineStart;
            int charactersOffsetFromTabSize = charactersFromLineStart % ViewModel.TabSize;

            if (isShiftDown)
            {
                int        charactersToDelete = charactersOffsetFromTabSize == 0 ? ViewModel.TabSize : charactersOffsetFromTabSize;
                ITextRange deletionRange      = selection.GetClone();

                if (deletionRange.StartPosition > lineStart)
                {
                    deletionRange.MoveStart(TextRangeUnit.Character, -1);

                    int safeCharactersToDelete;

                    for (safeCharactersToDelete = 0; safeCharactersToDelete < charactersToDelete && deletionRange.StartPosition >= lineStart && char.IsWhiteSpace(deletionRange.Character); safeCharactersToDelete++)
                    {
                        deletionRange.MoveStart(TextRangeUnit.Character, -1);
                    }

                    CodeEditor.Document.GetRange(selection.StartPosition, selection.StartPosition).Delete(TextRangeUnit.Character, -safeCharactersToDelete);
                }
            }
            else
            {
                int    charactersToAdd = ViewModel.TabSize - charactersOffsetFromTabSize;
                string tabString       = new string(' ', charactersToAdd);

                selection.TypeText(tabString);
            }
        }
        private void ApplyDiagnostics(IEnumerable <Diagnostic> diagnostics)
        {
            foreach (Diagnostic diagnostic in diagnostics)
            {
                TextSpan   span            = GetAdjustedTextSpan(diagnostic.Location.SourceSpan, ViewModel.CurrentText !, ViewModel.NewLineMode);
                ITextRange diagnosticRange = CodeEditor.Document.GetRange(span.Start, span.End);

                if (diagnosticRange.Length == 0)
                {
                    diagnosticRange.MoveStart(TextRangeUnit.Word, -1);
                }

                (diagnosticRange.CharacterFormat.Underline) = diagnostic.Severity switch
                {
                    DiagnosticSeverity.Hidden => (UnderlineType.Dotted),
                    DiagnosticSeverity.Info => (UnderlineType.Dotted),
                    DiagnosticSeverity.Warning => (UnderlineType.Wave),
                    DiagnosticSeverity.Error => (UnderlineType.Wave),
                    _ => (UnderlineType.Undefined)
                };
            }
        }
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------

        #region Internal Methods

        // returns the range that is visible in the RichEdit window
        internal ITextRange GetVisibleRange()
        {
            // get a range from the center point of the client rectangle
            Rect       rect  = BoundingRectangle;
            int        x     = ((int)rect.Left + (int)rect.Right) / 2;
            int        y     = ((int)rect.Top + (int)rect.Bottom) / 2;
            ITextRange range = _document.RangeFromPoint(x, y);

            // expand it to fill the window.
            range.Expand(TomUnit.tomWindow);

            // There is a bug with RichEdit 3.0.  The expand to tomWindow may gets 0 as the range's cpBegin (Start).
            // So need to trim off what is outside of the window.
            int start = range.Start;

            // The ITextRange::SetRange method sets this range's Start = min(cp1, cp2) and End = max(cp1, cp2).
            // If the range is a nondegenerate selection, cp2 is the active end; if it's a degenerate selection,
            // the ambiguous cp is displayed at the start of the line (rather than at the end of the previous line).
            // Set the end to the start and the start to the end to create an ambiguous cp.
            range.SetRange(range.End, range.Start);
            bool gotPoint = WindowsRichEditRange.RangeGetPoint(range, TomGetPoint.tomStart, out x, out y);

            while (!gotPoint || !Misc.PtInRect(ref rect, x, y))
            {
                range.MoveStart(TomUnit.tomWord, 1);
                gotPoint = WindowsRichEditRange.RangeGetPoint(range, TomGetPoint.tomStart, out x, out y);
            }

            if (start != range.Start)
            {
                // The trimming was done based on the left edge of the range.  The last visiable partial
                // character/word has been also added back into the range, need to remove it.  Do the comparing
                // against the characters right edge and the window rectangle.
                ITextRange rangeAdjust = _document.Range(0, range.Start - 1);
                gotPoint = WindowsRichEditRange.RangeGetPoint(rangeAdjust, TomGetPoint.TA_BOTTOM | TomGetPoint.TA_RIGHT, out x, out y);

                while (gotPoint && Misc.PtInRect(ref rect, x, y) && rangeAdjust.Start != rangeAdjust.End)
                {
                    rangeAdjust.MoveEnd(TomUnit.tomCharacter, -1);
                    range.MoveStart(TomUnit.tomCharacter, -1);
                    gotPoint = WindowsRichEditRange.RangeGetPoint(rangeAdjust, TomGetPoint.TA_BOTTOM | TomGetPoint.TA_RIGHT, out x, out y);
                }
            }

            // There is a bug with RichEdit 3.0.  The expand to tomWindow gets the last cp of the bottom
            // line in the window as the range's cpLim (End).  The cpLim may be passed the right side of
            // the window.
            // So need to trim off what is on the right side of the window.
            int end = range.End;

            gotPoint = WindowsRichEditRange.RangeGetPoint(range, TomGetPoint.TA_RIGHT, out x, out y);
            while (!gotPoint || !Misc.PtInRect(ref rect, x, y))
            {
                range.MoveEnd(TomUnit.tomWord, -1);
                gotPoint = WindowsRichEditRange.RangeGetPoint(range, TomGetPoint.TA_RIGHT, out x, out y);
            }

            if (end != range.End)
            {
                // The trimming was done based on the right edge of the range.  The last visiable partial
                // character/word has been also trimmed so add it back to the range.  Do the comparing
                // against the characters left edge and the window rectangle.
                ITextRange rangeAdjust = _document.Range(range.End, end);
                do
                {
                    if (range.MoveEnd(TomUnit.tomCharacter, 1) == 0)
                    {
                        break;
                    }
                    rangeAdjust.MoveStart(TomUnit.tomCharacter, 1);
                    gotPoint = WindowsRichEditRange.RangeGetPoint(rangeAdjust, TomGetPoint.tomStart, out x, out y);
                } while (gotPoint && Misc.PtInRect(ref rect, x, y));
            }

            return(range);
        }
        private async Task ShowDiagnostics(Point point)
        {
            ScrollViewer scrollViewer = CodeEditor.FindDescendant <ScrollViewer>();

            Point      position     = new Point(point.X + scrollViewer.HorizontalOffset - 12, point.Y + scrollViewer.VerticalOffset - 12);
            ITextRange pointerRange = CodeEditor.Document.GetRangeFromPoint(position, PointOptions.ClientCoordinates);
            TextSpan   caretSpan    = GetAdjustedTextSpan(TextSpan.FromBounds(pointerRange.StartPosition, pointerRange.EndPosition), ViewModel.CurrentText, ViewModel.NewLineMode, true);

            SyntaxNode?node = !char.IsWhiteSpace(pointerRange.Character) ? await ViewModel.GetSyntaxNodeAsync(caretSpan) : null;

            var diagnostics = await ViewModel.GetDiagnosticsAsync();

            diagnostics = diagnostics.Where(diagnostic =>
            {
                TextSpan span = GetAdjustedTextSpan(diagnostic.Location.SourceSpan, ViewModel.CurrentText, ViewModel.NewLineMode);
                ITextRange diagnosticRange = CodeEditor.Document.GetRange(span.Start, span.End);

                if (diagnosticRange.Length == 0)
                {
                    diagnosticRange.MoveStart(TextRangeUnit.Word, -1);
                }

                return(TextSpan.FromBounds(diagnosticRange.StartPosition, diagnosticRange.EndPosition).Contains(pointerRange.StartPosition));
            });

            if (node != CurrentSyntaxNodeAtPointerPosition || !CurrentDiagnosticsAtPointerPosition.SequenceEqual(diagnostics, DiagnosticsEqualityComparer.Default))
            {
                CurrentSyntaxNodeAtPointerPosition  = node;
                CurrentDiagnosticsAtPointerPosition = diagnostics;

                SemanticModel?semanticModel = null;
                ISymbol?      symbol        = null;

                if (node != null)
                {
                    semanticModel = await ViewModel.GetSemanticModelAsync();

                    if (semanticModel != null)
                    {
                        SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(node);
                        symbol = symbolInfo.Symbol;
                    }
                }

                CurrentSymbolAtPointerPosition = symbol;

                bool hasDiagnostics = diagnostics.Count() > 0;

                if (symbol != null || hasDiagnostics)
                {
                    StringBuilder stringBuilder = new StringBuilder();

                    if (symbol != null)
                    {
                        stringBuilder.Append(symbol.ToDisplayString());
                    }

                    if (hasDiagnostics)
                    {
                        if (symbol != null)
                        {
                            stringBuilder.AppendLine();
                            stringBuilder.AppendLine();
                        }

                        stringBuilder.Append(diagnostics.First().GetMessage());

                        foreach (Diagnostic diagnostic in diagnostics.Skip(1))
                        {
                            stringBuilder.AppendLine();
                            stringBuilder.AppendLine();
                            stringBuilder.Append(diagnostic.GetMessage());
                        }
                    }

                    CodeEditorToolTip.Content = stringBuilder.ToString();

                    Point toolTipPosition = GetAdjustedPointFromDocument(pointerRange);
                    Rect  exclusionRect   = new Rect(toolTipPosition, new Size(12, 12));

                    CodeEditorToolTip.PlacementRect = exclusionRect;
                    CodeEditorToolTip.IsOpen        = true;
                }
                else
                {
                    CodeEditorToolTip.IsOpen = false;
                }
            }
        }
        private static bool PreviousUnit(int start, ITextRange subrange, TomUnit unit)
        {
            if (subrange.Start <= start)
            {
                return false;
            }
            else
            {
                // collapse the range to the end and then extend it another unit.
                subrange.Collapse(TomStartEnd.tomStart);
                subrange.MoveStart(unit, -1);

                // truncate if necessary to ensure it fits inside the range
                if (subrange.Start < start)
                {
                    subrange.Start = start;
                }

                return true;
            }
        }