Предположение Брюса Боутона правильное. Метод GetChangedSpans не предназначен для использования в качестве механизма разграничения синтаксиса общего назначения для определения различий между двумя синтаксическими деревьями, которые не имеют общей истории. Скорее, он предназначен для того, чтобы взять два дерева, которые были созданы в результате редактирования, в общее дерево и определить, какие части деревьев отличаются из-за изменений.
Если бы вы взяли свое первое дерево разбора и вставили в него новый оператор как редактирование, то вы бы увидели гораздо меньший набор изменений.
Может помочь, если я кратко опишу, как работают лексер и парсер Roslyn, на высоком уровне.
Основная идея заключается в том, что "синтаксические токены", созданные лексером, и "синтаксические деревья" синтаксического анализатора * неизменны . Они никогда не меняются. Поскольку они никогда не меняются, мы можем повторно использовать части предыдущих деревьев разбора в новых деревьях разбора. (Структуры данных, которые имеют это свойство, часто называют «постоянными» структурами данных.)
Поскольку мы можем повторно использовать существующие детали, мы можем, например, использовать одно и то же значение для каждого экземпляра данного токена, скажем class
, который появляется в программе. Длина и содержание каждого class
токена абсолютно одинаковы; единственными вещами, которые различают два разных токена class
, являются их мелочи , (какой интервал и комментарии окружают их) и их позиция , и их родитель - какой синтаксический узел большего размера содержит токен.
Когда вы анализируете блок текста, мы генерируем синтаксические токены и синтаксические деревья в постоянной, неизменной форме, которую мы называем «зеленой» формой. Затем мы обертываем зеленые узлы в «красный» слой. Зеленый слой ничего не знает о положении, родителях и так далее. Красный слой делает. (Причудливые имена связаны с тем, что когда мы впервые нарисовали эту структуру данных на доске, это те цвета, которые мы использовали.) Когда вы создаете редактирование для данного синтаксического дерева, мы смотрим на предыдущее синтаксическое дерево, определяем узлы, которые изменились, а затем построить новые узлы только на позвоночнике изменений . Все остальные ветви зеленого дерева остаются прежними.
При дифференцировании двух деревьев мы в основном берем заданную разницу зеленых узлов . Если одно из деревьев было создано путем редактирования другого, то почти всех зеленых узлов будет одинаковым , потому что был восстановлен только позвоночник. Алгоритм определения дерева идентифицирует измененные узлы и обрабатывает затронутые участки.
Если два дерева не имеют общей истории, то единственными зелеными узлами, которые у них будут общие, являются отдельные токены, которые, как я уже говорил, используются везде. Каждый зеленый синтаксический узел более высокого уровня будет отличаться зеленым узлом, и поэтому механизм различий деревьев будет рассматривать его как отличающийся, даже если его текст одинаков.
Цель этого метода состоит в том, чтобы позволить коду редактора быстро сделать консервативное предположение о том, какие части текстового буфера необходимо, скажем, перекрашивать, после редактирования или отмены, или какая-то такая вещь. Предполагается, что деревья имеют историческую связь. Цель состоит не в том, чтобы предоставить универсальный механизм текстовых различий; для этого уже есть множество отличных инструментов.
Представьте, например, что вы вставили свою первую программу в редактор, затем выделили ее целиком, а затем вставили вторую программу в редактор. Можно было бы разумно ожидать, что редактор не будет тратить время, пытаясь выяснить, какие части вставленного кода оказались идентичными ранее вставленному коду. Это может быть очень дорого, и ответ, вероятно, будет "не очень". Скорее, редактор делает консервативное предположение, что весь вставленный регион является совершенно новым и совершенно другим кодом. Он не тратит время на попытки найти соответствие между старым кодом и новым кодом; он разбирает и, следовательно, перекрашивает все это.
Если, с другой стороны, вы только что вставили в один другой оператор, то механизм редактирования просто вставит редактирование в нужное место. Дерево синтаксического анализа будет регенерировано с повторным использованием существующих зеленых узлов, где это возможно , и механизм различий определит, какие отрезки необходимо перекрасить: те, которые имеют разные зеленые узлы.
Имеет ли это смысл?
UPDATE:
Ха, по-видимому, мы с Кевином одновременно печатали один и тот же ответ в соседних офисах. Немного дублированных усилий, но я думаю, что оба ответа имеют хорошие перспективы на ситуацию. : -)