Logo Hardware.com.br
elissonandrade
elissonandra... Tô em todas Registrado
1.3K Mensagens 166 Curtidas

[Tutorial] Desenvolvendo seu próprio editor de código com Eclipse

#1 Por elissonandra... 05/03/2021 - 02:09
Bem pessoal, pesquisei e parece que num tem nenhum tutorial para iniciantes em relação a um recurso relativamente recente do eclipse, o Generic Editor. Basicamente, o Generic Editor permite que você crie um plugin simples pro Eclipse para editar sua linguagem de programação preferida do jeito que você quiser, com o seu próprio autocompletar, seu próprio sintax highlitgh, etc. Isso tudo de forma bem simples, lembrando o notepad++.

Como eu sei que o eclipse já num é mais tão utilizado, e mais difícil ainda é alguém fazer plugins pra ele, vou ser bem detalhado. Nesse exemplo, vamos criar um editor capaz colorir um arquivo de extensão tpl que eu uso na minha biblioteca MarajoaraNet como arquivo de modelo(template). Esse modelos são simplesmente arquivos php que possuem algumas partes do arquivo que podem ser alteradas para gerar clases automaticamente. É semelhante aos macros do c/C++, só que em tempo de compilação. Cabe também uma resalva aqui que o ideal é usar o generic editor apenas para arquivo com extensões pouco conhecidas, e não comumente utilizadas, pois essas extensões tipo txt, json e etc provavelmente vão conflitar com outras implementações já presentes no eclipse.

Imagem

Vamos começar do zero (ou quase).

Pré-requisitos:
  • Conhecimento em xml e programação java

Como nós vamos criar um plugin para o eclipse, o conhecimento de java é essencial. Além disso, configurar os plugins exige mexer bastante com arquivos xml, então o conhecimento disso tudo é fundamental. No geral, o conhecimento necessário de java é de ser capaz de pegar uma framework de terceiros e ser capaz de usar nos seus próprios projetos usando apenas a documentação. Se você não for capaz disso, vai ter grandes problemas em usar esse tutorial pra algo útil.
  • JDK 1.8 ou superior

Bem, como vamos desenvolver java, o JDK é requisito básico. A partir do 1.8 já funciona. Tem, tipo, um zilhão de tutoriais por ai ensinado a instalar e configurar o JDK, então não entrarei em detalhes aqui.
  • Eclipse Oxygen (4.7) ou superior + PDE

Bem, no momento em que escrevo, essa é uma das versões mais novas da ferramenta, então pra quem ja tem uma versão instalada, um upgrade é provavelmente necessário. Além do próprio eclipse, é necessário que você instale o Plugin Development Environment, caso você ainda não tenha. Você pode instalar pelo repositório no link que eu acabei de postar, mas se estiver fazendo o upgrade do eclipse pra versão mais nova, é mais fácil baixar esse pacote que ja vem com tudo incluso.

Se voce é novo com eclipse, você pode descompactar o zip que você baixou em qualquer pasta de sua preferencia e executar o aplicativo eclipse.exe que foi descompactado para abrir a ferramenta. Você vai ser solicitado a escolher uma pasta de workspace, que é onde os plugin e projetos que criarmos vão ficar. Por padrão, ele cria uma pasta chamada eclipse-workspace (ou workspace, na imagem mostrada abaixo) na pasta do seu usuário, o mesmo local onde tem suas pastas Downloads, Documentos, Imagens, etc, quer você esteja no windows ou no linux. Fique atento a esse diretório, pois na hora de testar os seu plugin, diretórios parecidos, mas totalmente independentes, vão começar a aparecer, e você pode se confundir. É bom marcar a opção "use this as the default and do not ask again" pra não correr o risco de mudar o local da pasta acidentalmente toda vez que abrir o eclipse.

Imagem

Depois de escolher o diretório, clique em Launch. Então, após um breve carregamento, na tela que surgir, desmarque a opção "Always show Welcome at start up" para nem perder tempo nas próximas vezes que abrir o eclipse. Depois, procure o botão ou link "Workbench" e clique nele para começar a fazer seu plugin.

Imagem

Chega de enrolação, vamos começar

Primeira etapa: criando um projeto de plugin


Para trabalhar com plugins, vamos mudar para a perspective de desenvolvimento de plugin. Na barra de menus, vá em window > perspective > open perspective > Other ...
Na janela que aparecer, selecione Plug-in Development e clique em Open.

Imagem

A esquerda você ve seu Project explorer, que vai mostrar seus projetos na sua pasta workspace que eu mencionei.

Imagem

Vamos então criar um projeto de plugin. Novamente na barra de menus, vá agora em File > New > Plugin Project. Deve aparecer uma janela assim:

Imagem

Em project name, coloque um nome pro seu projeto, que no nosso caso vai ser EditorVisao. O resto, deixe como está, e clique em Next. Depois, deixe tudo como está novamente e clique em Finish.

Imagem

Perceba que ali no project Explorer foi criada uma pasta para o nosso projeto EditorVisao. É lá que vai ficar todos os nossos arquivos, incluindo o plugin. Acostume-se a identificar onde seus arquivos ficam guardados, pois isso nem sempre fica muito claro ao usar o eclipse (depois eu explico porque). De toda forma, com o projeto criado, agora é hora de começar a configurá-lo.

Segunda etapa: configurando o plugin

Aqui entra o conhecimento em xml para configurar o nosso plugin. Podemos fazer tudo usando só a interface do eclipse, mas apesar de ser mais organizado, algumas coisas ficam escondidas. Enfim, com o eclipse aberto, na perspectiva de Plugin Development (que aprendemos a selecionar na primeira etapa), no project explorer você vai ver o nosso projeto, EditorVisao. Clique na setinha ou sinal de "+" do lado dele para mostrar os arquivos e pacotes relacionados a esse plugin(casos eles ja não estejam sendo exibidos).

Imagem

De todos arquivo que apareceram, no momento nos interessa apenas um, o MANIFEST.MF. Dê um duplo clique nele(se não conseguir achar, tente o atalho ctrl+shift+Alt+M).

Imagem

A tela acima aparece. Muitas informações são apresentadas, mas para o que queremos, pouca coisa precisa ser modificada. Nesta tela, apenas marque a caixa "This plug-in is a singleton" e salve usando o atalho Ctrl+S.

Imagem

Você pode perceber que na tela, aparecem várias abas ali embaixo, como Dependencies, Runtime, Build, etc. Agora, vamos incluir as dependências. Basicamente, vamos incluir os plug-ins necessários para o funcionamento do Generic Editor. Para isso, clique na aba Dependencies e na seção "Required Plug-ins", clique no botão "Add..."

Imagem

Na tela que aparecer, abaixo de "Select a Plugin", digite "org.eclipse.core.runtime" e clique em "OK".

Imagem

Pronto, você adicionou o plugin "org.eclipse.core.runtime" como dependência do seu projeto. Repita o mesmo processo com os seguintes plugins:
  • org.eclipse.ui
  • org.eclipse.jface.text
  • org.eclipse.ui.editors
  • org.eclipse.ui.genericeditor

Pode parecer estranho estarmos usando outros plugins como dependência, mas os plugins dependem de outros plugins para funcionar, é assim que o eclipse foi desenvolvido. O bom disso que é você pode desenvolver um plugin pra servir de base para outro plugin maior ou mais específico. Salve(Ctrl+S).

Agora nós vamos usar o configura o Generic Editor de fato. A primeira coisa que temos que fazer é avisar o eclipse que arquivos devem ser abertos com o nosso Generic Editor. Para isso, vá na aba "Extensions", clique em "Add..."

Imagem

Na tela que aparecer, ao lado de "Extension Point filter:", digite "org.eclipse.core.contenttype.contentTypes", e selecione embaixo o item que tem o mesmo nome. Depois clique em "Finish"

Imagem

Notou que "org.eclipse.core.contenttype.contentTypes" apareceu na lista All Extensions? Pois então, esse é um Extension point. Agora que ele está na lista, nós podemos criar extensões a partir dele. Para cada função no nosso plugin, vamos precisar de diferentes Extension points.
Notou também que surgiu um novo arquivo? Não? Mais eles está lá, é o plugin.xml. Antes de continuarmos, vamos na aba "plugin.xml". Clique nela, conforme indicado na imagem a seguir:

Imagem

Esse é o arquivo que estamos editando. É ele que define os recursos e organiza todo o plugin que estamos fazendo. As outras abas basicamente modificam esse arquivo com uma interface mais intuitiva, mas quase tudo fica escrito nesse arquivo. É importante que você se acostume a fazer modificações nas outras abas e depois de uma olhada aqui pra ver como ficou. Como nós mesmos vamos ter que dar vários nomes aos recursos que vamos criar e utilizar, é bem comum errarmos uma letra aqui e ali e perdemos até mesmo horas de trabalho nos perguntando porque não está funcionando. Para achar esse tipo de erro, é bem mais fácil por aqui, pois como eu disse, a interface do eclipse nas outras abas esconde alguns detalhes a fim de organização.

Mas enfim, voltando a aba no "org.eclipse.core.contenttype.contentTypes" que apareceu na lista "All Extensions" , clique com o botão direito para abrir um menu e depois vá em "New" > "content-type".

Imagem

Surgem várias opções do lado direito da tela referentes a forma como os arquivos serão identificados. No nosso caso, queremos que todos os arquivos ".tpl" abertos no eclipse sejam abertos no eclipse, usem o nosso Generic Editor, então no campo "file-extensions", colocamos "tpl" apenas. Temos que escolher agora o atributo name e o atributo id. O atributo name é mais um nome para nós nos organizarmos, então eu coloquei "Editor Visao Formulario", pois esses arquivos geram formulários, mas você pode colocar algo diferente, se quiser. Entretanto, no campo id, você precisa colocar um nome único, pois ele vai ser a chave para o eclipse identificar quais arquivos vão usar o Generic Editor que estamos criando. Esse id pode ser alterado por nós ou pode ser o que o eclipse gerou automaticamente, mas é imprescindível que seja único, e usado somente quando desejarmos referenciar esses arquivos em específico. Preste atenção, porque esse id não é nome de classe, não é nome de pacote, nem nada disso, é um id para identificar esse arquivos, apenas isso. No nosso caso, vamos adotar o id "br.ericware.editorVisao.formulario". Salve novamente e o resultado ficará assim:

Imagem

Agora, vamos associar esse arquivos ao Generic Editor. Nessa mesma tela, vá em "Add...", digite "org.eclipse.ui.editors" e clique em "Finish", parecido com o que fizemos ainda a pouco. Quando "org.eclipse.ui.editors" aparecer na lista abaixo de "All Extensions", clique nele com o botão direito e no menu que aparecer, vá em "New" > "editorContentTypeBinding":

Imagem

No fomulario que surgir no lado direito da tela, no campo "contentTypeId*:", coloque o id que criamos para os arquivos ".tpl", ou seja, "br.ericware.editorVisao.formulario". Já no campo "editorId*:" coloque "org.eclipse.ui.genericeditor.GenericEditor", que basicamente é o Generic Editor que vai servir de base para nós. Salve e o resultado deve ficar assim:

Imagem

Basicamente, o que nós fizemos foi dizer para o eclipse, que os arquivos ".json" podem ser abertos pelo Generic Editor. Até esse ponto, nossa maior preocupação é acertar os nomes, pois se o id que você deu ao criar o filtro de arquivos(content-type) ".json" não for o mesmo que você usou no campo "contentTypeId*:" nesta tela, o plugin simplesmente não funciona. Mas o pior, é que ele não dá nenhum tipo de erro!!! Você pode testar, o eclipse vai abrir, seu plugin vai ser carregado, mas os arquivos não vão abrir com o Generic Editor, simples, e você não vai entender porque!! Por isso, se tiver algum problema daqui em diante, cheque se os ids correspondem ao esperado.

E antes de testarmos o que fizemos até agora, clique na aba "plugin.xml". Percebeu alguma coisa diferente? Se você fez tudo como instruído, então agora esse arquivo tem os dados das duas extension points que usamos. Como eu disse antes, tudo que nós fazemos nas outras abas se reflete nesse arquivo aqui, tudo não passa de uma interface mais automatizada. O inverso também vale, as alterações aqui também vão aparecer nas outras abas, mas, por hora, é melhor nós não mexermos aqui. Use essa aba mais pra conferir se os ids batem uns com os outros.

Muito bem, não temos praticamente nada, mas já deve ser o suficiente para um pequeno teste. Vamos lá.

Terceira Etapa: testando o plugin

Como estamos desenvolvendo um plugin para o eclipse, a melhor forma de testar é no próprio eclipse. Entretanto, se misturarmos o plugin que estamos testando com eclipse que ja temos, o resultado é no minimo uma confusão total na hora de usar qualquer um dos dois. Por isso, para testar plugin, vamos precisar abrir um eclipse especial, com uma pasta especial que guarda arquivos especiais só pra esses testes. Em resumo, vamos criar um novo workspace(Lembra que eu mencionei isso lá no começo?).

Faça o seguinte: vá no menu "Run" > "Run Configurations..." e essa tela deve aparecer:

Imagem

Observe a lista do lado esquerdo. Percebem que no item "Eclipse Application" tem um subitem também chamado "Eclipse Application"? Sim? Então pode pular este parágrafo. Não? Azar(lol). Mas sério agora, se não tiver, é porque ainda vamos criar um. Caso já tenha e ele seja diferente da figura, talvez seja melhor criar um outro mesmo. Para criar um novo eclipse para testes, clique com o botão direito no primeiro "Eclipse Application" e depois clique em "New". Pronto, simples assim.

O que eu quero destacar aqui , tanto caso já exista um ou caso voce tenha criado um novo é o campo "Location". Repare que ele parece que tem um caminho. Esse é o caminho do workspace de testes. Quando você abrir o eclipse de testes, todos os arquivos, projetos e configurações de teste vão ficar armazenados lá. Se você não mudar nada, o caminho fica numa pasta "ao lado" do o workspace que você está usando agora, aquele caminho de quando nós abrimos o eclipse pela primeira vez. Lembre-se disso quando estiver procurando seus arquivos de testes e etc. Outro ponto importante a notar é o nome (no campo "Name:"), que aparece lá em cima. Se for "Eclipse Application", não se preocupe, mas se não for, decore esse nome para mais tarde.

Imagem

Agora, para testar o plugin, clique em "Run". Vai demorar um pouco, mas um outro eclipse vai abrir. Se necessário, confirme o novo workspace e repita o processo de "Go to Workbench". Essa versão é muito semelhante a que usamos, mas perceba que o projeto que criamos não aparece mais na lista de projetos na lateral, no Project Explorer. Isso, porque como eu disse, estamos em um outro workspace, um que é só pra testes.

Imagem

Pois bem, nesse eclipse de testes que se abriu, vá no menu "File" > "New" > "Project...". Vai aparece a seguinte tela:

Imagem

Embaixo de "General", selecione o item "Project" e depois clique em Next. Na tela que se segue, digite um nome qualquer (no meu caso chamei de "Teste") e clique em "Finish"

Imagem

No "Project Explorer" vai aparece o projeto Teste. Clique com o botão direito em cima dele e vá em "New" > "File". Na janela que aparece, em File Name, digite "form.tpl" e clique em "Finish"

Imagem

Quando voce fizer isso, o arquivo deveria abrir na janela principal já usando o nosso Generic Editor. A melhor forma de confirma isso é olhando o ícone que aparece na aba do nosso novo arquivo:

Imagem

Percebem as duas chaves roxas? Pois então, isso significa que o nosso arquivo foi corretamente identificado e o generic editor correspondente foi utilizado. Se o nosso arquivo não terminasse com ".tpl", é bem provável que não abriria com o Generic Editor, mas com um outro editor ou com algum outro programa do windows. Se isso aconteceu com você, você ainda pode tentar clicar com o botão direito em cima do nome do arquivo no project explorer, ir em "Open with" > "Generic Text Editor" e deve abrir com o editor que estamos criando. Se a opção "Generic Text Editor" não aparece, então o mais provável é você ter feito alguma coisa errada. Verifique o nome das dependencias e do id do content-type que criamos, pois como eu destaquei, se aquilo tiver errado, não vai funcionar.

Se deu tudo certo, feche essa janela do eclipse de teste. Se ele perguntar se você quer mesmo sair confirme. No futuro, você pode abrir essa janela novamente apertando o botão verde de play, conforme destacado abaixo:

Imagem

Se ele não funcionar, clique na setinha preta do lado dele e vá em "Eclipse Application" (ou o nome que eu pedi pra você gravar depois de criar o workspace de teste, lembra-se?). É assim que faremos nossos teste de agora em diante, então quando eu pedi pra voce testar, basta apertar esse botão e ver as diferenças. Por enquanto, pode fechar esse "Eclipse de testes".

Bem, mesmo que já esteja funcionando, nosso editor ainda não faz nada! É um bloco de notas cheio de firula, no momento. Então, vamos começar a fazer algo mais sério (e também divertido!)

Quarta Etapa: Adicionando Sintaxe Highlight

Para o Generic Editor colorir partes do texto conforme o conteúdo, ele precisa utilzar objetos da classe PresentationReconciler. Para fazer Sintaxe Highlight do jeito que a gente quer, precisamos então criar uma classe que herde a classe PresentationReconciler. Ou seja, hora de programar em java! No project explorer, clique com o botão direito no projeto "EditorVisao" e no menu que aparecer, vá em "New" -> "Class":

Imagem

Na tela que surgir, vamos dar o nome da nossa classe de "PHPTemplatePresentationReconciler". Eu também coloquei um caminho para o package de "br.ericware.marajoaranet.editor", o que vai ser importante mais tarde para achar nossa classe. Finalmente, em Superclass você deve usar a classe "org.eclipse.jface.text.presentation.PresentationReconciler". O resultado final ficará assim:

Imagem

Clique no botão "Finish". A classe será criada e nos dará um bom esqueleto para fazermos nossa alterações. Mas precisamos de bem mais, na verdade. Portanto, substitua todo o código da classe por este:
[code=Javascript]package br.ericware.marajoaranet.editor;

import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.RuleBasedScanner;

public class PHPTemplatePresentationReconciler extends PresentationReconciler {

public PHPTemplatePresentationReconciler() {
RuleBasedScanner scanner = new RuleBasedScanner();
IRule[] rules = new IRule[3];

scanner.setRules(rules);
DefaultDamagerRepairer ddr = new DefaultDamagerRepairer(scanner);
this.setDamager(ddr, IDocument.DEFAULT_CONTENT_TYPE);
this.setRepairer(ddr, IDocument.DEFAULT_CONTENT_TYPE);
}
}
[/code]
De todo esse código, só o que realmente nos interessa é o conteúdo da array "rules". Esse tamanho 3 eu mesmo que defini, podia ser qualquer quantidade, dependendo do quão específico você quer ser. Basicamente, cada objeto que implementa a interface IRule pode coincidir com um texto e formata-lo como quisermos, definindo até mesmo a cor, que é o que queremos. Portanto, pra começo de conversa, precisamos definir que partes do texto queremos colorir, e de que cor. Para termos de ilustração, vamos com o seguinte:
  • Comentários: Cinza
  • Variáveis: Azul
  • Palavras Modelo: Vermelho

Com isso definido, precisamos criar objetos que armazenem essa informação de formatação. Para tanto, na nossa Classe PHPTemplatePresentationReconciler, vamos acrescentar algumas propriedades do tipo TextAttribute, uma para cada cor que vamos usar:
[code=JavaScript]
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.*;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;

public class PHPTemplatePresentationReconciler extends PresentationReconciler {

private final TextAttribute commentAttribute = new TextAttribute(new Color(Display.getCurrent(), new RGB(128,128,128)));
private final TextAttribute varAttribute = new TextAttribute(new Color(Display.getCurrent(), new RGB(0,0, 255)));
private final TextAttribute modelAttribute = new TextAttribute(new Color(Display.getCurrent(), new RGB(128,0,0)));
[/code]
No construtor dos objetos TextAttribute passamos a cor que desejamos(Color) em formato RGB. Além disso, acrescentamos alguns imports necessários para as novas classes que estamos usando e ainda vamos usar.

Feito isso, precisamos agora criar objeto que implementem a interface IRule para detectar as partes do texto que queremos colorir. Existem implementações prontas da interface IRule que podemos utilizar para detecar algumas parte do texto menos complexas. Então usaremos duas dessas implementações prontas para o nosso propósito. Usaremos um objeto da classe EndOfLineRule para detectar comentários que só duram uma linha e que começam com duas barras invertidas(//). Usaremos um objeto da classe SingleLineRule para detectarmos as palavras modelo que existem no arquivo. No caso, as palavras modelo são palavras chave que eu mesmo determinei no framework e que podem ser identificadas por estarem entre uma abertura de comentário(/*) e um fechamento (*/). Exemplos:
[php]/*dtab*/
/*ent*/
/*tab*/[/php]
Inserindo eses dois objetos em nosso código, o construtor de nossa classe PHPTemplatePresentationReconciler ficará assim:
[code=JavaScript] public PHPTemplatePresentationReconciler() {
RuleBasedScanner scanner = new RuleBasedScanner();
IRule[] rules = new IRule[2];
rules[0] = new EndOfLineRule("//",new Token(commentAttribute));
rules[1] = new SingleLineRule("/*", "*/", new Token(modelAttribute));

scanner.setRules(rules);
DefaultDamagerRepairer ddr = new DefaultDamagerRepairer(scanner);
this.setDamager(ddr, IDocument.DEFAULT_CONTENT_TYPE);
this.setRepairer(ddr, IDocument.DEFAULT_CONTENT_TYPE);
}[/code]

Observe que eu alterei o tamanho do array "rules" para o número de elementos que adicionamos lá. Observe também que os construtores recebem como argumento tanto os delimitadores de comentários e/ou de palavras modelo, como também um objeto do tipo Token que contem a formatação do texto correspondente, ou seja, a cor. Antes de prosseguirmos implementando o Sintax Highlight, vamos testar o que fizemos até agora. Abra novamente o arquivo plugin.xml do projeto TesteVisao e vamos na aba "Extensions" novamente. Clique no botão "Add..." e procure pelo Extension Point "PresentationReconciler", e selecione o único resultado encontrado:

Imagem

Clique no botão Finish. Feito isso, uma nova extensão será listada no lado esquerdo da tela, ao passo que no lado direito surgirão os campos a serem preenchidos. É aqui que faremos a ligação entre a classe que acabamos de criar e o Generic Editor. O campo "class" deve conter o nome da classe que acabamos de criar, incluindo o nome do pacote, ou seja: "br.ericware.marajoaranet.editor.PHPTemplatePresentationReconciler". Já no campo contentType, precisamos usar o id do content type que criamos na terceira etapa, ou seja: "br.ericware.editorVisao.formulario". Você não precisa copiar e colar esse valores, pode tentar encotrá-los clicando no botão "Browse..." que fica do lado de cada campo e digitar o início do nome para filtrar dentre as inúmeras opções que parecem. No casod a classe é um pouco mais complicado porque você precisa digitar todo o caminho de package até a classe antes de ele sugerir possíveis candidatos, mas mesmo assim isso facilita para casos que você escreveu uma letra errada ou um nome muito longo. O resultado final fica assim:

Imagem

Aperte Ctrl+S para Salvar e Ctrl+F11 para rodar. No novo eclipse de teste que se abrir, copie e cole o seguinte código no arquivo form.tpl:
[php]<?php
/**
* Arquivo que armazena a classe que representa a tabela /*dtab*/
*
* @autor Elisson
* @version 0.5
* @package Modelo
* @subpackage Entidades
*
* Copyright 2012 Elisson Andrade
*
* Este arquivo é parte do programa MarajoaraNet
*
* MarajoaraNet é um software livre; você pode redistribui-lo e/ou
*
* modifica-lo dentro dos termos da Licença Pública Geral GNU como
*
* publicada pela Fundação do Software Livre (FSF); na versão 2 da
*
* Licença, ou (na sua opnião) qualquer versão.
*
*
*
* Este programa é distribuido na esperança que possa ser util,
*
* mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÃO a qualquer
*
* MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a
*
* Licença Pública Geral GNU para maiores detalhes.
*
*
*
* Você deve ter recebido uma cópia da Licença Pública Geral GNU
*
* junto com este programa, se não, escreva para a Fundação do Software
*
* Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
*Classe que representa a tabela /*dtab*/
*
*@package Modelo
*@subpackage Entidades
*/
class /*ent*/ implements Entidade {
private $colunas = array();
private $nome = "/*ent*/";
private $nomeSQL = "/*tab*/";
private static $instancia;

/**
*Inicialização das colunas da entidade
*/
private function __construct() {
/*colunas*/
}

/**
*Fornece a instancia única desta classe
*
* @return /*dtab*/
*/
public static function getEntidade($renovar=false){
if(empty(/*ent*/::$instancia)||$renovar)
/*ent*/::$instancia = new /*ent*/();
return /*ent*/::$instancia;

}
/**
*Função que retorna todas as colunas da entidade
*
* @return array
*/
public function getColunas(){
return $this->colunas;
}

/**
*Retorna o nome da entidade
*
* @return string
*/
public function getNome() {
return $this->nome;
}

/**
*Retorna o nome da entidade no banco de dados
*
* @return string
*/
public function getNomeSQL() {
return $this->nomeSQL;
}
}
?>
[/php]
Se você descer com o curso até as linha 60, deve ver algo parecido com isso:

Imagem

Você pode ver que nossa palavras modelo foram coloridas por um brando tom de vermelho, mas algumas outras parte que não são palavras modelo também foram. É um erro de código, mas, para um primeiro exemplo, estamos bem. Salve o arquivo form.tpl e feche o Eclipse de teste.

Voltemos ao arquivo da classe PHPTemplatePresentationReconciler e vamos criar o outro objeto que implementa a interface IRule. Dessa vez, será um objeto que precisa identificar o que é variável no código e pintar de azul. Como nosso template é um código php, temos apenas que usar as caracteristicas de uma variável PHP para identificá-las. Em termos gerais, as variáveis PHP sempre seguem as duas regras:
[LIST=1]
Começam com o caracter $
Tem seus nomes compostos por letras, números ou o caractere underscore (_)
[/LIST]
Para seguir tais regras, usaremos um objeto do tipo WordRule. O construtor da classe WordRule, apesar de receber um objeto do tipo Token, não usa um delimitador como nos outros casos, mas sim um objeto que implementa a interface IWordDetector. Então, precisamos implementar a interface IWordDetector para identificar as variáveis, o que é relativamente simples. Sendo assim, nem criaremos uma classe pra isso, mas implementaremos diretamente no código a interface, utilizando o recurso de Anonymus Interface do java. Ou seja, acrescentaremos o seguinte código:
[code=JavaScript] rules[2] = new WordRule(new IWordDetector() {

@Override
public boolean isWordStart(char c) {
return c == '$';
}

@Override
public boolean isWordPart(char c) {
return Character.isAlphabetic(c) || c == '_';
}
},new Token(varAttribute));[/code]
Observem que a interface possui dois métodos que se adequam perfeitamente as regras de variaveis do php. O método isWordStart é usado para verificar quando o $ aparece no código. Já o método isWordPart é usado para identificar onde a variável acaba, ou seja, onde para de aparecer número, letras ou underscore depois de um $ no código PHP.

Tivemos outro problema mais cedo porque alguns comentários estavam sendo confundidos com palavras modelo. Além disso, os comentários de múltiplas linhas não ficaram cinza, como planejamos. Para resolver isso, precisaremos criar nossa própria classe que implementa a interface IRule. Então, clique com o botão direito no projeto EditorVisao->"New" ->"Class". No nome da classe, digite "MultipleLineCommentRule". No campo de Interfaces, clique no botão ao lado denominado "Add...". No campo abaixo do texto Choose Interfaces digite "IRule", e selecione a única entrada que aparecer na lista abaixo, assim:

Imagem

Aperte o botão Ok. Provavelmente, o campo package já vai estar prenchido com o mesmo package usado na classe anterior, ou seja, "br.ericware.marajoaranet.editor". Caso não esteja, digite você mesmo. Ficara assim:

Imagem

Clique em Finish. Uma nova classe vai se abrir com um método evaluate "só o esqueleto". Como este método é o que vai identificar as partes do texto que devem formatadas, antes de tudo precisamos criar um construtor que armazene a formatação, ou seja, armazene um objeto do tipo Token(que implementa a interface IToken retornada pelo método evaluate). Então acrescente o seguinte código na classe MultipleLineCommentRule:
[code=JavaScript] private final Token format;

public MultipleLineCommentRule(Token token) {
this.format= token;
}[/code]
Este código nos permite receber como argumento a formatação que desejamos para o código que é comentário e armazena na propriedade "format" desta classe.
A seguir, vamos montar o código que permite colorir o comentário de cinza sem afetar as palavras modelo. Para tanto, precisamos atender os seguintes requisitos:
  • Palavras modelo tem apenas caracteres alfanuméricos entre os delimitadores, ou o caractere "-".
  • Os delimitadores são "/*" para começo e "/*" para o final.
  • Podem haver palavras modelo dentro de comentários de várias linhas

Sendo assim, vamos agora entende o método evaluate. Basicamente, ele recebe um iterador de caracteres que a principio, aponta para o começo do documento. No método podemos usar o iterador pra ir avançando no documento e dividir as partes do texto que queremos colorir com o nosso token das partes que não vamos colorir. Toda vez que retornarmos deste método, o iterador será avançado em um caractere e chamado novamente, até retornarmos do método com o iterador no fim do documento. Sendo assim, nosso algoritmo será:
[LIST=1]
Verifica se o iterador está no início de uma palavra chave
[LIST=1]
Se estiver, pula a palavra chave e retorna como um texto que não é de comentário

Senão, prossegue o resto do algoritmo
[/LIST]
Percorrer o resto do documento até encontrar um delimitador de comentário ou até o documento acabar
Ao encontrar, verificar se é um delimitador de início
[LIST=1]
Se for, verificar se é uma palavra modelo
[LIST=1]
Se for, volta o iterador para o inicio da palavra modelo e parar de iterar

Se não for, marcar como inicio de comentário e parar de iterar
[/LIST]
Senão, verifica se um delimitador de inicio ja foi encontrado
[LIST=1]
Se foi, marca como final de comentário e para de iterar
Senão, continua iterando
[/LIST]
[/LIST]
Verifica se o comentário começou, mas ainda não terminou.
[LIST=1]
Se sim, marca o texto como comentário e retorna

Se não, não marca o texto como comentário e retorna
[/LIST]
[/LIST]

Existem várias formas de fazer isso, mas eu desenvolvi assim:
[code=JavaScript]

private boolean foundStart1 = false;
@Override
public IToken evaluate(ICharacterScanner scanner) {
boolean possiblyEnding = false;
boolean possiblyStarting = false;

boolean foundEnding1 = false;
int sizeModel = 0 ;
if(countModelWord(scanner) > 0) {

return Token.UNDEFINED;
}

int charCount = 1;
int c = scanner.read();

while(c != ICharacterScanner.EOF) {
if('*' == c) {
if(possiblyStarting) {
possiblyStarting = false;
if(!foundStart1) {
if(charCount ==3) {
foundStart1 = true;
scanner.unread();
scanner.unread();
scanner.unread();
charCount-= 3;
}
break;
}
}else {
possiblyEnding = true;
}
}else if('/' == c) {
scanner.unread();
sizeModel = countModelWord(scanner);
if(sizeModel > 0) {
for(;sizeModel>0; sizeModel--) {
scanner.unread();
}
charCount--;
break;
}
scanner.read();
if(possiblyEnding) {
foundEnding1 = true;
break;
}else {
possiblyStarting = true;
}
}else if(possiblyEnding || possiblyStarting ) {
possiblyEnding = false;
possiblyStarting = false;
}else {
}
c = scanner.read();
charCount++;
}
if(charCount == 1 && c == ICharacterScanner.EOF) {
return Token.EOF;
}else if( foundStart1) {
if(foundEnding1) {
foundStart1 = false;
}
return format;
}else {

for(;charCount>0; charCount--) {
scanner.unread();
}
return Token.UNDEFINED;
}

}

private int countModelWord(ICharacterScanner scanner) {
int c = scanner.read();
int length = 1;
boolean isModelWord = false;
if(c =='/') {
c = scanner.read();
length++;
if(c =='*') {
c = scanner.read();
length++;
while(Character.isAlphabetic(c)) {
c = scanner.read();
length++;
}
if(c =='*') {
c = scanner.read();
length++;
isModelWord = c =='/';
}
}
}

if(!isModelWord) {
for(;length>0; length--) {
scanner.unread();
}
}
return length;
}[/code]
Apesar de parecer complexo, foi o mais simples que consegui fazer. Se conseguirem pensar em outro, me mostrem aí XD. É importante notar que além de implementar o algoritimo que apresentei, o código acima também tenta manter as seções a um tamanho mínimos, especialmente as UNDEFINED geralmente tem o tamanho de um caractere. Por mais bizarro que possa parecer, isso é importante para garantir que os outros objetos rules que usamos não interfiram uns com os outros, além de tornar o editor menos "pesado". Além disso, também adicionei uma condição apra retornar um tipo especial de formatação para quando estiver no final do arquivo. Isso é importante para evitar crashes.

De toda forma, colocando esté código na classe MultipleLineCommentRule, agora podemos salvar e voltar na classe PHPTemplatePresentationReconciler e incluir um objeto da classe que criamos para aplicar a formatação de comentários. Assim,o trecho de código que preenche ao array rules fica assim:
[code=JavaScript] IRule[] rules = new IRule[4];
rules[0] = new EndOfLineRule("//",new Token(commentAttribute));

WordRule ruleWordModel = new WordRule(new IWordDetector() {

@Override
public boolean isWordStart(char c) {
return c=='/';
}

@Override
public boolean isWordPart(char c) {
return Character.isAlphabetic(c)||(c == '/') ||(c == '*');
}
});
ruleWordModel.addWord("/*dtab*/", new Token(modelAttribute));
ruleWordModel.addWord("/*ent*/", new Token(modelAttribute));
ruleWordModel.addWord("/*colunas*/", new Token(modelAttribute));
ruleWordModel.addWord("/*tab*/", new Token(modelAttribute));

rules[1] = ruleWordModel;

rules[2] = new MultipleLineCommentRule(new Token(commentAttribute));
rules[3] = new WordRule(new IWordDetector() {

@Override
public boolean isWordStart(char c) {
return c == '$';
}

@Override
public boolean isWordPart(char c) {
return Character.isAlphabetic(c) || c == '_';
}
},new Token(varAttribute));[/code]
Agora o objeto no índice 2 do nosso array rules recebe o token que indica a formatação de comentário no construtor e utiliza nossa classe. Além disso, expandimos o array de 2 posições para 4. Uma outra mudança que você deve notar eu troquei o objeto na posição 1 de um objeto SingleLineRule para outro WordRule. Só que nesse, eu utilizei o método addWord para adicionar a lista de palavras modelo que uso dentro do arquivo. Isso é útil para quando se quer formatar apenas um conjunto bem específico de palavras, como nosso caso, possibilitando até mesmo prover uma formatação diferente para cada palavra. Mas ainda assim, é necessário prover sua própria implementação da interface IWordDetector. De toda forma, salve a classe, rode o eclipse de teste e o resultado deverá ser este:

Imagem

Ok, esse é o resultado da nossa formatação. Tente escrever mais código no exemplo e veja o documento se colorindo de acordo. Legal né? Mas enfim, vamos para última etapa para deixar nosso editor digno de concorrer com qualquer outro!

Quinta Etapa: Adicionando Auto-complete

Finalmente, em um editor especializado, sempre vamos querer sugerir algumas palavras de uso recorrente para evitar erros de grafia, economizar tempo, etc. No nosso caso, temos as palavras modelo. Portanto, veremos como fazer para as palavra modelo serem sugeridas quando digitarmos o ínicio de toda palavra modelo:"/". Para tanto, precisaremos criar outra classe java, mas desa vez uma que implemente a interface IContentAssistProcessor.

No project explorer, novamente clique com o botão direito do mouse sobre o projeto EditorVisao, e no menu que aparecer, vá em "New" -> "Class". Digite no campo "Name:" o nome que daremos a nossa classe: WordModelContentAssistProcessor. Agora clique no botão "Add..." ao lado do campo de interfaces. Nele, digite "IContent" que já deve ser o suficiente para listar a Interface que queremos: "IContentAssistProcessor". Selecione-a na lista confome a figura abaixo e clique em Ok.

Imagem

Vamos colocar essa classe no mesmo pacote das outras que fizemos até aqui, mas por padrão, ele já se preenche com o último utilizado, acredito eu. Então o formulario completo deveria ficar assim:

Imagem

Clique no botão "Finish". Ok, classe criada com uma penca de métodos, mas pro que queremos, só precisamos implementar dois: computeCompletionProposals e getCompletionProposalAutoActivationCharacters.O primeiro método é o mais importante, pois ele deve retorna um array que nada mais é que a lista de palavras modelo. Ele também recebe dois parametros que, em linhas gerais, nos fornece a uma cópia do texto no editor e a localização do cursor.
Enfim, pra começo de conversa, vamos criar uma propriedade para guardar a lista com as palavras modelo. Pensando na economia de recursos computacionais mas ao mesmo tempo possibilitando uso de funções extremamente convenientes, vamos criar uma propriedade chamada LISTWORDMODEL do tipo List. Como ela não deve mudar em tempo de execução e vai sempre ser usada, podemos torna-la estática e final. Ou seja:
[code=JavaScript]import java.util.Arrays;
import java.util.List;

public class WordModelContentAssistProcessor implements IContentAssistProcessor {

public static final List LISTWORDMODEL = Arrays.asList("/*dtab*/","/*ent*/","/*colunas*/","/*tab*/");
[/code=JavaScript]
Observe que fiz uma lista a partir das mesma palavras modelo que utilizei na etapa anterior. Observe também os outros imports que incluí no código para que os tipos e métodos que usamos nessa declaração funcione. Enfim, agora para a implementação do método computeCompletionProposals, precisamos converter cada palavra modelo para um objeto que implementa a interface ICompleteProposal toda vez que o objeto for chamado. Aí que entra o motivo de termos usado uma propriedade do tipo List, pois o seu método stream() nos permite filtrar, criar os objetos baseados na lista, etc, em apenas algumas linhas, usando as lambda expressions do java. Ou seja, método inteiro pode ficar assim:

  @Override
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
return LISTWORDMODEL.stream().map(word -> new CompletionProposal(word, offset-1,1, word.length()))
.toArray(ICompletionProposal[]::new);
}

A primeira linha do "return" simplesmente converte cada palavra em um objeto apropriado utilizando o método map, e a segunda linha cria um array do tipo ICompletionProposal. Vale lembrar que é necessário fazer import da classe org.eclipse.jface.text.contentassist.CompletionProposal para que o código funcione corretamente(caso o eclipse não faça automatiamente pra você). Esta classe recebe como parametro a palavra que se deseja sugerir, a posição no documento que ela deve se inserir, o quanto de texto vai ser substituído ao se aceitar a sugestão e o tamanho total do texto que está sendo sugerido. Aqui, colocamos os valores considerando que o usuário sempre irá fazer a substituição da palavra após digitar o primeiro caractere da palavra modelo, para simplificar este exemplo.

No eclipse, se você quer sugestões para auto completar, basta usar o atalho Ctrl+espaço. Mas não queremos isso, queremos que as sugestões apareçam sozinhas quando o usário digitar o primeiro caractere da palavra modelo. Para isso, basta reescrevermos o método getCompletionProposalAutoActivationCharacters. O método deve simplesmente retornar os caracteres que devem ser usados para abrir a lista de sugestões automaticamente ao serem digitados, sem necessidade de utilizar atalhos. O caractere que usamos nas palavras modelo é o "/", então basta retornar um array só com ele:
[code=JavaScript] @Override
public char[] getCompletionProposalAutoActivationCharacters() {
return new char[] {'/'};
}[/code]

Ok, agora vamos salva nossa classe e testar. Vamos abrir novamente nosso plugin.xml do projeto EditorVisao, e na aba Extensions, cliquemos no botão "Add...". No campo "Extension Point filter", digite org.eclipse.ui.genericeditor.contentAssistProcessors, selecione o único resultado que aparecer e clique em no botão Finish:

Imagem

Temos novamente uma extensão onde devemos preencher o id usado para identificar o contentType e a classe. No lado direito da tela, preencha o campo class com a nossa classe br.ericware.marajoaranet.editor.WordModelContentAssistProcessor e o campo contentType com o id br.ericware.editorVisao.formulario. O resultado deverá ficar assim:

Imagem

Salve e rode o eclipse de teste. Vá em algum ponto do editor de texto e tente digitar "/" para a ver a lista se abrir. Escolha um da lista, pressione enter e tah-dah! A substituição é feita corretamente!
E olha só o que temos aqui:

Imagem

Se não é o editor que mostramos no início! Finalmente terminamos... só que não!

Sexta Etapa: Instalando o Editor


Lembrem-se, estamos fazendo um plugin. Só usamos eles em workspace de teste até agora, e se quisermos usá-lo no nosso eclipse, precisamos instalá-lo! Considerando que todo esse tutorial foi feito pensando em criar um plugin para uso particular, vou mostrar aqui apenas como instalar o plugin no próprio eclipse que você está usando. Ok, abra novamente o arquivo plugin.xml e vá na aba Overview. Confira o campo inferior direito:

Imagem

Esta seção conta como exportar seu plugin para distribuição. Apesar dos quatro passos listados, nosso caso só precisa seguir três. Clique no link "Organize Manifest Wizard" e na janela que aparecer, clique no botão "Finish". Sim, só isso mesmo, sem mexer em nada. Agora clique no link "Externalize Strings Wizard" e na janela que aparecer, clique no botão "Finish". Finalmente, clique no link "Export Wizard" e na tela que aparecer, clique na opção "Install into host. Repository:". O campo vai ser automaticamente preenchido com o local de instalação do eclipse local. Ficará assim:

Imagem

Aperte o botão Finish e o eclipse procederá a instalar. Quando terminar, ele pedirá para reiniciar. Confirme e pronto. tente abrir um arquivo tpl e ele será aberto com o generic editor!
Considerações finais


Sim, concluímos esse tutorial. Muito obrigado a todos que leram. Comecei a fazer esse tutorial alguns anos atrás quando quase não se tinha material sobre isso, mas sempre engavetava mais cedo ou mais tarde. Finalmente fiz uma pressão em mim mesmo pra terminar e está aí.

Se querem mais informações sobre as capacidade do generic editor, confiram essa página(em inglês)!
Se querem mais informações sobre distribuir seu plugin do eclipse, confira ese site(em inglês)!

Se acharem algum erro, me avisem. Vlw e obrigado.
© 1999-2024 Hardware.com.br. Todos os direitos reservados.
Imagem do Modal