ClangFormat является одним из лучших инструментов для автоматического форматирования исходных кодов на языках C, C++, Java, JavaScript, Objective-C, C#. Существуют плагины для популярных сред разработки (IDE), но часто нужно быстро отформатировать файл или часть файла с исходным кодом без запуска громоздкой IDE, поэкспериментировать с настройками форматирования и разными версиями ClangFormat с возможностью быстрой отмены изменений. Использовать для этих целей консольную версию ClangFormat неудобно. Возможным решением является вызов ClangFormat из текстового редактора. На официальном сайте описаны способы интеграции с редакторами Vim, Emacs и некоторыми другими, но по интеграции с Notepad++ информации нет. Далее приводится несложная инструкция применительно к Notepad++ (для Windows).

Исходные требования


  • форматирование файла, открытого в Notepad++, при помощи ClangFormat;
  • форматирование выделенного фрагмента в файле;
  • отмена изменений;
  • переключение стиля (набора правил) форматирования;
  • переключение на другую версию ClangFormat;
  • по возможности использование стандартных средств, без пересборки ClangFormat и без написания нового плагина для Notepad++.

Установка и настройка


1. Если Notepad++ еще не установлен, скачиваем и устанавливаем его


https://notepad-plus-plus.org

2. В Notepad++ устанавливаем плагин NppExec


NppExec позволяет вызывать сторонние приложения из Notepad++ и взаимодействовать с компонентами библиотеки Scintilla, на основе которой написан Notepad++.

Plugins –> Plugin Admin... –> NppExec –> Install
Notepad++ перезапустится, после чего появится каталог <Notepad++>/plugins/NppExec/ и пункт меню Plugins –> NppExec

3. Скачиваем исполняемый файл ClangFormat


Для этого на странице https://llvm.org/builds/ находим и скачиваем установочный файл для Windows, например LLVM-X.X.X-rYYYYYY-win64.exe. Весь пакет можно не устанавливать, достаточно архиватором извлечь файл clang-format.exe. Можно использовать 7-zip: удаляем у файла расширение .exe, открываем файл при помощи 7-zip и извлекаем из подкаталога bin/ файл clang-format.exe. Помещаем файл clang-format.exe в каталог <Notepad++>/plugins/NppExec/clang-format/.

4. Добавляем конфигурационные файлы для ClangFormat


Конфигурационные файлы должны иметь название .clang-format или _clang-format. В них содержится набор правил (стилей) форматирования, формат которых описан в руководстве ClangFormat Style Options.

Для примера используем файлы из проектов Linux Kernel и Qt.

Скачанные с GitHub файлы .clang-format помещаем в соответствующие каталоги:
<Notepad++>/plugins/NppExec/clang-format/LINUX_KERNEL/
<Notepad++>/plugins/NppExec/clang-format/QT/


5. Создаем скрипт для NppExec


Открываем окно редактирования и запуска скриптов NppExec Plugins –> NppExec –> Execute... или нажимаем F6. Копируем и вставляем в окно приведенный ниже текст скрипта и сохраняем скрипт под названием clang-format кнопкой Save....

Скрипт NppExec
// Hide console
NPP_CONSOLE 0

//------------------------------------------------------------------------------
// Uncomment a line to select a style
//set style = LINUX_KERNEL
set style = QT
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
set clangformatexe = $(CWD)\plugins\NppExec\clang-format\clang-format.exe
set clangformatcfgdir = $(CWD)\plugins\NppExec\clang-format
set tmpdir = $(SYS.TEMP)
set clangformatcfgfile = $(clangformatcfgdir)\$(style)\.clang-format
set srcfiletmp = $(tmpdir)\~src.tmp
//------------------------------------------------------------------------------

cmd.exe /c if exist "$(clangformatexe)" (exit 0) else (exit 1)
if $(EXITCODE) != 0 then
    NPP_CONSOLE 1
    echo ERROR: "$(clangformatexe)" not found
    exit
endif

cmd.exe /c if exist "$(clangformatcfgfile)" (exit 0) else (exit 1)
if $(EXITCODE) != 0 then
    NPP_CONSOLE 1
    echo ERROR: "$(clangformatcfgfile)" not found
    exit
endif

// Copy $(clangformatcfgfile) to $(tmpdir)\.clang-format if their temestamps are different
cmd.exe /v /c " for %i in ("$(clangformatcfgfile)") do set date1="%~ti" && for %i in ("$(tmpdir)\.clang-format") do set date2="%~ti" && if not "!date1!"=="!date2!" ( echo !date1! != !date2! && echo COPYING $(clangformatcfgfile) to $(tmpdir)\ && copy "$(clangformatcfgfile)" "$(tmpdir)\" /Y )"
if $(EXITCODE) != 0 then
    NPP_CONSOLE 1
    echo ERROR copying "$(clangformatcfgfile)" to "$(tmpdir)"
    exit
endif

// Get selected text length
sci_sendmsg SCI_GETSELTEXT

// If nothing is selected - select the current line
if $(MSG_RESULT) == 1 then
    sci_sendmsg SCI_VCHOMEWRAP
    sci_sendmsg SCI_LINEENDWRAPEXTEND
endif

// Save selected text to $(srcfiletmp)
sel_saveto "$(srcfiletmp)" :a

cmd.exe /c if exist "$(srcfiletmp)" (exit 0) else (exit 1)
if $(EXITCODE) != 0 then
    NPP_CONSOLE 1
    echo ERROR: "$(srcfiletmp)" not found
    exit
endif

// Run ClangFormat
$(clangformatexe) -i -style=file "$(srcfiletmp)"
if $(EXITCODE) != 0 then
    NPP_CONSOLE 1
    echo ERROR running "$(clangformatexe)"
    exit
endif

// Replace selected text with $(srcfiletmp)
sel_loadfrom "$(srcfiletmp)"

// Delete $(srcfiletmp) file
cmd.exe /c del "$(srcfiletmp)"


После запуска скрипт на основе выбранного стиля форматирования, заданного в переменной style, выбирает нужный файл .clang-format, проверяет дату его изменения и при необходимости копирует его во временный каталог. Туда же во временный файл копируется выделенный фрагмент исходного кода, после чего запускается clang-format.exe. Отформатированный фрагмент копируется обратно в окно Notepad++. Затем временный файл удаляется.

В ClangFormat нет возможности указать путь к конфигурационному файлу .clang-format. ClangFormat будет искать его в каталоге форматируемого файла и, если не найдет, перейдет к поиску в родительских каталогах. После окончания работы скрипта файл .clang-format останется во временном каталоге для того, чтобы не выполнять его копирование при каждом запуске форматирования.

На всех этапах работы скрипта выполняется проверка на ошибки, и при их возникновении открывается консольное окно NppExec, в которое выводится сообщение.

6. В Notepad++ добавляем новый пункт меню для запуска скрипта


Открываем Plugins –> NppExec –> Advanced Options..., в выпадающем списке Associated script выбираем название скрипта clang-format и нажимаем на кнопку Add/Modify.

Перезапускаем Notepad++, после чего появится пункт меню Plugins –> NppExec –> clang-format.

image

7. В Notepad++ настраиваем клавиатурную комбинацию для запуска скрипта


Будем использовать комбинацию Ctrl + I по аналогии с QtCreator. Открываем Settings –> Shortcut Mapper.

По умолчанию комбинация Ctrl + I занята, поэтому нужно ее освободить кнопкой Clear во вкладке Main Menu (строка 38 Split Lines в текущей версии Notepad++). После этого во вкладке Plugin commands присваиваем комбинацию Ctrl + I скрипту clang-format.

Готово, можем пользоваться!

Инструкция по использованию


Открываем файл с исходным кодом в Notepad++, выделяем нужный фрагмент или весь текст и нажимаем Ctrl + I. Если ничего не выделять, то будет отформатирована текущая строка.

image

Для отмены изменений используем стандартные средства редактора (Ctrl + Z).
Для изменения правил форматирования редактируем конфигурационные файлы .clang-format в каталоге <Notepad++>/plugins/NppExec/clang-format/
Если нужно использовать другую версию ClangFormat, то изменяем в скрипте путь к исполняемому файлу
set clangformat = "\path\to\clang-format.exe"
Для выбора другого стиля форматирования нажимаем F6 и в тексте скрипта clang-format выбираем нужный стиль путем раскомментирования одной из строк
set style = STYLE_NAME.

Все необходимые файлы находятся в архиве.

Удачного форматирования!

Комментарии (4)


  1. Orange11Sky
    27.06.2019 22:05

    Спасибо — заценил!!!


    1. ds-c Автор
      27.06.2019 22:53

      Очень рад, что пригодилось


  1. trdm
    29.06.2019 11:49

    Юзаю форматирование в Notepad++ с помощью плагина jN.
    Он позволяет использовать JavaScript для автоматизации N++.
    В данном случае использую Tidy для форматирования html


    кусок из trdmTidy.js
    var gFso = new ActiveXObject("Scripting.FileSystemObject");
    var gTempFolder = Editor.nppDir +"\\plugins\\jN\\Intell\\";
    var gTidyConfig = "tidy_config.txt";
    var gTidyConfigFPath = Editor.nppDir +"\\plugins\\jN\\Intell\\"+gTidyConfig;
    var gTidyExe = Editor.nppDir +"\\plugins\\jN\\system\\tidy.exe";
    var gShell = new ActiveXObject("WScript.Shell");
    
    var scriptsMenu;
    if (!jN.scriptsMenu){
        scriptsMenu = Editor.addMenu("Скрипты");
        jN.scriptsMenu = scriptsMenu;
    } else { 
        scriptsMenu = jN.scriptsMenu;
    }
    
    function formatText(psSelText) {
        var rv = psSelText;
        if(!gFso.FileExists(gTidyExe)) {
            return rv;
        }
        //debugger;
        var dt = new Date;
        var vTempFile1 = formatData(dt,"yyyyMMdd_hhmmss_ms")+".html";
        var vTempFile1F = gTempFolder +  vTempFile1;
        var vTempFile2 = formatData(dt,"yyyyMMdd_hhmmss_ms2")+".html";
        var vTempFile2F = gTempFolder + vTempFile2;
        saveToFile(rv, vTempFile1F);
        gShell.CurrentDirectory = gTempFolder;
        var gFilePath = gFso.GetFile(gTidyExe); 
        var cmdLine = ""+gFilePath.ShortPath+" -i -o "+ vTempFile2; //var cmdLine = ""+gFilePath.ShortPath+" -i -o "+ vTempFile2 + " -raw --wrap 120 " + vTempFile1;
        if(gFso.FileExists(gTidyConfigFPath)) {
            cmdLine = cmdLine + " -config "+gTidyConfig+" ";
        }
        cmdLine = cmdLine + " -raw --wrap 120 " + vTempFile1;   //message(cmdLine);
        gShell.Run(cmdLine,0,true);
    
        rv = loadFromFile(vTempFile2F);
        var vBodyPresentSrc = psSelText.indexOf('<body>');
        var vBodyPresent = rv.indexOf('<body>');
        if(vBodyPresentSrc == -1 && vBodyPresent>0) {    
            // если добавлены теги. Даже с настройками не хочет убирать разметку.
            rv = extractNecessaryText(rv);
        }   
    
        gFso.DeleteFile(vTempFile2F);
        gFso.DeleteFile(vTempFile1F);
        return rv;
    }
    
    // форматирование выделения
    function myFormatText() {
        //debugger;
        var rv = '';
        IntellPlus.init();
        if(! (IntellPlus.curExtension == "html" || IntellPlus.curExtension == "htm")) {
            return;
        }
        var selText = "";   
        var selText = Editor.currentView.selection;
        if(selText.length > 0) {
            var selText2 = formatText(selText);
            if(selText2 != selText) {
                Editor.currentView.selection = selText2;
            }
    
        }   
        return rv;
    }
    
    // структура для добавления хоткея и меню
    var myFormatTextCommand = {
        text: "Форматировать html \tCtrl+Y", 
        ctrl: true,    shift: false,    alt: false,
        key: 0x59, 
        cmd: myFormatText
    };
    
    addHotKey(myFormatTextCommand); 
    scriptsMenu.addItem(myFormatTextCommand);
    


    1. ds-c Автор
      29.06.2019 12:37

      Возможностей расширения функционала Notepad++ очень много. Через JavaScript-плагин? Почему бы и нет.