이메일 잘림 개선 (코드 블록에서는 잘리지 않음)

이 문제에 대해 GitHub 이슈도 생성해 두었지만, 더 많은 분들이 보고 계실 수 있으니 이곳에도 공유합니다:

이메일 트리밍 로직을 개선하여 코드 블록 내부에서 트리밍이 일어나지 않도록 하면 좋겠습니다. 예를 들어 다음과 같은 이메일이 있는 경우:

```
# This should not be deleted
#
# Or trimmed
# It is code
####
Code code code
```

첫 번째 ‘#’ 아래에 있는 모든 내용이 트리밍됩니다. 이는 다소 불편한 점입니다. 많은 분들이 코드의 섹션을 나누기 위해 주석 마커를 사용하며, 때로는 출력용 멀티라인 문자열에서도 이를 사용하거든요. 또한 프로그램 출력을 이메일에 복사해서 붙여넣을 때, 해당 출력에 이러한 줄이 포함되어 있더라도 프로그램 출력이 따옴표(틱)로 감싸져 있다면 이메일이 그 부분에서 트리밍되지 않는다는 편리한 기능이 있습니다. 이것이 충분히 일반적인 문제여서 누군가 개선할 수 있는지 살펴볼 시간이 있을까요? 매칭이 이루어지는 정규식(regex) 부분까지는 파악했지만, 코드 블록에 대한 예외를 추가하는 것이 얼마나 복잡한지 확신이 서지 않습니다.

감사합니다!

좋아요, Ruby를 조금 배워야 했지만:

토론 환영합니다!

3개의 좋아요

같은 맥락에서 이야기하기 위해, 문제가 동일한지 확인하고자 내용을 다시 정리해 보겠습니다 :slight_smile:

이메일 답변에서 Fenced code blocks를 올바르게 처리하는 기능이 필요하신 것 같습니다. 특히 # 기호가 포함된 경우를 말하는데, 이 기호는 종종 (서명) 구분선으로 사용되어 잘려 나가기 때문입니다.

예를 들어 다음과 같은 이메일 답변을 보낸다고 가정해 보겠습니다.

Here's my patch

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

answer = 42
```

Does it look good?

이 경우, ``` 사이의 줄이 실제 코드임을 인식하여 정규 처리 과정에서 "무시"하도록 하는 “지능적인” 처리가 필요합니다.

만약 위와 같은 상황이라면, 다른 솔루션/접근 방식을 권장합니다. preprocess! 함수에서 모든 코드 블록을 hoist(상위 레벨로 추출) 한 뒤 나중에 다시 삽입하는 것이 더 나을 수 있습니다.

정규 표현식(regex)을 사용하여 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) }

답변 감사합니다! 제가 겪고 있는 문제를 정확히 파악하신 것 같습니다.

저도 비슷한 방법을 생각해 보았으며, 해당 구현이 작동하고 브라우저 내 파서의 동작과 최대한 일치한다면 그렇게 구현해 주셔도 좋습니다. 예를 들어, 브라우저 인터페이스에서는 언어 선언 전에 공백을 허용하고, 닫는 태그 이후에도 공백을 허용합니다:

``` (여기에 많은 공백) c++
int x=42;
``` (여기에 많은 공백)

아직도 올바르게 다음과 같이 렌더링됩니다:

int x=42;

위 PR에서는 파서에서 식별할 수 있었던 규칙들을 따르려고 노력했습니다.

구현 방식에 대해 추가로 질문드리고 싶은 두 가지가 있습니다. 하나는 이것이 실제로 preprocess!에서 처리되어야 하는지, 즉 블록이 EmailReplyTrimmer 클래스에 의해 전달되거나 유지되어야 하는지(그리고 이것이 선호되는 방식인지)에 대한 것이며, 다른 하나는 버그가 있을 수 있다는 점입니다. 왜냐하면 거기서 반환되는 text는 치환이 수행되지 않은 원래 텍스트와 동일하기 때문입니다(겉보기에는 gsub가 매칭된 항목의 enumerators를 반환하는 것 같지만, 실제로는 치환을 수행하지 않는 것 같습니다?).

어쨌든, 위 PR에 추가된 테스트를 사용하셔도 좋으며, 만약 파서에 이 기능을 직접 추가하시길 원하시거나, 위에서 언급한 몇 가지 문제에 대해 알려주시면 새로운 pull request를 만들 수 있습니다. 문제를 정확히 파악하신 것 같고, 당신의 해결책이 거의 완성된 것으로 보이지만, 어떻게 마무리를 원하시는지 정확히 알지 못하겠습니다.

감사합니다!

음, 점점 복잡해지고 있네요… 가능은 하지만, 실제 파서를 사용하는 경우와 달리 항상 엣지 케이스가 생길 것입니다.

또한, 최소값인 3개보다 더 많은 ` 기호를 사용할 수도 있습니다 :wink:

preprocess! 호출 직후 trim 함수에서 처리하고, “postprocess” 단계를 끝부분에서 수행할 수도 있습니다.

맞아요, 그것은 대부분 의사 코드(pseudo-code)였습니다 :sweat_smile:

gsub!를 사용하거나 text = text.gsub...를 수행할 수도 있습니다.

좋아요, 감사합니다 — 여기서 새로운 PR을 열었습니다:

다시 한번 감사합니다!

3개의 좋아요

Discourse에서도 버전을 올렸습니다 :up:

3개의 좋아요

이 주제는 39시간 후 자동으로 닫혔습니다. 더 이상 새 답글을 작성할 수 없습니다.