![image](https://habrastorage.org/getpro/habr/post_images/a1e/a82/00b/a1ea8200bf15b676f93349d31fa4c880.png)
Среда, поддерживающая графическое представление кода
Во второй части речь пойдет о реализации, выполненной, в основном, на Питоне. Будут обсуждены реализованная и планируемая функциональность, а также предлагаемый микро язык разметки.
Общая информация
Реализация технологии выполнена в рамках проекта с открытыми исходными текстами, названного Codimension. Все исходные тексты доступны под лицензией GPL v.3 и размещены в трех репозиториях на github: два модуля расширения Питона cdm-pythonparser и cdm-flowparser плюс собственно среда IDE. Модули расширения, в основном, написаны на C/C++, а IDE на Питоне серии 2. Для графической части использовалась Питон обертка библиотеки QT — PyQT.
Разработка выполнена на Linux и для Linux. В основном использовался дистрибутив Ubuntu.
Среда предназначена для работы с проектами, написанными на Питоне серии 2.
Архитектура
На диаграмме ниже представлена архитектура IDE.
![image](https://habrastorage.org/getpro/habr/post_images/9f8/93e/eb6/9f893eeb63c043af0dfe77c6519f2e4f.png)
Архитектура IDE
Голубым на диаграмме обозначены части, разработанные в проекте. Желтым — сторонние Питон модули, а зеленым — сторонние бинарные модули.
С самого начала проекта было очевидно, что одному разработчику совершенно не под силу разработать все требуемые компоненты с чистого листа в разумные сроки. Поэтому уже существующие Питон пакеты использовались везде, где это было возможно и разумно. Приведенная диаграмма отлично отражает принятое решение.
Только три части разработаны в рамках проекта. IDE написана на Питоне с целью ускорения разработки и упрощения проведения экспериментов. Модули расширения написаны на C/C++ для лучшей отзывчивости IDE. Задача brief parser состоит в том, чтобы сообщить обо всех найденных в Питон файле (или буфере) сущностях, таких как импорты, классы, функции, глобальные переменные, строки документации и т.д. Наличие такой информации позволяет реализовать, например, такую функциональность: структурировано показать содержимое файла и обеспечить навигацию, предоставить анализ определенных, но нигде не использованных функций, классов и глобальных переменных в проекте. Задача flow parser предоставить в удобном виде данные, необходимые для отрисовки диаграммы.
Все остальные компоненты — сторонние. PyQT использовалась для интерфейсной и сетевой части. QScintilla в качестве основного текстового редактора и некоторых других элементов, таких как перенаправленный ввод/вывод отлаживаемых скриптов или показ svn версии файла определенной ревизии. Graphviz использовался для расчета расположения графических элементов на диаграмме зависимостей и т.п. Также использовались и другие готовые Питон пакеты: pyflakes, pylint, filemagic, rope, gprof2dot и т.д.
Конвейер от кода к графике
Реализация перехода от текста к графике построена по принципу конвейера. На каждой стадии выполняется определенная часть работы, а результаты передаются на следующую стадию. Диаграмма ниже показывает все стадии конвейера, слева которого на вход поступает исходный текст, а справа на выходе получается нарисованная диаграмма.
![image](https://habrastorage.org/getpro/habr/post_images/984/fe6/754/984fe675454b70416801b6d37d5f949b.png)
Конвейер от кода к графике
Начинается работа с построения синтаксического дерева по исходному тексту. Затем дерево обходится, и все обнаруженные блоки кода, циклы, функции и т.д. раскладываются в иерархическую структуру данных. После этого, по исходному коду делается еще один проход, в результате которого собирается информация о комментариях. На дальнейших стадиях конвейера удобнее иметь комментарии уже ассоциированными с распознанными конструкциями языка, а не как отдельные данные. Поэтому следующим этапом выполняется слияние комментариев с иерархической структурой, представляющей код. Описанные действия выполняются flow parser модулем расширения, который написан на C/C++ для достижения наилучшей производительности.
Последующие стадии реализованы уже внутри IDE и написаны на Питоне. Это обеспечивает большую легкость экспериментирования с отрисовкой по сравнению с реализацией на C++.
Сначала в структуре данных, названной виртуальным холстом, размещаются графические элементы в соответствии со структурой данных, полученной от модуля расширения. Затем наступает фаза рендеринга, основная задача которой рассчитать размеры всех графических элементов. Наконец, все графические элементы отрисовываются должным образом.
Обсудим все эти стадии в деталях.
Построение синтаксического дерева
Это самая первая стадия на пути от текста к графике. Ее задача — парсинг исходного текста и составление иерархической структуры данных. Это удобно делать с помощью синтаксического дерева, построенного по исходному тексту. Очевидно, что разрабатывать новый парсер Питона специально для проекта не хотелось, а наоборот, было желание воспользоваться чем-то уже готовым. К счастью, прямо в разделяемой библиотеке интерпретатора Питона нашлась подходящая функция. Это C — функция, которая строит дерево в памяти для указанного кода. Для визуализации работы этой функции была написана утилита, которая наглядно показывает построенное дерево. Например, для исходного текста:
#!/bin/env python
# encoding: latin-1
def f():
# What printed?
print 154
Будет построено такое дерево (для краткости приведен только фрагмент):
$ ./tree test.py Type: encoding_decl line: 0 col: 0 str: iso-8859-1 Type: file_input line: 0 col: 0 Type: stmt line: 4 col: 0 Type: compound_stmt line: 4 col: 0 Type: funcdef line: 4 col: 0 Type: NAME line: 4 col: 0 str: def Type: NAME line: 4 col: 4 str: f Type: parameters line: 4 col: 5 Type: LPAR line: 4 col: 5 str: ( Type: RPAR line: 4 col: 6 str: ) Type: COLON line: 4 col: 7 str: : Type: suite line: 4 col: 8 Type: NEWLINE line: 4 col: 8 str: Type: INDENT line: 6 col: -1 str: Type: stmt line: 6 col: 4 Type: simple_stmt line: 6 col: 4 Type: small_stmt line: 6 col: 4 Type: print_stmt line: 6 col: 4 . . .
В выводе каждая строка соответствует узлу дерева, вложенность показана отступами, а для узлов показана вся доступная информация.
В общем, дерево выглядит очень хорошо: есть информация о строке и колонке, есть тип каждого узла, который соответствует формальной грамматике Питона. Но есть и проблемы. Во-первых, в коде были комментарии, но информация о них в дереве нет. Во-вторых, информация о номерах строки и колонки для кодировке не соответствует действительности. В-третьих, само название кодировки изменилось. В коде была latin-1, а синтаксическое дерево рапортует iso-8859-1. В случае многострочных строковых литералов тоже есть проблема: в дереве нет информации о номерах строк. Все эти сюрпризы должны быть учтены в коде, который занимается обходом дерева. Однако все описанные проблемы скорее мелочи, по сравнению со сложностью целого парсера.
В модуле расширения определяются типы, которые будут видны в Питон коде на последующих стадиях. Типы соответствуют всем распознаваемым элемента, например Class, Import, Break и т.д. Помимо специфических для каждого случая членов данных и функций, все они предназначены для описания элемента в терминах фрагментов: где кусочек текста начинается и где заканчивается.
При обходе дерева формируется иерархическая структура, экземпляр класса ControlFlow, в который уложены все распознанные элементы нужным образом.
Сбор комментариев
Из-за того, что информации о комментариях не оказалось в синтаксическом дереве (очевидно, что интерпретатору в них нет необходимости), а они нужны для отображения кода без потерь, пришлось вводить дополнительный проход по исходному коду. За этот проход извлекается информация о каждой строке комментария. Сделать это просто, благодаря простой грамматике Питона и отсутствию как многострочных комментариев, так и препроцессора.
Комментарии собираются в виде списка фрагментов, где каждый фрагмент описывает одну строчку комментария набором атрибутов: номер строки, номер колонки начала и конца, абсолютные позиции в файле начала и конца комментария.
Например для кода:
#!/bin/env python
# encoding: latin-1
def f():
# What printed?
print 154
Будет собрано три фрагмента вида:
Line: 1 Pos: 1 … Line: 2 Pos: 1 … Line: 5 Pos: 5 …
Слияние комментариев с фрагментами кода
При построении диаграммы удобнее иметь дело не с двумя разными структурами данных — исполняемый код и комментарии — а с одной. Это упрощает процесс размещения элементов на виртуальном холсте. Поэтому в модуле расширения выполняется еще один этап: слияние комментариев с распознанными элементами.
Рассмотрим пример:
# leading comment
a = 10 # side comment 1
# side comment 2
![image](https://habrastorage.org/getpro/habr/post_images/b6b/2e6/dd7/b6b2e6dd7e4ed665783b8e5f46964e1c.png)
Слияние комментариев с кодом
В результате прохода по синтаксическому дереву для кода выше будет сформирован, в числе прочих, экземпляр класса CodeBlock. Он, в числе прочих полей, имеет поля body, leadingComment и sideComment, описывающие соответствующие элементы в терминах фрагментов. Поле body будет заполнено информацией из синтаксического дерева, а поля комментариев будут содержать None.
По результатам прохода для сбора комментариев будет сформирован список из трех фрагментов. При слиянии первый фрагмент будет использован для заполнения поля leadingComment в CodeBlock, а второй и третий фрагменты для поля sideComment. Слияние происходит на основе номеров строк, доступных для обоих источников информации.
Таким образом, на выходе этапа слияния имеется полностью готовая единая иерархическая структура данных о содержимом файла или буфера в памяти.
Производительность модуля
Описанные выше стадии конвейера написаны на C/C++ и оформлены в виде Питон модуля. Из общих соображений, работу всех этих стадий хотелось сделать быстрой, чтобы избежать неприятных задержек при перерисовках диаграмм в паузах модификации текста. Для проверки производительности модуль был запущен на имеющейся платформе:
- Intel Core i5-3210M ноутбук
- Ubuntu 14.04 LTS
для обработки всех файлов стандартной инсталляции Питона 2.7.6. При наличии 5707 файлов обработка заняла около 6 секунд. Конечно, файлы имеют разный размер и от него зависит время работы модуля, но результат в среднем около 1 миллисекунды на файл не на самом быстром оборудовании, мне кажется более чем приемлемым. На практике текст, который нужно обработать, чаще всего уже содержится в памяти и время дисковых операций уходит совсем, что также положительно влияет на производительность.
Размещение на виртуальном холсте
Цель этой стадии конвейера — разместить на виртуальном холсте все необходимые элементы с учетом взаимосвязей между ними. Виртуальный холст можно представить себе как полотно с прямоугольными ячейками. Ячейка может быть пустой, может содержать один графический элемент или вложенный виртуальный холст. На данном этапе важно только взаимное размещение элементов, а не точные размеры.
При этом холст не имеет фиксированного размера и может расти вниз и вправо по мере надобности. Такой подход соответствует подготовленной структуре данных и способу рисования диаграммы. Начинается процесс в левом верхнем углу. По мере надобности добавляются новые строки и колонки. Например, для следующего блока кода будет добавлена новая строка, а если у блока есть боковой комментарий — то новая колонка.
Набор графических элементов, которые могут быть размещены в ячейках, примерно соответствует распознаваемым элементам языка с небольшим расширением. Например, в ячейке может понадобиться соединитель, ведущий сверху вниз, а в языке такой элемент отсутствует.
Для виртуального холста используется список списков (двумерный массив), пустой в начале процесса.
Рассмотрим простой пример в качестве иллюстрации процесса.
a = 10 # side comment 1
# side comment 2
![image](https://habrastorage.org/getpro/habr/post_images/d1c/bd7/af9/d1cbd7af9f045aacc50a6b0df37425be.png)
Размещение графических элементов на холсте
Слева на картинке выше показана структура данных, сформированная по результатам анализа кода. Экземпляр ControlFlow содержит несколько атрибутов и контейнер suite, в котором всего один элемент — экземпляр CodeBlock.
В исходном состоянии холст пуст, и начинается обход ControlFlow. Модуль было решено рисовать как область видимости, т.е. как прямоугольник с закругленными краями. Для удобства последующих расчетов размеров графических элементов с учетом отступов, прямоугольник области видимости условно разбит на составляющие: грани и углы. В самом верхнем углу диаграммы будет находиться левый верхний угол прямоугольника модуля, поэтому добавляем новую строку к холсту, а в строке добавляем одну колонку и помещаем в ячейку scope corner элемент. Верхнюю грань прямоугольника можно не размещать правее, так как запас по вертикали уже есть за счет первой ячейки, а отрисовка прямоугольника все равно будет осуществляться как единая фигура в момент, когда будет обнаружен scope corner на этапе обхода холста.
Переходим к следующей строке. У модуля есть заголовок, в котором указаны строка запуска и кодировка. Значения соответствующих полей None, но заголовок все равно надо рисовать. Поэтому добавляем к холсту новую строку. На диаграмме заголовок должен находиться внутри прямоугольника с небольшим отступом, поэтому сразу размещать заголовок в созданной строке нельзя. В первой ячейке должна быть помещена левая грань, а затем заголовок. Поэтому добавляем две колонки и в первой размещаем scope left, а во второй — scope header. Размещать правую грань в третьей ячейке не нужно. Во-первых, сейчас еще неизвестно сколько колонок будет в самой широкой строке. А во-вторых, прямоугольник области видимости все равно будет нарисован целиком в момент обнаружения scope corner.
У модуля могла бы быть строка документации, и для нее понадобилась бы еще одна строка. Но строки документации нет, поэтому переходим к suite. Первый элемент в suite — это блок кода. Добавляем новую строку, а в ней добавляем колонку для левой грани. Затем добавляем еще одну колонку и размещаем в ячейке графический элемент для блока кода. В примере у блока есть боковой комментарий, который должен располагаться справа, поэтому добавляем еще одну колонку и располагаем в ячейке боковой комментарий.
Больше в suite нет элементов, поэтому процесс подготовки виртуального холста заканчивается. Левый нижний угол прямоугольника и нижнюю грань можно не добавлять по причинам, подобным описанным выше для верхней и правой граней. Необходимо только учесть, что они подразумеваются при расчете геометрических размеров графических элементов.
Рендеринг
Задача этой стадии конвейера состоит в вычислении размеров всех графических примитивов для рисования на экране. Делается это путем обхода всех размещенных ячеек, вычисления необходимых размеров и сохранения их в атрибутах ячейки.
Каждая ячейка имеет две вычисленных ширины и две высоты — минимально необходимые и фактически требуемые с учетом размеров соседних ячеек.
Сначала обсудим, как рассчитывается высота. Происходит это построчно. Рассмотрим строку, в которой размещены элементы, соответствующие оператору присваивания. Здесь видно, что оператор присваивания занимает одну текстовую строку, а комментарий — две. Значит, ячейка с комментарием потребует больше вертикальных пикселей на экране при отрисовке. С другой стороны, все ячейки в строке должны быть одной высоты, иначе ячейки ниже будут нарисованы со смещением. Отсюда вытекает простой алгоритм. Необходимо обойти все ячейки в строке и рассчитать для каждой минимальную высоту. Затем выбрать максимальное значение из только что вычисленных и сохранить его как фактически требуемую высоту.
Немного сложнее дело обстоит с расчетом ширины ячеек. С этой точки зрения есть два типа строк:
- такие, у которых ширина должна рассчитываться с учетом ширины ячеек в соседней строке
- такие, ширина ячеек которых может рассчитываться без учета соседних строк
Хорошим примером зависимых строк является оператор if. Ветвь, которая будет нарисована под блоком условия, может быть произвольной сложности, а значит и произвольной ширины. А вторая ветвь, которая должна быть нарисована правее, имеет соединитель от блока условия, находящийся в строке выше. Значит, ширина ячейки соединителя должна быть рассчитана в зависимости от строк, располагающихся ниже.
Таким образом, для независимых строк ширина рассчитывается за один проход и минимальная ширина совпадает с фактической.
Для регионов зависимых строк алгоритм сложнее. Сначала рассчитываются минимальные ширины всех ячеек в регионе. А затем для каждой колонки рассчитывается фактическая ширина как максимальное значение минимально требуемой ширины для всех ячеек в колонке региона. То есть очень похоже на то, что сделано для вычисления высоты в строке.
Вычисления производятся рекурсивно для вложенных виртуальных холстов. При вычислениях учитываются различные настройки: метрика выбранного шрифта, поля для различных ячеек, отступы текста и т.п. По завершению работы фазы все необходимое уже подготовлено для размещения графических примитивов уже на экранном холсте.
Рисование
Стадия рисования очень проста. Так как для интерфейса пользователя используется библиотека QT, то создается графическая сцена с рассчитанными на предыдущем этапе размерами. Затем осуществляется рекурсивный обход виртуального холста и на графическую сцену добавляются необходимые элементы с нужными координатами.
Процесс обхода начинается с верхнего левого угла и текущие координаты устанавливаются в 0, 0. Затем обходится строка, после обработки каждой ячейки ширина прибавляется к текущей координате x. При переходе к следующей строке, координата x сбрасывается в 0, а к координате y прибавляется высота только что обработанной строки.
На этом процесс получения графики по коду заканчивается.
Настоящее и будущее
Обсудим теперь функциональность, которая уже реализована и то, что можно добавить в будущем.
Список того, что сделано, довольно короткий:
- Автоматическое обновление диаграммы в паузах изменения кода.
- Ручная синхронизация видимых текста и графики в обоих направлениях. Если фокус ввода находится в текстовом редакторе, и нажата специальная комбинация клавиш, то на диаграмме выполняется поиск примитива, наиболее соответствующего текущему положению курсора. Далее примитив подсвечивается, а диаграмма прокручивается так, чтобы сделать примитив видимым. В обратном направлении синхронизация выполняется по двойному щелчку по примитиву, который приводит к перемещению текстового курсора к нужной строке кода и прокрутке текстового редактора, если необходимо.
- Масштабирование диаграммы. Текущая реализация использует встроенные средства масштабирования библиотеки QT. В будущем планируется заменить ее на масштабирование через изменение размера шрифта и пересчет размеров всех элементов.
- Экспорт диаграммы в PDF, PNG и SVG. Качество выходных документов определяется реализацией библиотеки QT, так как именно ее средства использованы для этой функциональности.
- Навигационная панель областей видимости. Графика интенсивно использует идею области видимости, поэтому типичная диаграмма содержит множество вложенных областей. Навигационная панель показывает текущий путь в терминах вложенных областей видимости для текущей позиции курсора мыши над графической сценой.
- Индивидуальное переключение размещения веток для оператора if. По умолчанию ветка N рисуется ниже, а ветка Y правее. Диаграмма позволяет поменять местами расположение веток, используя контекстное меню блока условия.
- Индивидуальная замена текста любого графического примитива. Иногда возникает желание заменить текст какого-нибудь блока на произвольный. Например, условие в терминах переменных и вызовов функций может быть длинным и совсем не очевидным, тогда как фраза на естественном языке может лучше описать происходящее. Диаграмма позволяет заменить отображаемый текст на произвольный и показывает исходный во всплывающей подсказке.
- Индивидуальная замена цветов любого примитива. Иногда возникает желание привлечь внимание к какому-либо участку кода путем подсветки графических примитивов. Например, потенциально опасный участок можно подсветить красным цветом или выбрать общий цвет для элементов, отвечающих за общую функциональность. Диаграмма позволяет изменить цвета фона, шрифта и обводки примитивов.
Совместное применение уже реализованных индивидуальных изменений примитивов, как показывает практика, может существенно изменить вид диаграммы.
Возможности, которыми можно дополнить имеющуюся основу, ограничиваются только фантазией. Здесь перечислены самые очевидные.
- Автоматическая синхронизация текста и графики при прокрутке.
- Поддержка редактирования на диаграмме: удаление, перемещение и добавление блоков. Редактирование текста внутри отдельных блоков. Копирование и вставка блоков.
- Поддержка групповых операций с блоками.
- Визуализация отладки на диаграмме. Как минимум подсветка текущего блока и текущей строки в нем.
- Поддержка поиска на диаграмме.
- Поддержка печати.
- Управление скрытием / показом различных элементов: комментариев, строк документации, тел функций, классов, циклов и т.п.
- Подсветка различных типов импортов: внутри проекта, системные, неопознанные.
- Поддержка дополнительных неязыковых блоков или картинок на диаграммах.
- Умное масштабирование. Можно ввести несколько фиксированных уровней масштаба: все элементы, без комментариев и строк документации, только заголовки классов и функций, зависимости между файлами в подкаталоге и указатели внешних связей. Если закрепить такое поведение за колесом мыши с каким-либо модификатором, то можно будет получать обзорную информацию чрезвычайно быстро.
- Свертка нескольких блоков в один и развертывание обратно. Группа блоков, выполняющих совместную задачу может быть выделена на диаграмме и свернута в один блок со своей собственной графикой и предоставленным текстом. Естественное ограничение здесь — группа должна иметь один вход и один выход. Такая функциональность может пригодиться при работе с неизвестным кодом. Когда приходит понимание, что делают несколько расположенных блоков, можно сократить сложность диаграммы, объединив группу и заменив ее одним блоком с, нужной подписью, например «MD5 calculation». Разумеется, в любой момент можно будет вернуться к подробностям. Эту функцию можно рассматривать как введение третьего измерения в диаграмму.
CML v.1
Возможности, перечисленные в предыдущем разделе, можно разделить на две группы:
- Требующие сохранения информации в связи с кодом.
- Полностью независимые от кода.
Например функциональность масштабирования совершенно не зависит от кода. Текущий масштаб, скорее, должен быть сохранен как текущая настройка IDE.
С другой стороны, переключение ветвей if связано с конкретным оператором и информация об этом должна быть каким-то образом сохранена. Ведь при следующей сессии работы if должен быть нарисован так, как было предписано раньше.
Очевидно, что есть два пути сохранения дополнительной информации: либо прямо в исходном тексте, либо в отдельном файле или даже множестве файлов. При принятии решения были приняты во внимание такие соображения:
- Представим себе, что над проектом работает команда разработчиков и часть из них с успехом использует графические возможности представления кода. А другая часть из принципиальных соображений для работы с кодом использует только vim. В этом случае, если дополнительная информация хранится отдельно от кода, то ее консистентность при попеременном редактировании кода членами разных лагерей будет поддержать чрезвычайно сложно, если вообще возможно. Наверняка дополнительная информация окажется несоответствующей действительности в какой-то момент времени.
- Если выбран подход дополнительных файлов, то они скорее замусоривают содержимое проекта и требуют дополнительных усилий от команды при работе с системами контроля версий.
- Когда разработчик вводит дополнительную разметку — например заменяет длинный код непонятного условия подходящей фразой — это делается не для развлечения, а потому что такая пометка имеет ценность. Хорошо бы сохранить эту ценность доступной и для тех, кто не пользуется графикой хотя бы и не в таком красивом виде.
Таким образом, если есть компактное решение для сохранения дополнительной информации непосредственно в файлах с исходным текстом, то лучше воспользоваться им. Такое решение нашлось и было названо CML: Codimension markup language.
CML — это микро язык разметки, использующий комментарии питона. Каждый CML комментарий состоит из одной или нескольких соседних строк. Формат первой строки выбран следующим:
# cml <версия> <тип> [пары ключ=значение]
А формат строк продолжения следующим:
# cml+ <продолжение предыдущей cml строки>
Литералы cml и cml+ это то, что отличает CML комментарий от других. Версия, представляющая из себя целое число, введена в расчете на будущее, если CML будет эволюционировать. Тип определяет как именно повлияет комментарий на диаграмму. В качестве типа используется строковый идентификатор, например rt (сокращение от replace text). А пары ключ=значения предоставляют возможность описать все необходимые параметры.
Как видно, формат простой и легко читается человеком. Таким образом удовлетворяется требование возможности извлечения дополнительной полезной информации не только при использовании IDE, но и при просмотре текста. При этом единственным добровольным соглашением между теми, кто использует и не использует графику является такое: не поломать CML комментарии.
CML: замена текста
Распознавание CML комментария для замены текста уже реализовано. Он может появиться как лидирующий комментарий перед блоком, например:
# cml 1 rt text="Believe me, I do the right thing here"
False = 154
![image](https://habrastorage.org/getpro/habr/post_images/8bd/40a/115/8bd40a1150132bbaaf249c7e834c1c08.png)
Блок кода с измененным текстом
Поддержки со стороны графического интерфейса пока нет. Добавить такой комментарий сейчас можно только из текстового редактора. Назначение параметра text вполне очевидно, а тип rt выбран исходя из сокращения слов replace text.
CML: переключение ветвей if
Распознавание CML комментария для переключения ветвей if также уже реализовано. Поддержка есть как со стороны текста, так и со стороны графики. По выбору из контекстного меню блока условия комментарий будет вставлен в нужное место кода, а затем диаграмма генерируется заново.
# cml 1 sw
if False == 154:
print("That’s expected")
else:
print("Hmmm, has the code above been run?")
![image](https://habrastorage.org/getpro/habr/post_images/f42/bf9/05f/f42bf905f58cc0e2f3cdc967c6d9f8a8.png)
Оператор if с ветвью N справа
В приведенном примере ветвь N нарисована справа от условия.
Видно, что этот CML комментарий не требует никаких параметров. А его тип sw выбран исходя из сокращения слова switch.
CML: смена цвета
Распознавание CML комментария для смены цвета уже реализовано. Он может появиться как лидирующий комментарий перед блоком, например:
# cml 1 cc background="255,138,128"
# cml+ foreground="0,0,255"
# cml+ border="0,0,0"
print("Danger! Someone has damaged False")
![image](https://habrastorage.org/getpro/habr/post_images/991/b3a/68b/991b3a68bd42899054f5473e6e4aed46.png)
Блок с индивидуальными цветами
Поддерживается смена цвета для фона (параметр background), цвета шрифта (параметр foreground) и цвета граней (параметр border). Тип cc выбран исходя из сокращения слов custom colors.
Поддержки со стороны графического интерфейса пока нет. Добавить такой комментарий сейчас можно только из текстового редактора.
CML: свертка группы
Поддержки этого типа комментария сейчас нет, однако уже понятно, как функциональность может быть реализована.
Последовательность действий может быть такой. Пользователь выделяет на диаграмме группу блоков с учетом ограничения: группа имеет один вход и один выход. Далее из контекстного меню выбирается пункт: объединить в группу. Затем пользователь вводит заголовок для блока, который будет нарисован вместо группы. По окончанию ввода диаграмма перерисовывается в обновленном виде.
Очевидно, что для группы как единой сущности, есть точки в коде где она начинается и где она заканчивается. Значит, в эти места можно вставить комментарии CML, например такие:
# cml gb uuid="..." title="..."
. . .
# cml ge uuid="..."
Здесь параметр uuid автоматически генерируется в момент создания группы и нужен он для того, чтобы правильно находить пары начала группы и ее конца, так как уровней вложенности может быть сколько угодно. Назначение параметра title очевидно — это текст введенный пользователем. Типы записей gb и ge введены из соображения сокращений слов group begin и group end соответственно.
Наличие uuid также позволяет диагностировать различные ошибки. Например, в результате редактирования текста один из пары CML комментариев мог быть удален. Еще один случай возможного применения uuid — запоминания в IDE групп, которые должны быть показаны в следующей сессии как свернутые.
Побочные эффекты
Практика использования инструмента показала, что у технологии есть интересные побочные эффекты, которые совершенно не рассматривались при первоначальной разработке.
Во-первых, оказалось удобным использовать сгенерированные диаграммы для документации и для обсуждения с коллегами, часто не имеющими отношения к программированию, но являющимися экспертами в предметной области. В таких случаях готовился код, не предназначенный для выполнения, но соответствующий логике действий и отвечающий формальному синтаксису питона. Сгенерированная диаграмма либо вставлялась в документацию, либо печаталась и обсуждалась. Дополнительное удобство проявилось в легкости внесения изменений в логику — диаграмма моментально перерисовывается со всеми нужными отступами и выравниваем без необходимости ручных операций с графикой.
Во-вторых, проявился интересный чисто психологический эффект. Разработчик, открывая свой же давно написанный код с использованием Codimension замечал, что диаграмма выглядит некрасиво — слишком сложна или запутанна. И с целью получения более изящной диаграммы вносил изменения в текст, фактически выполняя рефакторинг и упрощение кода. Что в свою очередь приводит к уменьшению сложности понимания и дальнейшей поддержки кода.
В-третьих, несмотря на то, что разработка выполнена для Питона, технология вполне может быть распространена и на другие языки программирования. Питон был выбран как полигон для испытаний по нескольким причинам: язык популярный, но в то же время синтаксически простой.
Благодарности
Я бы хотел поблагодарить всех, кто помогал в работе над этим проектом. Спасибо моим коллегам Дмитрию Казимирову, Илье Логинову, Дэвиду Макэлхани и Сергею Фуканчику за помощь с различными аспектами разработки на разных этапах.
Отдельные благодарности авторам и разработчикам Питон пакетов с открытыми исходными текстами, которые были использованы в работе над Codimension IDE.
Комментарии (17)
SergeySatskiy
28.01.2017 19:02Причины исторические. Проект начинался давно и тогда был сделан выбор в пользу питона 2. И до совсем недавнего времени с питоном 2 все было нормально. К сожалению, в силу обстоятельств непреодолимой силы, примерно в ноябре прошлого года пришлось начать портирование проекта на питон 3. Модули уже портированы (использован синтаксис 3.5.2, но думаю и для 3.6 работать будет). Портирование IDE в процессе, и там конца края не видно.
EagleAlex
29.01.2017 22:30Как долго длилась разработка? Планируется разработка для Pycharm?
SergeySatskiy
29.01.2017 22:36Кодирование было начато в 2010.
Надо понимать, что это проект, который был выполнен в свободное от работы и других дел время одним разработчиком. Случались и длительные перерывы — например, в 2016 с мая по декабрь было сделано крайне мало из-за занятости с другими проектами.
Но в целом, все равно — разработка идет медленно.
На PyCharm планов нет.
MinasAbrahamyan
30.01.2017 11:10Хотелось бы узнать: что за форс-мажор заставил переписывать на Python 3?
nikolay_karelin
28.01.2017 22:33А вы не пробовали реализовать библиотеки для обработки дерева кода на чистом Питоне? Интересно, насколько большая будет разница производительности.
И еще вопрос (или идея): есть такое IDE — Spyder, популярно в задачах численного анализа, активно развивается, написано на основе PyQt, остальное чистый Питон, причем и 2 и 3 ветки.
Может быть, имеет смысл сделать на основе вашего проекта плагин к Spyder, тогда будет проще развивать и поддерживать? Да и путь в университеты будет короче ;)
К сожалению, никак не доберусь сам попробовать Codimension. Спасибо за детальное описание, выгладит весьма впечатляюще!
SergeySatskiy
29.01.2017 09:11> А вы не пробовали реализовать библиотеки для обработки дерева кода на чистом Питоне?
Нет, не пробовал. Я достаточно комфортно себя чувствую с C/C++ и процесс работы над модулями был интересен. Я смотрю на разработку этих модулей с точки зрения конечного результата. В данном случае работает примерно такая логика: приемлемый результат достигнут, значит можно переходить к следующей задаче в длинном списке. Моя конечная цель совсем не модули, они лишь средство. Идея инвестировать время в достижение уже достигнутого результата другим способом у меня особого энтузиазма не вызывает. Разумеется, нет никаких препятствий, если кто-то захочет проделать эту работу и узнать разницу в производительности.
Косвенно разницу можно оценить с использованием модуля из стандартной поставки питона, который называется pyclbr. Он вытаскивает информацию о классах, функциях и т.п. из питон файлов, то есть примерно то, что делает мой python parser модуль. Только он не все умеет вытаскивать. Далеко не все. И несмотря на то, что моя реализация предоставляет гораздо больше информации, я обгоняю стандартный модуль примерно в 7 раз. Подробности сравнения можно посмотреть здесь: http://codimension.org/documentation/cdmpyparser.html
> Может быть, имеет смысл сделать на основе вашего проекта плагин к Spyder, тогда будет проще развивать и поддерживать?
Может быть. А может быть и нет. Это неизвестно, пока не попробуешь.
Вообще говоря, способ оформления проекта — плагин (для какого редактора/IDE, если вариантов много?), набор утилит, новая IDE — не слишком простой и ясный выбор. Со сторонними проектами всегда есть масса неясностей, вот лишь некоторые из них:
— достаточен ли интерфейс плагинов для того, что я хочу сделать?
— а не поменяют ли разработчики интерфейс по ходу дела так, что мне придется все переделывать или так, что у меня принципиально все перестанет работать?
— если интерфейс недостаточен, то примут ли они мои доработки или отвергнут их и я останусь с проблемой делать все заново для чего-то другого?
— если у них баги, которые не дают возможности работать моей части, то как быстро они их исправляют? Ну или примут ли они мои изменения?
— не закроют ли они проект через два года?
— не поменяют ли они лицензию?
и т.п.
Вы наверняка опытный разработчик и наверняка имеете опыт участия в сторонних open source проектах. Мой опыт показывает, что люди ведут себя очень по-разному.
Когда я начинал разработку, я провел анализ имеющихся проектов. Spyder в тот момент был совсем не таким, как сейчас (если вообще был, не помню уже). Выиграл тогда eric 4. У него оказался совершенно недостаточный интерфейс плагинов. Да и во многих случаях играет роль такой фактор, как конкретная реализация UI. Иногда она такая, что желание делать плагин отпадает. Так или иначе, выбор был сделан в пользу своей IDE и пока что меня выбор для open source устраивает. А в виде коммерческого проекта можно сделать все что угодно.
> Да и путь в университеты будет короче
Не понял, что вы имеете в виду. Поясните пожалуйста.nikolay_karelin
04.02.2017 16:38Спасибо за развернутый ответ. Если вы не против, я отвечу несколькими комментариями, чтобы было проще воспринимать.
Позиция по использованию Си понятна, просто я больше привык к иной последовательности — сначала делаем на чистом Питоне, а потом узкие места конвертируем в Си или Cython для производительности. Поэтому спросил о производительности.
С модулем pyclbr я не работал, заглянул в него после вашего комментария, и, действительно, судя по реализации, он будет медленным.
Я бы начинал с модуля ast (тоже из стандартной библиотеки), у него большое преимущество — это тонкая обертка над внутренним представлением дерева разбора внутри интерпретатора, и его скорость должна быть сравнимой с компилированным расширением.
Если интересно, я некоторое время назад рассказывал про ast на митапе, видео и ссылка на презентацию есть вот здесь: https://habrahabr.ru/company/wargaming/blog/256425/
nikolay_karelin
04.02.2017 17:21Теперь о Spyder: эта IDE достаточно активно развивается, за проектом стоит достаточно серьезное сообщество (за последние 5 лет там 2 раза менялся основной разработчик, и это проходило в целом незаметно для пользователей).
Я разок с ними общался про решение существенного для меня бага, более того, занес в комментарий свой "костыль", на его основе потом и окончательное решение было сделано.
Более того, Spyder — это часть по умолчанию для крупных дистрибутивов Питона, в первую очередь Anaconda, и поэтому, например в сообществе data science это достаточно популярная программа.
Я совершенно согласен, что при использовании стороннего решения, а тем более при переходе с одной кодовой базы на другую возникает много рисков, в первую очередь не технических, а организационных.
Не совсем понял, что вы имели в виду:
А в виде коммерческого проекта можно сделать все что угодно.
На мой взгляд, как раз со стороны коммерческой разработки, сделать можно то, за что будут платить пользователи. А это бывает узнать или угадать ой как сложно.
nikolay_karelin
04.02.2017 17:28И пояснение про университеты:
В комментариях к вашей предыдущей статье, здесь https://habrahabr.ru/post/320184/#comment_10029832 вы писали
Мне тоже приходила мысль о том, что в учебном процессе инструмент мог бы быть полезен. У меня,
однако, нет хороших знакомых в университетской среде. И я плохо себе представляю вариант зайти
куда-нибудь и предложить использовать Codimension.Я хотел сказать, что если ваша разработка появится, как часть популярного в университетах решения, того же Spyder, то профессора будут пользоваться им как для исследований, так и для обучения ;)
Но это скорее информация к размышлению. Когда ближе разберусь (у меня почему-то Codimension с первого раза на RedHat Linux не установился, но это скорее в каких-то локальных настройках косяк), может появятся еще предложения.
SergeySatskiy
06.02.2017 21:40Что ж, теперь понятно — путь в университеты лежит через переделку модулей на чистом питоне и разработке решения для spyder.
Codimension на RedHat практически не тестировался. В travis есть поддержка RedHat и этот вариант был туда добавлен. Дома я использую ubuntu, а на работе с windows машины Codimension запускается с centos (транковой версии, не через установленные пакеты). Поэтому на RedHat возможны проблемы. Если есть что-то конкретное, пожалуйста, сообщите. Можно либо на github баг заправить, либо на электронную почту мне написать: sergey.satskiy@gmail.com. Не могу обещать, что все будет исправлено, но может быть с чем-то смогу помочь.nikolay_karelin
07.02.2017 14:19Насчет чистого Питона — не уверен: если будет стабильное решение на основе компилируемого расширения хотя бы для Линукс, то можно пользоваться. Скорее надо писать в популярные ресурсы и выступать на конференциях, привлекать контрибьюторов и пр.
Могу кстати в группу по Питону на LinkedIn забросить ссылку.
Не уверен, что буду снова пытаться на RedHat поставить, у меня и на ubuntu машинка есть.
Кстати, по поводу Spyder — могу еще порекомендовать его код посмотреть, просто как пример большого приложения, с поддержкой нескольких версий Qt, плагинов и многого другого.
SergeySatskiy
07.02.2017 18:01> Могу кстати в группу по Питону на LinkedIn забросить ссылку.
Сейчас для меня это не актуально. Следующий запланированный шаг в направлении распространении информации это перевод статей на английский и публикация либо на ycombinator, либо на reddit.
> могу еще порекомендовать его код посмотреть, просто как пример большого приложения, с поддержкой нескольких версий Qt, плагинов и многого другого.
Спасибо. Я время от времени смотрю код не только у них, но и еще у нескольких проектов IDE. Для spyder я делал и мелкую правку (что-то бросилось в глаза, уже не помню) несколько лет назад.
Leo5700
31.01.2017 19:31Такая оболочка ещё более снизит порог вхождения в Python для новичков. Это здорово. И вообще, всегда полезно увидеть свой код «в новом ракурсе», это снижает число ошибок. Спасибо за статью.
phoenixweiss
31.01.2017 21:36Снимаю шляпу, очень серьезная работа проделана.
Интересно, а в нашем Ruby-мире никто не озадачивался подобным?
perfect_genius
04.02.2017 21:37Радует, что кто-то пытается развивать программирование в сторону визуального способа.
icoz
Круто. Спасибо за проделанную работу.
Только один вопрос. Почему не питон 3? Почему именно 2?