Я пишу надстройку COM, которая расширяет IDE, в которой она остро нуждается. Здесь задействовано много функций, но давайте сузим их до 2 ради этого поста:
- Существует окно инструментов Code Explorer, которое отображает древовидное представление, которое позволяет пользователю перемещаться по модулям и их членам.
- Существует окно инструментов проверки кода, которое отображает сетку данных, которая позволяет пользователю перемещаться по проблемам кода и автоматически исправлять их.
Оба инструмента имеют кнопку «Обновить», которая запускает асинхронную задачу, которая анализирует весь код во всех открытых проектах; Code Explorer использует результаты синтаксического анализа для построения TreeView , а код Осмотры использует синтаксический анализ результаты поиска проблем коды и отображение результатов в своей DataGridView .
Здесь я пытаюсь поделиться результатами разбора между функциями, чтобы при обновлении Code Explorer Инспекции кода знали об этом и могли обновляться без необходимости повторения работы синтаксического анализа, которую только что сделал Code Explorer. ,
Итак, что я сделал, я сделал свой класс синтаксического анализатора провайдером событий, для которого функции могут зарегистрироваться:
private void _parser_ParseCompleted(object sender, ParseCompletedEventArgs e)
{
Control.Invoke((MethodInvoker) delegate
{
Control.SolutionTree.Nodes.Clear();
foreach (var result in e.ParseResults)
{
var node = new TreeNode(result.Project.Name);
node.ImageKey = "Hourglass";
node.SelectedImageKey = node.ImageKey;
AddProjectNodes(result, node);
Control.SolutionTree.Nodes.Add(node);
}
Control.EnableRefresh();
});
}
private void _parser_ParseStarted(object sender, ParseStartedEventArgs e)
{
Control.Invoke((MethodInvoker) delegate
{
Control.EnableRefresh(false);
Control.SolutionTree.Nodes.Clear();
foreach (var name in e.ProjectNames)
{
var node = new TreeNode(name + " (parsing...)");
node.ImageKey = "Hourglass";
node.SelectedImageKey = node.ImageKey;
Control.SolutionTree.Nodes.Add(node);
}
});
}
И это работает. У меня проблема в том, что ... это работает - я имею в виду, когда проверки кода обновляются, анализатор говорит проводнику кода (и всем остальным): "Чувак, кто-то разбирает, что ты хочешь с этим делать?" " - и когда анализ завершается, парсер говорит слушателям: «Ребята, у меня есть свежие результаты разбора для вас, что вы хотите с этим сделать?».
Позвольте мне показать вам пример, чтобы проиллюстрировать эту проблему:
- Пользователь вызывает Code Explorer, который говорит пользователю: «Держись, я здесь работаю»; Пользователь продолжает работать в IDE, Code Explorer перерисовывает себя, жизнь прекрасна.
- Затем пользователь вызывает проверки кода, которые говорят пользователю «держись, я здесь работаю»; синтаксический анализатор говорит проводнику кода: "Чувак, кто-то разбирает, что ты хочешь с этим сделать?" - Code Explorer говорит пользователю «держись, я здесь работаю»; Пользователь все еще может работать в IDE, но не может перемещаться по Code Explorer, потому что он обновляется. И он ждет завершения проверки кода.
- Пользователь видит проблему с кодом в результатах проверки, которую он хочет устранить; они дважды щелкают, чтобы перейти к нему, подтверждают, что есть проблема с кодом, и нажимают кнопку «Исправить». Модуль был изменен и требует повторного анализа, поэтому проверки кода продолжаются; Обозреватель кода говорит пользователю «держись, я здесь работаю», ...
Видишь, куда это идет? Мне это не нравится, и я уверен, что пользователям это тоже не понравится. Что мне не хватает? Как мне лучше обмениваться результатами анализа между функциями, но при этом оставить пользователю контроль над тем, когда функция должна выполнять свою работу ?
Причина, по которой я спрашиваю, заключается в том, что я решил, что если я отложу реальную работу до тех пор, пока пользователь не решит активно обновлять данные, и не "кэширует" результаты анализа по мере их поступления ... что ж, я бы обновил древовидную структуру и обнаружение проблем с кодом в возможном устаревшем результате анализа ... что буквально возвращает меня к исходной точке, где каждая функция работает со своими собственными результатами анализа: есть ли способ, которым я могу поделиться результатами анализа между функциями и иметь прекрасный UX?
Код C # , но я не ищу код, я ищу концепции .
VBAParser
генерируется ANTLR и дает мне дерево разбора, но функции этого не потребляют. ОнRubberduckParser
берет дерево синтаксического анализа, обходит его и выдает объектVBProjectParseResult
, содержащий всеDeclaration
объекты, для которых все ониReferences
разрешены - это то, что функции принимают для ввода ... так что да, это в значительной степени ситуация "все или ничего". ОнRubberduckParser
достаточно умен, чтобы не анализировать модули, которые не были изменены. Но если есть узкое место, дело не в анализе, а в проверках кода.Ответы:
Вероятно, я подойду к этому так, чтобы меньше сосредоточиться на обеспечении идеальных результатов, а вместо этого сосредоточиться на подходе с максимальными усилиями. Это приведет по крайней мере к следующим изменениям:
Преобразуйте логику, которая в настоящее время запускает повторный анализ для запроса вместо инициации.
Логика запроса на повторный анализ может в итоге выглядеть примерно так:
Это будет связано с логикой обтекания синтаксического анализатора, который может выглядеть примерно так:
Важно то, что синтаксический анализатор работает до тех пор, пока не будет удовлетворен самый последний запрос на повторный анализ, но в каждый момент времени выполняется не более одного анализатора.
Удалить
ParseStarted
обратный вызов. Запрос на повторный анализ - теперь операция «забыл и забыл».В качестве альтернативы, преобразуйте его, чтобы он ничего не делал, кроме отображения освежающего индикатора в какой-то непонятной части графического интерфейса, которая не блокирует взаимодействие с пользователем.
Попробуйте обеспечить минимальную обработку для устаревших результатов.
В случае Code Explorer это может быть так же просто, как поиск разумного количества строк вверх и вниз для метода, к которому пользователь хочет перейти, или ближайшего метода, если точное имя не найдено.
Я не уверен, что подойдет для инспектора кода.
Я не уверен в деталях реализации, но в целом это очень похоже на то, как редактор NetBeans обрабатывает это поведение. Это всегда очень быстро, чтобы указать, что это в настоящее время обновляет, но также не блокирует доступ к функциональности.
Устаревшие результаты часто достаточно хороши - особенно если сравнивать с отсутствием результатов.
источник
ParseStarted
чтобы отключить кнопку [Обновить] (Control.EnableRefresh(false)
). Если я удалю этот обратный вызов и позволю пользователю щелкнуть его ... тогда я попаду в ситуацию, когда у меня есть две параллельные задачи, выполняющие синтаксический анализ ... как избежать этого, не отключая обновление всех других функций, пока кто-то это разбор?ParseStarted
событие, если вы хотите разрешить пользовательскому интерфейсу (или другому компоненту) иногда предупреждать пользователя о повторном выполнении. Конечно, вы можете захотеть задокументировать, что вызывающие абоненты должны пытаться не мешать пользователю использовать устаревшие результаты анализа.