Улучшение обрезки email (без обрезки в блоках кода)

Я также открыл задачу на GitHub по этому поводу, но хотел разместить её и здесь, на случай если за этим следят больше людей:

Мне кажется, было бы отлично, если бы логику обрезки писем можно было улучшить, чтобы избежать обрезки внутри блоков кода. Например, в письме, содержащем:

```
# Это не должно быть удалено
#
# Или обрезано
# Это код
####
Код код код
```

Всё, что находится ниже первой ‘#’, обрезается. Это немного неудобно, так как многие используют символы комментариев для разделения секций своего кода, иногда даже в многострочных строках для вывода. Также это имеет удобное свойство: если люди хотят скопировать-вставить вывод программы в письмо, и этот вывод включает такие строки, письмо не будет обрезано в этих местах, если вывод программы заключён в обратные кавычки. Есть ли шанс, что это достаточно распространённая проблема, и кто-то сможет уделить время и посмотреть, можно ли это улучшить? Я дошёл до регулярного выражения, где происходит сопоставление, но не уверен, насколько сложно будет добавить исключение для блоков кода.

Спасибо!

Хорошо, мне пришлось немного изучить Ruby, но:

Обсуждение приветствуется!

3 лайка

Чтобы мы говорили об одном и том же, позвольте мне переформулировать вашу проблему :wink:

Вам нужно корректно обрабатывать блоки кода с ограничителями в ответах по электронной почте, особенно если они содержат символ #, который часто используется как разделитель (подписи) и поэтому обрезается.

Например, если вы отправите такой ответ по электронной почте:

Вот мой патч

```
# Это комментарий
####

answer = 42
```

Выглядит ли это нормально?

Система должна быть достаточно «умной», чтобы распознавать, что строки между ``` — это настоящий код, и поэтому их следует «игнорировать» при обычной обработке.

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

Парсинг блоков кода с ограничителями с помощью регулярных выражений довольно сложен, но для достаточно хорошего решения этот метод должен сработать:

def hoist_code_blocks(text)
  blocks = {}
  pattern = /^```\w*$\n.*?^```$/m
  
  text.gsub(pattern) do |block|
    token = SecureRandom.hex
    blocks[token] = block
    token
  end

  [text, blocks]
end

Этот метод заменит все блоки кода случайным значением и сохранит соответствие между случайным значением и содержимым блока в хеше blocks.

Вы можете вызвать его так:

text = "some text\n```ruby\ndef foo\nend\n```\nmore text"
new_text, blocks = hoist_code_blocks(text)

А затем «восстановить» блоки кода следующим кодом:

blocks.each { |token, block| new_text.gsub!(token, block) }

Спасибо за ответ! Да, вы правильно поняли проблему, с которой я столкнулся.

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

``` (много пробелов здесь) c++
int x=42;
``` (много пробелов здесь)

Это всё ещё корректно рендерится как:

int x=42;

В приведённом выше PR я попытался следовать правилам, которые смог выявить в парсере.

Ещё два вопроса по поводу вашей реализации: действительно ли это должно выполняться в preprocess!, чтобы блоки также передавались или хранились в классе EmailReplyTrimmer (и какой вариант предпочтительнее), и нет ли там ошибки? Дело в том, что возвращаемый там text идентичен исходному тексту без каких-либо замен (кажется, gsub возвращает итератор совпадений, но вы на самом деле не выполняете замену?).

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

Спасибо!

Да, всё начинает усложняться… Это выполнимо, но всегда будут пограничные случаи, в отличие от использования настоящего парсера.

Также можно использовать более 3 символов ``, где 3 — это минимум :wink:

Вы также можете сделать это в функции trim, сразу после вызова preprocess!, и выполнить шаг «постобработки» ближе к концу.

Верно, это был в основном псевдокод :sweat_smile:

Вы, вероятно, можете использовать gsub! или сделать text = text.gsub....

Отлично — я открыл новый PR здесь:

Ещё раз спасибо!

3 лайка

Обновили версию в Discourse тоже :up:

3 лайка

Эта тема была автоматически закрыта через 39 часов. Новые ответы больше не принимаются.