... 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>Здравствуйте, 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 можно хоть слона загрузить и пока памяти хватает ничего не тормозит. Вообщем хочется чтоб было не хуже чем у больших дядек.
Здравствуйте, 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>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.