Yzh "неБлог" | Статьи | Three Man in a Boat | TODO Tree | Обо мне
Флэш в Беларуси: BAFPUG | Ventur | No Title flash | Flastar | Samoiloff | gorodalive.by
RSS | Содержание | Как добавить комментарий

Используем Eclipse Monkey чтобы сделать Flex удобнее

7 октября 2008

Новый AS3-класс во Flex я создаю следующим образом:

Обратите внимание -- шаблон класса именно мой, отформатированный так, как мне хочется, а не какой-то код по умолчанию, забитый где-то в недрах Flexа и никак не редактируемый. Если хотите, чтобы у вас было также, или даже лучше, читайте дальше...

Вступление

Всякий, кто после Eclipse + FDT пересаживался на Flex испытывал некоторые неудобства. Очень не хватает такой важной штуки, как шаблоны кода. И мириться с этим недоразумением нельзя, нужно искать средство, чтобы помочь беде.

Опыт работы с линуксом, хоть и не особо долгий, научил меня некоторым очень полезным штукам. Первое: правильный инструмент, это такой инструмент, который можно гибко настроить под себя и неограниченно расширять его функционал. Редактор Vim тому яркий пример. Второе: если тебе что-то нужно, сделай это сам.

Исходя из того, что Eclipse -- это правильный инструмент (а Flex, это расширение Eclipse, и значит он не может загубить всю его "правильность"), мы можем расчитывать, что средства для его расширения имеются. Нужно их найти.

Eclipse Monkey

Кто не хочет заморачиваться и интересуется только готовым решением, может пропустить эту часть, сразу читать про установку, копировать мой скрипт и использовать его. Однако есть смысл заморочиться. Ибо во-первых, возможности Eclipse Monkey довольно велики, и стоит хотя бы получить о них общее преставление; во-вторых, информации по этому плагину маловато, а документации вообще нет, и поэтому желательно знать, где можно поискать эту информацию.

Тем, кто намерен использовать Eclipse Monkey, желательно пройти тем же путем, что шел я. А начал я с блога 33 коровы, где, как мне помнилось, была статья о расширениях для Flex. Там я узнал про Eclipse Monkey и пошел по линку на сайт Кости Ковалева, на статью о применении Eclipse Monkey для генерации геттеров-сеттеров. Пример хороший, и уже дает кое-что полезное. Костя предложил заглянуть на сайт aptana.com, где есть кое-какие решения и даже зачатки документации (единственный толковый раздел -- про метаданные). А уже после этого я погуглил, и попал на официальную страницу проекта: wiki.eclipse.org/Eclipse_Monkey_Overview. Там полезной информации оказалось еще меньше, и выяснились две крайне неприятные вещи. Во-первых, проект мертв, по нему уже год никто не работает. Во-вторых, ссылка на официальную документацию не работает, и ее, эту документацию, нигде нельзя добыть (если кто-то имеет эту документацию или знает, где ее можно добыть, сообщите плз).

Еще один нюанс касательно Flex -- Eclipse Monkey не работает с MXML-редактором, только с AS3-редактором.

Итак, в изучении Eclipse Monkey можно опираться на примеры скриптов (несколько их устанавливаются вместе с плагином, некоторые можно найти в инете) и на документацию по Eclipse API.

Ах, да, я же не сказал, что это такое, Eclipse Monkey. Это плагин к Eclipse который позволяет писать к нему расширения на языке JavaScript (поддерживаются также Ruby, Python и Groove). Мощь этого плагина в том, что из скриптового языка мы имеем доступ к Eclipse API и ко многим классам Java. А значит мы можем работать с редакторами, видами, workspaces, проектами и т.д. -- ко всему тому, что доступно в Eclipse разработчикам его плагинов.

Установка

Выполняется непосредственно из Eclipse (точно также и из Flex). Help -> Software Updates -> Find and Install, добавляем Remote Site: http://download.eclipse.org/technology/dash/update/, выбираем и устанавливаем Eclipse Monkey.

В результате получаем папку с примерами скриптов, новый пункт в меню для запуска скриптов, и новый View для той же цели.

Используем

На самом простом уровне можно получить выделенный текст из активного редактора. Обработать его по своему усмотрению и вставить обратно в редактор. Именно так и сделан пример с генерацией геттеров-сеттеров. И некие аналоги шаблонов кода из FDT уже можно реализовать этими средствами.

Это неплохо, и из этого уже можно извлечь немало пользы. Но мне захотелось большего. Захотелось генерировать AS3-классы из собственного шаблона. А для этого нужно:

Все это удалось сделать, хотя стоило оно немалых трудов. Главной сложностью было отсутствие документации, из-за чего приходилось опираться не на Eclipse Monkey API, которое было совершенно неизвестным, а на Eclipse API, которое доступно в справке самого Eclipse, раздел Platform Plug-in Developer Guide -> Programmer`s Guide (этой справки нет во Flex).

Ну и некоторые примеры, нарытые в инете, тоже немало помогли.

Отдельный вопрос то, что некоторые нужные объекты (Flex Navigator и AS3 Editor) являются частью Flex, а не Eclipse, что создает дополнительные сложности. В частности, для получения доступа к этим объектам нужно знать их ID. ID Flex Navigator я нашел, копаясь в xml-файлах в каталогах Flex, ID AS3 Editor случайно нашел в инете.

Но труды эти не пропали даром, ибо появилось понимание, как все устроено в Eclipse и как с этим работать.

Аналогичную задачу я делал в Vim под Linux, средствами VimScript и консольными скриптами. Там это было намного проще.

Monkey-скрипт для генерации AS3-классов

/*
 * Menu: Create AS3 Class
 * Key: M3+1
 * Kudos: Zhloba Yuri  http://yzh44yzh.com 
 * License: Public Domain
 * DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript
 */

var flexNavigatorView = window.getActivePage().findView('com.adobe.flexbuilder.navigator');
var workspace = Packages.org.eclipse.core.resources.ResourcesPlugin.getWorkspace(); 
var project = workspace.getRoot().getProject("yourProjectName"); //TODO - must be some way to get current project
var srcDir = 'src';
var classTemplate = '/your/absolute/path/to/template/AS3ClassTemplate.as';

function main()
{
	// get selected package from Flex Navigator
	var currentPackage = GetCurrentPackage();

	// ask user about full class name
	var fullClassName = AskFullClassName(currentPackage);
	if(fullClassName == '') return; // no class name given -- abort script
	
	// check is file exists already
	var newFilePath = srcDir + '/' + fullClassName.replace(/\./g, '/') + '.as';
	var newFile = project.getFile(newFilePath);
	if(newFile.exists()) 
	{
		// we don`t want to rewrite existing file
		alert('File is already exists:\n' + newFile.getFullPath());
		return;
	}

	CreateDirs(newFilePath);
	
	// get class template and write it to file
	var inputStream = new java.io.FileInputStream(classTemplate);
    newFile.create(inputStream, false, null);

    // open new file in editor
	window.getActivePage().openEditor(
		new Packages.org.eclipse.ui.part.FileEditorInput(newFile),
		"com.adobe.flexbuilder.editors.actionscript.ActionScriptEditor");
		
	// parse template in editor
	var editor = editors.activeEditor;
	var source = editor.source;
	var source = ParseTemplate(fullClassName, editor.source);
	editor.applyEdit(0, editor.source.length, source);
	
	ExpandTree(newFilePath);
}

function GetCurrentPackage()
{
	var path = '';
	var pathInSrcRoot = false;
	var curItem = flexNavigatorView.getViewer().getControl().getSelection()[0];
	if(curItem)
	{
		var packageName = curItem.getText();
		
		// check is it valid package (directory) or just a file
		if(!packageName.match(/\./)) path = packageName + '.';
		else { /* this is a file, skip it */ }
		
		while(parentItem = curItem.getParentItem())
		{
			packageName = parentItem.getText(); 
			if(packageName == srcDir) 
			{
				pathInSrcRoot = true;
				break; 
			}
			
			path = packageName + '.' + path;
			curItem = parentItem;
		}
	}
	
	if(pathInSrcRoot) return path;
	return '';
}

function AskFullClassName(path)
{
	dialog = new Packages.org.eclipse.jface.dialogs.InputDialog(
		window.getShell(), 
		'Create Class',
		'Full class name:',
		path, null);
	result = dialog.open();

	if(result == Packages.org.eclipse.jface.window.Window.OK) 
		return '' + dialog.getValue();

	return '';
}

function CreateDirs(newFilePath)
{
	// check and create directories
	var dirs = newFilePath.split('/');
	var path = dirs[0];
	for(var i = 1; i < dirs.length - 1; i++)
	{
		path += '/' + dirs[i];
		var nextFolder = project.getFolder(path);
		if(!nextFolder.exists()) nextFolder.create(false, false, null);
	}
}

//TODO - does not see new created directories
//and does not expand them
function ExpandTree(newFilePath)
{
	// find node for srcDir in tree view
	var tree = flexNavigatorView.getViewer().getControl();
	var items = tree.getItem(0).getItems();

	var dirs = newFilePath.split('/');
	for(var i = 0; i < dirs.length - 1; i++)
	{
		// expand folders in Flex Navigator
		for(var j = 0; j < items.length; j++)
		{
			var item = items[j];
			if(item.getText() == dirs[i]) 
			{
				item.setExpanded(true);
				items = item.getItems();
				break;
			}
		}
	}
}

function ParseTemplate(fullClassName, content)
{
	names = fullClassName.split('.');
	var className = names.pop();
	var packageName = names.join('.');
	
	content = content.replace(/\{packageName\}/g, packageName);
	content = content.replace(/\{className\}/g, className);
	
	return content;
}

// trace msg to Console View
function trace(msg) { out.println(msg); }

Пока здесь есть пара слабых мест (помеченных TODO). Во-первых, нежелательно явно указывать имя проекта, а нужно как-то получать ссылку на текущий проект. Но я пока не знаю, как это можно сделать. Во-вторых, путь к файлу во Flex Navigator раскрывается только если не создавались новые каталоги. Иначе это раскрытие обрывается на таком каталоге. Видимо, дерево каталогов в навигаторе просто не успевает обновится к моменту обхода.

Сам шаблон класса должен лежать где-нибудь во внешнем файле и нужно указать полный путь к этому файлу (причем использовать прямой слэш "/", а не обратный "\"). Шаблон вы можете написать любой по своему вкусу. У меня такой:

/**
 * @author Yura
 * TODO - don`t forget to write class description here
 */
package {packageName}
{
	
	public class {className}
	{
		// constants
		
		// properties
	
		// constructor
		public function {className}()
		{
		}
		
		// getters & setters
	
		// methods
		public function toString():String { return '{className}'; }		
	}
}

Еще одну приятную мелочь я сделал скриптом. Известно, что в Eclipse переключение видов делается по Ctrl+F7. Но это не всегда удобно, потому что хочется сразу попасть на нужный вид (во Flex Navigator, или в Outline, или еще куда-нибудь) одним нажатием, а не перебором видов по Ctrl+F7. И это тоже можно сделать через Alt-Shift-Q,X. Но и это не очень удобно -- комбинация слишком громоздкая и не для всех View есть варианты. Ну я сделал скриптик, который по Alt-2 передает фокус на Flex Navigator. Просто и очень удобно.

/*
 * Menu: Focus Flex Navigator
 * Key: M3+2
 */

function main()
{
	window.getActivePage().showView("com.adobe.flexbuilder.navigator");
}
RSS | Содержание | Как добавить комментарий
Всякое-разное: haxe.org | osflash.org | AlternativaPlatform | flash-ripper.com | haxe.ru | Айкидо в Беларуси | delicious/yzh44yzh