private async Task ShowCompletionListAsync()
        {
            if (ViewModel.CodeEditor is null)
            {
                return;
            }

            ITextRange range = CodeEditor.Document.Selection;

            if (range.Length < 0)
            {
                range.EndPosition = range.StartPosition;
            }
            else
            {
                range.StartPosition = range.EndPosition;
            }

            TextSpan selectionSpan = GetAdjustedTextSpan(TextSpan.FromBounds(range.StartPosition, range.EndPosition), ViewModel.CurrentText, ViewModel.NewLineMode, true);

            CompletionList?completionList = await ViewModel.GetCompletionListAsync(selectionSpan.Start);

            if (completionList != null)
            {
                bool expandsToWord = true;

                ITextRange wordRange = range.GetClone();

                if (wordRange.Character == '\r')
                {
                    ITextRange wordRangeClone = range.GetClone();
                    wordRangeClone.Move(TextRangeUnit.Word, -1);

                    expandsToWord = IsValidCSharpIdentifierCharacter(wordRangeClone.Character);
                }

                if (expandsToWord)
                {
                    wordRange.Move(TextRangeUnit.Word, -1);
                    wordRange.MoveEnd(TextRangeUnit.Word, 1);
                }

                string word = wordRange.Text.Trim();

                var completionItems = ViewModel.FilterCompletionItems(completionList.Items, word);

                CompletionListFlyout.Items.Clear();

                foreach (CompletionItem item in completionItems)
                {
                    var properties = item.Properties;

                    if (properties.TryGetValue("SymbolName", out string symbolName))
                    {
                        MenuFlyoutItem menuFlyoutItem = new MenuFlyoutItem
                        {
                            Text = symbolName
                        };

                        menuFlyoutItem.Click += async(s, e) =>
                        {
                            CompletionChange?completionChange = await ViewModel.GetCompletionChangeAsync(item);

                            if (completionChange != null)
                            {
                                TextSpan span = GetAdjustedTextSpan(completionChange.TextChange.Span, ViewModel.CurrentText, ViewModel.NewLineMode);

                                CodeEditor.Document.Selection.StartPosition = span.Start;
                                CodeEditor.Document.Selection.EndPosition   = span.End;

                                CodeEditor.Document.Selection.TypeText(completionChange.TextChange.NewText);
                            }
                        };

                        CompletionListFlyout.Items.Add(menuFlyoutItem);
                    }
                }

                if (CompletionListFlyout.Items.Count > 0)
                {
                    Point flyoutPosition = GetAdjustedPointFromDocument(range);
                    flyoutPosition = new Point(flyoutPosition.X + 12, flyoutPosition.Y + 8);

                    CompletionListFlyout.ShowAt(CodeEditor, new FlyoutShowOptions {
                        Position = flyoutPosition, ShowMode = FlyoutShowMode.Transient
                    });
                    return;
                }
            }

            CompletionListFlyout.Hide();
        }
        //------------------------------------------------------
        //
        //  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 static bool NextUnit(int end, ITextRange subrange, TomUnit unit)
        {
            if (subrange.End >= end)
            {
                return false;
            }
            else
            {
                // collapse the range to the end and then extend it another unit.
                subrange.Collapse(TomStartEnd.tomEnd);
                subrange.MoveEnd(unit, 1);

                // truncate if necessary to ensure it fits inside the range
                if (subrange.End > end)
                {
                    subrange.End = end;
                }

                return true;
            }
        }