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; } }