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