Как вообще реализуются текстовые редакторы?

Этот вопрос, вероятно, заставит меня показаться довольно невежественным. Это потому, что я.

Я просто думаю: если бы я был гипотетически заинтересован в разработке собственного элемента управления графическим интерфейсом текстового редактора, виджета или того, что вы хотите называть (а я не такой), как бы я вообще это сделал?

Для новичка, такого как я, возникнет соблазн хранить содержимое текстового редактора в виде строки, что кажется довольно дорогостоящим (не то чтобы я слишком хорошо знаком с тем, как строковые реализации различаются между одним языком / платформой и другим. ; но я знаю, что в .NET, например, они неизменяемы, поэтому частые манипуляции, такие как то, что вам нужно поддерживать в текстовом редакторе, были бы чрезвычайно расточительными, создавая один экземпляр строки за другим в очень быстрой последовательности).

Предположительно вместо этого используется некоторая изменяемая структура данных, содержащая текст; но выяснение того, как может выглядеть эта структура, кажется мне немного сложной задачей. Произвольный доступ был бы хорош (я бы подумал, в конце концов, разве вы не хотите, чтобы пользователь мог прыгать в любое место текста?), Но тогда я задаюсь вопросом о стоимости о, скажем, перехода к чему-то в середине огромного документа и немедленного начала ввода. Опять же, подход для новичков (скажем, вы храните текст как массив символов с изменяемым размером) приведет к очень плохой производительности, я думаю, поскольку с каждым символом, вводимым пользователем, будет огромный объем данных для «сдвига» над.

Итак, если бы мне пришлось гадать, я бы предположил, что текстовые редакторы используют какую-то структуру, которая разбивает текст на более мелкие части (может быть, строки?), Которые индивидуально составляют массивы символов с произвольным доступом и которые сами по себе являются случайными доступны как отдельные фрагменты. Даже это кажется, что это должно быть довольно чудовищное упрощение, хотя, если оно хотя бы отдаленно близко к началу.

Конечно, я также понимаю, что не может быть «стандартного» способа реализации текстовых редакторов; возможно, это сильно различается от одного редактора к другому. Но я подумал, поскольку это явно проблема, которую решали много-много раз, возможно, с годами всплыл относительно общий подход.

В любом случае, мне просто интересно узнать, есть ли у кого-нибудь какие-то знания по этой теме. Как я уже сказал, я определенно не собираюсь писать собственный текстовый редактор; Мне просто интересно.


person Dan Tao    schedule 28.10.2010    source источник
comment
Хотя это, несомненно, огромная задача, почему бы не взглянуть на исходный код vim? vim.org/sources.php   -  person KevinDTimm    schedule 28.10.2010
comment
У меня сложилось впечатление, что вы, как и я, специалист по C #, и я тоже немного исследовал это. Следует отметить, что для тяжелого редактирования текста элементы управления TextBox и RichTextBox, поставляемые с Winforms, не очень хороши. Если вы углубитесь в их реализацию, то увидите, что самые глубокие части - это просто оболочки вокруг собственных методов Windows API. Было бы намного лучше использовать какой-нибудь другой инструментарий графического интерфейса (не уверен в WPF) или самостоятельно обернуть собственные методы.   -  person CodexArcanum    schedule 28.10.2010
comment
Я думаю, что Piece Table - это структура данных, используемая для текстовых редакторов, хотя не уверен :)   -  person willcodejavaforfood    schedule 28.10.2010
comment
Связано: stackoverflow. com / questions / 1520022 / stackoverflow.com/questions/3860423/ и есть другие, хотя это сложная тема для поиска из-за всех просьб редакторов заморозить что-то как-то на какой-то платформе ...   -  person dmckee --- ex-moderator kitten    schedule 28.10.2010


Ответы (4)


Один из распространенных методов (особенно в старых редакторах) называется разделенным буфером. По сути, вы «разбиваете» текст на все, что находится перед курсором, и на все, что находится после курсора. Все, что было до, идет в начало буфера. Все после идет в конец буфера.

Когда пользователь вводит текст, он переходит в пустое пространство между ними, не перемещая никаких данных. Когда пользователь перемещает курсор, вы перемещаете соответствующее количество текста с одной стороны «разрыва» на другую. Обычно в одной области много движений, поэтому вы обычно перемещаете только небольшие объемы текста за раз. Самое большое исключение - если у вас есть возможность «перейти к строке xxx».

Чарльз Кроули написал более полное обсуждение темы . Вы также можете ознакомиться с The Craft of Text Editing, в котором рассматриваются разделенные буферы (и другие возможности) гораздо глубже.

person Jerry Coffin    schedule 28.10.2010
comment
Ни один вопрос по этой теме не должен быть без ссылки на книгу Кроули. - person dmckee --- ex-moderator kitten; 28.10.2010
comment
Мне потребовалась секунда, чтобы понять, что вы говорите (так что в основном есть значительный зазор между концами большого блока, и когда вы вставляете текст, он идет посередине, чтобы его сдвинуть в одну или другую сторону, в зависимости от того, где вы Нажмите "Далее). Это очень умно! То, что этот дизайн полагается на то, что вы не будете перемещаться вперед и назад между удаленными точками в тексте (и вставлять их), действительно кажется довольно значительным ограничением, не так ли? Опять же, я полагаю, что если он охватывает значительно более распространенный вариант использования, это определенно сильная сторона. - person Dan Tao; 28.10.2010
comment
Существует еще одно подробное руководство (буклет с инструкциями), в котором описывается реализация простого, но настоящий текстовый редактор (kilo) с подсветкой синтаксиса и поиском в 184 шага. Я не дочитал его, но он хорошо написан, включая пример кода на C. В нем также есть много полезной информации о том, как заставить терминал работать хорошо. - person Carl Smith; 11.10.2019

Некоторое время назад я написал свой собственный текстовый редактор на Tcl (на самом деле, я украл код откуда-то и расширил его до неузнаваемости, ах, чудеса открытого исходного кода).

Как вы упомянули, выполнение строковых операций с очень, очень большими строками может быть дорогостоящим. Таким образом, редактор разбивает текст на более мелкие строки в каждой новой строке («\ n», «\ r» или «\ r \ n»). Итак, все, что мне осталось, это редактировать небольшие строки на уровне строки и выполнять операции со списком при перемещении между строками.

Другое преимущество этого состоит в том, что это простая и естественная концепция. Я уже считаю, что каждая строка текста является отдельной, усиленной годами программирования, когда новые строки имеют стилистическое или синтаксическое значение.

Также помогает то, что мой текстовый редактор я использую как редактор для программистов. Например, я реализовал подсветку синтаксиса, но не перенос слов / строк. Итак, в моем случае существует карта 1: 1 между символами новой строки в тексте и линиями, нарисованными на экране.

Если вы хотите взглянуть, вот исходный код моего редактора: http://wiki.tcl.tk/16056

Кстати, это не игрушка. Я использую его ежедневно в качестве стандартного текстового редактора консоли, если только файл не слишком велик, чтобы поместиться в ОЗУ (серьезно, что это за текстовый файл? Даже романы, которые обычно имеют размер от 4 до 5 МБ, помещаются в ОЗУ. Я видел только файлы журналов вырасти до сотен МБ).

person slebetman    schedule 31.10.2010
comment
Хороший. Я скажу, что я сталкиваюсь со многими ситуациями, когда текстовые файлы не помещаются в оперативную память должным образом. На ум приходят файлы Rinex, базы данных csv и многое другое. Довольно часто заказчик присылает мне данные, которые заставляют меня использовать непонятные методы редактирования. Мне было бы интересно узнать больше о стратегиях смягчения этого. - person Chris Mauer; 19.08.2020
comment
@ChrisMauer Классическая стратегия для вещей, которые не помещаются в ОЗУ, - это редактирование на диске. Обычно это невозможно с текстовыми файлами, но текстовые процессоры, такие как старые версии Word и Wordstar, имели довольно сложные структуры данных на диске, позволяющие редактировать на диске, просто изменяя значения разметки на месте. Для этого требуется, чтобы разметки имели фиксированный размер на диске (это означает, что они не могут быть похожи на HTML, где <b> - 3 байта, а <em> - 4 байта). - person slebetman; 19.08.2020
comment
... Редактирование файла не приводит к изменению фактического содержимого на диске, только разметка, указывающая, что эта строка или область теперь устарела, и, пожалуйста, загляните в файл для фактического обновленного содержимого этой строки - вроде как git работает сегодня - person slebetman; 19.08.2020
comment
Это, конечно, не подходящая стратегия для реальных текстовых файлов, таких как csv. Но вы можете реализовать своего рода механизм рабочего диска, создав временный файл, представляющий структуру редактирования - что-то вроде базы данных для бедняков. На самом деле, теперь, когда я думаю об этом, это идеальное применение для чего-то вроде SQLite - простой бессерверной базы данных на диске. - person slebetman; 19.08.2020

В зависимости от количества текста, который должен быть в редакторе одновременно, подход с одной строкой для всего буфера, вероятно, будет подходящим. Я думаю, что Блокнот делает это - когда-нибудь замечает, насколько медленнее становится вставка текста в большой файл?

Наличие одной строки в строке в хеш-таблице кажется хорошим компромиссом. Это сделало бы навигацию к определенной строке и удаление / вставку эффективной без особых сложностей.

Если вы хотите реализовать функцию отмены, вам понадобится представление, которое позволит вам вернуться к предыдущим версиям без сохранения 30 копий всего файла для 30 изменений, хотя, опять же, это, вероятно, было бы хорошо, если бы файл был достаточно маленьким.

person Chris Smith    schedule 28.10.2010
comment
Я сомневаюсь, что подход «строка на строку» осуществим. Это больше проблема рендеринга, чем проблема моделирования данных. Это было бы очень обременительно, если вам нужны более продвинутые функции, такие как форматирование ширины документа, размер шрифта и т. Д. - person willcodejavaforfood; 28.10.2010
comment
Да, если бы было много требований к макету и форматированию, вам бы потребовался более сложный элемент макета, чем линии. Хотя это звучало как простой текстовый редактор. - person Chris Smith; 28.10.2010
comment
@willcodejavaforfood: В моем собственном текстовом редакторе по одной строке на строку. Под строкой я подразумеваю новые строки (\ n, \ r или \ r \ n), а не физические строки на экране. При этом используется то преимущество, что почти весь известный читаемый человеком текст имеет частые символы новой строки. Таким образом, мне нужно иметь дело только с небольшими строками в большом массиве (или, в моем случае, в списке). - person slebetman; 31.10.2010

Самый простой способ - использовать какой-то класс строкового буфера, предоставляемый языком. Даже простой массив объектов char подойдет в крайнем случае.

Добавление, замена и поиск текста в этом случае выполняются относительно быстро. Другие операции, конечно, потенциально занимают больше времени, а вставка последовательности символов в начало буфера является одним из наиболее дорогостоящих действий.

Однако это может быть вполне приемлемым с точки зрения производительности для простого случая использования.

Если стоимость вставки и удаления особенно велика, я бы хотел оптимизировать, создав класс-оболочку буфера, который внутренне поддерживает список буферных объектов. Любое действие (кроме простой замены), которое не произошло в конце существующего буфера, приведет к тому, что соответствующий буфер будет разделен в соответствующей точке, так что буфер может быть изменен в его конце. Однако внешняя оболочка будет поддерживать тот же интерфейс, что и простой буфер, так что мне не пришлось переписывать, например. мое действие поиска.

Конечно, этот простой подход быстро закончится чрезвычайно фрагментированным буфером, и я бы подумал о том, чтобы иметь какое-то правило для объединения буферов, когда это необходимо, или для отсрочки разделения буфера в случае, например, вставка одного символа. Может быть, правилом будет то, что у меня будет не более двух внутренних буферов, и я объединю их перед созданием нового - или когда что-то попросит меня сразу просмотреть весь буфер. Точно сказать не могу.

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

Однако я определенно не стал бы начинать с неизменяемых объектов String!

person Bill Michell    schedule 28.10.2010
comment
Да, я согласен с тем, что такой простой подход (ну, то, что вы описываете в своих первых нескольких абзацах), вероятно, будет приемлемым для небольших файлов, с тем преимуществом, что его чрезвычайно легко реализовать. Мне почти кажется, что если бы вы создали такой текстовый редактор, вашей обязанностью было бы заставить его отказываться открывать файлы, превышающие определенный размер. - person Dan Tao; 28.10.2010