Панель выбора build-вариантов Build System в Sublime Text 2. (или как изобретался велосипед)

в 21:54, , рубрики: sublime plugin, Программирование, разработка, метки: ,

Идея и реализация: жмем в SublimeText2 сочетание клавиш, получаем панель со списком build-вариантов, автоматически сформированным по файлу выбранной Build System, выбираем нужный вариант и наблюдаем как непосредственно в интерфейсе Sublime Text 2 отображается процесс сборки проекта.

Сразу скажу, что здесь я делюсь своим бесценным опытом совместной разработки плагина ProjectBuild для Sublime Text 2, потому что в результате только опыт и был получен, поскольку, как оказалось, необходимости в реализации данного плагина не было, и весь этот процесс оказался изобретением велосипеда. Печально, но опыт все же бесценен. Повествование будет таким, что о «велосипеде» будет сказано только в конце.

Вступление

Итогом ранее написанной статьи «Автоматизация сборки проекта в Sublime Text 2 c использованием Ant» была возможность вызова в Sublime Text 2 различных build-вариантов выбранной Build System (на примере Ant) c использованием назначенных сочетаний клавиш для них. (сейчас бы я использовал в теме того поста еще и слово «настройка»)

Что мне не нравилось в этом подходе:

  1. У различных Build System могли быть совершенно разные имена их вариантов, а сочетания клавиш уже явно были привязаны к конкретным именам вариантов, которых могло и не быть в другой Build System. Моим допущением было то, что большинству разработчиков не приходится часто переключаться между несколькими Build System, и в основном разработчик работает с одной Build System. И если захочет разработчик, то изменит сочетания клавиш заново. Но согласитесь, это немного напрягает. И об этом нужно помнить. И в продолжение, также
  2. Нужно помнить все назначенные вами сочетания клавиш. Понятно, что есть основные «F7», «Ctrl+B», «Ctrl+Shift+B» — но для каждого вашего варианта будет еще одно сочетание. Это значит, что нужно либо переопределить сочетание клавиш, которое уже имеется, либо отыскать незадействованное, которое было бы удобно и для пальцев и для мозга. Я пробовал найти удобные незанятые, но остановился на переопределении. И это тоже удручает, так как потенциально может лишить вас первоначальных возможностей данных сочетаний при их переопределении. А также ограничить в последующем, когда вам понадобятся новые комбинации клавиш для новых плагинов, а удобные уже задествованы для вызовов разных build-вариантов.

И тут я попробовал отписанный мне в комментариях плагин ProjectBuild для Sublime Text 2 от snegovikufa. Что мне очень-очень понравилось, так это выпадающая панель со списком команд. Выбрав элемент из представленного списка, можно было запустить соответствующую команду. Все настройки этого плагина хранились в отдельном файле, доступ к которому был из меню самого Sublime Text 2, там можно было перечислить список именований и соответствующих им команд. Проще говоря, этот плагин мог быть настроен для запуска «сторонней программы» непосредственно из интерфейса Sublime Text 2. И в частности его можно было использовать для запуска Ant c именами необходимых целей. Я в лоб прикрутил ProjectBuild к своему «AntProjectBuilder.sublime-build». Таким образом, это решило вторую проблему. Назначаем одно сочетание клавиш (да хоть тоже «Ctrl+Shift+B»), оно формирует панель плагина ProjectBuild в интерфейсе Sublime Text 2, где можно выбрать и запустить нужную команду в соответствии с конфигурацией ProjectBuild.

Но плагин работал так, что вызывая «стороннюю программу» хотя и не блокировал интерфейс самого Sublime Text 2, но не оставлял следов об успешности или неуспешности вызова. Например, у меня вызывалась командная строка, в ней отрабатывал Ant, и она пропадала. В консоли самого Sublime Text 2 следов не оставалось. А этого удалось добиться только с блокировкой интерфейса Sublime Text 2. То есть вызвали команду, интерфес Sublime Text 2 подвисал, а отработав этот процесс выплевывал весь свой output в консоль Sublime Text 2. Весь процесс «по ходу» наблюдать не удавалось.

И при таком использовании, когда ProjectBuild должен реализовывать функционал именно build-механизма Sublime Text 2, не решалась первая проблема — явного указания вариантов, причем так же существовало излишнее конфигурирование. Приходилось конфигурировать ProjectBuild для того, чтоб сформировать список вариантов для панели, хотя этот список вариантов уже есть непосредственно в самом "*.sublime-build" файле выбранной Build System. Я связался с snegovikufa, он быстро ввел меня в курс дела, как работать с GitHub, и я приступил к изменению плагина.

Оффтоп о процессе переделки плагина ProjectBuild

GitHub

Mеня очень порадовала эта система «учета кода» (конечно же я утрирую), я никогда ранее не работал c git, но на то, чтоб разобраться с ней ушло менее получаса, тем более, что есть исчерпывающее руководство по git как для Linux, так и для Windows пользователей на самом GitHub. Я написал рабочий код, snegovikufa его качественно переработал на предмет нотации Python, и так далее, поправляя друг друга и списываяь, получили результат, вылизанный рабочий итог сейчас в dev ветке Вещь! Мое первое впечатление, совместная разработка и git (GitHub в частности) — созданы друг для друга.

API Sublime Text 2 и Python

Есть официальная и неофициальная документации. Вначале казалось, что возможности API у Sublime Text 2 не так уж и велики, но их вполне хватило. Чего не хватило, реализовалось на Python. Как сказал ранее, я не знаю Python, но для разработки задуманного функционала плагина больших знаний и не потребовалось. Да и snegovikufa оперативно поправил потенциально непонятные для третьих лиц моменты. Единственное, что я хотел ради интереса найти, но так и не смог, так это список всех возможных ключей и значений Settings, которые существуют у Sublime Text 2 по умолчанию, хотя может плохо искал.

По формату файла "*.sublime-workspace" в документации сказано, что это формат JSON, но было выяснено, что при json-парсинге этого файла может вылетать ошибка. Дело в том, что json-ключ в его данных может быть пустым, но это было проигнорировано плагином ProjectBuild так:

... json.load (f, strict = False)

Еще у Sublime Text 2 в "*.sublime-build" файле может быть задействована переменная с "$project_path", и мне совершенно не понятно, почему разработчики Sublime Text 2 не предусмотрели ее (и других) использование в путях у сочетаний клавиш, где, например, вызываются «сторонние» для Sublime программы. Может это в целях безопасности, чтоб какой-нибудь плагин по сработавшему сочетанию клавиш что-нибудь не упёр что-то «себе» из проекта, но все же.

Итоговый код плагина

Спойлер

import sublime
import sublime_plugin
import json
import sys
import os

class ProjectBuildCommand (sublime_plugin.TextCommand) :
    def run (self, edit = None) :
        # Save file if dirty
        if self.view.is_dirty () :
            self.view.run_command ('save')

        ############################################
        #
        # Определяем файл .sublime-workspace
        # он должен быть размещен в одной из корневых директорий
        # проекта и должен быть единственным
        #
        workspace_file = None
        root_folders = self.view.window ().folders ()
        for dir_ in root_folders :
            for dir_item in os.listdir (dir_) :
                if dir_item.endswith ('.sublime-workspace') :
                    if workspace_file == None :
                        workspace_file = os.path.join (os.path.normpath(dir_), dir_item)
                    else :
                        self.showError (
                            'Must be only one ".sublime-workspace" file in project root folder.n'
                            'Plugin found %s and %s files.' %
                            (workspace_file, os.path.join (dir_, dir_item)))
                        return
        if workspace_file == None :
            self.showError (
                'There are no ".sublime-workspace" file in any root folder of project.')
            return
        self.debug(workspace_file)
        #
        ############################################

        ############################################
        #
        # Получаем относительный путь до файла текущей Build System
        #
        with open (workspace_file) as f :
            try:
                workspace_json_data = json.load (f, strict = False)
            except Exception, e:
                self.showError (
                    'File .sublime-workspace is empty or has incorrect json data')
                return
        if not 'build_system' in workspace_json_data :
            self.showError (
                'There are no "build_system" value in %s file.n'
                'Choose Build System and save project.' % workspace_file)
            return
        build_filename = workspace_json_data['build_system']
        self.debug(build_filename)
        #
        ############################################
        
        ############################################
        #
        # Определяем наличие файла текущей Build System
        # по полному пути до него
        # 
        build_filename_fullpath = os.path.normpath( os.path.join(os.path.dirname(sublime.packages_path()),build_filename))
        if not(os.path.isfile(build_filename_fullpath)):
            self.showError (
                'Plugin could not find Build System file: "%s".' %
                build_filename_fullpath)
            return
        self.debug(build_filename_fullpath)
        #
        ############################################

        ############################################
        #
        # Загружаем JSON данные
        #
        with open (build_filename_fullpath) as f :
            try:
                json_data = json.load (f)
            except Exception, e:
                self.showError (
                    'File %s is empty or has incorrect json data' %
                    build_filename_fullpath)
                return
        #
        ############################################

        ############################################
        #
        # Формируем словарь build-вариантов текущей Build System
        #
        build_variants = []

        if "cmd" in json_data:
            build_variants.append (['Default', " ".join (json_data["cmd"])])

        for variant in json_data.get ("variants", {}) :
            build_variants.append (
                [variant['name'], " ".join (variant['cmd'])])
        #
        ############################################
 
        ############################################
        #
        # Демонстрируем панель вариантов.
        #
        def run (selected) :
            if (selected >= 0) :
                self.execute_variant (build_variants[selected][0])

        names = [name for name, args in build_variants]
        self.view.window ().show_quick_panel (names, run)
        #
        ############################################

    def execute_variant (self, variant_name) :
        self.view.window ().run_command ("build", {"variant": variant_name})

    def showError (self, err) :
        # демонстрируем сообщение об ошибке через Sublime API 
        # иногда не срабатывало, хотя оно и должно было появляться
        sublime.error_message ('ProjectBuild:nn%s' % err)
        
    def debug (self, message) :
        # change True to False or vice versa
        if (False): print message

Выводы

Теперь для ProjectBuild не нужен конфиг. Вам достаточно выбрать необходимую Build System (стандартную систему или вашу собственную, как моя «AntProjectBuilder.sublime-build»). Сохранить проект в Sublime Text 2 таким образом, чтоб в одной из корневых его папок лежал файл манифеста проекта "*.sublime-workspace". Плагином ProjectBuild отслеживается наличие этого файла и его уникальность, так как сведения о текущей Build System берутся именно из него. При изменении Build System в Sublime Text 2 не забудте сохранить проект, чтоб указанный выше файл обновился. Нажимаем назначенное для ProjectBuild сочетание клавиш и видим панель со списком build-вариантов. Список формируется автоматически по существующему в "*.sublime-build" файле описанию вариантов выбранной в Sublime Text 2 системы Build System.

http://pics.simhost.org/images/034211550300a30b3e40.png

Указанные в самом начале статьи проблемы были успешно решены: задействовано одно сочетание клавиш и оно работает с build-механизмом непосредственно самого Sublime Text 2 и в его интерфейсе без жесткой привязки и явного именования вызываемых build-вариантов.

У плагина осталось пространство для доработки, например, сейчас игнорируется платформо-срецифичные опции, которые могут быть указаны в файле Build System, но лично мне пока и так вполне достаточно, да и необходимо мне именно то, что он сейчас и реализует.

На самом деле сложно сказать, чей вклад больше, да и важно ли это, если в итоге плагин ProjectBuild стал тем, чем, наверно, и должен был быть. И что уж греха таить, я испытываю неописуемое чувство гордости, что вот не было такого в интерфейсе и функционале Sublime Text 2, а теперь такое есть, и что это написано отчасти мной. (тут я еще не знал о «велосипеде», о, горе мне, горе)

Прежний вариант ProjectBuild будет скорее всего переделан в плагин OneHotkey по группировке команд в одно сочетание клавиш, не обязательно build команд, а просто команд запуска «сторонних» для Sublime Text 2 программ и т.п. и там уже пригодится файл настроек, который был раньше у ProjectBuild и оказался для него лишним, так как содержал для build-механизма избыточную информацию.

Обещанный велосипед

Все острова давным давно открыты… Как оказалось, уже изначально в Sublime Text 2 уже есть возможность вызова панели выбора вариантов текущей Build System, вам необходимо просто добавить сочетание клавиш:

{ "keys": ["f8"], "command": "show_overlay", "args": {"overlay": "command_palette", "text": "Build: "} }

http://pics.simhost.org/images/806507850301c0d10dc5.png

И эта панель, как не печально было бы об этом говорить, удобнее, так как не привязывает вас с файлу "*.sublime-workspace" и демонстрирует назначенные сочетания клавиш для каждого варианта, если таковые уже имеются. Однако она не поддерживает их сортировку, а плагин можно доработать на предмет упорядочивания вариантов (хотя может опять я чего не ведаю).

http://pics.simhost.org/images/240805050301cda70f56.png

Наверно, все что можно выжать из этого всего, так это вернуться обратно к OneHotkey.

Спасибо за внимание.

Автор: BorisPlus


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js