<commit_before>/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant 
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#import "CKComponentBoundsAnimation+UICollectionView.h"

#import <vector>

@interface CKComponentBoundsAnimationCollectionViewContext : NSObject
- (instancetype)initWithCollectionView:(UICollectionView *)cv;
- (void)applyBoundsAnimationToCollectionView:(const CKComponentBoundsAnimation &)animation;
@end

id CKComponentBoundsAnimationPrepareForCollectionViewBatchUpdates(UICollectionView *cv)
{
  return [[CKComponentBoundsAnimationCollectionViewContext alloc] initWithCollectionView:cv];
}

void CKComponentBoundsAnimationApplyAfterCollectionViewBatchUpdates(id context, const CKComponentBoundsAnimation &animation)
{
  [(CKComponentBoundsAnimationCollectionViewContext *)context applyBoundsAnimationToCollectionView:animation];
}

@implementation CKComponentBoundsAnimationCollectionViewContext
{
  UICollectionView *_collectionView;
  NSInteger _numberOfSections;
  std::vector<NSUInteger> _numberOfItemsInSection;
  NSDictionary *_indexPathsToSnapshotViews;
  NSDictionary *_indexPathsToOriginalLayoutAttributes;
}

- (instancetype)initWithCollectionView:(UICollectionView *)cv
{
  if (self = [super init]) {
    _collectionView = cv;
    _numberOfSections = [cv numberOfSections];
    for (NSInteger i = 0; i < _numberOfSections; i++) {
      _numberOfItemsInSection.push_back([cv numberOfItemsInSection:i]);
    }

    // We might need to use a snapshot view to animate cells that are going offscreen, but we don't know which ones yet.
    // Grab a snapshot view for every cell; they'll be used or discarded in -applyBoundsAnimationToCollectionView:.
    NSMutableDictionary *sv = [NSMutableDictionary dictionary];
    for (NSIndexPath *ip in [cv indexPathsForVisibleItems]) {
      UIView *snapshotView = [[cv cellForItemAtIndexPath:ip] snapshotViewAfterScreenUpdates:NO];
      if (snapshotView) {
        sv[ip] = snapshotView;
      }
    }
    _indexPathsToSnapshotViews = sv;

    NSMutableDictionary *la = [NSMutableDictionary dictionary];
    for (NSIndexPath *ip in visibleAndJustOffscreenIndexPaths(cv)) {
      la[ip] = [_collectionView layoutAttributesForItemAtIndexPath:ip];
    }
    _indexPathsToOriginalLayoutAttributes = la;
  }
  return self;
}

static NSArray *visibleAndJustOffscreenIndexPaths(UICollectionView *cv)
{
  NSMutableArray *sortedIndexPaths = [[[cv indexPathsForVisibleItems] sortedArrayUsingSelector:@selector(compare:)] mutableCopy];
  if ([sortedIndexPaths count] == 0) {
    return sortedIndexPaths;
  }

  // Obviously we want to animate all visible cells. But what about cells that were not previously visible, but become
  // visible as a result of an item becoming smaller? We grab the layout attributes of a few more items that are
  // offscreen so that we can animate them too. (Only some, though; we don't attempt to get *all* layout attributes.)

  NSIndexPath *afterIndexPath = [sortedIndexPaths lastObject];
  static const NSUInteger kOffscreenIndexPathCount = 10;
  for (NSUInteger i = 0; i < kOffscreenIndexPathCount; i++) {
    if ([afterIndexPath item] == [cv numberOfItemsInSection:[afterIndexPath section]] - 1) {
      if ([afterIndexPath section] == [cv numberOfSections] - 1) {
        break; // No more rows to animate.
      }
      afterIndexPath = [NSIndexPath indexPathForItem:0 inSection:[afterIndexPath section] + 1];
    } else {
      afterIndexPath = [NSIndexPath indexPathForItem:[afterIndexPath item] + 1 inSection:[afterIndexPath section]];
    }
    [sortedIndexPaths addObject:afterIndexPath];
  }

  return sortedIndexPaths;
}

- (void)applyBoundsAnimationToCollectionView:(const CKComponentBoundsAnimation &)animation
{
  if (animation.duration == 0) {
    return;
  }
  // The documentation states that you must not use these functions with inserts or deletes. Let's be safe:
  if ([_collectionView numberOfSections] != _numberOfSections) {
    return;
  }
  for (NSInteger i = 0; i < _numberOfSections; i++) {
    if (_numberOfItemsInSection.at(i) != [_collectionView numberOfItemsInSection:i]) {
      return;
    }<commit_msg>Fix out-of-bounds bug with component bounds animation<commit_after>/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant 
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#import "CKComponentBoundsAnimation+UICollectionView.h"

#import <vector>

@interface CKComponentBoundsAnimationCollectionViewContext : NSObject
- (instancetype)initWithCollectionView:(UICollectionView *)cv;
- (void)applyBoundsAnimationToCollectionView:(const CKComponentBoundsAnimation &)animation;
@end

id CKComponentBoundsAnimationPrepareForCollectionViewBatchUpdates(UICollectionView *cv)
{
  return [[CKComponentBoundsAnimationCollectionViewContext alloc] initWithCollectionView:cv];
}

void CKComponentBoundsAnimationApplyAfterCollectionViewBatchUpdates(id context, const CKComponentBoundsAnimation &animation)
{
  [(CKComponentBoundsAnimationCollectionViewContext *)context applyBoundsAnimationToCollectionView:animation];
}

@implementation CKComponentBoundsAnimationCollectionViewContext
{
  UICollectionView *_collectionView;
  NSInteger _numberOfSections;
  std::vector<NSUInteger> _numberOfItemsInSection;
  NSDictionary *_indexPathsToSnapshotViews;
  NSDictionary *_indexPathsToOriginalLayoutAttributes;
}

- (instancetype)initWithCollectionView:(UICollectionView *)cv
{
  if (self = [super init]) {
    _collectionView = cv;
    _numberOfSections = [cv numberOfSections];
    for (NSInteger i = 0; i < _numberOfSections; i++) {
      _numberOfItemsInSection.push_back([cv numberOfItemsInSection:i]);
    }

    // We might need to use a snapshot view to animate cells that are going offscreen, but we don't know which ones yet.
    // Grab a snapshot view for every cell; they'll be used or discarded in -applyBoundsAnimationToCollectionView:.
    NSMutableDictionary *sv = [NSMutableDictionary dictionary];
    for (NSIndexPath *ip in [cv indexPathsForVisibleItems]) {
      UIView *snapshotView = [[cv cellForItemAtIndexPath:ip] snapshotViewAfterScreenUpdates:NO];
      if (snapshotView) {
        sv[ip] = snapshotView;
      }
    }
    _indexPathsToSnapshotViews = sv;

    NSMutableDictionary *la = [NSMutableDictionary dictionary];
    for (NSIndexPath *ip in visibleAndJustOffscreenIndexPaths(cv)) {
      la[ip] = [_collectionView layoutAttributesForItemAtIndexPath:ip];
    }
    _indexPathsToOriginalLayoutAttributes = la;
  }
  return self;
}

static NSArray *visibleAndJustOffscreenIndexPaths(UICollectionView *cv)
{
  NSArray *sortedIndexPaths = [[cv indexPathsForVisibleItems] sortedArrayUsingSelector:@selector(compare:)];
  if ([sortedIndexPaths count] == 0) {
    return sortedIndexPaths;
  }

  // Obviously we want to animate all visible cells. But what about cells that were not previously visible, but become
  // visible as a result of an item becoming smaller? We grab the layout attributes of a few more items that are
  // offscreen so that we can animate them too. (Only some, though; we don't attempt to get *all* layout attributes.)

  NSIndexPath *previousIndexPath = [sortedIndexPaths lastObject];
  static const NSUInteger kOffscreenIndexPathCount = 10;
  NSMutableArray *offscreenIndexPaths = [NSMutableArray array];
  while ([offscreenIndexPaths count] < kOffscreenIndexPathCount) {
    if ([previousIndexPath item] == [cv numberOfItemsInSection:[previousIndexPath section]] - 1) {
      NSUInteger nextSection = [previousIndexPath section] + 1;
      while (nextSection < [cv numberOfSections] && [cv numberOfItemsInSection:nextSection] == 0) {
        nextSection++;
      }
      if (nextSection == [cv numberOfSections]) {
        break; // No more rows to animate.
      }
      previousIndexPath = [NSIndexPath indexPathForItem:0 inSection:nextSection];
    } else {
      previousIndexPath = [NSIndexPath indexPathForItem:[previousIndexPath item] + 1 inSection:[previousIndexPath section]];
    }
    [offscreenIndexPaths addObject:previousIndexPath];
  }

  return [sortedIndexPaths arrayByAddingObjectsFromArray:offscreenIndexPaths];
}

- (void)applyBoundsAnimationToCollectionView:(const CKComponentBoundsAnimation &)animation
{
  if (animation.duration == 0) {
    return;
  }
  // The documentation states that you must not use these functions with inserts or deletes. Let's be safe:
  if ([_collectionView numberOfSections] != _numberOfSections) {
    return;
  }
  for (NSInteger i = 0; i < _numberOfSections; i++) {
    if (_numberOfItemsInSection.at(i) != [_collectionView numberOfItemsInSection:i]) {
      return;
    }
{'fraction_non_alphanumeric': 0.08351818988464951, 'fraction_numerical': 0.003438331854480923, 'mean_word_length': 4.975480450629556, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3021724', 'n_tokens_mistral': 2598, 'n_tokens_neox': 2465, 'n_words': 718}