View Issue Details

IDProjectCategoryView StatusLast Update
0002693SAS.ПланетаРефакторинг / Refactoringpublic22-04-2015 13:31
Reporterzed Assigned Tozed  
PrioritynormalSeverityminorReproducibilityhave not tried
Status resolvedResolutionfixed 
Product Version141212 
Target Version150915Fixed in Version150915 
Summary0002693: Отсортировать юниты в SASPlanet.dpr
DescriptionМне уже давно не нравится тот хаос, что творится в uses в dpr файле (особенно, необходимость лазить туда руками, после добавления нового юнита).

Хочу отсортировать юниты по имени и поддерживать сортировку в автоматическом режиме. Для этого написал скриптик на питоне (в аттаче).

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

Сортировка не затрагивает системные юниты и юнит SASPlanet.modules.
Additional InformationПомимо сортировки, скрипт помогает при перемещениях юнитов по папкам в файловой системе. Использование весьма простое: файловым менеджером перемещаем юниты как хотим (главное не переименовывать юнит), запускаем скрипт и получаем валидные пути к юниту в файлах проекта (dpr и dproj).

Данный скрипт хочу положить в папку Tools.
TagsNo tags attached.
Attached Files
UnitsSortHelper.py (4,605 bytes)   
#!/usr/bin/python
# -*- coding: utf-8 -*-


import os
import re
import logging


def init_log(log_file, level=logging.NOTSET):
    logging.basicConfig(level=level,
                        format='%(asctime)s.%(msecs).03d %(levelname)-8s %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S',
                        filename=log_file,
                        filemode='w')
    console = logging.StreamHandler()
    console.setLevel(level)
    formatter = logging.Formatter('%(levelname)-8s %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)

    
def check_path(path):
    if path:
        path = os.path.abspath(path)
        if path and path[-1:] != os.path.sep:
            path += os.path.sep
    return path


def sort_dpr(proj_file, by_unit_name=True):
    logging.info('Sorting uses in dpr: ' + proj_file)

    def sort_func_by_unit_path(x, y):
        x1 = x.split(' in ')
        y1 = y.split(' in ')
        return cmp(x1[1], y1[1])

    def uses_to_text(uses_list, text):
        for unit in uses_list:
            if unit:
                if text:
                    text += ','
                text += unit
        return text

    with open(proj_file, 'rb') as f:
        data = f.read()

    uses_sys = []
    uses_sorted = []

    uses_text = ''

    match = re.findall(r'uses\r\n(.*?);', data, re.DOTALL | re.IGNORECASE)
    if match:
        uses = match[0].split(',')
        for pas in uses:
            if ' in ' in pas.lower() and not ('sasplanet.modules' in pas.lower()):
                uses_sorted.append(pas)
            else:
                uses_sys.append(pas)

        if uses_sorted:
            if by_unit_name:
                uses_sorted.sort()
            else:
                uses_sorted.sort(cmp=sort_func_by_unit_path)

            uses_text = uses_to_text(uses_sys, uses_text)
            uses_text = uses_to_text(uses_sorted, uses_text)

            if uses_text:
                if match[0] != uses_text:
                    data = data.replace(match[0], uses_text)
                    with open(proj_file, 'wb') as f:
                        f.write(data)
                    logging.info('Sorting update')
                else:
                    logging.info('Sorting OK')


def patch_proj_file(proj_file, proj_info):
    
    logging.info('Patching file: ' + proj_file)
    is_patched = False
    
    # read proj
    with open(proj_file, 'rb') as f:
        data = f.read()

    # apply patch
    unit_expr = r"[\"'](.*?)[\"']"
    for match in re.finditer(unit_expr, data, re.DOTALL | re.IGNORECASE):
        if match.group(1).endswith('.pas'):
            unit_path, unit_name = os.path.split(match.group(1))
            for pas, unit in proj_info:
                if unit_name in pas:
                    if unit != match.group(1):
                        is_patched = True
                        data = data.replace(match.group(1), unit)
                        logging.debug('Replaced: {} --> {}'.format(match.group(1), unit))
                    else:
                        logging.debug('OK: {} == {}'.format(match.group(1), unit))

    if not is_patched:
        logging.info('Proj file ' + proj_file + ' is OK')
    else:
        # save proj    
        with open(proj_file, 'wb') as f:
            f.write(data)
        logging.info('Proj file ' + proj_file + ' is updated')

    
def main(src_path):

    flst = []

    logging.info('Scan file system: ' + src_path)
    
    for root, dirs, files in os.walk(src_path):

        if '.bin' in dirs:
            dirs.remove('.bin')
        if '.dcu' in dirs:
            dirs.remove('.dcu')
        if '.hg' in dirs:
            dirs.remove('.hg')
        if '__history' in dirs:
            dirs.remove('__history')

        for pasfile in files:
            if pasfile.endswith('.pas'):
                unit = root.replace(src_path, '')
                if unit:
                    unit += '\\'
                unit += pasfile
                flst.append((pasfile, unit))
                logging.debug('Found ' + pasfile + ' in ' + unit)

    if flst:
        patch_proj_file(src_path + 'SASPlanet.dpr', flst)
        patch_proj_file(src_path + 'SASPlanet.dproj', flst)
        patch_proj_file(src_path + 'SASPlanet.XE2.dproj', flst)
        sort_dpr(src_path + 'SASPlanet.dpr')


if __name__ == '__main__':
    init_log('UnitsSortHelper.log')
    src = '..\\'
    main(check_path(src))
UnitsSortHelper.py (4,605 bytes)   

Activities

zed

22-04-2015 10:44

manager   ~0015660

У кого какие мысли, возражения?

vasketsov

22-04-2015 10:53

manager   ~0015663

>или по его пути
Так представляется логичным вот почему: поскольку файлы раскиданы по папкам, то изменения с большей вероятностью затронут меньшее количество строк в dpr.
А вообще конечно не принципиально.
Но вообще если решите так формировать dpr и dproj - тоже перейду на этот скрипт, только вот дополнительные файлы куда-то деть надо будет (в отдельный модуль видимо).

>главное не переименовывать юнит
А что страшного будет, если юнит переименуется?
Старый пропадёт, новый появится. Diff-ы видно же. Чай не 100500+ файлов меняется за раз.

vdemidov

22-04-2015 10:53

manager   ~0015664

Я за сортировку по пути. А еще лучше было бы сначала сортировать по префиксу в имени юнита, а потом уже по пути. Причем префиксы отсортировать не по алафавиту, а по предварительно заданному порядку: t_*.pas, c_*.pas, i_*.pas, u_*.pas, fr_*.pas, frm_*.pas

vasketsov

22-04-2015 11:13

manager   ~0015669

Last edited: 22-04-2015 11:13

>i_*.pas, u_*.pas
Хм. А чем плохо, если будет так:
i_BinaryData in 'Src\Basic\i_BinaryData.pas',
u_BinaryData in 'Src\Basic\u_BinaryData.pas',
u_BinaryDataByFileMapping in 'Src\Basic\u_BinaryDataByFileMapping.pas',
u_BinaryDataByMemStream in 'Src\Basic\u_BinaryDataByMemStream.pas',
i_BinaryDataListChangeable in 'Src\Basic\i_BinaryDataListChangeable.pas',
i_BinaryDataListStatic in 'Src\Basic\i_BinaryDataListStatic.pas',
u_BinaryDataListStatic in 'Src\Basic\u_BinaryDataListStatic.pas',

То есть, u_ цепляем к существующему i_, если такового нет, то смотрим какой максимальный есть (CamelCase тут рулит) и цепляем туда, сортируя среди всех его u_?

Не вижу смысла разделять "одноимённые" i_ и u_ пропастью в половину файлов проекта.

zed

22-04-2015 11:21

manager   ~0015670

>А что страшного будет, если юнит переименуется?
Скрипт его проигнорирует и не исправит файлы проекта.

>А еще лучше было бы сначала сортировать по префиксу в имени юнита
По-моему, фигня получится.

vasketsov

22-04-2015 11:24

manager   ~0015671

Last edited: 22-04-2015 11:29

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

>фигня получится
Никогда не понимал, зачем все формы так яростно совать в самый конец проекта.
Так что уж лучше при сортировке вообще игнорировать префикс, чем такая вот фигня с перечнем префиксов.
XE2 вполне корректно обрабатывает ситуацию внешнего изменения фалов проекта (в том же блокноте), так что после запуска скрипта среда вполне в состоянии будет сама поднять его с диска, и лазить руками в файлы проекта не понадобится, соответственно, какой там порядок, важно только для самого порядка и изменений, а не для правки руками. И где будут frm - сугубо пофигу.

vdemidov

22-04-2015 12:55

manager   ~0015672

>Хм. А чем плохо, если будет так
>То есть, u_ цепляем к существующему i_, если такового нет, то смотрим какой максимальный есть (CamelCase тут рулит) и цепляем туда, сортируя среди всех его u_?
Нетривиальная сортировалка выходит.

>>фигня получится
>Никогда не понимал, зачем все формы так яростно совать в самый конец проекта.
Может быть и фигня.

zed

22-04-2015 13:19

manager   ~0015673

Отсортировал по пути и залил в репо. Для запуска скрипта нужен python 2.7.

vasketsov

22-04-2015 13:31

manager   ~0015674

>Нетривиальная сортировалка
Ну в общем-то да, не сильно тривиальная.
Но это проблема скрипта и принципиального наличия консенсуса в том, что это вообще НАДО. А то если например кто-либо будет руками править dpr, то толку будет мало.
Поскольку мне тоже кажется предпочтительнее по папкам раскидывать, алгоритм вижу примерно такой:
а) скрипт берёт файл проекта, ищет начало списка модулей и его конец (весьма тривиально по характерным строчкам в файле проекта, что для dpr что для dproj);
б) проходит по дереву каталогов, строит его и в алфавитном порядке для каждой папки выполняет следующие шаги;
в) получает исходный список файлов;
г) сортирует i_ в чистый результирующий список (из исходного на каждом шаге обработанное удаляется);
д) добавляет в этот список все u_ (по алгоритму выше, это как раз самое нетривиальное);
е) добавляет в этот список все прочие файлы, для которых есть в точности одноимённый i_ или u_, непосредственно ПЕРЕД ним или любым другим с другим префиксом и той же смысловой частью имени файла (чтобы t_ и c_ соответствующие i_ были перед ними, но реализацию в u_ от i_ не отделяли) *);
ж) остальное сортируется между собой тупо по алфавиту (для форм и фреймов оставляется одна соответствующая запись вместо двух имён файлов в dpr) и помешается в результирующий список в начало (для простоты, а также если будут ещё новые префиксы);
з) результирующий список обрабатывается и падает в файл проекта;
и) цикл повторяется, покуда не прошли все каталоги;

*) если на шаге е) в список попадают одноимённые формы и фреймы, значит разработчик сделал это неспроста, и их тоже надо обработать на этом шаге аналогичным образом.

Поскольку краеугольным камнем проекта является именно интерфейсный файл i_, а реализация его в u_, подобный алгоритм представляется весьма оправданным.
А поскольку бардак в dpr существенно разросся с увеличением папок в проекте, делать всё равно что-то придётся. Например, я просто руками с разделяющими комментариями раскидал всё по папкам. Но идея со скриптом мне нравится больше. Особенно если его напишет уважаемый товарищ zed )))

Осталась самая "мелочь" - прийти к консенсусу.

Issue History

Date Modified Username Field Change
22-04-2015 10:43 zed New Issue
22-04-2015 10:43 zed File Added: SASPlanet.by_unit.dpr
22-04-2015 10:44 zed File Added: SASPlanet.by_path.dpr
22-04-2015 10:44 zed File Added: UnitsSortHelper.py
22-04-2015 10:44 zed Note Added: 0015660
22-04-2015 10:53 vasketsov Note Added: 0015663
22-04-2015 10:53 vdemidov Note Added: 0015664
22-04-2015 11:13 vasketsov Note Added: 0015669
22-04-2015 11:13 vasketsov Note Edited: 0015669
22-04-2015 11:21 zed Note Added: 0015670
22-04-2015 11:24 vasketsov Note Added: 0015671
22-04-2015 11:29 vasketsov Note Edited: 0015671
22-04-2015 12:55 vdemidov Note Added: 0015672
22-04-2015 13:19 zed Note Added: 0015673
22-04-2015 13:19 zed Status new => resolved
22-04-2015 13:19 zed Fixed in Version => 150915
22-04-2015 13:19 zed Resolution open => fixed
22-04-2015 13:19 zed Assigned To => zed
22-04-2015 13:20 zed File Deleted: SASPlanet.by_path.dpr
22-04-2015 13:20 zed File Deleted: SASPlanet.by_unit.dpr
22-04-2015 13:31 vasketsov Note Added: 0015674
08-08-2025 13:25 zed Category Рефакторинг => Рефакторинг / Refactoring