Здравствуйте, Воронков Василий, Вы писали:
ВВ>Какое-то описание есть здесь. Причем это не самый эффективный алгоритм.
Сдается мне, что это в данном случае не причем.
ВВ>Меня тут больше потребление памяти занимает, чем скорость.
А что память? Ну, занялось на долю секунды пару сотен байт (ну, максимум пару килобайт) памяти. И что? При следующей сборке мусора она будет считаться свободной. Вся эта память занимается в нулевом поколении ЖЦ. Объем мелкий, так что на размер нулевого поколения это повлиять не может. Так что память тут жалеть нечего.
Ну, а с глобальной точки зреня память хранится довольно оптимально. Команда хранит только то, что нужно. Конечно есть небольшой оврехэд от ЖЦ, но это все фигня.
ВВ> Скорость по большому счету упирается в отрисовку.
Редактирования? Да.
ВВ>Не, ну я же не против абстракций. Собственно, я вообще не пытаюсь критиковать реализацию.
Кстати, я не против критики. Я очень за критику. Я только хочу, чтобы она была продуманной. А то у меня со временем ужасные проблемы и не хочется убивать его на борьбу с догмами. (не обижайся) По этому если ты показывашь пальцем и говоришь, "вот это мне кажется странным (глупым, непродуманным...)", я постораюсь разобраться и подумать не ошибся ли я. Возможно даже переосмыслить свою позицию. Но когда все сводится к "ой, а не много ли тут памяти расходуется", то разговаривать то в общем-то не очем. Ведь предмет разговора не определен.
ВВ>Я пытаюсь разобраться. И мне всегда казалось, что дизайн приложения не "измеряется" количеством созданных классов.
Естественно. Так же как архитектура не определяется количеством кирпичей/панелей в здании. Но здание строится из кирпичей/панелей и если мы начнем рассуждать о строительстве здания на уровне количества арматурин или песка в расстворе, то смысла в обсуждении не останется.
ВВ>На самом деле я пытался въехать во что упирается работа с текстами только по-строчно. Ведь все-таки string[] — это скорее _представление_ данных, чем сам данные.
Во что упирается? Одна строка занимается, другая освобождается. В итоге мы имеем новую строку у которой длинна больше или меньше. ЖЦ при одной из сборок мусора сожмет кучу и мы заплатим только за разницу. Ну, еще мы заплатим временем затрачиваемым ЖЦ на работу. Но ведь мы уже оговорились, что со скорость воде как проблем нет? Далее какое-то место уйдет на хранение команы. Но это плата за возможность сделать анду и реду. Все промежуточные команды опять же подберет ЖЦ. Если вдруг когда-то хранение команд покажется черезчур накладным, то можно сбросить их на диск. Но вот пока что мне не кажется это нужным (хотя думл я об этом еще когда проектирваол редактирование).
ВВ>Да, согласен. Но чем gap buffer этому мешает?
А что он мне даст? Читаем из твой ссылки:
A gap buffer is a structure that models a string but allows relatively
efficient insertion of text somewhere in the middle.
У меня есть проблемы с производительностью? А вот на это efficient прийдется потратить память чтобы организовать дыры. К тому же я уже буду работать не со строкой, а с какой-то сложной абстракцией которая вряд ли будет так же эффективна на чтение как строка. Ну, и плюс лишняя память которую уже ЖЦ не подберт.
Идем дальше... Избавляет меня это от хранения команды для анду? Ясный пестик — нет.
Так что мне это дает? Я вижу только один вопрос совершенно не нужный геморрой.
ВВ>Скорость хорошая.
Вот видишь? Значит предложенный тобой gap buffer нам ничего не даст. Он ведь эффективность поднимает. Не так ли?
ВВ> Но она по большому счету не от этого зависит. Что такое создание одного объекта для перфоманса, даже тысячи..
Ну, всему есть пределы. Боюсь, что тысячи уже могут серьезно сказаться. Хотя все опять же зависит от обстоятельств.
Да и на апаратуру нужно смотреть. Такие решения на какой нибудь IBM PC XT вряд ли были возможны. Там бы я и не заикнулся о множественном анду, а строку редактировал бы в спец-буфере. Причем старался бы скинуть все не использованные строки на диск. В общем, я имел бы куда более примитивный редактор с куда большим геморроем в его разработке.
ВВ>А память
ВВ>Как я уже говорил, я собственно не критикую и даже не предлагаю что-то переделывать. Мне интересно почему были выбраны именно такие решения при реализации, а не другие. В чем их бенефит и пр. И вот это мне пока непонятно.
Наверно об этом стоит небольшую статейку написать. Я понимаю, что без поднаготной все выглядит довольно туманно.
Кстати, я в общем-то пршел в программирование когда как раз высшим пилотажем считались хакерские оптимизации экомномящие пару бит и порой сам боюсь чистых ОО-решений. Но пытаюсь с этим бороться.

Понимаю, что нужно все взвешивать и замерять, а не бояться и додумывать.
ВВ>Ну имелась в виду "полная" работа с редактором, включая работу со стилями и пр.
Ты забавно мыслиь. Ты пыташся сравнить работу редактора с (внимание!)
интерфейсом Сцинтилы.

Если сравнить интерфейсы (а у Сцинтилы он весь из сообщений в стиле Виндовс стостит), то уверяю тебя интерфейс Rsdn.Editor куда проще. От част просто потому, что в нем не реализованы некторые вещи

, а от части потому, что нет необходимости возиться с низкоуровневыми абстракциями вроде char* и т.п. Например, я сразу ввел понятие координат и пересчеты между координатами представления и документа. В синтиле этого нет. Казалось бы проще... Но на самом деле разбираясь все время надо понимать, что вот этот int — это номер строки в документе, а вот этот — это в колонка в представлении. В итоге сам черт ноку сломит. То же самео с командами. Команда Сцинтилы — это один класс с кучей полей которые используются в разных случаях. У меня это набор классов которые используются только там где это возможно и нужно. Причем для большинства классов у меня есть визуализация в отладчике. В то же время в сцинтиле одно преобразование из/в utf-8 и
хнанение байтов стилей вперемешку с байтами текста так шифрует суть происходящего, что даже строку то увидеть не возможно. Я же четко отделил стили от текста и храню его в виде простой строки. В результате все время видно, что делается и почему.
VD>>Теперь о реализации. В Сцинтиле, точно так же как и в Rsdn.Editor применяются команды редактирования. Разница только в том, что у меня разные типы команд описываются разными классами и составляют иерархию:
VD>>VD>>Command - абстрактный
VD>> MultiCommand
VD>> ModifyTextCommand - абстрактный
VD>> InsertCommand
VD>> DeleteCommand
VD>> UpdateCommand
VD>>
VD>>в которой все конкретные (не абстрактные) классы реализуют ICommand.
ВВ>А зачем?
Затем, чтобы видеть что рельно происходит. Чтобы отделять действия по вставке от действий по редактированию. Прицип действия моей команды очень прост. У любой команды можно вызвать ICommand.Execut() и ты получишь а) действия связанные с командой, б) команду обратную этой (то есть если выполнить полученную команду, то действия предыдущей будут откачены). А почему иерархия классов, а не "все в одном"? Дык между прочим InsertCommand должна хранить текст, DeleteCommand только координаты удаляемого текста, а UpdateCommand и то и другое. Ну, а MultiCommand вообще должны хранить список команд которые нужно выполнить как единую транзацию. Так что это Сцинтила расходует память по напрасну, так как ее команда всегда хранит "все что нужно".
Но делал иерархию я не для этого. Иерархия позволяет хорошо отделить логику. В купе с наследованием реализации, которое позволяет выделить общую логику, это позволяет очень хоршо отделить разную логику друг от дурга и в то же время получить отличную абстаркцию.
ВВ>Да, да, ты прав. В сцинтилле все ужасно. Тем не мнее когда потребовалось сделать для нее раскраску нужно было разобраться со следующими функциями, что заняло минут 10-20:
ВВ>ВВ>SCI_GETENDSTYLED
ВВ>SCI_STARTSTYLING(int position, int mask)
ВВ>SCI_SETSTYLING(int length, int style)
ВВ>SCI_SETSTYLINGEX(int length, const char *styles)
ВВ>SCI_SETLINESTATE(int line, int value)
ВВ>SCI_GETLINESTATE(int line)
ВВ>SCI_GETMAXLINESTATE
ВВ>
ВВ>А вот в работу со стилями у тебя я до сих пор до конца не въеду. Почем бы это? Может, последствия "тяжелых" новогодних праздников?
Возможно отчасти потому, что Сцинтиле много лет и всей все уже отработно, а у меня работа со стилями откровенно не доделана.
Хотя аналоги этих методов есть. Это два свойства из класса DocumentRow:
int StartStylerState { get; set; }
AppliedStyle[] AppliedStyles { get; set; }
Не скзал бы что интерйес уж настолько сложнее

.
AppliedStyle — это структура содержащая ссялку на стиль, позицию в строке на которой начинается применение стиля и длинну на которую простирается действие стиля. То есть практически как в Сцинтиле. Разница тольк в том, что в Сцинтиле ты вынужден задавать стили битовой маской. Причем если стили пересекаются ты вынужден сам думать как это разрулить. У меня же ты просто задашь список стилей которые могут перескаться как угодно. Я сам "сложу" пересекшиеся стили когда будет надо.
При этом исчезает еще одна проблема. Ты не ограничен количеством битов отводимых на стиль. Размер массива может быть любым! А вот в Сцинтиле, если мне не изменяет память, количество стилей не дожно привышать 16.
ВВ>Прежде чем писать лексер хотелось бы разобраться в том, что на настоящий момент является "черновой" реализацией и может быть изменено в будущем, а что как бы уже "доделано". Все-таки согласись писать лексер который парсит текст по-строчно и который парсит любые фрагменты — немножко разные вещи.
Черновой реализацией скорее всего являются:
1. Стили.
2. Форматер.
3. Применяемый лексер.
Лексер пойдет в помойку полностью. Но его интерфейс должен быть похож.
Форматер нужно отрефакторить так, чтобы для изменения, к примеру, лексера не нужно было переопределять весь класс форматера.
Стили... С ними сложнее. С одной стороны идеологически они уже вряд ли изменяться. Но я сделал одну промашку. Для эффективного наложения стилей нужно сделать так, чтобы можно было отдельно задавать такие атрибуты шрифта как жирный, наклонный, размер, гарнитура. Короче, все

. Нужно это потому, что при более менее сложной подсветке нужно сочетать разные стили. А если я не могу сложить атрибуты шрифта, то мне нужно создавать пересечение стилей на все случаи жизни. Например, если я хочу реализовать форматирования для Януса, то мне нужно сделать так, чтобы внутри кода было возможно выделить отдельные слова жирным. Но как это сделать если код сам по себе содержит тучу стилей? На сегодня я вынужден создать пересечение для всех стилей! А это при некоторых условиях приводит к комбинаторному взрыву. Вывод — нужно изобретать свой шрифт, а рельный собирать по частям непосредственно перед отрисовкой.
В ты говришь на строках экономить.
В общем, от лексера, точнее стайлера нужно следующее:
1. Он должен уметь начать сканирование исходя из передаваемого в него идентификатора состояния. Состояние на сегодня это просто целочисленная константа, но это не догма.
2. Он обязан сканировать код построчно.
3. Он должен в конце сканирования строки возвращать идентификатор состояния. Именно его ему же передадут при сканировании следующий строки.
4. Единственная задача стайлера — заполнить массив AppliedStyles и присвоить его в одноименное свойство строки документа (которую ему передадут через параметр).
Собственно, все! Далее я уже сам прикручу его куда надо.
Основная проблема стайлера на сегодня это скорость. Это одино из самых узких мест в редакторе. Он вызвается в самых вложенных циклах. Так что вот он то должен создавать минимум временных объектов (использовать их повторно если нужно) и быть максимально быстрым.
Понятно, что для одного-двух язяков не трудно написать стайлер вручную. В Сцинтиле все стайлеры написаны вручную (Уроды, блин! Правда есть редакторы на базе Сцинтилы которые имеют свои генераторы стайлеров.). Но мы не индусы

. Так что нужно сделать генератор лексеров. Самый простой и продуктивный путь — это выдрать генератор детерминированного конечного автомата (ДКА) из CocoR и изменив в нем кодогенерацию порождать эффективные стайлеры вместо CocoR-ного Scanner-а.
Можно конечно просто пользоваться генерируемым Scanner-ном и создавать AppliedStyles на основании токенов, но это довольно не эффективно. Ведь для стайлера неважно даже какое ключевое словно найдено. Важно только его местоположение. А уж создавать экземпляры строк — это точно лишнее. Достаточно типа токена.
... << RSDN@Home 1.2.0 alpha rev. 631>>