Пишем универсальную форматировалку исходного кода в 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.
Теперь нужна функция копирования текста из буфера обмена и вставки в буфер нового текста.
Теперь самое главное: как отформатировать текст? Это невероятно просто! Скачав исходники AStyle, мы можем скомпилировать DLL, в которой нам нужна всего одна функция для преобразования текста в необходимый стиль. Функция объявлена так:
pSourceIn — текст, который нужно преобразовать
pOptions — список опций AStyle, здесь устанавливается нужный стиль, табуляция и много много всего
fpError — callback функция для обработки ошибок
fpAlloc — callback функция для выделения памяти
Видно, что AStyle не работает с Unicode, поэтому мы заботливо все перекодируем туда-сюда с помощью функций WideCharToMultiByte и MultiByteToWideChar. Для этого напишем простые обертки:
Вот и все, что нам нужно для успешной реализации задуманного.
Готовая программа с исходным кодом лежит на Google Code. Горячая клавиша и настройки для AStyle задаются в ini файле. Пользоваться очень просто — запускаем программу, она оседает в «трэе», открываем свою IDE или текстовый редактор, выделяем участок кода, жмем горячую клавишу и получаем результат «не отходя от кассы»!
P.S. Данный метод позволяет удобно форматировать не только код на С++ (см. документация к AStyle)
Готовая программа с исходниками http://codeformater.googlecode.com/files/code_formater.zip
AStyle http://astyle.sourceforge.net/
Вариантов может быть несколько, но я рассмотрел только три (ну лень мне было).
Первый вариант
Тут все просто, загоняем в текстовый файл кусок кода, скармливаем его 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/