terça-feira, 8 de junho de 2010

Customizando o Help Insight no Delphi

Recentemente li um artigo sobre Help Insight que chamou bastante minha atenção.
Acredito que todos saibam o que é ou já tiveram contato com essa funcionalidade, mas para aqueles que não estão acostumado com o nome Help Insight, esse é um recurso do IDE do Delphi que apresenta um popup com uma breve descrição sobre o identificador (classe, método, função, variável, ...) na qual o cursor do mouse está posicionado.
Também é possível invocar o Help Insight através do pressionamento das teclas CTRL + SHIFT + H.
Por padrão é apresentado nesse popup menu as seguintes informações:
  • Nome do identificador
  • Categoria do identificador (Campo, variável, método, classe, propriedade, ...)
  • Localização (nome da unit seguido pelo número da linha e da coluna)
  • Tipo (Se o identificador é string, integer, ...)

No caso do identificador ser um método, function ou procedure, a descrição é extendida apresentando também o(s) parâmetro(s) e o valor de retorno.

Isso tudo é feito automaticamente pelo IDE em tempo de edição, ou seja, nem necessitar que o código seja compilado.
O que eu não sabia, e é o objetivo desse artigo, é que o conteúdo do popup menu pode ser customizado exibindo informações mais detalhadas a respeito do identificador em questão.
Essa customização deve estar situada imediatamente acima do identificador a qual se deseja trabalhar e tem seu início demarcado através de um comentário coringa (///, três barras) seguido por uma tag XML reconhecida pelo Help Insight viewer.

As tags XML atualmente reconhecida são:
  • summary
  • param
  • returns
  • permission
  • remarks
  • comments
Agora vamos ver um exemplo prático contendo todas essas tags.

/// <summary>    Verificar se um determinado componente/field teve ou não seu conteúdo
///              alterado. O método é utilizado dentro do framework para sinalizar os
///              componentes que foram modificados durante uma operação de update.</summary>
/// <param name="FieldComp"> Registro contendo informações sobre o field/componente. </param>
/// <param name="Index">    Índice do registro dentro da lista (<c>FFieldList</c>). </param>
/// <param name="Modified">  Parâmetro de saída que deve ser setado para True/False, dependendo
///                          do status verificado pelo método. Por padrão, o parâmetro vem
///                          com o valor True. </param>
/// <returns>    O valor retornado deve significar que o método obteve sucesso ao comparar o
///              valor original com o valor atual do componente/field, ou seja, que o valor
///              do parâmetro Modified é válido.</returns>
/// <permission> Esse método é utilizado internamente apenas quando o formulário está em estado
///              de alteração de registro. A implementação original trás suporte para os
///              componentes mais comumente utilizados, entretanto, em caso de customização ou
///              extenção do suporte a outros componentes, esse método pode sempre ser
///              reimplementado nos formulários filhos. </permission>
/// <remarks>    Originalmente esse método foi concebido para ser utilizado pelo framework como
///              um serviço. Assim, evite chamá-lo diretamente a menos que saiba com exatidão
///              o que está fazendo. </remarks>
/// <comments>  Caso necessite reimplementar o método, é recomendado que faça uma chamada à
///              implementação da classe pai (Exemplo: Result := inherited;). </comments>

Observe que foi utilizado também as tags <c> e </c>. Essas tags delimitam um área de texto que será formatada no estilo de código fonte do Delphi (por padrão, Courier New).
O resultado é:

Convenhamos que se você está navegando pelo código, um help assim é muito, mas muito mais útil do que simplesmente visualizar a assinatura do identificador (padrão do Delphi), certo? Além do mais, você não precisa se deslocar até a definição/implementação/declaração do identificador para obter maiores detalhes, o que quase sempre resulta na interrupção da linha de raciocínio durante alguma investigação.
Uma outra vantagem que notei sobre a utilização do Help Insigh é poder ter dois tipos de documentação a respeito de um método sobre dois pontos de vista.
Deixa eu exemplificar. Observe o código fonte abaixo.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm1 = class(TForm)
  private
    { Private declarations }

  protected
    function FieldIsModified(var FieldComp: TFieldItem; const Index: Integer; out Modified: Boolean): Boolean; virtual;

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

{_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\
  Definition.: Protected          Strict [ ]     Virtual/Override [X]     Event [ ]
  Ojective...:
  Usefulness.:
  Parameters.: [ I ] FieldComp =
               [ I ] Index     =
               [ O ] Modified  =
  Result.....:
  Comments...:
_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\}
function TForm1.FieldIsModified(var FieldComp: TFieldItem; const Index: Integer; out Modified: Boolean): Boolean;
begin
  // ...
end;

end.

Creio que a prática mais comum de documentação de código seja a inclusão de comentários imediatamente acima da implementação dos métodos/funções (como apresentando no exemplo acima), ficando a área de interface da unit com apenas as definições.

O código fonte acima contém a estrutura do bloco de comentário que costumo utilizar para documentar um método ou uma function (removi o texto para ficar mais fácil sua visualização). As informações que acrescento nesse bloco estão relacionadas principalmente com seu funcionamento, com sua dependência perante a solução como um todo e com cuidados que devem ser tomados na sua utilização.
Quando penso no Help Inside, penso em um ponto de vista onde saber como é o funcionamento interno de um método não é tão importante, naquele momento, quanto saber o que o método faz, suas entradas e suas saídas. Assim, quando digo que o Help Insight proporciona um segundo tipo de comentários é justamente a possibilidade de documentar o código fonte sob uma perspectiva mais focada no conceito do que em detalhes intrínsecos ao funcionamento.

Apesar de muito útil, fique um tanto desconfortável com relação a legibilidade (ou melhor, à falta de legibilidade) que a estrutura de definição das tags causa ao layout da classe.
Por exemplo, se você possui um classe com vários métodos e propriedades. Acrescentar um help insigh para cada definição irá dinimuir a visão do todo por exigir constante deslocamento entre as linhas para navegar pela classe (aqui o layout struct do Delphi se mostrou ainda mais útil).
Fiz alguns testes e notei que o IDE aceita a utilização de REGIONS para delimitar a área do Help Insight, o que pareceu uma forma bem razoável para resolver ou, no mínimo, minimizar a área visível ocupada.


Um segundo ponto com relação a legibilidade é que não há uma formatação específica para as tags.
  a) É possível escrever a descrição de uma tag utilizando multi-linhas ou em apenas uma única linha que o conteúdo será apresentado igualmente no popup;
  b) Pode-se acrescentar espaços vários espaços em brancos, mas os excedentes serão ignorados (assim como em HTML);
  c) Qualquer quebra de linha será ignorada, o que dá margem para formatar melhor o texto na unit, mas na hora de exibir o popup, as frases serão concatenadas em um único parágrafo.
  Isso faz com que não seja possível escrever um pequeno trecho de código, por exemplo.

Em outras palavras, fiquei com a impressão que é mais legível ler as descrições do popup do que diretamente do código fonte.

Caso deseje analisar o código fonte completo da unit criada durante a escrita desse artigo, faça o download nesse link.


A última dica é para aqueles de desejam alterar a saída do popup para um layout mais adequado ou apenas queiram fazer pequenos incrementos. Nesse caso basta editar o arquivo HelpInsight.xsl (sub-diretório ObjRepos do diretório de instalação do RAD Studio, para mim é C:\Program Files\Embarcadero\RAD Studio\7.0\ObjRepos). Lá você encontrará também outras opções não descrita nesse artigo.

Boa sorte.

2 comentários:

Anônimo disse...

Excelente artigo, obrigado por compartilhar seu conhecimento conosco.

Saberia me dizer apartir de qual versão do delphi esta disponivel estes recursos?

Fabius Lovato disse...

Obrigado pelo comentário.
Help Insight é um recurso que já tem algum tempo de vida, mas, infelizmente, não tão utilizado.
Ele passou a fazer parte do Delphi a partir da versão 2005, lançada no último quarter de 2004.

Na página 40 do "Borland Delphi 2005 Reviewer's Guide" você encontra uma breve descrição.
http://www.borland.com/resources/en/pdf/white_papers/delphi_2005_reviewers_guide.pdf