/// <summary> /// Calculate tab offsets by going up /// </summary> private void CalculateTabOffsetUp(ITextSnapshotLine curLine, int colNumber, ColumnSizeInfo colTabOffset) { int curLineNumber = curLine.LineNumber - 1; ITextSnapshot textSnapshot = _textView.TextSnapshot; ElasticTabstopsLine elasticLine = _elasticTabstopsLinesCache[curLine.LineNumber]; bool isLastColumnInLine = elasticLine.IsLastColumnInLine(colNumber); while (curLineNumber >= 0) { ITextSnapshotLine upLine = textSnapshot.GetLineFromLineNumber(curLineNumber); ElasticTabstopsLine upElasticLine = GetElasticTabstopsLine(upLine); if (upElasticLine.IsLastColumnInLine(colNumber) != isLastColumnInLine) { break; } ElasticTabstopsColumn upColumn = upElasticLine.GetColumnOrDefault(colNumber); if (upColumn == null) { break; } upColumn.TabOffset = colTabOffset; ShrinkTabOffset(upElasticLine, colNumber); curLineNumber--; } }
/// <summary> /// Calculate tab offsets by going down /// </summary> private void CalculateTabOffsetDown(ITextSnapshotLine curLine, int colNumber, ColumnSizeInfo colTabOffset) { int curLineNumber = curLine.LineNumber + 1; ITextSnapshot textSnapshot = _textView.TextSnapshot; ElasticTabstopsLine elasticLine = _elasticTabstopsLinesCache[curLine.LineNumber]; bool isLastColumnInLine = elasticLine.IsLastColumnInLine(colNumber); while (curLineNumber < textSnapshot.LineCount) { ITextSnapshotLine downLine = textSnapshot.GetLineFromLineNumber(curLineNumber); ElasticTabstopsLine downElasticLine = GetElasticTabstopsLine(downLine); if (downElasticLine.IsLastColumnInLine(colNumber) != isLastColumnInLine) { break; } ElasticTabstopsColumn downColumn = downElasticLine.GetColumnOrDefault(colNumber); if (downColumn == null) { break; } downColumn.TabOffset = colTabOffset; ShrinkTabOffset(downElasticLine, colNumber); curLineNumber++; } }
/// <summary> /// Fix tab offset for a column if needed /// </summary> private void ShrinkTabOffset(ElasticTabstopsLine tabLine, int colNumber) { ElasticTabstopsColumn colTabOffset = tabLine.ElasticColumns[colNumber]; double width = CalculateInitialWidth(tabLine, colNumber); if (colTabOffset.TabOffset.ColumnWidth < width) { colTabOffset.TabOffset.ColumnWidth = width; colTabOffset.TabOffset.TabOffset = CalculateInitialTabOffset(tabLine, colNumber); } }
/// <summary> /// Returns true if current line offsets changed regarding to given line /// false otherwise /// </summary> internal bool ChangedRegardingTo(ElasticTabstopsLine oldLine) { if (ElasticColumns == oldLine.ElasticColumns) { return(false); } if (ElasticColumns.Length != oldLine.ElasticColumns.Length) { return(true); } return(ElasticColumns.Where((etc, i) => etc.ChangedRegardingTo(oldLine.ElasticColumns[i])).Any()); }
/// <summary> /// Calculates column width for a specific column in specific line /// </summary> private double CalculateInitialWidth(ElasticTabstopsLine elasticLine, int colNumber) { ITextSnapshot textSnapshot = _textView.TextSnapshot; ElasticTabstopsColumn column = elasticLine.ElasticColumns[colNumber]; Span span = new Span(column.Start, column.ColumnTextLength); if (span.Start > textSnapshot.Length || span.End > textSnapshot.Length) { return(0); } SnapshotSpan columnSpan = new SnapshotSpan(textSnapshot, span); double columnWidth = _textMeasureService.GetWidth(columnSpan); return(Math.Max(columnWidth, _minCellWidth)); }
/// <summary> /// Calculate tab offsets for line in a given direction /// </summary> private void CalculateTabOffsets(ITextSnapshotLine line, CalculateDirection direction, bool forceInvalidate) { //Calculates tab offset for a given line for the given direction ElasticTabstopsLine elasticLine = GetElasticTabstopsLine(line, forceInvalidate); for (int colNumber = 0; colNumber < elasticLine.ElasticColumns.Length; colNumber++) { ElasticTabstopsColumn column = elasticLine.ElasticColumns[colNumber]; //Tab offset is allready calculated during other line calculation if (!forceInvalidate && column.TabOffset != null) { continue; } //Assign the same ColumnTabOffset to all columns in the same block ColumnSizeInfo colTabOffset = new ColumnSizeInfo { TabOffset = CalculateInitialTabOffset(elasticLine, colNumber), ColumnWidth = CalculateInitialWidth(elasticLine, colNumber) }; column.TabOffset = colTabOffset; switch (direction) { case CalculateDirection.Up: CalculateTabOffsetUp(line, colNumber, colTabOffset); break; case CalculateDirection.Down: CalculateTabOffsetDown(line, colNumber, colTabOffset); break; case CalculateDirection.DownUp: CalculateTabOffsetDown(line, colNumber, colTabOffset); CalculateTabOffsetUp(line, colNumber, colTabOffset); break; default: throw new ArgumentException("direction"); } } }
/// <summary> /// Returns ElasticTabstopsLine with initialized ElasticColumns /// </summary> private ElasticTabstopsLine GetElasticTabstopsLine(ITextSnapshotLine line, bool forceInvalidateColumns = false) { ElasticTabstopsLine elasticTabstopsLine = _elasticTabstopsLinesCache[line.LineNumber]; if (elasticTabstopsLine.ElasticColumns == null || forceInvalidateColumns) { string lineText = line.GetText(); string[] tabSplits = lineText.Split('\t'); elasticTabstopsLine.ElasticColumns = new ElasticTabstopsColumn[tabSplits.Length]; int curPosInLine = line.Start.Position; for (int i = 0; i < tabSplits.Length; i++) { string ts = tabSplits[i]; ElasticTabstopsColumn column = new ElasticTabstopsColumn { ColumnTextLength = ts.Length, Start = curPosInLine }; //skeep tab curPosInLine += ts.Length + 1; elasticTabstopsLine.ElasticColumns[i] = column; } } return(elasticTabstopsLine); }
/// <summary> /// Calulates tab offset depending column widthes before current column /// This method assume that column widthes before current column are calculated allready /// </summary> private double CalculateInitialTabOffset(ElasticTabstopsLine tabLine, int colNumber) { return(tabLine.ElasticColumns.Take(colNumber).Sum(ct => ct.TabOffset.ColumnWidth) + colNumber * _paddingWidth); }
/// <summary> /// Invalidates tabs cache depending given changes, and return changed line numbers /// </summary> /// <param name="changes">changed made</param> internal void InvalidateChanges(INormalizedTextChangeCollection changes) { if (!changes.Any()) { return; } #region Old var firstChange = changes.First(); var start = Math.Min(firstChange.OldSpan.Start, firstChange.NewSpan.Start); var end = Math.Max(firstChange.OldSpan.End, firstChange.NewSpan.End); foreach (var change in changes) { var lineNumber = _textView.TextSnapshot.GetLineNumberFromPosition(change.NewPosition); if (change.LineCountDelta > 0) { _elasticTabstopsLinesCache.InsertRange(lineNumber, Enumerable.Range(0, change.LineCountDelta).Select( c => new ElasticTabstopsLine())); } else if (change.LineCountDelta < 0) { _elasticTabstopsLinesCache.RemoveRange(lineNumber, -change.LineCountDelta); } start = Math.Min(start, Math.Min(change.OldSpan.Start, change.NewSpan.Start)); end = Math.Max(end, Math.Max(change.OldSpan.End, change.NewSpan.End)); } var topLine = _textView.TextSnapshot.GetLineFromPosition(start); var topLineNumber = topLine.LineNumber; if (changes.IncludesLineChanges && topLineNumber != 0) { topLineNumber--; topLine = _textView.TextSnapshot.GetLineFromLineNumber(topLineNumber); } while (topLineNumber > 0 && topLine.Start != topLine.End) { topLineNumber--; topLine = _textView.TextSnapshot.GetLineFromLineNumber(topLineNumber); } end = Math.Min(end, Math.Max(0, _textView.TextSnapshot.Length - 1)); var bottomLine = _textView.TextSnapshot.GetLineFromPosition(end); var bottomLineNumber = bottomLine.LineNumber; if (changes.IncludesLineChanges && bottomLineNumber < _textView.TextSnapshot.LineCount - 1) { bottomLineNumber++; bottomLine = _textView.TextSnapshot.GetLineFromLineNumber(bottomLineNumber); } while (bottomLineNumber < _textView.TextSnapshot.LineCount - 1 && bottomLine.Start != bottomLine.End) { bottomLineNumber++; bottomLine = _textView.TextSnapshot.GetLineFromLineNumber(bottomLineNumber); } #endregion //InvalidateChanges(); for (var i = topLineNumber; i <= bottomLineNumber; i++) { _elasticTabstopsLinesCache[i] = new ElasticTabstopsLine(); } for (var i = topLineNumber; i <= bottomLineNumber; i++) { var line = _textView.TextSnapshot.GetLineFromLineNumber(i); CalculateTabOffsets(line, CalculateDirection.Down, false); } }