Здравствуйте, _FRED_, Вы писали:
_FR>Что такое "плоский текст" и "не плоский текст"
Как бы это по проще объяснить?...
Ну, вот взять, к примеру, каой нибудь FrontPage. У него есть два редактора. Первый wiswig-редактор преобразующий плоский формат в сложный. Второй отображает и редактирует HTML/XML в исхдном виде но при этом одсвечивает синтаксис формата.
В общем, редакторв в которых можно назначить стиль произвольному участку текста и редакторы которые занимаются подсветкой текста — это разный класс редакторов. У них разные задачи.
Rsdn.Editor не преобразует текст в кокой-то сложный формат. Он просто хранит текст в исходном виде, ну разве что в разбитом на строки.
... << RSDN@Home 1.2.0 alpha rev. 587>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
_FR>>Ага, то есть есть подсветка и другие визуальные эффекты зависят... как бы от смысла текста, но не от желания пользователя.
VD>Можно сказать и так, но это бедет несовсем точно. Выключать фичи можно. Например, отключить подвсетку или не показывать смайлики.
Всё, теперь понял.
VD>>>Rsdn.Editor не преобразует текст в кокой-то сложный формат. Он просто хранит текст в исходном виде, ну разве что в разбитом на строки.
_FR>>Почему так? Сложность? Желание простоты? Может быть, стремление решить другой класс задач?
VD>Ну, как тебе сказать?... Задача стаяла сделать замену Сцинтиле, т.е. нужен бы редактор кода с подсветкой синтаксиса. Делать Ворд или РичЭдит задачи не было.
VD>Потенциально можно конечно создать специальный тип строки в котором реализовать хоть черта лысого, но лично мне этого не нужно. Мне достаточно чтобы показывались смайлики, подсвечивался в риалтайме синтаксис и теги форматирования отображались бы соотвествующим начертанием. А скрывать их смысла особого нет.
Возможно...
VD>Может когда-нить если руки дойдут и желане будет можно будет расширить функциональность. А может этим займется кто-то дургой. Я же лучше сосредоточусь на том, что как следует отрефакторю код, добавлю коментариев по больше и опишу все все что смогу в статьях. А там, кому нужно, пусть делает все что хочет.
Если не сложно, отметь пожалуйста коментариями места, которые сделаны "как надо" и "на первое время, дабы только работало", чтобы понять можно было, над изменением чего надо втрое больше подумать. Было бы очень удобно.
<< RSDN@Home 1.1.4 beta 7 rev. 500 >> =01:49= [Windows XP — 5.1.2600.0]
under «*none*»
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, alsemm, Вы писали:
VD>>Чтобы сделать точный скролинг и точное отображение позиции в документе. A>Вернее чтобы упростить реализацию этих функций
Я в последнее время вообще стараюсь не делать вещи сложенее чем нужно. Но тут это не причем.
При редактировании текстов с большими абзацами (вроде текста этого сообщения) скролинг должен быть не менее удобным чем при редактировании файла с кодом, где стоки почти не переносятся. Отсюда надо заранее рассчитывать переносы строк. Темболее, что это относительно недолго.
VD>>И каждый раз бегать по тексту рассчитывая строку? А смысл? Тормоза от этого конечно увеличатся, а вот бенефиты... A>Если юзать string[] для хранения текста, то да, смысла нет, не спорю.
Я не понимаю о чем ты говоришь. Что еще можно "юзать"? Массивы символов? Ну, и какая разница?
Объясни мне доходчиво.
A>Загрузили в редактор большой текст и стали его с головы редактировать.
И? Размер файла будет влиять только на время загрузки, ну, и на время изменения размера окна если включен перенос по его границе. А на редактирование это никак не скажется. Стой ты хоть в начале текста, хоть в конце.
A> Ограничения стурктуры данных (в данном случае string[]) не будут видны пока мощности железа хватает, но рано или поздно они вылезут. Вообще IMHO использование Position<Document> для адресации сомволов очень напоминает старничную память с кошмариками в виде long и short pointer-ов.
Ограничения string[]? Ты о чем? В редакторе вообще нет никаких string[]. Строки хранятся в специальном классе храняещем по одному string-гу на строку редактируемого файла.
A>... VD>>1. Так плохой идеей было использовать дотнетный Font. Это не позволяет менять в стилях отдельные атрибуты шрифта. Например, чтобы сделать жирным ключевые слова. Это же не дает реализовать простое масштабирование шрифтов. Мне очень нравится как в Сцинтиле или ИЕ можно менять размер ширфтов просто покрутив колесико мыши удерживая при этом Ctrl. A>Думаю это не проблема шрифта, а не совсем удачный дизайн класса Style. Если пользователи захотят что-то большее кроме как рисовать вместо текста картинки? Например обводить подстроку в рамку. Что делать будете? Добавлять поле _drawRectAroundText в Style?
А какие проблемы? Стиль == класс. Создавай наследника и храни хоть черта лысого. Другое дело, что прийдется отрисовку менять. Над расширением процесса отрисовки я даже не думал, так как не нужно. Проект с открытым кодом. Всякие плагины для него не особо то актуальный. Хотя если понадобится, то прикрутить будет не долго.
Тут же речь не о том. Речь о банальном удобстве. Человек решивший создать подсветку для некоторого формата не должен трахаться создвая стили для всех пересечений, или как ты предлагашь писать какой-то код вместо того, чтобы декларативно задать нужные ему свойства.
A>IMHO коряво.
Каждый имеет право на лево. Я считаю корявы знаниматься программированием там где можно без него обойтись.
A>class IDecoratorListener
A>{
A>public:
A> virtual ~IDecoratorListener() {}
A>public:
A> virtual bool
A> onSubstringDecorated(
A> unsigned line,
A> const char_type* str,
A> unsigned strLen,
A> const char_type* substr,
A> unsigned substrLen,
A> const TextStyle& ts) = 0;
A> virtual bool
A> onSubstringDecorated(
A> unsigned line,
A> const char_type* str,
A> unsigned strLen,
A> const char_type* substr,
A> unsigned substrLen,
A> const IDecoratedSubstring& ds) = 0;
A>};
И как это поможет тебе с рисованием той же рамки?
A>Вот, например, как можно сделать изменение размера шрифта "как в Сцинтиле или ИЕ": A>
A>...
A> virtual bool
A> onSubstringDecorated(
A> unsigned line,
A> const char_type* str,
A> unsigned strLen,
A> const char_type* substr,
A> unsigned substrLen,
A> const TextStyle& ts)
A> {
A> HDC dc = getTempDC(); // каким-то образом получить HDC
A> ::SelectFont(dc, ts.font);
A> TEXTMETRICS tm;
A> ::GetTextMetrics(dc, &tm);
A> LOGFONT lf = textMetrics2LogFont(tm); // как-то сконвертировали
A> lf.height += (lf.height * 10) / 100; // увеличить шрифт на 10%
A> TextStyle newTs(ts);
A> newTs.font = ::CreateFontIndirect(lf);
A> // передать в листенер измененный шрифт
A> listener_.onSubstringDecorated(line, str, strLen, substr, substrLen, newTs);
A> }
A>};
A>
Да уж... Прямо как в ИЕ или Сцинтиле... почти! Так как динамическое пересоздание шрифта на каждый токен — это задница для производительности. На относительно старом железе это будет тормозить как АБС.
К тому же изменение размеров шрифта должно быть незаметно для пользователя контрола. Да, и вобще, прямая возня с GDI — это само по себе фигня какая-то. Даже Сцинтила от нее абстрагироуется. Хотя это конечно к делу не относится.
A>Редактор дергает IDecorator::decorate каждый раз когда надо отрисовать строку текста передавая свои реализации интерфейса IDecoratorListener. Есть реализации IDecoratorListener-а которые высчитывают ширину подстроки/целой строки, высоту ну и т.д. Word wrap так же сделан через IDecoratorListener.
Ага. И на каждое вычисление размера токена мы имеем пересоздание шрифата. Плюс его же на отрисовку. Ну, нафиг такие "грамотные решения". К тому же еще и влезание пользовательского кода в низкоуровневые.
Масштибировние шрифта — задача редактора. Это относительно низкоуровневая пробелма и я лично на сегодня вижу ее решение на уровне библиотеки вывода на физическое устройство (ну, нечто вроде замены Graphics из GDI+).
В приложении будет использоваться объект-пустышка который только хранить описание шрифта (что-то вроде высокоуровневого объектно ориентированного LOGFONT). Функции вывода будут получать его и на его основании получать из кэша шрифт. Это будет и быстро, и добноно, и прозрачно для основного кода. Представлние, перед отрисовкой или рассчетом высот строк, будет выставлять свой масштаб, а библиотека отображения будет его отрабатывать.
Что касается прикладного оформления (тех самых стилей), то я считаю совершенно не верным делать какие-то потойные лазейки в данной области. Все форматирование должно определяться стилями. Если нужен новый вид форматировани, то просто добавляй новый стиль.
Я же говорил вообще о том что уже реализовано. Сейчас чтобы создать стилевой набор в котором используется один шрифт у котрого могут меняться характиристики вроде "жирный", "курсив" и т.п. я должен создать новые объекты Font. А ведь некоторые стили могут пересекаться. Если пересекаются стили с разными шрифтами, то возникает проблема. Ведь я не могу логически объеденить описание шрифтов или задать его частично (как это делаетс с цветами). Один шрифт переоределить другой. Вот я и говорю, что это просчет который нужно устранить. Должна быть возможность задавать шрифт частично. Чтобы наложение стилей не перекрывало один шрифт другим, а объеденяло их описания.
A>Бенефиты такого подхода: A>1. декоратор дергается только для тех строк которые видны на экране — не надо мучать процессор лишней работой;
А как на счет рассчета переноса строк? Забить на него? Лично я против. Мне нужен удобный в использовании редактор, а не набор компромисов.
A>2. легко сделать кэш строк обработанных декоратором, т.к. для этого достаточно просто написать еще одну реализацию IDecoratorListener;
А зачем мне кэш строк? Я и без него отлично живу. Дотнет и так жрет слишком много памти, чтобы забиват ее совершенно не нужными кэшами.
A>3. Визуализация текста не ограничена рисованием текста или рисованием картинки вместо текста, т.к. реализация IDecoratedSubstring ограничена только фантазией юзверя (в разумных пределах конечно ;
Я не заметл, чтобы твой прмер что-то мог изменить в отрисовке. Да и ливно мне это не нужно. Любое расширение дожно быть продумано и ограничено, чтобы оно не мешала остальным частям приложения. Твое предложение убивает к чертям всю диею стилей. Ну, нафиг такие расширения.
A>4. декораторы можно комбинировать. Пример — FontChangeDecorator который изменяет результаты разбора уже существующего декоратора.
А зачем мне вообще заниматься импиратившиной зависящей от последовательности выполнения и приводящей к мутному коду, когда я могу все что нужно вырзить в виде стилей?
На то я их и проектировал, чтобы потом не трахаться с кодированием и не офигивать от побочных эффектов от наложения таких вот расширений.
Мне нужна простота, ясность и скорость.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, alsemm, Вы писали:
A>>Task manager вот что говорит: A>>Rsdn.Editor: A>>- 0М текста занимает 22М A>>- 16М текста занимает 135М (значение плавает, max 180 сразу после загрузки файла в редактор, потом падает) A>>Мое барахло: A>>- 0М текста занимает 20М A>>- теже 16М текста занимают 90М
VD>Сцинтила, если не ошибаюсь, жрет сильно меньше. Но там нет ЖЦ и Юникода.
Стормозил я: мерил память в debug билде, в релизе у меня получилось близко к сцинтиле, но у нее маньше отжирается памяти процентов на 15 при начальной загрузке файла чем у меня. Потом сцинтила начинает тупить: выделил весь загруженный текст и удалил — стала занимать в два раза больше памяти. Как-то интересно они там undo/redo сделали...
... A>>...Понятно что при начальной загрузке текста распарсить его надо чтобы знать где многострочные комментарии и прочая лабуда, но сохранять результаты разбора всего текста вовсе не обязательно. IMHO это не оптимизация даже, а просто разумный подход.
VD>Это именно что оптимизация. Причем рассчитанная на некоторую эвристику. Например, если объем изменений файла будет большим, то опримизация прерватится в писимизацию, или прийдется написать еще гору кода.
Гора кода:
cacheSize = numberOfVisibleLines;
onLinesDeleted(int startLine, int count)
{
if(count > cacheSize)
{
clearCache();
}
else
{ // в цикле перебрать все удаленные строки и если для них есть записи в кэше, то удалить их из кэша
}
}
В кэше хранится только вспомогательные данные для тех строк, который на экране видны, т.е. макисмально в кэше будет 100-200 записей — совсем немного.
VD>Как почти любая оптимизация, эта требует дополнительных усилий по кодированию и приводит к некрасиым дизайнерским решениям.
От кривизны исходного дизайна и рук оптимизатора много чего зависит
VD>А ведь в раельных условиях проблем то нет. Этот контрол будет жить внутри дургих дотнетных приложений и память для него будет разделяться с памятью для всего процесса. Плюс вероятность того, что кто-то станет редактировать в нем огромные файлы крайне мала. Это все же редактор кода/контента. Так зачем ломать копья до того как это действительно потребуется?
В Visual Studio можно хоть слона загрузить и пока памяти хватает ничего не тормозит. Вообщем хочется чтоб было не хуже чем у больших дядек.
— Распростанение, т.е. на каких условиях я могу использовать этот контрол?
— Возможно ли будет вместо смайликов внедрять другие объекты (хотябы и графические), но с возможностью выполнения каких-либо действий, при щелчке мышью по ним (например, показа меню, а лучше всего — вызова какой-либо функции)?
Здравствуйте, Fahrain, Вы писали:
F> — Распростанение, т.е. на каких условиях я могу использовать этот контрол?
Чесно говоря и у самого это вопрос открытый. Пока что остановился на следующем: http://rsdn.ru/projects/Rsdn.Editor/Article/Info.xml
F> — Возможно ли будет вместо смайликов внедрять другие объекты (хотябы и графические),
Можно внедрять любые наследники Image. Ну, и ни что не мешает изменить код ка нужно.
F>но с возможностью выполнения каких-либо действий, при щелчке мышью по ним (например, показа меню, а лучше всего — вызова какой-либо функции)?
Событие MouseDown никто не отменял. Можно подписаться на него у View и при приходе события вызвать функцию HitTest получив текст под нажатием. Ну, а далее проанализировав текст и его стиль делать все что угодно.
... << RSDN@Home 1.2.0 alpha rev. 557>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Fahrain, Вы писали:
F>> — Распростанение, т.е. на каких условиях я могу использовать этот контрол?
VD>Чесно говоря и у самого это вопрос открытый. Пока что остановился на следующем: VD>http://rsdn.ru/projects/Rsdn.Editor/Article/Info.xml
F>> — Возможно ли будет вместо смайликов внедрять другие объекты (хотябы и графические),
VD>Можно внедрять любые наследники Image. Ну, и ни что не мешает изменить код ка нужно.
F>>но с возможностью выполнения каких-либо действий, при щелчке мышью по ним (например, показа меню, а лучше всего — вызова какой-либо функции)?
VD>Событие MouseDown никто не отменял. Можно подписаться на него у View и при приходе события вызвать функцию HitTest получив текст под нажатием. Ну, а далее проанализировав текст и его стиль делать все что угодно.
...ещё интересует подсветка и соответствующая реакция на гиперссылки.
Одинаковые ошибки необязательно делать каждый раз, достаточно сделать одну, а затем обращаться к ней по мере необходимости из любого места программы.
Здравствуйте, Max404.NET, Вы писали:
MN>...ещё интересует подсветка
И что именно?
MN>и соответствующая реакция на гиперссылки.
Гиперссылки я не реализовывал, но по той же схеми можно сделать и их. Если стайлер посветит гиперссылку особым образом, то нет особых проблем отловить клик мыши в оласть этого стиля и что-то предпринять.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Max404.NET, Вы писали:
MN>>...ещё интересует подсветка
VD>И что именно?
автоподсветка цветом и подчеркиванием (например) так же как на Веб-странице (посещенные, непосещенные) + изменение формы указателя мыши.
MN>>и соответствующая реакция на гиперссылки.
VD>Гиперссылки я не реализовывал, но по той же схеми можно сделать и их. Если стайлер посветит гиперссылку особым образом, то нет особых проблем отловить клик мыши в оласть этого стиля и что-то предпринять.
хотелось бы событие, как в RichTextBox.
кроме того это вопрос декорирования, например в веб видна только часть ссылки (текст). Мне нужно создание собственных ссылок с параметрами, нестандартных, например данные ссылки
DB_User:ID=123&NewWindow=true
+ ещё должна быть отображаемая часть. Во втором фреймворке можно использовать WebBrowser, но начальство не хочет переходить на него ((. Видимо придется парсить текст и генерировать контролы. Но это полумера. Было бы неплохо определять стиль для текста... в общем все мысли сводятся на расширение функциональности HTML-браузера
Одинаковые ошибки необязательно делать каждый раз, достаточно сделать одну, а затем обращаться к ней по мере необходимости из любого места программы.
Здравствуйте, Max404.NET, Вы писали:
MN>хотелось бы событие, как в RichTextBox.
Я не знаком с системой событий RichTextBox. Что конкретно интересно?
MN>кроме того это вопрос декорирования, например в веб видна только часть ссылки (текст). Мне нужно создание собственных ссылок с параметрами, нестандартных, например данные ссылки MN>DB_User:ID=123&NewWindow=true MN>+ ещё должна быть отображаемая часть. Во втором фреймворке можно использовать WebBrowser, но начальство не хочет переходить на него ((. Видимо придется парсить текст и генерировать контролы. Но это полумера. Было бы неплохо определять стиль для текста... в общем все мысли сводятся на расширение функциональности HTML-браузера
Судя по описанию Rsdn.Editor тут не лучший выбор. Это все же редактор плоского текста. Он подсвечивает текст документа и не умеет отображать другой текст. RichTextBox — это все же немного другое дело. Это контрол предназначенный для форматирования текста.
... << RSDN@Home 1.2.0 alpha rev. 578>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
ЧВA>Авторы: ЧВA> Чистяков Влад aka VladD2
ЧВA>Аннотация: ЧВA>Статья рассказывает о новом открытом проекте Rsdn.Editor, его целях и проектных решениях, принятых при его реализации. ЧВA>Требуется знание C#.
Что такое "плоский текст" и "не плоский текст"
<< RSDN@Home 1.1.4 beta 7 rev. 500 >> =02:23= [Windows XP — 5.1.2600.0]
under «*none*»
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, VladD2, Вы писали:
_FR>>Что такое "плоский текст" и "не плоский текст"
VD>Как бы это по проще объяснить?...
VD>Ну, вот взять, к примеру, каой нибудь FrontPage. У него есть два редактора. Первый wiswig-редактор преобразующий плоский формат в сложный. Второй отображает и редактирует HTML/XML в исхдном виде но при этом одсвечивает синтаксис формата.
VD>В общем, редакторв в которых можно назначить стиль произвольному участку текста и редакторы которые занимаются подсветкой текста — это разный класс редакторов. У них разные задачи.
Ага, то есть есть подсветка и другие визуальные эффекты зависят... как бы от смысла текста, но не от желания пользователя.
И разве на таком редакторе нельзя написать редактор HTML, представляющий текст в таком виде: "<b>Это жирный текст</b>"? (то есть тэги не скрываются, но эффект от их применения заметен)?
VD>Rsdn.Editor не преобразует текст в кокой-то сложный формат. Он просто хранит текст в исходном виде, ну разве что в разбитом на строки.
Почему так? Сложность? Желание простоты? Может быть, стремление решить другой класс задач?
<< RSDN@Home 1.1.4 beta 7 rev. 500 >> =01:14= [Windows XP — 5.1.2600.0]
under «*none*»
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Ага, то есть есть подсветка и другие визуальные эффекты зависят... как бы от смысла текста, но не от желания пользователя.
Можно сказать и так, но это будет не совсем точно. Выключать фичи можно. Например, отключить подвсетку или не показывать смайлики.
_FR>И разве на таком редакторе нельзя написать редактор HTML, представляющий текст в таком виде: "<b>Это жирный текст</b>"? (то есть тэги не скрываются, но эффект от их применения заметен)?
Не скрывая — можно. Можно даже так "<b>Это жирный текст</b>". Редактору в приципе по фигу. Он подсвечивает все на основании стилей которые расставляет стайлер (ну, лексер специализированный).
VD>>Rsdn.Editor не преобразует текст в кокой-то сложный формат. Он просто хранит текст в исходном виде, ну разве что в разбитом на строки.
_FR>Почему так? Сложность? Желание простоты? Может быть, стремление решить другой класс задач?
Ну, как тебе сказать?... Задача стаяла сделать замену Сцинтиле, т.е. нужен бы редактор кода с подсветкой синтаксиса. Делать Ворд или РичЭдит задачи не было.
Потенциально можно конечно создать специальный тип строки в котором реализовать хоть черта лысого, но лично мне этого не нужно. Мне достаточно чтобы показывались смайлики, подсвечивался в риалтайме синтаксис и теги форматирования отображались бы соответствующим начертанием. А скрывать их смысла особого нет.
Может когда-нить если руки дойдут и желане будет можно будет расширить функциональность. А может этим займется кто-то дургой. Я же лучше сосредоточусь на том, что как следует отрефакторю код, добавлю комментариев по больше и опишу все что смогу в статьях. А там, кому нужно, пусть делает все что хочет.
... << RSDN@Home 1.2.0 alpha rev. 587>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, ENGLER, Вы писали:
ENG>Здравствуйте, Чистяков Влад aka VladD2, Вы писали:
ENG>Здравствуйте, с удовольствием присоеденился бы, но для начала хотелось бы дочитать статью ENG>http://www.rsdn.ru/article/mag/200502/rsdn.editor.xml
Здравствуйте, _FRED_, Вы писали:
_FR>Если не сложно, отметь пожалуйста коментариями места, которые сделаны "как надо" и "на первое время, дабы только работало", чтобы понять можно было, над изменением чего надо втрое больше подумать. Было бы очень удобно.
ОК, попробую.
... << RSDN@Home 1.2.0 alpha rev. 587>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Igor Trofimov, Вы писали:
iT>Подкину участникам проекта ссылочку, которая может оказаться полезной:
iT>Dissecting a C# Application iT>Inside SharpDevelop
А что там?
iT>Там есть несколько разделов по теме.
Например?
... << RSDN@Home 1.2.0 alpha rev. 591>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
В статье в журнале написано
"На рисунке 3 приведена диаграмма классов для иерархии классов стилей. Эту диаграмму также можно созерцать в интерактивном виде внутри проекта. Ее имя StyleDiagram.cd."
Открыл проект в VC# 2005 Express Beta2, а как работать с диаграммой не понимаю
Здравствуйте, Igor Trofimov, Вы писали:
iT>Проект действительно может оказаться очень нужным.
iT>Подкину участникам проекта ссылочку, которая может оказаться полезной:
iT>Dissecting a C# Application iT>Inside SharpDevelop
iT>Christian Holm iT>Mike Krüger iT>Bernhard Spuida
iT>Там есть несколько разделов по теме. iT>Без труда находится в google, ну вот например тут.
Большое спасибо за ссылочку, действительно очень полезно
Здравствуйте, Spaider, Вы писали:
S>Не сочтите за труд, выложите куда-нибудь слепок SVN репозитория. S>Сижу за проксей, достучаться Черепугой нет возможности.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Rsdn.Editor
От:
Аноним
Дата:
17.01.06 16:42
Оценка:
У меня парочка мыслей появилась после прочтения статьи и беглово знакомства с исходниками:
1. Зачем делать word wrap для строк которые не видны на экране? Лишняя работа.
2. Для адресации позиции в тексте достаточно знать смещение символа от начала текста, а уж строку которой принадлежит символ и смещение в строке можно расчитать. Тогда можно было бы обойтись без Position<View> и Position<Document>. Понятно, что если использовать, грубо говоря, string[] в качестве хранилища текста, то вариант с адресацией символов по номеру строки и смещению выглядит естественно. IMHO простота string[] обманчива. Упретесь рано или поздно в масштабируемость.
3. IMHO не получилось полностью отделить документ от вида. Зачем в Document-е ссылки на IView и Formatter? IMHO разумно в классе Document держать только код для доступа к тексту, его изменеию (insert/erase) и undo/redo. Модификация документа IMHO никак не связана с тем как он отформатирован.
Здравствуйте, <Аноним>, Вы писали:
А>У меня парочка мыслей появилась после прочтения статьи и беглово знакомства с исходниками: А>1. Зачем делать word wrap для строк которые не видны на экране? Лишняя работа.
Чтобы сделать точный скролинг и точное отображение позиции в документе.
А>2. Для адресации позиции в тексте достаточно знать смещение символа от начала текста, а уж строку которой принадлежит символ и смещение в строке можно расчитать.
И каждый раз бегать по тексту рассчитывая строку? А смысл? Тормоза от этого конечно увеличатся, а вот бенефиты...
А> Тогда можно было бы обойтись без Position<View> и Position<Document>. Понятно, что если использовать, грубо говоря, string[] в качестве хранилища текста, то вариант с адресацией символов по номеру строки и смещению выглядит естественно. IMHO простота string[] обманчива. Упретесь рано или поздно в масштабируемость.
Как-то проблем с масштабируемостью я пока не видел. Как и ее необходимости. Поясни, плиз, о чем речь.
А>3. IMHO не получилось полностью отделить документ от вида. Зачем в Document-е ссылки на IView и Formatter? IMHO разумно в классе Document держать только код для доступа к тексту, его изменеию (insert/erase) и undo/redo. Модификация документа IMHO никак не связана с тем как он отформатирован.
Возможно. Надо будет подумать на этот счет.
ЗЫ
Чесно говоря сейчас волнуют несколько другие вещи. Есть несклько просчетов и несколько недоделок.
1. Так плохой идеей было использовать дотнетный Font. Это не позволяет менять в стилях отдельные атрибуты шрифта. Например, чтобы сделать жирным ключевые слова. Это же не дает реализовать простое масштабирование шрифтов. Мне очень нравится как в Сцинтиле или ИЕ можно менять размер ширфтов просто покрутив колесико мыши удерживая при этом Ctrl.
2. Надо сделать сменяемый парсер для языков. Для этого нужно как-то создать генератор лексеров по удобной граматике.
3. Для реализации п. 2 не плохо было бы реарганизовать Formatter. Выделить из него парсер и т.п. Возможно твой п. 3 можно как раз объеденить с этим пунктом.
В любом случае, спасибо за конструктивные комментарии.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, <Аноним>, Вы писали:
А>>У меня парочка мыслей появилась после прочтения статьи и беглово знакомства с исходниками: А>>1. Зачем делать word wrap для строк которые не видны на экране? Лишняя работа.
VD>Чтобы сделать точный скролинг и точное отображение позиции в документе.
Вернее чтобы упростить реализацию этих функций
А>>2. Для адресации позиции в тексте достаточно знать смещение символа от начала текста, а уж строку которой принадлежит символ и смещение в строке можно расчитать.
VD>И каждый раз бегать по тексту рассчитывая строку? А смысл? Тормоза от этого конечно увеличатся, а вот бенефиты...
Если юзать string[] для хранения текста, то да, смысла нет, не спорю.
... VD>Как-то проблем с масштабируемостью я пока не видел. Как и ее необходимости. Поясни, плиз, о чем речь.
Загрузили в редактор большой текст и стали его с головы редактировать. Ограничения стурктуры данных (в данном случае string[]) не будут видны пока мощности железа хватает, но рано или поздно они вылезут. Вообще IMHO использование Position<Document> для адресации сомволов очень напоминает старничную память с кошмариками в виде long и short pointer-ов.
... VD>1. Так плохой идеей было использовать дотнетный Font. Это не позволяет менять в стилях отдельные атрибуты шрифта. Например, чтобы сделать жирным ключевые слова. Это же не дает реализовать простое масштабирование шрифтов. Мне очень нравится как в Сцинтиле или ИЕ можно менять размер ширфтов просто покрутив колесико мыши удерживая при этом Ctrl.
Думаю это не проблема шрифта, а не совсем удачный дизайн класса Style. Если пользователи захотят что-то большее кроме как рисовать вместо текста картинки? Например обводить подстроку в рамку. Что делать будете? Добавлять поле _drawRectAroundText в Style? IMHO коряво.
Я сам счас редактор делаю. Вот как раскраска у меня задизайнена:
Вот, например, как можно сделать изменение размера шрифта "как в Сцинтиле или ИЕ":
class FontChangeDecorator : public IDecorator, IDecoratorListener
{
IDecorator* decorator_;
IDecoratorListener* listener_;
public:
FontChangeDecorator(IDecorator* decorator): decorator_(decorator) {}
public:
virtual void
decorate(
unsigned line,
const char_type* str,
unsigned len,
IDecoratorListener& dl) const
{
// запомнить текущее значение listener_, чтобы восстановить его в случае рекурсивного
// вызова FontChangeDecorator::decorate
IDecoratorListener* l = listener_;
// подменить листенер
listener_ = dl;
decorator_->decorate(line, str, len, *this);
// восстановить листенер
listener_ = l;
}
private:
virtual bool
onSubstringDecorated(
unsigned line,
const char_type* str,
unsigned strLen,
const char_type* substr,
unsigned substrLen,
const TextStyle& ts)
{
HDC dc = getTempDC(); // каким-то образом получить HDC
::SelectFont(dc, ts.font);
TEXTMETRICS tm;
::GetTextMetrics(dc, &tm);
LOGFONT lf = textMetrics2LogFont(tm); // как-то сконвертировали
lf.height += (lf.height * 10) / 100; // увеличить шрифт на 10%
TextStyle newTs(ts);
newTs.font = ::CreateFontIndirect(lf);
// передать в листенер измененный шрифт
listener_.onSubstringDecorated(line, str, strLen, substr, substrLen, newTs);
}
};
Использование FontChangeDecorator:
void
setFontChangeDecorator(HWND hwnd /* хендл на окно моего редактора */)
{
IDecorator* d = ::SendMessage(hwnd, MYEDITOR_MSG_GETDECORATOR, 0, 0);
IDecorator* d2 = new FontChangeDecorator(d);
::SendMessage(hwnd, MYEDITOR_MSG_SETDECORATOR, d2, 0)
}
Редактор дергает IDecorator::decorate каждый раз когда надо отрисовать строку текста передавая свои реализации интерфейса IDecoratorListener. Есть реализации IDecoratorListener-а которые высчитывают ширину подстроки/целой строки, высоту ну и т.д. Word wrap так же сделан через IDecoratorListener.
Бенефиты такого подхода:
1. декоратор дергается только для тех строк которые видны на экране — не надо мучать процессор лишней работой;
2. легко сделать кэш строк обработанных декоратором, т.к. для этого достаточно просто написать еще одну реализацию IDecoratorListener;
3. Визуализация текста не ограничена рисованием текста или рисованием картинки вместо текста, т.к. реализация IDecoratedSubstring ограничена только фантазией юзверя (в разумных пределах конечно ;
4. декораторы можно комбинировать. Пример — FontChangeDecorator который изменяет результаты разбора уже существующего декоратора.
...
VD>>>И каждый раз бегать по тексту рассчитывая строку? А смысл? Тормоза от этого конечно увеличатся, а вот бенефиты... A>>Если юзать string[] для хранения текста, то да, смысла нет, не спорю.
VD>Я не понимаю о чем ты говоришь. Что еще можно "юзать"? Массивы символов? Ну, и какая разница? VD>Объясни мне доходчиво.
У тебя сейчас, если я ничего не путаю, так:
Такое решние дает очень эффективную реализацию Document::getLine. В минусе то, что время выполнения вставки/удаления строки не const и растет с увеличением количества элементов в массиве lines[].
A>>Загрузили в редактор большой текст и стали его с головы редактировать.
...
VD>Ограничения string[]? Ты о чем? В редакторе вообще нет никаких string[]. Строки хранятся в специальном классе храняещем по одному string-гу на строку редактируемого файла.
Этот специальный класс (Document, если ничего не путаю) инкапсулирует string[], так? Я об этом писал.
...
VD>А какие проблемы? Стиль == класс. Создавай наследника и храни хоть черта лысого. Другое дело, что прийдется отрисовку менять. Над расширением процесса отрисовки я даже не думал, так как не нужно. Проект с открытым кодом. Всякие плагины для него не особо то актуальный. Хотя если понадобится, то прикрутить будет не долго.
Тебя в сцинтиле все устраивает? Видимо нет, раз взялся за свой редактор. А почему сцинтилу не стал "подкручивать"? IMHO потому что, разбираться в чужом коде радость та еще. Ты идешь по тому же пути — захотят расширения делать, пусть правят код, а не плагин пишут.
Плагины и открытый код вещи не взаимоисключающие.
VD>Тут же речь не о том. Речь о банальном удобстве. Человек решивший создать подсветку для некоторого формата не должен трахаться создвая стили для всех пересечений, или как ты предлагашь писать какой-то код вместо того, чтобы декларативно задать нужные ему свойства.
Уже были вроде посты про то, что писать свой стайлер для твоего редактора пока не очень удобно.
... VD>И как это поможет тебе с рисованием той же рамки?
Так же как с фонтом.
VD>Да уж... Прямо как в ИЕ или Сцинтиле... почти! Так как динамическое пересоздание шрифта на каждый токен — это задница для производительности. На относительно старом железе это будет тормозить как АБС.
Я код написал чтоб принцип был понятен. На самом деле на каждый токен фонт рожать не надо, заводим в FontChangeDecorator std::map<HFONT, HFONT>, где ключи — шрифты от существующего декоратора, а зчачения — новые, увеличенные и все дела.
VD>К тому же изменение размеров шрифта должно быть незаметно для пользователя контрола. Да, и вобще, прямая возня с GDI — это само по себе фигня какая-то. Даже Сцинтила от нее абстрагироуется. Хотя это конечно к делу не относится.
Ну привел бы я тебе код с использованием своих классов оберток над GDI, стал бы он поятней?
VD>Ага. И на каждое вычисление размера токена мы имеем пересоздание шрифата. Плюс его же на отрисовку. Ну, нафиг такие "грамотные решения". К тому же еще и влезание пользовательского кода в низкоуровневые.
Создавать или нет шрифт решает декоратор, он может вообще один и тот же фонт юзать созданный заранее (так мой дефолтовый декоратор и делает).
VD>Масштибировние шрифта — задача редактора. Это относительно низкоуровневая пробелма и я лично на сегодня вижу ее решение на уровне библиотеки вывода на физическое устройство (ну, нечто вроде замены Graphics из GDI+).
... VD>Что касается прикладного оформления (тех самых стилей), то я считаю совершенно не верным делать какие-то потойные лазейки в данной области. Все форматирование должно определяться стилями. Если нужен новый вид форматировани, то просто добавляй новый стиль.
Дело хозяйское.
...
A>>Бенефиты такого подхода: A>>1. декоратор дергается только для тех строк которые видны на экране — не надо мучать процессор лишней работой;
VD>А как на счет рассчета переноса строк? Забить на него? Лично я против. Мне нужен удобный в использовании редактор, а не набор компромисов.
Все считается и перенос строк и прокрутка. Не в лоб конечно, но и каши в коде нет. Кстати, у тебя можно иметь строки разной высоты? Например, если в одной строке шрифт высотой 10, а в другой 20, то они будут 10 и 20 или обе по 20 высотой? У меня можна
A>>2. легко сделать кэш строк обработанных декоратором, т.к. для этого достаточно просто написать еще одну реализацию IDecoratorListener;
VD>А зачем мне кэш строк? Я и без него отлично живу. Дотнет и так жрет слишком много памти, чтобы забиват ее совершенно не нужными кэшами.
Кэш затем, чтобы юзверьский код лишний раз не дергать. Дернул декоратор для строки и запомнил результаты в кэш.
A>>3. Визуализация текста не ограничена рисованием текста или рисованием картинки вместо текста, т.к. реализация IDecoratedSubstring ограничена только фантазией юзверя (в разумных пределах конечно ;
VD>Я не заметл, чтобы твой прмер что-то мог изменить в отрисовке. Да и ливно мне это не нужно. Любое расширение дожно быть продумано и ограничено, чтобы оно не мешала остальным частям приложения. Твое предложение убивает к чертям всю диею стилей. Ну, нафиг такие расширения.
Я не навязываю свое решение, просто озвучил свой взгляд на задачу раскраски текста. Чем плохо на проблему с разных сторон посмотреть?
... VD>Мне нужна простота, ясность и скорость.
Сколько методов нужно реализовать юзеру чтобы написать свой стайлер? В IDecorator-е он всего один
Здравствуйте, alsemm, Вы писали:
A>У тебя сейчас, если я ничего не путаю, так: A>
A>class Document
A>{
A> string[] lines;
A>public:
A> string getLine(int idx) { return lines[idx]; }
A>};
A>
A>Такое решние дает очень эффективную реализацию Document::getLine. В минусе то, что время выполнения вставки/удаления строки не const и растет с увеличением количества элементов в массиве lines[].
Нет. Строки это наследники класса DocumentRow. В документе они хранятся в коллекции DocumentRowCollection. DocumentRowCollection внутри себя хранит динамический массив DocumentRow (List<DocumentRow>).
A>Этот специальный класс (Document, если ничего не путаю) инкапсулирует string[], так? Я об этом писал.
Не так, как видишь. И об этом рассказано в стаье.
Что касается скорости, то дабавление или удаление даже в огромных текствых файлах (3 метра и больше) занимает меньше 1% времени вставки. И вообще вставка небольшого кличества строк в любую точку документа для пользователя незаметна (выглядит как ралтаймная операция).
В свое время я задумался не будет ли тормозить List<T> в качестве основы для коллекции строк. Думал, а не использовать ли BigList<T> из PowerCollections. Но тесты показали, что в самом большом текстовом файле который я смог найти на своей машине (около 3 метров) List<T> вел себя даже лучше чем BigList<T>.
A>Тебя в сцинтиле все устраивает? Видимо нет, раз взялся за свой редактор. А почему сцинтилу не стал "подкручивать"?
Потому что в Сцинтиле был ряд проблем устранение которых практически невозможно. Вот они:
1. Сцинтила написана на С++ причем в такой манере, что ее практически невозможно переписать на МС++. А редактор нужно использовать в управляемых приложениях. Неуправляемый С++-код в таких условиях приводит к ряду проблем (от плохой интерграции и траты ресурсов, до невозможности запуска прилоежния в 64-битном окружении).
2. Стили в Сцинтиле наначаются битами. Это очень неудобно для моих потребностей.
3. Сцинтила трудно поддается рефакторингу. Опять же С++-ный код для которого нет человеческих средств автоматизированного рефакторинга. Плюс написана она кривовато.
4. Сцинтила не юникодная. Возня с UTF-8 и т.п. сильно задалбывает.
Плюс было интересно написать аналог Сцинтилы на управляемом языке чтобы было можно сравнивать результат.
Результат оказался интересным. По скорости проблем практически нет. По памяти управляемый вариант выглядит значительно хуже, но приемлемо. По объему и простоте кода управляемый вариант намного обходит Сцинтилу.
A> IMHO потому что, разбираться в чужом коде радость та еще. Ты идешь по тому же пути — захотят расширения делать, пусть правят код, а не плагин пишут. A>Плагины и открытый код вещи не взаимоисключающие.
Мы, в свое время, пытались делать плагины-интерфейсы к Хоуму. Потом отказались, так как смысла это не имело. Намного проще и эффектинвее просто развивать проект.
A>Уже были вроде посты про то, что писать свой стайлер для твоего редактора пока не очень удобно.
Да, это так. Но проблема тут в том, что именно вопрос со стайлером и вообще форматером банально плохо проработан. Когда доведем его до ума, то и вопросов не возникнет. Нет же вопросов по работе с клавиатурой?
A>... VD>>И как это поможет тебе с рисованием той же рамки? A>Так же как с фонтом.
То есть ручками рисовать конкурируя с основным кодом? Мне это не нравится.
A>Я код написал чтоб принцип был понятен. На самом деле на каждый токен фонт рожать не надо, заводим в FontChangeDecorator std::map<HFONT, HFONT>, где ключи — шрифты от A> существующего декоратора, а зчачения — новые, увеличенные и все дела.
ОК, согласен. Производительность можно поднять. Но иделогических проблем это не снимает.
VD>>А как на счет рассчета переноса строк? Забить на него? Лично я против. Мне нужен удобный в использовании редактор, а не набор компромисов. A>Все считается и перенос строк и прокрутка. Не в лоб конечно, но и каши в коде нет. Кстати, у тебя можно иметь строки разной высоты? Например, если в одной строке шрифт высотой 10, а в другой 20, то они будут 10 и 20 или обе по 20 высотой? У меня можна
В редакторе каждый стилевой диапазон может иметь отдельную высоку. Более того так как диапазоны можно менять на лету, то и высота отдельных строк может меняться динамически. Когда у меня был глюк и стиль выделения менял характеристики шрифта, то было вообще кртуто. Изменение выделения приводило к динамическому перетеканию текста и изменению высоты строк. Выглядело просто фиерично.
Так что разная высота строк это самое простое.
A>Кэш затем, чтобы юзверьский код лишний раз не дергать. Дернул декоратор для строки и запомнил результаты в кэш.
Вот по этому я и считаю, что зеру можно давать лезть только куда нужно. Тогда и кэширование не потребуется.
A>Я не навязываю свое решение, просто озвучил свой взгляд на задачу раскраски текста. Чем плохо на проблему с разных сторон посмотреть?
Разные мнения и решения всегда полезны. Просто так же как ты имешь право высказать свое мнение, я имею право с ним не согласиться.
Полезны же здесь аргументы. Ведь они наталкивают на мысли. Да и несогласие при некотором количестве качественных аргументов может превратиться в согласие.
VD>>Мне нужна простота, ясность и скорость. A>Сколько методов нужно реализовать юзеру чтобы написать свой стайлер? В IDecorator-е он всего один
Когда все будет доделоно, то вообще ничего писать не прийдется. А пока что просто есть некоторая реализация которая позволила написать основной код.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
...
VD>Не так, как видишь. И об этом рассказано в стаье.
Видимо проглядел
VD>Что касается скорости, то дабавление или удаление даже в огромных текствых файлах (3 метра и больше) занимает меньше 1% времени вставки. И вообще вставка небольшого кличества строк в любую точку документа для пользователя незаметна (выглядит как ралтаймная операция).
Интересно, надо бы у себя посмотреть как время распределяется.
VD>В свое время я задумался не будет ли тормозить List<T> в качестве основы для коллекции строк. Думал, а не использовать ли BigList<T> из PowerCollections. Но тесты показали, что в самом большом текстовом файле который я смог найти на своей машине (около 3 метров) List<T> вел себя даже лучше чем BigList<T>.
Открой в редакторе любой из его же исходников приличного размера (10-20К) и повтори последовательность {ctr+a, ctr+c, ctr+v} раз 10.
... VD>То есть ручками рисовать конкурируя с основным кодом? Мне это не нравится.
Именно так. Проблем не вижу.
... VD>ОК, согласен. Производительность можно поднять. Но иделогических проблем это не снимает.
Просто другой подход.
... VD>Когда все будет доделоно, то вообще ничего писать не прийдется. А пока что просто есть некоторая реализация которая позволила написать основной код.
Дык подстроку в рамку обвести можно будет или нет?
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Fahrain, Вы писали:
F>> — Распростанение, т.е. на каких условиях я могу использовать этот контрол?
VD>Чесно говоря и у самого это вопрос открытый. Пока что остановился на следующем: VD>http://rsdn.ru/projects/Rsdn.Editor/Article/Info.xml
Может, уберете пункт про GNU? В конце концов некрасиво бороться с врагом его же методами. Да и лишние грабли это для разработчиков — не грех в общем-то выпустить что-нибудь под GNU, если уж очень нужно использовать какой-нибудь GNUтый код.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Может, уберете пункт про GNU? В конце концов некрасиво бороться с врагом его же методами. Да и лишние грабли это для разработчиков — не грех в общем-то выпустить что-нибудь под GNU, если уж очень нужно использовать какой-нибудь GNUтый код.
Пункт про GNU запрещает ГНУть код этого контрола. Но он не запрещает использовать этот код в GNUтых проектах. Просто в лицензии нужно указать, что код редактора подчиняется не лицензии GNU GPL, а той что описана по этой ссылке. Только и всего. Так что в любом открытом или закрытом проекте можно использовать этото код.
Просто если в рамках GPLного проекта кто-то сделает модификацию кода этого контрола, то ее тоже можно будет использовать в коммерческих проектах. Вот какова цель этого ограничения.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, alsemm, Вы писали:
A>Открой в редакторе любой из его же исходников приличного размера (10-20К) и повтори последовательность {ctr+a, ctr+c, ctr+v} раз 10.
И? Взял исходинк BreakLine.cs (~50 кил). Вставил 40 раз его же в его же начало. Особых проблем не обнаружил.
Ты сам то пробовал?
VD>>То есть ручками рисовать конкурируя с основным кодом? Мне это не нравится. A>Именно так. Проблем не вижу.
А я вижу.
Я предпочитаю чистые решения. Даже сказал бы декларативные. Это же решение никак нельзя называть чистым. Это скорее какой-то хак во славу гибкости.
VD>>ОК, согласен. Производительность можно поднять. Но иделогических проблем это не снимает. A>Просто другой подход.
Естественно другой. И его минусы я описал.
A>Дык подстроку в рамку обвести можно будет или нет?
Если кому-то понадобится, то сделает нужный стиль и обведет. Это не проблема.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, alsemm, Вы писали:
A>>Открой в редакторе любой из его же исходников приличного размера (10-20К) и повтори последовательность {ctr+a, ctr+c, ctr+v} раз 10.
VD>И? Взял исходинк BreakLine.cs (~50 кил). Вставил 40 раз его же в его же начало. Особых проблем не обнаружил. VD>Ты сам то пробовал?
Нет не пробовал.
Если бы ты внимательно прочитал мой комментарий, то 40 раз забить 50Кб файл вряд-ли получилось. Так попробуй:
1. вставить через клипборд 50К текста в начало
2. вставить через клипборд 50К текста в начало
3. выделить весь текст в редакторе и скопировать в клипборд
4. вставить через клипборд уже 100К текста в начало
5. выделить весь текст в редакторе и скопировать в клипборд (в клипборде уже 200К)
6. вставить...
Подозреваю что 40 итераций сделать не получится
... VD>>>ОК, согласен. Производительность можно поднять. Но иделогических проблем это не снимает. A>>Просто другой подход.
VD>Естественно другой. И его минусы я описал.
Спорить не буду.
Здравствуйте, alsemm, Вы писали:
A>Нет не пробовал.
Так попробовал бы и все вопросы потпали бы.
A>Если бы ты внимательно прочитал мой комментарий, то 40 раз забить 50Кб файл вряд-ли получилось. Так попробуй: A>1. вставить через клипборд 50К текста в начало A>2. вставить через клипборд 50К текста в начало A>3. выделить весь текст в редакторе и скопировать в клипборд A>4. вставить через клипборд уже 100К текста в начало A>5. выделить весь текст в редакторе и скопировать в клипборд (в клипборде уже 200К) A>6. вставить... A>Подозреваю что 40 итераций сделать не получится
Да, так не получится. Правда не потому, что структуры данных не те, а потому что алгоритм квардаричный. Уже на 10 разах получается 15 метров чистого текса в Win1251. В оперативке же это получается уже ~200 метров. Ведь там и анду-буфер, и юникод, и ОО-оверхэд и куча всего другого.
Но 10 вставок делаются довольно не напряжно. Правда в Rsdn.Editor очень халявно реализована работа с выделением. На любой чих просто перепарсивается вся область выделения, но вставка новой строки, после 10 вставок проходит молниеносно. Так что с динамическим массивом проблем нет никаких.
С другой стороны эксперемент совершенно бессмысленный. Ведь текстов такого размера попросту не бывает. 5 метров текста — это нехилая книжка.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, alsemm, Вы писали:
A>>Нет не пробовал.
VD>Так попробовал бы и все вопросы потпали бы.
Я в NET не силен, возиться не охота было со сборкой. Теперь попробую раз все так шустро работает.
... VD>Но 10 вставок делаются довольно не напряжно. Правда в Rsdn.Editor очень халявно реализована работа с выделением. На любой чих просто перепарсивается вся область выделения, но вставка новой строки, после 10 вставок проходит молниеносно. Так что с динамическим массивом проблем нет никаких.
Стало быть я ошибался насчет тормозов. Бывает
VD>С другой стороны эксперемент совершенно бессмысленный. Ведь текстов такого размера попросту не бывает. 5 метров текста — это нехилая книжка.
Логи бывают и побольше.
Собрал и погонял редактор. Вполне шустро. Правда P4 3.2G с 1G памяти замучать непросто
Подтормаживает если удалять большие куски текста( >10000 строк), когда ресайзится окно если включен вордврап и в контроле > 20000 строк текста.
Но это все вообщем-то близкие к параноидальным значения, так что можно внимание не заострять.
Табы imho неправильно расставляет если в строке несколько шрифтов используется. Если используется немоноширинный шрифт или несколько шрифтов, то расчет ширины таба должен не от его позиции в строке зависеть, а делаться как в wordpad-е (там задаются позиции для табов в пикселях). Для этого даже спец. функция GDI есть — TabbedTextOut.
... VD>Да, так не получится. Правда не потому, что структуры данных не те, а потому что алгоритм квардаричный. Уже на 10 разах получается 15 метров чистого текса в Win1251. В оперативке же это получается уже ~200 метров. Ведь там и анду-буфер, и юникод, и ОО-оверхэд и куча всего другого.
Task manager вот что говорит:
Rsdn.Editor:
— 0М текста занимает 22М
— 16М текста занимает 135М (значение плавает, max 180 сразу после загрузки файла в редактор, потом падает)
Мое барахло:
— 0М текста занимает 20М
— теже 16М текста занимают 90М
wordwrap отключен
Т.е. в твоем редакторе на хранение одного байта текста — уходит (135-22) / 16, т.е. примерно 8байт, в моем около 5. Думаю разница из-за того, что при загрузке ты сразу текст парсишь и результаты разбора сохраняешь. Если не путаю ничего (не силен я в NET-е), то память отжирается тут:
using (FreezeEventHelper herper = new FreezeEventHelper(this))
{
Document.ResetViewsInfo();
Items.Clear();
foreach (string line in Parse(text))
Items.Add(new DocumentRow(line));
Document.Formatter.PrepareRows(this);
}
Понятно что при начальной загрузке текста распарсить его надо чтобы знать где многострочные комментарии и прочая лабуда, но сохранять результаты разбора всего текста вовсе не обязательно. IMHO это не оптимизация даже, а просто разумный подход.
Здравствуйте, alsemm, Вы писали:
A>... VD>>Да, так не получится. Правда не потому, что структуры данных не те, а потому что алгоритм квардаричный. Уже на 10 разах получается 15 метров чистого текса в Win1251. В оперативке же это получается уже ~200 метров. Ведь там и анду-буфер, и юникод, и ОО-оверхэд и куча всего другого. A>Task manager вот что говорит: A>Rsdn.Editor: A>- 0М текста занимает 22М A>- 16М текста занимает 135М (значение плавает, max 180 сразу после загрузки файла в редактор, потом падает) A>Мое барахло: A>- 0М текста занимает 20М A>- теже 16М текста занимают 90М
90М в варианте MBCS и 106М с юникодом.
A>Т.е. в твоем редакторе на хранение одного байта текста — уходит (135-22) / 16, т.е. примерно 8байт, в моем около 5. Думаю разница из-за того, что при загрузке ты сразу текст парсишь и результаты разбора сохраняешь. Если не путаю ничего (не силен я в NET-е), то память отжирается тут:
Какой-то неправильный замер...
Для сравнения в htmlayout на один wchar_t текста расходуется ровно один wchar_t memory storage.
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, alsemm, Вы писали:
A>>Т.е. в твоем редакторе на хранение одного байта текста — уходит (135-22) / 16, т.е. примерно 8байт, в моем около 5. Думаю разница из-за того, что при загрузке ты сразу текст парсишь и результаты разбора сохраняешь. Если не путаю ничего (не силен я в NET-е), то память отжирается тут:
CS>Какой-то неправильный замер...
Зная размер загруженного в редактор документа и размер используемой редактором памяти можно оценить примерно сколько байт тратится на хранение 1 байта текста.
Вполне правильный замер?
Здравствуйте, alsemm, Вы писали:
A>Собрал и погонял редактор. Вполне шустро.
О том и речь. Я прежде чем принимать решения, которые заведомо могут сказаться на скорости работы приложения, стараюсь делать тесты.
A>Правда P4 3.2G с 1G памяти замучать непросто
Да как два байта об асфальт... если "с умом" к делу подойти.
A>Подтормаживает если удалять большие куски текста( >10000 строк), когда ресайзится окно если включен вордврап и в контроле > 20000 строк текста.
Потом погляжу под профалером. Но думаю, что столь редкие ситуации вряд ли нужно оптимизировать. Хотя возможно что-то где-то упустил.
A>Но это все вообщем-то близкие к параноидальным значения, так что можно внимание не заострять.
Вот и я о том же. А вот памяти жрется значительно больше чем следовало бы. Но это уже скорее заслуга сборщика мусора. Для его алгоритмов чем больше памяти отводится для кучь тем лучше. Хотя конечно печально. Надо бы тоже подумать над этим делом. Вот только перед этим нужно доделать функционал. А то у меня времени нет, а проект уже несколько месяцев почти в том состоянии в каком он был летом (когда я его создал).
A>Табы imho неправильно расставляет если в строке несколько шрифтов используется.
Дело не в шрифтах. Я там просто не верную логику заложил. Я сейчас пляшу от размера пробелов, а надо от позиции текста на экране. С моноширными шрифтами там все должно быть ОК, а вот с пропорциональными задница. Но так как она проявляется только при сложном колоночном форматировании, а оно рельно имеет смысл только при использовании моноширных шрифтов, то опять же я определил статус данной ошибки как низкий, т.е. откровенно на нее забил до поры.
A> Если используется немоноширинный шрифт или несколько шрифтов, то расчет ширины таба должен не от его позиции в строке зависеть, а делаться как в wordpad-е (там задаются позиции для табов в пикселях). Для этого даже спец. функция GDI есть — TabbedTextOut.
Так и есть. Это багофича. Ну, в смысле баг в проектном решении. Тормознул когда продумывал как нужно делать. Решил решить проблему на уровне текста, а это в принципе невозможно для моноширных шрифтов.
Ну, да главное, что код довольно хорошо рефакторится, так что не я так кто-нить эту проблему устранит.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, alsemm, Вы писали:
A>Task manager вот что говорит: A>Rsdn.Editor: A>- 0М текста занимает 22М A>- 16М текста занимает 135М (значение плавает, max 180 сразу после загрузки файла в редактор, потом падает) A>Мое барахло: A>- 0М текста занимает 20М A>- теже 16М текста занимают 90М
Сцинтила, если не ошибаюсь, жрет сильно меньше. Но там нет ЖЦ и Юникода.
A>Т.е. в твоем редакторе на хранение одного байта текста — уходит (135-22) / 16, т.е. примерно 8байт, в моем около 5.
Не, так считать нельзя. Тут дело не в том сколько байт уходит на хранение. Вернее, не столько. Тут дело еще и в организации хипа. ЖЦ штуковина такая, что простая матиматика с ней не проходит. Если на машине будет меньше памяти, то и расход будет меньше. Если болше, то ЖЦ заюзает столько памяти, сколько нужно для максимально шустрой работы. Это целая наука. Но в целом ЖЦ обычно расходует от 1.5 до 4 раз больше памяти.
Если хочешь поглядеть на то сколько реально потребляет памяти ОО-модель приложения, то скачай Process Explorer с www.sysinternals.com, и изучай память в управляемом хипе с его помощью. В свойствах процесса у Process Explorer есть закладка с дотнетными данными. В ней есть комбик, в ктором можно найти раскладку памяти. Вот в ней реальные данные за вычитаом накладных расходов дотнета.
Еще нужно учесть, что в следствии того, что в клипборд попадаеют большие куски данных, начинает рости и фрагментироваться LOH (хип для больших объектов). А он зараза очень редко "подметается" если памяти в системе выше крыши. У нас с тобой как раз тот случай (гиг оперативки) . Вот дотнет паскуда и жрет память.
A> Думаю разница из-за того, что при загрузке ты сразу текст парсишь и результаты разбора сохраняешь.
Рассчет простой каждая строка больше чем просто область памяти на 12 байт (8 оверхэд ЖЦ + 4 размер массива). Кроме того какая-то память уходит на хранение самих объектов строк. Опять же + 8 байтов. Далее можно умножить количество строк на 12 и размер объекта описателя. Не думаю, что этот объем будет в 20 раз больше превышать размер файла. Даже если это будет 100% от размера файла, то все остально это уже не мое. Хотя еще конечно Юникод, Ведь в нем строка сразу в два раза больше становится. Так что плесать нужно от 30 метров. А при этом оверхэд уже 10 раз, что хотя и многовато, но все же менее пугающе, чем 20.
A>...Понятно что при начальной загрузке текста распарсить его надо чтобы знать где многострочные комментарии и прочая лабуда, но сохранять результаты разбора всего текста вовсе не обязательно. IMHO это не оптимизация даже, а просто разумный подход.
Это именно что оптимизация. Причем рассчитанная на некоторую эвристику. Например, если объем изменений файла будет большим, то опримизация прерватится в писимизацию, или прийдется написать еще гору кода.
Как почти любая оптимизация, эта требует дополнительных усилий по кодированию и приводит к некрасиым дизайнерским решениям. А ведь в раельных условиях проблем то нет. Этот контрол будет жить внутри дургих дотнетных приложений и память для него будет разделяться с памятью для всего процесса. Плюс вероятность того, что кто-то станет редактировать в нем огромные файлы крайне мала. Это все же редактор кода/контента. Так зачем ломать копья до того как это действительно потребуется?
В общем, это уже отдельная тема не раз поднимавшася в "Философии программирования" на этом сайте. И развивать ее здесь смысла нет. Я и сам не против эффективного кода и оптимизаций, но только там где это надо и не в ущерб качеству кода (легкости изменения и развития).
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
... VD>Дело не в шрифтах. Я там просто не верную логику заложил. Я сейчас пляшу от размера пробелов, а надо от позиции текста на экране. С моноширными шрифтами там все должно быть ОК, а вот с пропорциональными задница. Но так как она проявляется только при сложном колоночном форматировании, а оно рельно имеет смысл только при использовании моноширных шрифтов, то опять же я определил статус данной ошибки как низкий, т.е. откровенно на нее забил до поры.
В баглисте что в svn лежит такой известной ошибки вроде нет, вот я и написал.
...
VD>Ну, да главное, что код довольно хорошо рефакторится, так что не я так кто-нить эту проблему устранит.
Это тебе так кажется, т.к. ты этот код писал. Надо мнение постороннего человека чтоб оценить насколько там все рефакторится
Здравствуйте, VladD2, Вы писали:
VD>Вот и я о том же. А вот памяти жрется значительно больше чем следовало бы. Но это уже скорее заслуга сборщика мусора. Для его алгоритмов чем больше памяти отводится для кучь тем лучше.
Мне вот показалось, что реализация примитивных операций редактирования типа удаления текста довольно накладно в плане расхода памяти. Взять например тот же Document.InternalDelete:
При этом при вызове Rows.RemoveRange может происходить еще и копирование внутреннего массива коллекции Rows. Т.е. представим, я удалил символ — и "в ответ" выполняется эта гора кода. Я допускаю, что на производительность это может и несильно сказывается, но вот на память.. Это учитывая, что в принципе удаление символа может быть реализовано просто как удаление символа.
Может я чего и не понял, но мне показалось, что удаление строк и удаление символов в строке — это у тебя однообразные операции.. Вот возникает вопрос почему... Ты же используешь для хранения строк коллекцию, а не string[] с фиксированной длиной, изменение которого предполагает перевыделение памяти.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Может я чего и не понял, но мне показалось, что удаление строк и удаление символов в строке — это у тебя однообразные операции.. Вот возникает вопрос почему... Ты же используешь для хранения строк коллекцию, а не string[] с фиксированной длиной, изменение которого предполагает перевыделение памяти.
А какая разница что удалять, если эту подстроку один фиг класть в анду-буфер? В общем, все там обычно сделано. Оверхэд конечно есть, но без него никуд. Программирование без абстракций — это рыдание. А экономить нужно на том, чтобы объеденять соседние команды редактирования. Что и делается. Временные же объекты один фиг мусорщик подберет.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>А какая разница что удалять, если эту подстроку один фиг класть в анду-буфер? В общем, все там обычно сделано. Оверхэд конечно есть, но без него никуд. Программирование без абстракций — это рыдание. А экономить нужно на том, чтобы объеденять соседние команды редактирования. Что и делается. Временные же объекты один фиг мусорщик подберет.
Собственно, я не понимаю почему бы gap buffer здесь не использовать. Аналогия такая же, что обычный массив и коллекция которая как бы особым образом с этим массивом работает благодаря чему не приходится на каждое удаление/добавление память под массив перевыделять. И я не вижу чем это мешает реализации Undo-stack (если есть какие-то реальные проблемы, объясни плиз). Просто мне кажется, что реализация атомарных операций должна быть более экономной. Временные объекты мусорщик конечно подберет, вопрос когда он их подберет и зачем их создавать если без этого можно обойтись. Почему бы вместо модицикации по принципу:
P.S. Касательно абстракций — забавная фигня получается... Почему-то с АПИ сцинтиллы — по кр. мере мне так показалось на первый взгляд — разбираться несколько проще чем с твоими объектами. А ведь там по-большому счету все просто в кучу свалено..
В общем мое скромное мнение на настоящий момент — то, что ты немножко с абстракциями переборщил. Прошу не кидать тухлыми помидорами...
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Собственно, я не понимаю почему бы gap buffer здесь не использовать.
Я незнаю что такое "gap buffer".
ВВ> Аналогия такая же, что обычный массив и коллекция которая как бы особым образом с этим массивом работает благодаря чему не приходится на каждое удаление/добавление память под массив перевыделять. И я не вижу чем это мешает реализации Undo-stack (если есть какие-то реальные проблемы, объясни плиз).
Дык нет проблем. В том числе с каким то сильно непродуктивным расходованием памяти. Для в анду-буфере хранятся команды обратные той что произвела редактирование. Классика, блин, ОО-дизайна. Команад сореджит информацию требуемую для востановаления редактора в том состоянии в каком он был до редактирования. Идущие друг за другом одинаковые команды укрупняются.
В общем, нет тут пролем. Ну, нету.
Откровенно говоря даже жаль тратить время на пустопорожнии рассуждения.
ВВ> Просто мне кажется, что реализация атомарных операций должна быть более экономной.
С какой целью? Что есть какие-то проблемы при вводе? Зачем заниматься привентивными оптимизациями темболее когда уже видно что они ровным счетом ничего не дадут?
ВВ> Временные объекты мусорщик конечно подберет, вопрос когда он их подберет
Это не вопрос. Временные объекты ничего не стоят. Это пустое место. Нет разницы 1 временный объект создается или 1000. Если они умирают до сборки мусора, то начхать на них и забыть.
ВВ> и зачем их создавать если без этого можно обойтись.
Есть такое понятие — дизайн приложения. Конечно все что угодно можно написать на уровне битов. Но на практике мало мальски серьезные приложения просто нвозможно создать без применения выскоуровневых абстракций.
Тот же механизм редактирования с поддержкой анду/реду является довольно сложным алгоритмом затрагивающим множество программных сущьностей. Для его упрощения был придуман паттерн команада и еще куца разной лабуды. Они делают реализацию в меру сложной (доступной для понимания простым смертным вроде меня). Без них же это просто не подемная задача. По крайней мере я не хочу решать эту задачу без них. Я вообще не получаю удовольствие от возни на уровне битов.
ВВ> Почему бы вместо модицикации по принципу:
ВВ>
Потому что нафиг не нужно. Есть абстракция строка и ее модификация. В процессе модификации строки мы получаем другую. Заниматься ловлей блох ради совсем не ясного результата у меня желания нет.
Ты лучше ответь на простой вопрос. При вводе текста ты ощущашь какой-то дискомфорт? Или видишь какой-то странный раост используемой памяти?
Если на оба вопроса ответ — нет, то можно смело закрыть этот разговор.
Если уж тебе хочетс порассуждать об экономии памяти, то надо обратить внимание на откровенно халявное считвание файлов с диска. Сейчас я их просто глатаю в память цликом. Это вызвает, для больших файлов, кратковременный заем довольно больших блоков памяти. Вот это действительно плохо, так как большие блоки памяти в дотнете управляются крайне не красиво. Они живут в LOH и освобождаются только при сборке мусора второго поколения.
Вот в таких местах можно наэкономить немало памяти. Только опять же стоит подумать "а стоти ли?". Ведь пока памяти в системе много, это не играет никакого значения. А если ее станет мало, то произойдет сборка второго поколения и ненужная память будет возвращена в систему.
ВВ>P.S. Касательно абстракций — забавная фигня получается... Почему-то с АПИ сцинтиллы — по кр. мере мне так показалось на первый взгляд — разбираться несколько проще чем с твоими объектами. А ведь там по-большому счету все просто в кучу свалено..
Я незнаю почему с API Сцинтилы лично тебе разбираться проще. По-моему, Сцинтила — это тиъий ужас. Все "API" редактирвоания текста документа в Rsdn.Editor сводится к:
Что тут вообще нужно изучать? По-моему, все и без объяснения понятно. А учитывая, что каждый метод/свойство еще собжены коментариями...
Теперь о реализации. В Сцинтиле, точно так же как и в Rsdn.Editor применяются команды редактирования. Разница только в том, что у меня разные типы команд описываются разными классами и составляют иерархию:
в которой все конкретные (не абстрактные) классы реализуют ICommand.
А в Сцинтиле все запихнуто в один класс и только во время исполнения можно понять с каким типом команды ты имешь дело.
Причем в следствии того, что все запихнуто в один класс команды хранят избыточную информацию, а код работы с командами запутан.
Сдается мне, что ты просто не разобрался в том как устроена Сцинтила, раз говоришь такие вщеи.
ВВ>В общем мое скромное мнение на настоящий момент — то, что ты немножко с абстракциями переборщил. Прошу не кидать тухлыми помидорами...
Никто в тебя кидаться ничем не собирается. Но по-моему ты не прав. Абстракции — это очень нужная вещь. Ее правильный выбор практически обуславливает успех или провал того или иного проекта. В данном случае я использовал старую проверенную абстракцию. Просто ты не привык видеть дакое вольное обращение. Тебе страшно видеть объект там, где казалось бы можно обойтись манипуляцией над массивами. Но на самом деле тут проблем нет.
Вот в создании лексера действительно на первый план выходит скорость и эффективность. Там действительно есть простор над битовыми манипуляциями. Можешь попытаться применить свои силы на этом поприще. Увидишь, что это отнюдь не так просто.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
ВВ>>Собственно, я не понимаю почему бы gap buffer здесь не использовать. VD>Я незнаю что такое "gap buffer".
Какое-то описание есть здесь. Причем это не самый эффективный алгоритм.
VD>С какой целью? Что есть какие-то проблемы при вводе? Зачем заниматься привентивными оптимизациями темболее когда уже видно что они ровным счетом ничего не дадут?
Меня тут больше потребление памяти занимает, чем скорость. Скорость по большому счету упирается в отрисовку.
ВВ>> и зачем их создавать если без этого можно обойтись. VD>Есть такое понятие — дизайн приложения. Конечно все что угодно можно написать на уровне битов. Но на практике мало мальски серьезные приложения просто нвозможно создать без применения выскоуровневых абстракций.
Не, ну я же не против абстракций. Собственно, я вообще не пытаюсь критиковать реализацию. Я пытаюсь разобраться. И мне всегда казалось, что дизайн приложения не "измеряется" количеством созданных классов.
На самом деле я пытался въехать во что упирается работа с текстами только по-строчно. Ведь все-таки string[] — это скорее _представление_ данных, чем сам данные.
VD>Тот же механизм редактирования с поддержкой анду/реду является довольно сложным алгоритмом затрагивающим множество программных сущьностей. Для его упрощения был придуман паттерн команада и еще куца разной лабуды. Они делают реализацию в меру сложной (доступной для понимания простым смертным вроде меня). Без них же это просто не подемная задача. По крайней мере я не хочу решать эту задачу без них. Я вообще не получаю удовольствие от возни на уровне битов.
Да, согласен. Но чем gap buffer этому мешает?
VD>Ты лучше ответь на простой вопрос. При вводе текста ты ощущашь какой-то дискомфорт? Или видишь какой-то странный раост используемой памяти?
Скорость хорошая. Но она по большому счету не от этого зависит. Что такое создание одного объекта для перфоманса, даже тысячи.. А память
Как я уже говорил, я собственно не критикую и даже не предлагаю что-то переделывать. Мне интересно почему были выбраны именно такие решения при реализации, а не другие. В чем их бенефит и пр. И вот это мне пока непонятно.
VD>Что тут вообще нужно изучать? По-моему, все и без объяснения понятно. А учитывая, что каждый метод/свойство еще собжены коментариями...
Ну имелась в виду "полная" работа с редактором, включая работу со стилями и пр.
VD>Теперь о реализации. В Сцинтиле, точно так же как и в Rsdn.Editor применяются команды редактирования. Разница только в том, что у меня разные типы команд описываются разными классами и составляют иерархию: VD>
VD>в которой все конкретные (не абстрактные) классы реализуют ICommand.
А зачем?
VD>А в Сцинтиле все запихнуто в один класс и только во время исполнения можно понять с каким типом команды ты имешь дело. VD>Причем в следствии того, что все запихнуто в один класс команды хранят избыточную информацию, а код работы с командами запутан. VD>Сдается мне, что ты просто не разобрался в том как устроена Сцинтила, раз говоришь такие вщеи.
Да, да, ты прав. В сцинтилле все ужасно. Тем не мнее когда потребовалось сделать для нее раскраску нужно было разобраться со следующими функциями, что заняло минут 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
А вот в работу со стилями у тебя я до сих пор до конца не въеду. Почем бы это? Может, последствия "тяжелых" новогодних праздников?
VD>Вот в создании лексера действительно на первый план выходит скорость и эффективность. Там действительно есть простор над битовыми манипуляциями. Можешь попытаться применить свои силы на этом поприще. Увидишь, что это отнюдь не так просто.
Прежде чем писать лексер хотелось бы разобраться в том, что на настоящий момент является "черновой" реализацией и может быть изменено в будущем, а что как бы уже "доделано". Все-таки согласись писать лексер который парсит текст по-строчно и который парсит любые фрагменты — немножко разные вещи.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Какое-то описание есть здесь. Причем это не самый эффективный алгоритм.
Сдается мне, что это в данном случае не причем.
ВВ>Меня тут больше потребление памяти занимает, чем скорость.
А что память? Ну, занялось на долю секунды пару сотен байт (ну, максимум пару килобайт) памяти. И что? При следующей сборке мусора она будет считаться свободной. Вся эта память занимается в нулевом поколении ЖЦ. Объем мелкий, так что на размер нулевого поколения это повлиять не может. Так что память тут жалеть нечего.
Ну, а с глобальной точки зреня память хранится довольно оптимально. Команда хранит только то, что нужно. Конечно есть небольшой оврехэд от ЖЦ, но это все фигня.
ВВ> Скорость по большому счету упирается в отрисовку.
Редактирования? Да.
ВВ>Не, ну я же не против абстракций. Собственно, я вообще не пытаюсь критиковать реализацию.
Кстати, я не против критики. Я очень за критику. Я только хочу, чтобы она была продуманной. А то у меня со временем ужасные проблемы и не хочется убивать его на борьбу с догмами. (не обижайся) По этому если ты показывашь пальцем и говоришь, "вот это мне кажется странным (глупым, непродуманным...)", я постораюсь разобраться и подумать не ошибся ли я. Возможно даже переосмыслить свою позицию. Но когда все сводится к "ой, а не много ли тут памяти расходуется", то разговаривать то в общем-то не очем. Ведь предмет разговора не определен.
ВВ>Я пытаюсь разобраться. И мне всегда казалось, что дизайн приложения не "измеряется" количеством созданных классов.
Естественно. Так же как архитектура не определяется количеством кирпичей/панелей в здании. Но здание строится из кирпичей/панелей и если мы начнем рассуждать о строительстве здания на уровне количества арматурин или песка в расстворе, то смысла в обсуждении не останется.
ВВ>На самом деле я пытался въехать во что упирается работа с текстами только по-строчно. Ведь все-таки 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>>в которой все конкретные (не абстрактные) классы реализуют 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:
Не скзал бы что интерйес уж настолько сложнее .
AppliedStyle — это структура содержащая ссялку на стиль, позицию в строке на которой начинается применение стиля и длинну на которую простирается действие стиля. То есть практически как в Сцинтиле. Разница тольк в том, что в Сцинтиле ты вынужден задавать стили битовой маской. Причем если стили пересекаются ты вынужден сам думать как это разрулить. У меня же ты просто задашь список стилей которые могут перескаться как угодно. Я сам "сложу" пересекшиеся стили когда будет надо.
При этом исчезает еще одна проблема. Ты не ограничен количеством битов отводимых на стиль. Размер массива может быть любым! А вот в Сцинтиле, если мне не изменяет память, количество стилей не дожно привышать 16.
ВВ>Прежде чем писать лексер хотелось бы разобраться в том, что на настоящий момент является "черновой" реализацией и может быть изменено в будущем, а что как бы уже "доделано". Все-таки согласись писать лексер который парсит текст по-строчно и который парсит любые фрагменты — немножко разные вещи.
Черновой реализацией скорее всего являются:
1. Стили.
2. Форматер.
3. Применяемый лексер.
Лексер пойдет в помойку полностью. Но его интерфейс должен быть похож.
Форматер нужно отрефакторить так, чтобы для изменения, к примеру, лексера не нужно было переопределять весь класс форматера.
Стили... С ними сложнее. С одной стороны идеологически они уже вряд ли изменяться. Но я сделал одну промашку. Для эффективного наложения стилей нужно сделать так, чтобы можно было отдельно задавать такие атрибуты шрифта как жирный, наклонный, размер, гарнитура. Короче, все . Нужно это потому, что при более менее сложной подсветке нужно сочетать разные стили. А если я не могу сложить атрибуты шрифта, то мне нужно создавать пересечение стилей на все случаи жизни. Например, если я хочу реализовать форматирования для Януса, то мне нужно сделать так, чтобы внутри кода было возможно выделить отдельные слова жирным. Но как это сделать если код сам по себе содержит тучу стилей? На сегодня я вынужден создать пересечение для всех стилей! А это при некоторых условиях приводит к комбинаторному взрыву. Вывод — нужно изобретать свой шрифт, а рельный собирать по частям непосредственно перед отрисовкой.
В ты говришь на строках экономить.
В общем, от лексера, точнее стайлера нужно следующее:
1. Он должен уметь начать сканирование исходя из передаваемого в него идентификатора состояния. Состояние на сегодня это просто целочисленная константа, но это не догма.
2. Он обязан сканировать код построчно.
3. Он должен в конце сканирования строки возвращать идентификатор состояния. Именно его ему же передадут при сканировании следующий строки.
4. Единственная задача стайлера — заполнить массив AppliedStyles и присвоить его в одноименное свойство строки документа (которую ему передадут через параметр).
Собственно, все! Далее я уже сам прикручу его куда надо.
Основная проблема стайлера на сегодня это скорость. Это одино из самых узких мест в редакторе. Он вызвается в самых вложенных циклах. Так что вот он то должен создавать минимум временных объектов (использовать их повторно если нужно) и быть максимально быстрым.
Понятно, что для одного-двух язяков не трудно написать стайлер вручную. В Сцинтиле все стайлеры написаны вручную (Уроды, блин! Правда есть редакторы на базе Сцинтилы которые имеют свои генераторы стайлеров.). Но мы не индусы . Так что нужно сделать генератор лексеров. Самый простой и продуктивный путь — это выдрать генератор детерминированного конечного автомата (ДКА) из CocoR и изменив в нем кодогенерацию порождать эффективные стайлеры вместо CocoR-ного Scanner-а.
Можно конечно просто пользоваться генерируемым Scanner-ном и создавать AppliedStyles на основании токенов, но это довольно не эффективно. Ведь для стайлера неважно даже какое ключевое словно найдено. Важно только его местоположение. А уж создавать экземпляры строк — это точно лишнее. Достаточно типа токена.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
ВВ>>Какое-то описание есть здесь. Причем это не самый эффективный алгоритм. VD>Сдается мне, что это в данном случае не причем.
Честно, не хочется тут особо спорить. Помнится, есть более подробное описание этого в Inside SharpDevelop. Была еще и интересная книжка про написание текстовых редакторов, но ссылки не приведу, так как ее надо искать, а интернет мне на работе урезали за то, что порнуху качал.
ВВ>> Скорость по большому счету упирается в отрисовку. VD>Редактирования? Да.
Кстати, а почему для рисования TextRenderer не используется? Или он совсем недавно появился.
ВВ>>Не, ну я же не против абстракций. Собственно, я вообще не пытаюсь критиковать реализацию. VD>Кстати, я не против критики. Я очень за критику. Я только хочу, чтобы она была продуманной. А то у меня со временем ужасные проблемы и не хочется убивать его на борьбу с догмами. (не обижайся) По этому если ты показывашь пальцем и говоришь, "вот это мне кажется странным (глупым, непродуманным...)", я постораюсь разобраться и подумать не ошибся ли я. Возможно даже переосмыслить свою позицию. Но когда все сводится к "ой, а не много ли тут памяти расходуется", то разговаривать то в общем-то не очем. Ведь предмет разговора не определен.
ОК, я понял. "Мне кажется странным, что работа со строками ведется в лоб, а не используется какой-либо из алгоритмов, позволяющих эффективно модифицировать строки". Так лучше?
ВВ>>На самом деле я пытался въехать во что упирается работа с текстами только по-строчно. Ведь все-таки string[] — это скорее _представление_ данных, чем сам данные. VD>Во что упирается? Одна строка занимается, другая освобождается. В итоге мы имеем новую строку у которой длинна больше или меньше. ЖЦ при одной из сборок мусора сожмет кучу и мы заплатим только за разницу. Ну, еще мы заплатим временем затрачиваемым ЖЦ на работу. Но ведь мы уже оговорились, что со скорость воде как проблем нет? Далее какое-то место уйдет на хранение команы. Но это плата за возможность сделать анду и реду. Все промежуточные команды опять же подберет ЖЦ. Если вдруг когда-то хранение команд покажется черезчур накладным, то можно сбросить их на диск. Но вот пока что мне не кажется это нужным (хотя думл я об этом еще когда проектирваол редактирование).
Я немножко не об этом. Бог с ней, с памятью. Непонятно почему данные хранятся как, фактически, массив строк. Это было наиболее удобным/эффективным решением? Потому что на настоящий момент по крайней мере одна проблема имеет место быть — то вся работа со стилями как бы "привинчена" к конкретной строке, что несколько неудобно.
ВВ>>Да, согласен. Но чем gap buffer этому мешает? VD>А что он мне даст? Читаем из твой ссылки: VD>
A gap buffer is a structure that models a string but allows relatively
VD>efficient insertion of text somewhere in the middle.
VD>У меня есть проблемы с производительностью? А вот на это efficient прийдется потратить память чтобы организовать дыры.
Ну ты же не жалуешься на то, что List<String> жрет слишком много памяти по сравнению со string[]. А здесь аналогия примерно такая же — да, мы оставляем дыры, тратим лишние байты, но благодаря этому можем делать in-place модицирование строки. Впрочем, достаточно об этом.
Кстати, неплохой идеей было бы как-то выделить низкоуровевый код отвечающий за работу с текстом — как это сделано в шарп-девелопе например. Чтобы при желании, если например потребуется сделать на основе Rsdn.Editor редактор для сверх-больших файлов можно было бы легко и просто это участок поменять.
VD>Ты забавно мыслиь. Ты пыташся сравнить работу редактора с (внимание!) интерфейсом Сцинтилы. Если сравнить интерфейсы (а у Сцинтилы он весь из сообщений в стиле Виндовс стостит), то уверяю тебя интерфейс Rsdn.Editor куда проще. От част просто потому, что в нем не реализованы некторые вещи , а от части потому, что нет необходимости возиться с низкоуровневыми абстракциями вроде char* и т.п.
А чем отличается "низкоуровневая абстракция" char* от System.String? Наличием звездочки?
VD>Например, я сразу ввел понятие координат и пересчеты между координатами представления и документа. В синтиле этого нет. Казалось бы проще... Но на самом деле разбираясь все время надо понимать, что вот этот int — это номер строки в документе, а вот этот — это в колонка в представлении. В итоге сам черт ноку сломит. То же самео с командами. Команда Сцинтилы — это один класс с кучей полей которые используются в разных случаях. У меня это набор классов которые используются только там где это возможно и нужно. Причем для большинства классов у меня есть визуализация в отладчике. В то же время в сцинтиле одно преобразование из/в utf-8 и хнанение байтов стилей вперемешку с байтами текста так шифрует суть происходящего, что даже строку то увидеть не возможно. Я же четко отделил стили от текста и храню его в виде простой строки.
Дык или я чего-то не понял или текст у тебя все-таки хранится не в виде простой строки, а виде _массива_ строк.
VD>>>Теперь о реализации. В Сцинтиле, точно так же как и в Rsdn.Editor применяются команды редактирования. Разница только в том, что у меня разные типы команд описываются разными классами и составляют иерархию: VD>>>
VD>>>в которой все конкретные (не абстрактные) классы реализуют ICommand.
ВВ>>А зачем?
VD>Затем, чтобы видеть что рельно происходит. Чтобы отделять действия по вставке от действий по редактированию. Прицип действия моей команды очень прост. У любой команды можно вызвать ICommand.Execut() и ты получишь а) действия связанные с командой, б) команду обратную этой (то есть если выполнить полученную команду, то действия предыдущей будут откачены). А почему иерархия классов, а не "все в одном"? Дык между прочим InsertCommand должна хранить текст, DeleteCommand только координаты удаляемого текста, а UpdateCommand и то и другое. Ну, а MultiCommand вообще должны хранить список команд которые нужно выполнить как единую транзацию. Так что это Сцинтила расходует память по напрасну, так как ее команда всегда хранит "все что нужно".
VD>Но делал иерархию я не для этого. Иерархия позволяет хорошо отделить логику. В купе с наследованием реализации, которое позволяет выделить общую логику, это позволяет очень хоршо отделить разную логику друг от дурга и в то же время получить отличную абстаркцию.
А как будет в дальнейшем расширяться функциональность? Т.е. ты описал через эти интерфейсы примитивные операции, но помимо них будут и другие — да и собственно уже есть. Почему нет, например, SetStyleCommand?
Я не против идеи, идея интересная, но зачем 1) выставлять наружу детали реализации (всякие InsertCommand, DeleteCommand и пр.); это должны ИМХО быть частные реализации, иначе представь как загрузится АПИ если "дописать" сюда команд, 2) такой подход должен быть наверное общим, а не избирательным, а то придется смотреть что реализовано в качестве команды, а что в качестве DocumentRow.XXX.
VD>Возможно отчасти потому, что Сцинтиле много лет и всей все уже отработно, а у меня работа со стилями откровенно не доделана.
Вот, было подозрение И наверное лучше сначала доделать.
VD>При этом исчезает еще одна проблема. Ты не ограничен количеством битов отводимых на стиль. Размер массива может быть любым! А вот в Сцинтиле, если мне не изменяет память, количество стилей не дожно привышать 16.
Да ладно уж, там почти целый байт. Кстати int32 мне тоже кажется небольшим излишеством — хотелось бы например иметь возможность ассоциировать какую-нибудь custom-информацию со строкой (например, для browser-like навигации по тексту), не отводить же для этого очередной Int?
VD>Понятно, что для одного-двух язяков не трудно написать стайлер вручную. В Сцинтиле все стайлеры написаны вручную (Уроды, блин! Правда есть редакторы на базе Сцинтилы которые имеют свои генераторы стайлеров.). Но мы не индусы . Так что нужно сделать генератор лексеров. Самый простой и продуктивный путь — это выдрать генератор детерминированного конечного автомата (ДКА) из CocoR и изменив в нем кодогенерацию порождать эффективные стайлеры вместо CocoR-ного Scanner-а.
А как ты себе представляешь генератор лексеров? Т.е. чего он будет генерировать и на основе чего? У меня кстати была в свое время сделать что-то типа враппера над кокой — т.е. на основе простой описанной в ХМЛ грамматике генерится EBNF-грамматика для коки, на основе которой уже динамически собирается парсер. Однако особых бенефитов такой способ не даст, а гемора будет не много.
Мне казалось, что не так сложно написать ручками лексер который подойдет для большинства грамматик, а если уж будет что-то слишком навороченное, то можно и свой написать, так как всевозможные случаи все равно не предусмотришь.
Кстати, забыл спросить. А как сделать так, что лексер не создавал строк? Надо ведь как-то проверить что разобранный идентификатор является ключевым словом.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Кстати, а почему для рисования TextRenderer не используется? Или он совсем недавно появился.
Потому что совсем тормозной. Они там на каждый чих шрифт пересоздают, а это очень не дешовое удовольствие. Примерно такое же как отрисовать строку. А так как они и при рассчетах его создают каждый раз, то получается вообще задница.
ВВ>ОК, я понял. "Мне кажется странным, что работа со строками ведется в лоб, а не используется какой-либо из алгоритмов, позволяющих эффективно модифицировать строки". Так лучше?
Э какой хитрый. Ты мне статистику хотя бы приведи. Мол воодил текст минуту... исчерпалась вся память в системе. Или еще что. А так "кажется". Мне вообще страшно смореть как целые объекты — команды на каждое нажатие кнопки создаются и уничтожаются пачками. Но я же креплюсь?!
ВВ>Я немножко не об этом. Бог с ней, с памятью. Непонятно почему данные хранятся как, фактически, массив строк. Это было наиболее удобным/эффективным решением?
По-моему — да. Кстати, в Сцинтиле сделано (вроде бы) точно так же.
ВВ> Потому что на настоящий момент по крайней мере одна проблема имеет место быть — то вся работа со стилями как бы "привинчена" к конкретной строке, что несколько неудобно.
Дык, это принципиальное дизайнерское решение. Именно это позволяет редактировать 15-ти мегабайтный файл и усом не вести.
Ведь сдвижка байтов в огромном файле это не единственная проблема. (Хотя и актуальная. Создай, например, 15 метровый файл и попробуй оредактировать его с конца в Нотпэде) Главная проблема — это подсветка! Если ее делать для всего файла, то каюк. Уже на 300 килобайтном файле начинаются тормоза. А метровый становится вообще не подъемным. Надо вводить какой-то квант. Строка, точнее параграф (строки могут переноситься) как раз и является оптимальным выбором. Сюдаже прекрасно вписывается и то, что отрисовка идет по строкам. Да и прокрутка.
Как еще, по-твоему, организовать работу в реальном времени с большими файлами?
ВВ>Ну ты же не жалуешься на то, что List<String> жрет слишком много памяти по сравнению со string[].
Не жалуюсь, так как это копейки. Он хранит ссылки. И его оверхэд в худщем случае 2х. По сравнению с объемом памяти затрачиваемым на строки — это фигня. Зато он дает молниеносное изменение размера массива.
ВВ> А здесь аналогия примерно такая же — да, мы оставляем дыры, тратим лишние байты, но благодаря этому можем делать in-place модицирование строки. Впрочем, достаточно об этом.
Хм. Объем байтов будет куда больше чем то что я потрачу на этот самый массив и оверхэд на отдельные строки. И реализация сильно усложится. А вот оверхэд на строки таки останется, так как один фиг нам хранить информацию о строках.
ВВ>Кстати, неплохой идеей было бы как-то выделить низкоуровевый код отвечающий за работу с текстом — как это сделано в шарп-девелопе например. Чтобы при желании, если например потребуется сделать на основе Rsdn.Editor редактор для сверх-больших файлов можно было бы легко и просто это участок поменять.
Возможно. Такой задачи не стаяло. А я тоже не фанат бессмысленных абстракций. Я упаваю на то, что код легко рефакторится и если что выделим все что нужно.
ВВ>А чем отличается "низкоуровневая абстракция" char* от System.String? Наличием звездочки?
Хотя бы тем, что она безопасна и содержит информацию о длинне. Мне не нужно таскать за собой еще int с ее размером и везде расставлять провекри не вышел ли я за пределы. Но я то говорил не о string. Я то говорил о такой абстракции как строка документа или строка представления. Я, в отличии от разработчиков Сцинтилы, чаще манипулирую объектами, а не низкоуровневыми абстракциями.
ВВ>Дык или я чего-то не понял или текст у тебя все-таки хранится не в виде простой строки, а виде _массива_ строк.
Массива, массива. И не строк в смысле string, а строк в смысле DocumentRow. И по-моеуму — это правильно.
ВВ>А как будет в дальнейшем расширяться функциональность? Т.е. ты описал через эти интерфейсы примитивные операции, но помимо них будут и другие — да и собственно уже есть.
Вообще-то для текстового редактор команд редактирования достаточно. И развивать их реально вряд ли потребуется. Но если что... классы я не запечатывал. Так что создаешь наследника и вперед... с песней.
ВВ>Почему нет, например, SetStyleCommand?
Потму что это не операция редактирования. Стили зависят от текста. Так что их бессмысленно назначать в произвольное время. Их нужно обновлять для обновленного участка текста. Квантом обновления является строка. Так что вместо SetStyleCommand есть интерфейс IFormatter, а в нем методы:
void StartParse(DocumentRowCollection rows, int startState);
void PrepareRows(DocumentRowCollection rows);
int PrepareRows(DocumentRowCollection rows, int start, int count);
На сегодня ты должен реализовать их и в них уже разначить нужные стили, для заданных строк. В будущем выделим стайлер в отдельный класс и вообще сделаем генерируемым. Ну, а для ручной "подстройки" сделаем события.
ВВ>Я не против идеи, идея интересная, но зачем 1) выставлять наружу детали реализации (всякие InsertCommand, DeleteCommand и пр.);
Начем с того, что их не обязательно использовать. Более того их вовсе не нужно использовать если ты не решил залудить что-то уж особо ядреное. Я тебе привед список методов которых вполне достаточно для того втобы как угодно надругаться над документом. Кстати, код контроллера и представления вообще не обращается к методам работающим с ICommand.
А закончим тем, что ICommand и все кто его реализуют вполне законченные классы и их без проблем можно использовать для расширения рботы редактора. Например, их можно использовать для записи и воспроизведения макросов. Они леко сереализуются... в общем это внутренняя механика досупная во вне, но не обязательная для использования. Ты же не докапывашся до тех кто делал Ворд, что они выставляют его объектную модель в скрипты?
ВВ> это должны ИМХО быть частные реализации, иначе представь как загрузится АПИ если "дописать" сюда команд,
Хм. В интерфейсе документа есть ровно один метод для явной работы с командами это:
void ExecuteCommand(ICommand command);
Конечно можно и его скрыть, но имеет ли это смысл? Не уж то от этого интерфейс так упростится? А сами классы команд можно просто не описывать.
ВВ> 2) такой подход должен быть наверное общим, а не избирательным, а то придется смотреть что реализовано в качестве команды, а что в качестве DocumentRow.XXX.
Не понял. А что не общего в подходе? Это паттерн. Паттерн примененный по его прямому назначению. Неужели это не очевидно?
VD>>Возможно отчасти потому, что Сцинтиле много лет и всей все уже отработно, а у меня работа со стилями откровенно не доделана.
ВВ>Вот, было подозрение И наверное лучше сначала доделать.
Точно лучше. Но вот мы, видишь, заняты... беседуем.
VD>>При этом исчезает еще одна проблема. Ты не ограничен количеством битов отводимых на стиль. Размер массива может быть любым! А вот в Сцинтиле, если мне не изменяет память, количество стилей не дожно привышать 16.
ВВ>Да ладно уж, там почти целый байт.
Почти! Байт — это 8 бит. То есть еще меньше.
ВВ> Кстати int32 мне тоже кажется небольшим излишеством — хотелось бы например иметь возможность ассоциировать какую-нибудь custom-информацию со строкой (например, для browser-like навигации по тексту), не отводить же для этого очередной Int?
Я сначала сделал тип состояния обобщенным (задаваемым параметром у класса), но быстро отказался от этой идеи, так как стайлеры могут быть разные и они могут меняться для одного и того же документа в реальном времени. Опять же в качестве примера приведу формат сообщения януса. В нем могут быть волженные участки на разных языках. Каждый из них хранит свой набор стилей и свои состояния.
К тому же состояние не связаны с твоей информацией. Это состояние стайлера. А ты хочешь расширять понятие строка. Это и так легко делается. Делай наследника от DocumentRow или от любой другой и без проблем добавляй свои поля. Хотя для навигации наверно это не нужно. Тебе ведь только подсветку нужно менять, а вся информация должны быть в тексте. Вот и добывай ее от туда.
ВВ>А как ты себе представляешь генератор лексеров?
Легко. На входе лексическая грамматика описывающая правила и некая таблица соотвествия между правилами и стилями редактора. На выходе код класса который парсит строки и выдает заполненный массив AppliedStyle-ей.
ВВ> Т.е. чего он будет генерировать и на основе чего?
Как и сейчас на основе грамматики заданной в виде декста. Ну, плюс описание стилей и ассоциация стилей с правилами.
ВВ> У меня кстати была в свое время сделать что-то типа враппера над кокой — т.е. на основе простой описанной в ХМЛ грамматике генерится EBNF-грамматика для коки, на основе которой уже динамически собирается парсер. Однако особых бенефитов такой способ не даст, а гемора будет не много.
А зачем здесь ХМЛ? Описание грамматики вообще не задача. Если бы дело было только в ней, то я бы давно ее действительно на ХМЛ-е сбацал. Сложность в создании эффективного ДКА лексера по этой грамматике. Коко это делает очень неплохо. Тлько она генерирует не то. Она генериует класс Scanner который конечно удобен для скармливания его парсерам, но для стайлера это перебор. К тмоу же у него есть еще один недостаток. В том виде в котором он существует сейча он не пригоден для использования в стайлере. Ведь генерируемый им код хранит состояние хотя и в виде целого числа, но не там. Это число хранится в виде локальной переменной, а надо, чтобы егом ожно было задавать и считывать извне. К тому же он не чесно обрабатывает комментарии. Он их просто пропускает (впесте с вложенными концами строк), а надо их чесно парсить как часть грамматики. Это позволит останавливать парсинг в конце строки даже если она находится внтри многострочного комментария или строки.
ВВ>Мне казалось, что не так сложно написать ручками лексер который подойдет для большинства грамматик, а если уж будет что-то слишком навороченное, то можно и свой написать, так как всевозможные случаи все равно не предусмотришь.
Хм. Для каждой грамматики лексер будет свой. Или он будет не очень эффективным. Можно конечно попробовать создать некий вариант который позволяет менять тип строк, список ключевых строк, коментариев и т.п. Но это будет не самое эффективное решение и уж точно не самое гибкое. Уж если делать, то делать так чтобы было приятно посмотреть.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Кстати, забыл спросить. А как сделать так, что лексер не создавал строк? Надо ведь как-то проверить что разобранный идентификатор является ключевым словом.
Это проблема. Я сйчас создаю. Правда только для идентификаторов. Но генерируемый парсер может эту проблему решить. Ведь распознование строк точно так же можно сделать через ДКА. Только код ДКА может оказаться большим если строк много.
Насколько я знаю Кока тоже создает строки для идентификаторов. Но ей по фигу так как она все равно отплевывает их в поле val токена.
Еще одним решением может быть создание ручной реализации хэш-таблицы способной работать на подстроке. В приципе это не сложно, так как можно просто покоцать стандартный Dictionary<K,V>. За одно его можно ускорить если отказаться от виртуальных вызовов при рассчете хэша и сравнении.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Возникла тут одна идейка — она как раз и предполагает создание некоторой собственной хэш-таблицы. Основная суть такая — на настоящий момент мы вычитываем идентификатор до самого конца и уже только потом начинаем его искать — тут без создания строки не обойтись. Решением может быть поиск который производится по одновременно с чтением идентификатора (предполагается разумеется что хэш-таблица будет сохранять состояния и сама процедура поиска это всегда синхронная операция). Концептуально это выглядит примерно так:
class StringTable
{
private readonly ArrayList Buckets = new ArrayList();
private Bucket lastBucket;
private int lastOffset = -1;
public StringTable()
{
}
public void Add(string value)
{
if (value == null || value.Length == 0)
throw new ArgumentException();
InternalAdd(value, 0, Buckets);
}
private void InternalAdd(string value, int offset, ArrayList buckets)
{
char c = value[offset];
foreach (Bucket b in buckets)
if (b.Char == c)
{
if (offset == 2 || value.Length == offset + 1)
b.Strings.Add(value);
else
InternalAdd(value, offset++, b.Buckets);
return;
}
Bucket newBuck = new Bucket(c);
buckets.Add(newBuck);
if (offset == 2 || value.Length == offset + 1)
newBuck.Strings.Add(value);
else
InternalAdd(value, offset++, newBuck.Buckets);
}
public bool FindSegment(char c)
{
if (++lastOffset < 2)
{
foreach (Bucket b in lastBucket == null ? Buckets : lastBucket.Buckets)
if (b.Char == c)
{
lastBucket = b;
return true;
}
}
else
foreach (string s in lastBucket.Strings)
if (s.Length >= lastOffset + 1 && s[lastOffset] == c)
return true;
return false;
}
public void ResetSearch()
{
lastBucket = null;
lastOffset = -1;
}
}
class Bucket
{
public readonly char Char;
public readonly ArrayList Buckets = new ArrayList();
public readonly StringCollection Strings = new StringCollection();
public Bucket(char c)
{
Char = c;
}
}
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Чего думаешь?
Думаю, что это страдания. Эта область давно изучена наукой и в ней найдено всего два достойных решения.
1. Использования хэширования. То о чем я тебе говорил. Создать специализированную хэш-таблицу которая будет работать не с целими строками, а с подстроками. Для этого нужно раздраконить хэш-таблицу из фрэймворка.
2. Создать генератор ДКА разбирающего строку. Ради эксперемента написал генартор ДКА для произвольного набора строк. Вот его код:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using CharMap = System.Collections.Generic.Dictionary<char,
System.Collections.Generic.List<string>>;
class Program
{
static string _strs =
"using assembly event field method param property return "
+ "type typevar namespace void where class struct new partial "
+ "interface enum delegate base this ref out __arglist params "
+ "public protected internal private unsafe abstract sealed "
+ "static object string sbyte byte short ushort int uint "
+ "long ulong char extern override readonly virtual volatile const "
+ "implicit explicit operator add remove float double decimal "
+ "bool get set true false is as stackalloc checked unchecked "
+ "if else switch while do for foreach in break continue yield "
+ "throw lock fixed case default null typeof sizeof try finally "
+ "catch goto";
static void Main(string[] args)
{
File.WriteAllText(@"..\..\dfa.cs", Make.Dfa(_strs.Split(' ')));
}
}
class Make
{
public static string Dfa(string[] strs)
{
StringBuilder str = new StringBuilder(1024);
// Сортировка не обязательна. Просто так получается более читабельный код.
Array.Sort(strs);
// Генерируем класс с функцией-распознователем.
// Внутренности функции-распознователя генериуются отдельным методом.
str.AppendFormat(@"
class Dfa
{{
public static int CheckStr(string text, int start)
{{
{0}
return -1;
}}
}}
", MakeLevel(strs, 2));
return str.ToString();
}
/// <summary>
/// Рекурсивная функция генерирующая распознования одного уровня символов.
/// </summary>
/// <param name="strs">Набор строк для которых нужно сгенерировать распознование
/// </param>
/// <param name="level">Уровень вложенности. Используется как для корректноой
/// генерации отступов, так и для рассчета длинны строк.</param>
/// <returns></returns>private static string MakeLevel(string[] strs, int level)
{
StringBuilder str = new StringBuilder(1024);
CharMap charMap = MakeDic(strs);
// Генерируем проверку выхода за конец строки и switch для данного уровня.
str.Append(
@"
if (start > text.Length)
return -1;
switch(text[start])
{
");
foreach (KeyValuePair<char, List<string>> pair in charMap)
{
str.AppendFormat(
@" case '{0}':
{{
", pair.Key);
// Удаляем у всех строк в списке первый символ (мы ведь его уже
// обработали). При этом если строка пустая, то она удаляется из списка.string[] nextStrs = RemoveFirstChars(pair.Value);
// Опримизации код с целью сократить его объем.if (nextStrs.Length == 1) // если остался один альтернатива?...
{
string ident = nextStrs[0];
if (ident.Length == 1) // если осталось проверить один символ строки...
{
// Генерируем проверку одного символа строки. Если она не удачна,
// то или возвращаем -1 (строка не распознана), или длинну строки,
// если nextStrs.Length != pair.Value.Count.
// Условие nextStrs.Length != pair.Value.Count выполняется если
// на этом уровне есть строки являющиеся полной подстрокой для днугих
// строк. Например, "test" является полной подстрокой для "test1".
str.AppendFormat(
@" return ++start < text.Length && text[start] == '{0}' ? {1} : {2};
", ident[0], level, nextStrs.Length != pair.Value.Count ? level - 1 : -1);
}
else
{
// подстрока одна, но символов в ней много...
// используем для проверки функцию string.Compare().
str.AppendFormat(
@" return string.Compare(text, start + 1, ""{0}"", 0, {1}) == 0
? {2} : -1;
", ident, ident.Length, level + ident.Length - 1);
}
}
else if (nextStrs.Length > 0)
{
// Если есть алтернативы, то генерируем вложеный switch.
// Для этого вызваем эту же функцию рекурсивно.
str.Append(@" start++;
");
str.Append(MakeLevel(nextStrs, level + 1));
str.Append(" break;");
}
else// Мы распознали строку. При этом ее длинна равна level - 1.
str.AppendFormat(" return {0};", level - 1);
// Закрываем блок case-а.
str.Append(
@"
}
");
}
// Закрываем блок switch-а.
str.Append(@"}");
// Добавляем отступы.
str.Replace("\r\n", "\r\n" + new string('\t', level));
return str.ToString();
}
/// <summary>
/// Удаляет первые символы строк и копирует непустые строки в новый массив.
/// </summary>
/// <param name="strs">Исходная коллекция строк.</param>static string[] RemoveFirstChars(IList<string> strs)
{
List<string> list = new List<string>(strs.Count);
foreach (string str in strs)
if (str.Length > 1)
list.Add(str.Substring(1));
return list.ToArray();
}
/// <summary>
/// Создает словарь ключем которого является первые символы строк, а
/// значением списки строк начинающихся на этот символ.
/// </summary>
/// <param name="strs"></param>
/// <returns></returns>private static CharMap MakeDic(string[] strs)
{
CharMap map = new CharMap();
foreach (string str in strs)
{
List<string> list;
if (!map.TryGetValue(str[0], out list))
map.Add(str[0], list = new List<string>());
list.Add(str);
}
return map;
}
}
А вот тест для этого дела:
using System;
class Program
{
static void Check(string text, int start)
{
int ret = Dfa.CheckStr(text, start);
if (ret >= 0)
Console.WriteLine("В строке '{0}' распознано ключевое слово '{1}'",
text, text.Substring(start, ret));
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("В строке '{0}' нераспознано ключевых слово", text);
Console.ResetColor();
}
}
static void Main(string[] args)
{
Check(" test", 1);
Check(" test1", 1);
Check(" case", 1);
Check(" az", 1);
Check(" as", 1);
Check(" absract", 1);
Check(" abstract", 1);
Check(" abstrac", 1);
Console.ReadLine();
}
}
Этот тест генерирует ДКА для ключевых слов C# 2.0. Размер генерируемого файла 21.66 килобайт (1053 строк). В принципе довольно много, но не так чтобы смертельно много. Хорошо бы сделать тест и сравнить скорость этого дела с распознованием через хэш-таблицу (как обычную, так и специализированную). Для сравнения сканер генерируемый Кокой для полного лексера C# (Scanner.cs) имеет размер 107 кил (около 6000 строк). Но он использует хэш-таблицу для выделения ключевых слов. В общем-то если он разжиреет еще на 22К, то это мало кто заметит. Но есть языки где количество ключевых слов намного больше. Например, ассемблер. Его подстветка на рсдн, кстати, сильно тормозит.
Короче, тут нужны эксперементы. Но твоя идея явно не лучшая.
В общем, нужно или использовать генерируемый ДКА, или пользоваться идеей хэширования. Но это я говорил с самого начала.
ЗЫ
Если выбрать генерацию автомата, то наверно лучше хакать Коку. Кстати, саму генерацию лучше перевести на StringTemplates (классная библиотека используемая в ANTLR). Она сделает код значительно чище и упростит его модификацию.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Тут у тебя ошибка. Надо: start >= text.Length
VD>Этот тест генерирует ДКА для ключевых слов C# 2.0. Размер генерируемого файла 21.66 килобайт (1053 строк). В принципе довольно много, но не так чтобы смертельно много. Хорошо бы сделать тест и сравнить скорость этого дела с распознованием через хэш-таблицу (как обычную, так и специализированную). Для сравнения сканер генерируемый Кокой для полного лексера C# (Scanner.cs) имеет размер 107 кил (около 6000 строк).
Обычная хэш-таблица в 2.5-3 раза быстрее
Свой вариант тоже померил (в оптимизированной версии без энумераторов и пр.). На коротких идентификаторах раза в 2 быстрее хэш-таблицы, на длинных типа "interface" почти в два раза медленее.
Кстати, я не очень понял как поможет этот Dfa при по-символьном разборе текста. Он же хочет, чтобы ему строку передавали.
С чего бы это ошибкой стало? Если стартовый символ равен или больше длинны строки, то мы уже за ее пределами. Стало быть строка не распознана.
VD>>Этот тест генерирует ДКА для ключевых слов C# 2.0. Размер генерируемого файла 21.66 килобайт (1053 строк). В принципе довольно много, но не так чтобы смертельно много. Хорошо бы сделать тест и сравнить скорость этого дела с распознованием через хэш-таблицу (как обычную, так и специализированную). Для сравнения сканер генерируемый Кокой для полного лексера C# (Scanner.cs) имеет размер 107 кил (около 6000 строк).
ВВ>Обычная хэш-таблица в 2.5-3 раза быстрее
Это на каком объеме и на каких строках? Можно поглядеть тест?
ВВ>Свой вариант тоже померил (в оптимизированной версии без энумераторов и пр.). На коротких идентификаторах раза в 2 быстрее хэш-таблицы, на длинных типа "interface" почти в два раза медленее.
Темболее хорошо бы поглядеть на тесты.
ВВ>Кстати, я не очень понял как поможет этот Dfa при по-символьном разборе текста. Он же хочет, чтобы ему строку передавали.
ДКА просто сканирует строку символ за символом. Если мы попадаем в току где символ распознан, то можем остановиться вернув состояние и информацию о том, что мы распознали.
Алгоритмически по фигу как получается следующий символ. text[++start] можно заменить на любой вариант функции возвращающей следующий символ.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>>>@" VD>>>if (start > text.Length) VD>>> return -1;
VD>>>switch(text[start]) VD>>>{ VD>>>");
ВВ>>Тут у тебя ошибка. Надо: start >= text.Length
VD>С чего бы это ошибкой стало? Если стартовый символ равен или больше длинны строки, то мы уже за ее пределами. Стало быть строка не распознана.
Да, что-то торможу. Это же мой код с ">". Конечно ошибка.
... << RSDN@Home 1.2.0 alpha rev. 631>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.