Как стать автором
Обновить

Пишем универсальную форматировалку исходного кода в MS Windows с помощью AStyle

Случилась у меня такая проблема, пришлось работать с большим количеством С/С++ кода, да еще и написанного в разных IDE. Все бы ничего, но в числе этих IDE оказался и C++Builder, в котором никаких средств украшения кода не предусмотрено. Сам исходный код представлял собой огромную кучу букав со всеми, которые только возможно, стилями форматирования и без форматирования вовсе (это явилось результатом трудов большого количества программистов). Наверное проблемы бы не возникло, если бы имелись конкретные стандарты оформления кода, но в моем случае это был просто зоопарк. Для наведения порядка можно было бы просто натравить OpenSource утилиту AStyle на папку проекта и получить красивый код с моим любимым форматированием, но для меня этот способ не совсем подходит, т.к. с кодом работает много людей и я не могу навязывать им свои вкусы и предпочтения. Зато если я работаю над каким то своим модулем, или переделываю старый, написанный кем то до меня, то я считаю уместным сделать все в удобном для меня стиле. В связи с этим возникает задача: как удобно и быстро отформатировать кусок кода независимо от используемого редактора?

Вариантов может быть несколько, но я рассмотрел только три (ну лень мне было).

Первый вариант


Тут все просто, загоняем в текстовый файл кусок кода, скармливаем его AStyle, копируем результат и вставляем на свое место. Есть недостаток — ну очень неудобно и долго. Двигаем дальше.

Второй вариант


Опять не обойдется без AStyle. Открываем Code::Blocks (его правда еще нужно скачать и установить, если его нет изначально), создаем пустой файл .cpp, копируем туда кусок кода и в один клик форматируем код с помощью плагина AStyle. Тоже есть недостаток — все равно неудобно, но уже побыстрее.

И наконец третий вариант


А способ такой — сделать все самому. Не хотелось ничего больше искать и придумывать, просто появилась идея как все сделать своими руками.

Значит идея такова: каким то образом получить из редактора кусок текста, отформатировать его с помощью всемогущей утилиты AStyle и запихнуть текст взамен старого.

Самый универсальный метод получить нужный кусок текста, на мой взгляд, это с помощью буфера обмена и операции «Копировать». Почти любой текстовый редактор по горячей клавише Ctrl+C копирует в буфер выделенный текст. Замена текста на новый делается тем же путем — Ctrl+V и вместо выделенного текста вставляется текст и буфера обмена.

Вырисовывается общий алгоритм работы: заставить редактор скопировать выделенный текст в буфер обмена, затем взять его из буфера, отформатировать, засунуть назад в буфер обмена взамен старого и заставить редактор сделать операцию вставки из буфера.

Заставлять «любой» редактор делать необходимые действия будем путем эмуляции нажатия горячих клавиш. Сгенерировали нажатие Ctrl+C — текст скопировался, сгенерировали Ctrl+V — текст вставился. Вот и все что нам нужно. Открываем Visual Studio и начинаем кодить.

Рассмотрим только основные моменты реализации, остальное можно увидеть в иходниках программы по ссылке в конце статьи. Нам понадобятся функции, генерирующие Ctrl+C и Ctrl+V. Сделать это можно с помощью API функции keybd_event.

//Ctrl+C
void SendCopyToClipboard()
{
	Sleep(200);  //никуда не торопимся
	keybd_event(VK_CONTROL, 0x1D, 0, 0);
	keybd_event(0x43, 0x2E, 0, 0);
	keybd_event(0x43, 0x2E, KEYEVENTF_KEYUP, 0);
	keybd_event(VK_CONTROL, 0x1D, KEYEVENTF_KEYUP, 0);
	Sleep(1000);  //надо дать чужой программе подумать
}

//Ctrl+V
void SendPasteFromClipboard()
{
	keybd_event(VK_CONTROL, 0x1D, 0, 0);
	keybd_event(0x56, 0x2F, 0, 0);
	keybd_event(0x56, 0x2F, KEYEVENTF_KEYUP, 0);
	keybd_event(VK_CONTROL, 0x1D, KEYEVENTF_KEYUP, 0);
}


Теперь нужна функция копирования текста из буфера обмена и вставки в буфер нового текста.

//получаем текст из буфера в Unicode кодировке
wchar_t* GetClipboardText()
{
	wchar_t* result = 0;

	if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) 
		return result;
	if (!OpenClipboard(0)) 
		return result;

	HGLOBAL glob = GetClipboardData(CF_UNICODETEXT);
	if (glob)
	{
		const wchar_t* str = (const wchar_t*)GlobalLock(glob);
		if (str)
		{
			int size = wcslen(str);
			result = new wchar_t[size+1];
			wcscpy(result, str);
			GlobalUnlock(glob);
		}
		CloseClipboard();
	}

	return result;
}

//записываем новый текст в буфер обмена
void SetClipboardText(const wchar_t* text)
{
	if (!OpenClipboard(0)) return;
	EmptyClipboard();

	int size = wcslen(text)+1;
	HGLOBAL glob = GlobalAlloc(GMEM_MOVEABLE, size * sizeof(wchar_t));
	if (glob)
	{
		wchar_t* cbd = (wchar_t*)GlobalLock(glob);
		if (cbd)
		{
			wcscpy(cbd, text);
			GlobalUnlock(glob);

			SetClipboardData(CF_UNICODETEXT, glob);
		}

		CloseClipboard();
	}
}


Теперь самое главное: как отформатировать текст? Это невероятно просто! Скачав исходники AStyle, мы можем скомпилировать DLL, в которой нам нужна всего одна функция для преобразования текста в необходимый стиль. Функция объявлена так:

char* __stdcall AStyleMain(const char* pSourceIn,
                          const char* pOptions,
                          void(__stdcall *fpError)(int, char*),
                          char*(__stdcall *fpAlloc)(unsigned long));


pSourceIn — текст, который нужно преобразовать
pOptions — список опций AStyle, здесь устанавливается нужный стиль, табуляция и много много всего
fpError — callback функция для обработки ошибок
fpAlloc — callback функция для выделения памяти

Видно, что AStyle не работает с Unicode, поэтому мы заботливо все перекодируем туда-сюда с помощью функций WideCharToMultiByte и MultiByteToWideChar. Для этого напишем простые обертки:

char* UnicodeToAnsi(const wchar_t* str)
{
	int ret = WideCharToMultiByte(CP_ACP, 0, str, -1, 0, 0, 0, 0);
	if (ret)
	{
		char* result = new char[ret+1];
		memset(result, 0, ret+1);

		char def = '?';

		ret = WideCharToMultiByte(CP_ACP, 0, str, -1, result, ret, &def, 0);
		result[ret] = 0;

		return result;
	}

	return 0;
}

wchar_t* AnsiToUnicode(const char* str)
{
	int ret = MultiByteToWideChar(CP_ACP, 0, str, -1, 0, 0);
	if (ret)
	{
		wchar_t* result = new wchar_t[ret+1];
		memset(result, 0, (ret + 1) * sizeof(wchar_t));

		ret = MultiByteToWideChar(CP_ACP, 0, str, -1, result, ret);
		result[ret] = 0;

		return result;
	}

	return 0;
}


Вот и все, что нам нужно для успешной реализации задуманного.
Готовая программа с исходным кодом лежит на Google Code. Горячая клавиша и настройки для AStyle задаются в ini файле. Пользоваться очень просто — запускаем программу, она оседает в «трэе», открываем свою IDE или текстовый редактор, выделяем участок кода, жмем горячую клавишу и получаем результат «не отходя от кассы»!

P.S. Данный метод позволяет удобно форматировать не только код на С++ (см. документация к AStyle)

Ссылки


Готовая программа с исходниками http://codeformater.googlecode.com/files/code_formater.zip
AStyle http://astyle.sourceforge.net/
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.