メールのトリミング改善(コードブロック内のトリミングなし)

これもGitHubでissueを立てましたが、より多くの人が見ている可能性があるので、こちらにも投稿しておきます。

コードブロック内でのトリミングを避けるように、メールのトリミングロジックを改善できれば素晴らしいと思います。例えば、以下のようなメールの場合:

```
# これは削除されるべきではありません
#
# またはトリミングされるべきではありません
# コードです
####
コード コード コード
```

最初の「#」より下のすべてがトリミングされてしまいます。これは少し不便です。なぜなら、多くの人がコードのセクションを区切るためにコメントマーカーを使用しており、印刷のために複数行の文字列にさえ使用することがあるからです。また、人々がプログラムの出力をメールにコピー&ペーストしたい場合に、その出力にそのような行が含まれていても、プログラムの出力が引用符で囲まれていれば、メールはそれらの行でトリミングされないという便利な機能もあります。これが十分に一般的な問題であるため、誰かが改善できるかどうかを確認する時間がある可能性はありますか?正規表現のマッチングが行われる箇所まではたどり着きましたが、コードブロックの例外を追加するのがどれほど複雑になるかはわかりません。

よろしくお願いします!

Rubyを少し学ぶ必要がありましたが、以下の通りです。

議論は歓迎します!

「いいね!」 3

確認のため、問題を言い換えますね :wink:

メールの返信で、特に(署名の)区切り文字としてよく使われ、トリミングされてしまう可能性のある「#」記号を含むFenced code blocksを正しく処理する機能が必要ということですね。

つまり、以下のようなメールの返信を送った場合

Here's my patch

```
# This is some comment
####

answer = 42
```

Does it look good?

```の間の行が実際のコードであると認識し、通常の処理から「無視」されるように「賢く」処理されるべき、ということでしょうか。

もしそうであれば、別の解決策/アプローチをお勧めします。preprocess!関数ですべてのコードブロックを**ホイスト(持ち上げ)**て、後で再度挿入するのが良いかもしれません。

Fenced code blocksは正規表現で正しく解析するのがやや難しいですが、十分な解決策としては、これが機能するはずです。

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) }

返信ありがとうございます!おっしゃる通り、私が抱えている問題を正しく理解していただいています。

私も同様のことを考えており、それが機能し、ブラウザ上のパーサーの動作にできるだけ近いのであれば、実装していただけることを嬉しく思います。例えば、ブラウザのインターフェースでは、言語宣言の前に空白を入れ、閉じタグの後にも空白を入れることができます。

int x=42;<br>
```         (多くのスペース)

これは正しく次のようにレンダリングされます。
```                    c++
int x=42;

上記のPRでは、パーサーで特定できるルールに従おうとしました。

実装に関して、さらに2つの質問があります。これは実際に preprocess! で行うべきでしょうか?そうすると、ブロックも渡されたり、EmailReplyTrimmer クラスで保持されたりする必要があるのでしょうか(どちらが望ましいですか?)、また、そこにバグがある可能性はありませんか?なぜなら、そこで返される text は、置換が行われていない元のテキストと同じだからです(gsub はマッチの列挙子を返しますが、実際にはここで置換を行っていませんか?)。

いずれにしても、もしあなたがこのコードをパーサーに追加したいのであれば、または上記の数点の問題について教えていただければ、新しいプルリクエストを作成できますので、上記のプルリクエストに追加されたテストを使用していただければ幸いです。あなたは問題を完全に正しく把握しており、あなたの解決策は近いように見えますが、どのように仕上げたいのかよくわかりません。

ありがとうございます!

ええ、複雑になってきていますね…。実現は可能ですが、実際のパーサーを使用する場合と比べて、常にエッジケースが存在することになります。

また、バッククォートは3つ以上使用することもできます。3つが最小値です :wink:

preprocess! の直後にある trim 関数で行うこともできますし、「後処理」ステップを最後の方で行うこともできます。

はい、それは主に擬似コードでした :sweat_smile:

おそらく gsub! を使用するか、text = text.gsub... のように記述できます。

承知しました。こちらで新しいPRを開きました。

重ねてお礼申し上げます!

「いいね!」 3

Discourse のバージョンも更新しました :up:

「いいね!」 3

このトピックは39時間後に自動的に閉じられました。返信はもうできません。