Estou escrevendo um suplemento de COM que estende um IDE que precisa desesperadamente dele. Existem muitos recursos envolvidos, mas vamos reduzi-lo para 2 por causa deste post:
- Há uma janela de ferramenta do Code Explorer que exibe uma visualização em árvore que permite ao usuário navegar pelos módulos e seus membros.
- Há uma janela da ferramenta Inspeções de código que exibe uma visualização de datagrid que permite ao usuário navegar pelos problemas de código e corrigi-los automaticamente.
Ambas as ferramentas têm um botão "Atualizar" que inicia uma tarefa assíncrona que analisa todo o código em todos os projetos abertos; o Code Explorer usa os resultados da análise para criar a visualização em árvore , e o Code Inspections usa os resultados da análise para encontrar problemas de código e exibir os resultados em sua visualização de datagrid .
O que estou tentando fazer aqui é compartilhar os resultados da análise entre os recursos, para que, quando o Code Explorer for atualizado, as Inspeções de Código o reconheçam e possam se atualizar sem precisar refazer o trabalho de análise que o Code Explorer acabou de realizar. .
Então, o que fiz foi transformar minha classe de analisador em um provedor de eventos no qual os recursos podem ser registrados:
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);
}
});
}
E isso funciona. O problema que estou tendo é que ... funciona - quero dizer, quando as inspeções de código são atualizadas, o analisador diz ao explorador de código (e a todos os outros) "cara, alguém está analisando, alguma coisa que você queira fazer? " - e quando a análise é concluída, o analisador diz a seus ouvintes "pessoal, eu tenho novos resultados de análise para você, qualquer coisa que você queira fazer sobre isso?".
Deixe-me mostrar um exemplo para ilustrar o problema que isso cria:
- O usuário abre o Code Explorer, que informa ao usuário "espere, estou trabalhando aqui"; O usuário continua trabalhando no IDE, o Code Explorer se redesenha, a vida é linda.
- O usuário então exibe as Inspeções de código, que dizem ao usuário "espere, estou trabalhando aqui"; o analisador diz ao Code Explorer "cara, alguém está analisando, qualquer coisa que você queira fazer sobre isso?" - o Code Explorer diz ao usuário "espere, estou trabalhando aqui"; o usuário ainda pode trabalhar no IDE, mas não pode navegar no Code Explorer porque está atualizando. E ele também está aguardando a conclusão das inspeções de código.
- O usuário vê um problema de código nos resultados da inspeção que deseja resolver; eles clicam duas vezes para navegar até ele, confirmam que há um problema com o código e clique no botão "Corrigir". O módulo foi modificado e precisa ser analisado novamente, para que as inspeções de código continuem com ele; o Code Explorer diz ao usuário "espere, estou trabalhando aqui", ...
Veja onde isso está indo? Eu não gosto, e aposto que os usuários também não vão gostar. o que estou perdendo? Como devo compartilhar os resultados da análise entre os recursos, mas ainda deixar o usuário no controle de quando o recurso deve fazer seu trabalho ?
A razão pela qual estou perguntando é porque achei que se eu adiasse o trabalho real até o usuário decidir ativamente atualizar e "armazenasse em cache" os resultados da análise à medida que eles chegassem ... bem, eu estaria atualizando uma exibição em árvore e localizando problemas de código em um resultado de análise possivelmente obsoleto ... o que literalmente me leva de volta à estaca zero, onde cada recurso trabalha com seus próprios resultados de análise: existe alguma maneira de compartilhar resultados de análise entre recursos e ter um UX adorável?
O código é c # , mas não estou procurando por código, estou procurando por conceitos .
VBAParser
é gerada pela ANTLR e me fornece uma árvore de análise, mas os recursos não consomem isso. Ele RubberduckParser
pega a árvore de análise, a percorre e emite um VBProjectParseResult
que contém Declaration
objetos que foram todos References
resolvidos - é isso que os recursos levam para entrada .. então sim, é praticamente uma situação de tudo ou nada. O RubberduckParser
é inteligente o suficiente para não analisar novamente os módulos que não foram modificados. Mas se houver um gargalo, não é a análise, é a inspeção do código.