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