Архитектура GitHub Copilot: Как отобразить пачку правок на миллион строк
Инженеры GitHub переписали интерфейс просмотра различий (diff) в приложении Copilot, чтобы пользователи могли удобно работать с огромными Pull Request (PR). Новое решение позволяет открывать пачки правок объемом более миллиона измененных строк кода и с сотнями инлайн-комментариев без задержек прокрутки, используя уникальный гибридный подход к виртуализации.
#RenderingHugePR: Архитектура GitHub Copilot
Команда разработки GitHub недавно завершила масштабную переработку того, как приложение GitHub Copilot отображает различия кода (diff). Целью был отказ от стандартных механизмов прокрутки, которые «падают» при большом объеме данных. Теперь разработчики могут открывать пачки правок (Pull Request) с более чем одним миллионом измененных строк и свыше 400 инлайн-комментариев, и интерфейс сохраняет высокую плавность работы. Ниже приводится технический разбор того, как это было реализовано.
Проблема масштабируемости и комментарии
Обработка больших объемов кода сама по себе является хорошо изученной задачей. Стандартный подход — виртуализация: отображение на экране только тех строк, которые находятся в видимой области, плюс небольшой запас, с переработкой (рециклингом) элементов по мере прокрутки. Если каждая строка кода имеет предсказуемую высоту (зависит только от шрифта), систему легко поддерживать, так как геометрия списка неизменна.
Однако в Pull Request вводятся комментарии ревьюеров. Высота таких элементов не детерминирована заранее. Она зависит от динамических факторов: * Как обернется Markdown-текст в зависимости от ширины окна; * Открыты ли блоки <details>; * Появились ли вложенные ответы; * Загрузились ли изображения; * Находится ли пользователь в режиме редактирования.
Эта неопределенность ломает классическую модель виртуализации, где высота каждого элемента должна быть известна *до* отрисовки («contract of all heights known before paint»). Попробуем применить стандартную оценку высоты для комментариев — и мы столкнемся с ошибками: либо появятся большие пустые пробелы, либо элементы вылезут за пределы видимой области, либо возникает эффект «телепортации» при прокрутке, когда реальная высота элемента отлична от запланированной. На массивных пачках правок это приводит к рывкам интерфейса (jank) и плохому пользовательскому опыту.
Решение: Две геометрии вместо одной
Инженеры GitHub решили проблему, отказавшись от идеи единой геометрии для всего контента. Вместо этого документ разбит на две независимые области, каждая из которых управляется по-своему:
1. Детерминированная геометрия кода: Строчки исходного кода сохраняют свойство «высота известна заранее». Таблица с позициями строк вычисляется один раз и не обновляется динамически. Это обеспечивает идеальную скорость прокрутки основной части кода. 2. Динамическая геометрия блоков: Комментарии, ответы, редакторы и вложенные блоки обрабатываются как отдельные «блоки». У каждого блока есть стабильный ключ, привязанный к файлу и строке, но не к пиксельной координате.
Высота общего документа вычисляется как сумма точной высоты кода и суммы эффективных высот динамических блоков. Это позволяет системе игнорировать изменения размера комментариев при прокрутке основного кода — перерасчет геометрии происходит только для затрагиваемых областей.
Алгоритм планирования измерений
Первоначальный подход использовал для каждого динамического блока собственный наблюдатель (ResizeObserver), который обновлял высоту при изменении. Это вызывало проблемы: циклы обратной связи приводили к избыточным вычислениям и перегрузке браузера.
В финальной реализации внедрен единый график измерений, работающий по строгому регламенту:
* Не на горячем пути: Процесс запускается только после завершения прокрутки или в режиме простоя, чтобы не прерывать отрисовку. * Ограниченный охват: Измеряются только блоки, находящиеся в радиусе примерно 2400 пикселей от видимой области. Это гарантирует, что стоимость вычислений остается пропорциональной высоте окна просмотра, а не общему количеству комментариев. * Приоритет актуальности: Если блок находится на экране, используется его реальная отрисованная высота. Оценки применяются только для скрытых блоков, приближающихся к экрану.
Кроме того, используются «отпечатки» (fingerprints) состояния блока (например, открыт ли детали, загружено ли изображение). Если состояние не изменилось, новая отрисовка не требуется. Только при изменении отпечатка блок помечается для перепроверки в следующем цикле.
Заключение
Перестройка дифф-поверхности в Copilot демонстрирует, как инженерные принципы и тщательная работа с геометрией интерфейса позволяют создавать инструменты, способные обрабатывать экстремальные объемы данных. Для разработчиков это означает возможность вести диалог с огромными рефакторингами без потерь в производительности, даже когда один Pull Request содержит миллионы строк кода.