diff --git a/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs b/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs index fcdda52c..968fb840 100644 --- a/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs +++ b/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs @@ -198,20 +198,29 @@ static void OnTab(object target, ExecutedRoutedEventArgs args) if (textArea != null && textArea.Document != null) { using (textArea.Document.RunUpdate()) { if (textArea.Selection.IsMultiline) { - var segment = textArea.Selection.SurroundingSegment; - DocumentLine start = textArea.Document.GetLineByOffset(segment.Offset); - DocumentLine end = textArea.Document.GetLineByOffset(segment.EndOffset); - // don't include the last line if no characters on it are selected - if (start != end && end.Offset == segment.EndOffset) - end = end.PreviousLine; - DocumentLine current = start; - while (true) { - int offset = current.Offset; - if (textArea.ReadOnlySectionProvider.CanInsert(offset)) - textArea.Document.Replace(offset, 0, textArea.Options.IndentationString, OffsetChangeMappingType.KeepAnchorBeforeInsertion); - if (current == end) - break; - current = current.NextLine; + if (!textArea.Selection.EnableVirtualSpace) { + var segment = textArea.Selection.SurroundingSegment; + DocumentLine start = textArea.Document.GetLineByOffset(segment.Offset); + DocumentLine end = textArea.Document.GetLineByOffset(segment.EndOffset); + // don't include the last line if no characters on it are selected + if (start != end && end.Offset == segment.EndOffset) + end = end.PreviousLine; + DocumentLine current = start; + while (true) { + int offset = current.Offset; + if (textArea.ReadOnlySectionProvider.CanInsert(offset)) + textArea.Document.Replace(offset, 0, textArea.Options.IndentationString, OffsetChangeMappingType.KeepAnchorBeforeInsertion); + if (current == end) + break; + current = current.NextLine; + } + } else { + IEnumerable segments = textArea.Selection.Segments; + foreach (var segment in segments.Reverse()) { + int offset = segment.StartOffset; + if (textArea.ReadOnlySectionProvider.CanInsert(offset)) + textArea.Document.Replace(offset, 0, textArea.Options.IndentationString, OffsetChangeMappingType.KeepAnchorBeforeInsertion); + } } } else { string indentationString = textArea.Options.GetIndentationString(textArea.Caret.Column); @@ -225,17 +234,37 @@ static void OnTab(object target, ExecutedRoutedEventArgs args) static void OnShiftTab(object target, ExecutedRoutedEventArgs args) { - TransformSelectedLines( - delegate (TextArea textArea, DocumentLine line) { - int offset = line.Offset; - ISegment s = TextUtilities.GetSingleIndentationSegment(textArea.Document, offset, textArea.Options.IndentationSize); - if (s.Length > 0) { - s = textArea.GetDeletableSegments(s).FirstOrDefault(); - if (s != null && s.Length > 0) { - textArea.Document.Remove(s.Offset, s.Length); + TextArea textArea = GetTextArea(target); + if (textArea != null && textArea.Document != null) { + if (textArea.Selection.IsEmpty || !textArea.Selection.EnableVirtualSpace) { + TransformSelectedLines( + delegate (TextArea textAreaIn, DocumentLine line) { + int offset = line.Offset; + ISegment s = TextUtilities.GetSingleIndentationSegment(textAreaIn.Document, offset, textAreaIn.Options.IndentationSize); + if (s.Length > 0) { + s = textAreaIn.GetDeletableSegments(s).FirstOrDefault(); + if (s != null && s.Length > 0) { + textAreaIn.Document.Remove(s.Offset, s.Length); + } + } + }, target, args, DefaultSegmentType.CurrentLine); + } else { + using (textArea.Document.RunUpdate()) { + IEnumerable segments = textArea.Selection.Segments; + foreach (var segment in segments.Reverse()) { + int offset = segment.StartOffset - 1; + ISegment s = TextUtilities.GetSingleIndentationSegment(textArea.Document, offset, + textArea.Options.IndentationSize); + if (s.Length > 0) { + s = textArea.GetDeletableSegments(s).FirstOrDefault(); + if (s != null && s.Length > 0) { + textArea.Document.Remove(s.Offset, s.Length); + } + } } } - }, target, args, DefaultSegmentType.CurrentLine); + } + } } #endregion diff --git a/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs b/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs index e0287a38..6ec61e95 100644 --- a/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs +++ b/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs @@ -436,7 +436,7 @@ void textArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) startWord = GetLineAtMousePosition(e); } else { mode = MouseSelectionMode.WholeWord; - startWord = GetWordAtMousePosition(e); + startWord = GetWordOrWhitespacesAtMousePosition(e); } if (startWord == SimpleSegment.Invalid) { mode = MouseSelectionMode.None; @@ -511,6 +511,48 @@ SimpleSegment GetWordAtMousePosition(MouseEventArgs e) } } + SimpleSegment GetWordOrWhitespacesAtMousePosition(MouseEventArgs e) + { + TextView textView = textArea.TextView; + if (textView == null) return SimpleSegment.Invalid; + Point pos = e.GetPosition(textView); + if (pos.Y < 0) + pos.Y = 0; + if (pos.Y > textView.ActualHeight) + pos.Y = textView.ActualHeight; + pos += textView.ScrollOffset; + VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y); + if (line != null) { + int visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace); + var ch = textArea.Document.GetCharAt(line.StartOffset + visualColumn); + if (ch == ' ' || ch == '\t') { + int wordStartVC = line.GetNextCaretPosition(visualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.WordBorderOrSymbol, textArea.Selection.EnableVirtualSpace); + if (wordStartVC == -1) + wordStartVC = 0; + int wordEndVC = line.GetNextCaretPosition(visualColumn, LogicalDirection.Forward, CaretPositioningMode.WordStartOrSymbol, textArea.Selection.EnableVirtualSpace); + if (wordEndVC == -1) + wordEndVC = line.VisualLength; + int relOffset = line.FirstDocumentLine.Offset; + int wordStartOffset = line.GetRelativeOffset(wordStartVC) + relOffset; + int wordEndOffset = line.GetRelativeOffset(wordEndVC) + relOffset; + return new SimpleSegment(wordStartOffset, wordEndOffset - wordStartOffset); + } else { + int wordStartVC = line.GetNextCaretPosition(visualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.WordStartOrSymbol, textArea.Selection.EnableVirtualSpace); + if (wordStartVC == -1) + wordStartVC = 0; + int wordEndVC = line.GetNextCaretPosition(wordStartVC, LogicalDirection.Forward, CaretPositioningMode.WordBorderOrSymbol, textArea.Selection.EnableVirtualSpace); + if (wordEndVC == -1) + wordEndVC = line.VisualLength; + int relOffset = line.FirstDocumentLine.Offset; + int wordStartOffset = line.GetRelativeOffset(wordStartVC) + relOffset; + int wordEndOffset = line.GetRelativeOffset(wordEndVC) + relOffset; + return new SimpleSegment(wordStartOffset, wordEndOffset - wordStartOffset); + } + } else { + return SimpleSegment.Invalid; + } + } + SimpleSegment GetLineAtMousePosition(MouseEventArgs e) { TextView textView = textArea.TextView; @@ -638,7 +680,7 @@ void ExtendSelectionToMouse(MouseEventArgs e) else textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position); } else if (mode == MouseSelectionMode.WholeWord || mode == MouseSelectionMode.WholeLine) { - var newWord = (mode == MouseSelectionMode.WholeLine) ? GetLineAtMousePosition(e) : GetWordAtMousePosition(e); + var newWord = (mode == MouseSelectionMode.WholeLine) ? GetLineAtMousePosition(e) : GetWordOrWhitespacesAtMousePosition(e); if (newWord != SimpleSegment.Invalid) { textArea.Selection = Selection.Create(textArea, Math.Min(newWord.Offset, startWord.Offset), @@ -650,7 +692,7 @@ void ExtendSelectionToMouse(MouseEventArgs e) textArea.Caret.Offset = Math.Max(newWord.EndOffset, startWord.EndOffset); } } - textArea.Caret.BringCaretToView(5.0); + textArea.Caret.BringCaretToView(textArea.Options.MinimumDistanceToViewBorder); } #endregion diff --git a/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs b/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs index 27ae5bd4..46d325d2 100644 --- a/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs +++ b/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs @@ -55,6 +55,14 @@ public FoldingManager(TextDocument document) } #endregion + /// + /// Adds another text view to the same document + /// + public void AddTextView(TextView textView) + { + textViews.Add(textView); + } + #region ReceiveWeakEvent /// protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) diff --git a/ICSharpCode.AvalonEdit/Rendering/TextView.cs b/ICSharpCode.AvalonEdit/Rendering/TextView.cs index 9e1aa1f8..6d2fdc3b 100644 --- a/ICSharpCode.AvalonEdit/Rendering/TextView.cs +++ b/ICSharpCode.AvalonEdit/Rendering/TextView.cs @@ -994,6 +994,21 @@ double CreateAndMeasureVisualLines(Size availableSize) while (yPos < availableSize.Height && nextLine != null) { VisualLine visualLine = GetVisualLine(nextLine.LineNumber); if (visualLine == null) { + + // Don't build visual lines from collapsed lines + var bBreakAll = false; + while (heightTree.GetIsCollapsed(nextLine.LineNumber)) { + if (nextLine.NextLine != null) + nextLine = nextLine.NextLine; + else { + bBreakAll = true; + break; + } + } + + if (bBreakAll) + break; + visualLine = BuildVisualLine(nextLine, globalTextRunProperties, paragraphProperties, elementGeneratorsArray, lineTransformersArray, diff --git a/ICSharpCode.AvalonEdit/TextEditorOptions.cs b/ICSharpCode.AvalonEdit/TextEditorOptions.cs index 66ee37f9..db536737 100644 --- a/ICSharpCode.AvalonEdit/TextEditorOptions.cs +++ b/ICSharpCode.AvalonEdit/TextEditorOptions.cs @@ -492,5 +492,21 @@ public bool AllowToggleOverstrikeMode { } } } + + double minimumDistanceToViewBorder = 5.0; + + /// + /// Gets/Sets the minimum distance to the view border. + /// + [DefaultValue(5.0)] + public double MinimumDistanceToViewBorder { + get { return minimumDistanceToViewBorder; } + set { + if (Math.Abs(minimumDistanceToViewBorder - value) >= 1e-6) { + minimumDistanceToViewBorder = value; + OnPropertyChanged("MinimumDistanceToViewBorder"); + } + } + } } }